Organisatorisches Freitag, 10. Januar 14 Klausur - Spickzettel • Es gibt ein Google Docs File • https://docs.google.com/document/d/1- eBQcOpKYQKDsduN95Ui7CgVC0kAQTj Yq0Hpw6PsC4s/edit • Editiert es nach Belieben • In der letzten Übung stellen wir den Spickzettel zusammen Freitag, 10. Januar 14 Wiederholung • Es wird noch ein Übungsblatt geben • Besprechung ist am 23.1 • Am 30.1 mache ich in der Übung eine Wiederholung • Am 6.2 besprechen wir den Spickzettel und machen nochmal eine Fragestunde • Beginn der Übung am 30.1 und 6.2 um 8:30 Uhr Freitag, 10. Januar 14 Projekt Die Clojure Bibliothek ist euer Freund! Freitag, 10. Januar 14 Macros Freitag, 10. Januar 14 Wiederholung • Evaluationsstrategien Funktionen (Argumente rekursiv evaluieren) Macros (Argumente unevaluiert als Form) Special Forms (Spezialregeln - idR wie Macros) Quoting ' ` ~ ~@ # Macros sollen sparsam eingesetzt werden Für AOT-Berechnung Für Syntax-Zucker Macros sind Hooks in den Compiler / kleine Compiler • • • • • • • • • • Freitag, 10. Januar 14 Warum Macros? Freitag, 10. Januar 14 Once upon a time • • Freitag, 10. Januar 14 Project Coin Teil von Java 7, kleine Sprachänderungen Switch mit Strings Literale für Binärzahlen Underscores in Zahl-Literalen Multicatch Diamond Operator Try with resource • • • • • • Try with Resource public class OldTry { public static void main(String[] args) { OldResource res = null; try { res = new OldResource(); res.doSomeWork("Writing an article"); } catch (Exception e) { System.out.println(e.getMessage()); } finally{ try { res.close(); } catch (Exception e) { System.out.println(e.getMessage()); } } } } Freitag, 10. Januar 14 Try with Resource public class TryWithRes { public static void main(String[] args) { try(NewResource res = new NewResource("close msg")){ res.doSomeWork("Listening to podcast"); } catch(Exception e){ System.out.println(e.getMessage()); } } } Freitag, 10. Januar 14 So you want to change the Java Programming Language... By darcy on May 28, 2007 With the talk of closures, modules, more annotations, and other language features in the air for JDK 7, what are all the tasks that might need to happen to fully add a language feature to the platform? Besides the general advice of being open about the project's status and soliciting feedback, there are specific technical considerations for language changes. Designing language features generates a lot of interest so providing a design rationale and FAQ is especially important. Based on my experiences helping out with adding the host of language features in JDK 5, there are many interactions to consider; while some of them are obvious, others are quite surprising. The following list is not exhaustive, but everything on the list is an item to consider: Freitag, 10. Januar 14 Update the Java Language Specification. This is obviously a required task for a language change, but the JLS is a large and complicated document and it may not be immediately obvious how and where all the updates need to occur. Considering the JLS roughly chapter by chapter: ◦ How does the grammar need to be revised? ◦ How is the type system affected? ◦ Are any new conversions defined? ◦ Are naming conventions or name visibility modified? ◦ Is the existing structure of packages, classes, or interfaces changed? ◦ How can the new feature be annotated? ◦ Is method resolution impacted? ◦ How does the change impact source compatibility? ◦ How does the change impact binary compatibility? ◦ Does the feature affect the reachability of code or the definite assignment of variables? ◦ Compute the Buckley Complexity Quotient of your change. Implement the language change in a compiler. Sun's javac compiler has been open-sourced and experiments are welcome in the Kitchen Sink Language project. Be warned, the kitchen sink may have a garbage disposal. However, batch compiler support alone is not sufficient; language changes today should have IDE support too. Add any essential library support. Some language changes rely on concomitant library updates. For example, all enum types are subclasses of java.lang.Enum. Freitag, 10. Januar 14 + + --- a/make/build.properties! Thu Jul 15 16:31:56 2010 +0100 +++ b/make/build.properties! Fri Jul 16 19:35:24 2010 -0700 @@ -107,7 +107,8 @@ javac.includes = \ javax/annotation/processing/ \ javax/lang/model/ \ javax/tools/ \ com/sun/source/ com/sun/tools/javac/ + com/sun/source/ \ + com/sun/tools/javac/ /** Derived visitor method: attribute an expression tree with @@ -976,14 +995,34 @@ public class Attr extends JCTree.Visitor } javac.tests = \ tools/javac --- a/src/share/classes/com/sun/source/tree/TryTree.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/source/tree/TryTree.java! Fri Jul 16 19:35:24 2010 -0700 @@ -49,4 +49,5 @@ public interface TryTree extends Stateme BlockTree getBlock(); List<? extends CatchTree> getCatches(); BlockTree getFinallyBlock(); + List<? extends Tree> getResources(); } --- a/src/share/classes/com/sun/source/util/TreeScanner.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/source/util/TreeScanner.java! Fri Jul 16 19:35:24 2010 -0700 @@ -209,7 +209,8 @@ public class TreeScanner<R,P> implements } public R visitTry(TryTree node, P p) { R r = scan(node.getBlock(), p); R r = scan(node.getResources(), p); r = scanAndReduce(node.getBlock(), p, r); r = scanAndReduce(node.getCatches(), p, r); r = scanAndReduce(node.getFinallyBlock(), p, r); return r; --- a/src/share/classes/com/sun/tools/javac/code/Lint.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Lint.java! Fri Jul 16 19:35:24 2010 -0700 @@ -208,7 +208,12 @@ public class Lint /** * Warn about potentially unsafe vararg methods */ VARARGS("varargs"); + VARARGS("varargs"), + + /** + * Warn about arm resources + */ + ARM("arm"); + + LintCategory(String option) { this(option, false); --- a/src/share/classes/com/sun/tools/javac/code/Source.java! +++ b/src/share/classes/com/sun/tools/javac/code/Source.java! @@ -159,6 +159,9 @@ public enum Source { public boolean enforceMandatoryWarnings() { return compareTo(JDK1_5) >= 0; } + public boolean allowTryWithResources() { + return compareTo(JDK1_7) >= 0; + } public boolean allowTypeAnnotations() { return compareTo(JDK1_7) >= 0; } --- a/src/share/classes/com/sun/tools/javac/code/Symbol.java! +++ b/src/share/classes/com/sun/tools/javac/code/Symbol.java! @@ -993,12 +993,17 @@ public abstract class Symbol implements return data == ElementKind.EXCEPTION_PARAMETER; } + + + + Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 public boolean isResourceVariable() { return data == ElementKind.RESOURCE_VARIABLE; } public // if if /** The symbol representing the final finalize method on enums */ public final MethodSymbol enumFinalFinalize; /** The symbol representing the close method on TWR AutoCloseable type */ public final MethodSymbol autoCloseableClose; /** The predefined type that belongs to a tag. */ @@ -444,6 +448,12 @@ public class Symtab { suppressWarningsType = enterClass("java.lang.SuppressWarnings"); inheritedType = enterClass("java.lang.annotation.Inherited"); systemType = enterClass("java.lang.System"); + autoCloseableType = enterClass("java.lang.AutoCloseable"); + autoCloseableClose = new MethodSymbol(PUBLIC, + names.close, + new MethodType(List.<Type>nil(), voidType, + List.of(exceptionType), methodClass), + autoCloseableType.tsym); synthesizeEmptyInterfaceIfMissing(cloneableType); synthesizeEmptyInterfaceIfMissing(serializableType); --- a/src/share/classes/com/sun/tools/javac/comp/Attr.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Attr.java! Fri Jul 16 19:35:24 2010 -0700 @@ -192,7 +192,7 @@ public class Attr extends JCTree.Visitor Type check(JCTree tree, Type owntype, int ownkind, int pkind, Type pt) { if (owntype.tag != ERROR && pt.tag != METHOD && pt.tag != FORALL) { if ((ownkind & ~pkind) == 0) { owntype = chk.checkType(tree.pos(), owntype, pt); + owntype = chk.checkType(tree.pos(), owntype, pt, errKey); } else { log.error(tree.pos(), "unexpected.type", kindNames(pkind), @@ -239,7 +239,11 @@ public class Attr extends JCTree.Visitor !((base == null || (base.getTag() == JCTree.IDENT && TreeInfo.name(base) == names._this)) && isAssignableAsBlankFinal(v, env)))) { log.error(pos, "cant.assign.val.to.final.var", v); + if (v.isResourceVariable()) { //TWR resource + log.error(pos, "twr.resource.may.not.be.assigned", v); + } else { + log.error(pos, "cant.assign.val.to.final.var", v); + } } } @@ -372,6 +376,10 @@ public class Attr extends JCTree.Visitor */ Type pt; + + + + /** Visitor argument: the error key to be generated when a type error occurs */ String errKey; /** Visitor result: the computed type. */ Type result; @@ -385,13 +393,19 @@ public class Attr extends JCTree.Visitor * @param pt The prototype visitor argument. */ Type attribTree(JCTree tree, Env<AttrContext> env, int pkind, Type pt) { + return attribTree(tree, env, pkind, pt, "incompatible.types"); + } + + Type attribTree(JCTree tree, Env<AttrContext> env, int pkind, Type pt, String errKey) { Env<AttrContext> prevEnv = this.env; int prevPkind = this.pkind; Type prevPt = this.pt; + String prevErrKey = this.errKey; try { this.env = env; this.pkind = pkind; this.pt = pt; + this.errKey = errKey; tree.accept(this); if (tree == breakTree) throw new BreakAttr(env); @@ -403,6 +417,7 @@ public class Attr extends JCTree.Visitor this.env = prevEnv; this.pkind = prevPkind; this.pt = prevPt; + this.errKey = prevErrKey; } } @@ -410,6 +425,10 @@ public class Attr extends JCTree.Visitor */ public Type attribExpr(JCTree tree, Env<AttrContext> env, Type pt) { return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType); + } + Freitag, 10. Januar 14 + + + public void visitTry(JCTry tree) { // Create a new local environment with a local Env<AttrContext> localEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); boolean isTryWithResource = tree.resources.nonEmpty(); // Create a nested environment for attributing the try block if needed Env<AttrContext> tryEnv = isTryWithResource ? env.dup(tree, localEnv.info.dup(localEnv.info.scope.dup())) : localEnv; // Attribute resource declarations for (JCTree resource : tree.resources) { if (resource.getTag() == JCTree.VARDEF) { attribStat(resource, tryEnv); chk.checkType(resource, resource.type, syms.autoCloseableType, "twr.not.applicable.to.type"); VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource); var.setData(ElementKind.RESOURCE_VARIABLE); } else { attribExpr(resource, tryEnv, syms.autoCloseableType, "twr.not.applicable.to.type"); } } // Attribute body attribStat(tree.body, env.dup(tree, env.info.dup())); attribStat(tree.body, tryEnv); if (isTryWithResource) tryEnv.info.scope.leave(); // Attribute catch clauses for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { JCCatch c = l.head; Env<AttrContext> catchEnv = env.dup(c, env.info.dup(env.info.scope.dup())); + localEnv.dup(c, localEnv.info.dup(localEnv.info.scope.dup())); Type ctype = attribStat(c.param, catchEnv); if (TreeInfo.isMultiCatch(c)) { //check that multi-catch parameter is marked as final @@ -1003,7 +1042,9 @@ public class Attr extends JCTree.Visitor } // Attribute finalizer if (tree.finalizer != null) attribStat(tree.finalizer, env); if (tree.finalizer != null) attribStat(tree.finalizer, localEnv); + + + localEnv.info.scope.leave(); result = null; } @@ -2139,6 +2180,15 @@ public class Attr extends JCTree.Visitor checkAssignable(tree.pos(), v, tree.selected, env); } /** The symbol representing the length field of an array. */ @@ -158,6 +159,9 @@ public class Symtab { + + + + + + + + + + + + + + + + + + + + + Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 Object getConstValue() { TODO: Consider if getConstValue and getConstantValue can be collapsed (data == ElementKind.EXCEPTION_PARAMETER) { (data == ElementKind.EXCEPTION_PARAMETER || data == ElementKind.RESOURCE_VARIABLE) { return null; } else if (data instanceof Callable<?>) { // In this case, this is final a variable, with an as + // In this case, this is a final variable, with an as // yet unevaluated initializer. Callable<?> eval = (Callable<?>)data; data = null; // to make sure we don't evaluate this twice. --- a/src/share/classes/com/sun/tools/javac/code/Symtab.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/code/Symtab.java! Fri Jul 16 19:35:24 2010 -0700 @@ -148,6 +148,7 @@ public class Symtab { public final Type inheritedType; public final Type proprietaryType; public final Type systemType; + public final Type autoCloseableType; + + public Type attribExpr(JCTree tree, Env<AttrContext> env, Type pt, String key) { return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, key); } + + + + + + + + + if (sitesym != null && sitesym.kind == VAR && ((VarSymbol)sitesym).isResourceVariable() && sym.kind == MTH && sym.overrides(syms.autoCloseableClose, sitesym.type.tsym, types, true) && env.info.lint.isEnabled(Lint.LintCategory.ARM)) { log.warning(tree, "twr.explicit.close.call"); } // Disallow selecting a type from an expression if (isType(sym) && (sitesym==null || (sitesym.kind&(TYP|PCK)) == 0)) { tree.type = check(tree.selected, pt, --- a/src/share/classes/com/sun/tools/javac/comp/Check.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java! Fri Jul 16 19:35:24 2010 -0700 @@ -393,6 +393,10 @@ public class Check { * @param req The type that was required. */ Type checkType(DiagnosticPosition pos, Type found, Type req) { + return checkType(pos, found, req, "incompatible.types"); + } + + Type checkType(DiagnosticPosition pos, Type found, Type req, String errKey) { if (req.tag == ERROR) return req; if (found.tag == FORALL) @@ -411,7 +415,7 @@ public class Check { log.error(pos, "assignment.to.extends-bound", req); return types.createErrorType(found); } return typeError(pos, diags.fragment("incompatible.types"), found, req); + return typeError(pos, diags.fragment(errKey), found, req); } /** Instantiate polymorphic type to some prototype, unless --- a/src/share/classes/com/sun/tools/javac/comp/Flow.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Flow.java! Fri Jul 16 19:35:24 2010 -0700 @@ -28,6 +28,8 @@ package com.sun.tools.javac.comp; package com.sun.tools.javac.comp; import java.util.HashMap; +import java.util.Map; +import java.util.LinkedHashMap; import com.sun.tools.javac.code.*; import com.sun.tools.javac.tree.*; @@ -265,6 +267,10 @@ public class Flow extends TreeScanner { */ List<Type> caught; + + + + /** The list of unreferenced automatic resources. */ Map<VarSymbol, JCVariableDecl> unrefdResources; /** Set when processing a loop body the second time for DU analysis. */ boolean loopPassTwo = false; @@ -963,6 +969,7 @@ public class Flow extends TreeScanner { public void visitTry(JCTry tree) { List<Type> caughtPrev = caught; List<Type> thrownPrev = thrown; + Map<VarSymbol, JCVariableDecl> unrefdResourcesPrev = unrefdResources; thrown = List.nil(); for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { List<JCExpression> subClauses = TreeInfo.isMultiCatch(l.head) ? @@ -977,6 +984,32 @@ public class Flow extends TreeScanner { pendingExits = new ListBuffer<PendingExit>(); Bits initsTry = inits.dup(); uninitsTry = uninits.dup(); + unrefdResources = new LinkedHashMap<VarSymbol, JCVariableDecl>(); + for (JCTree resource : tree.resources) { + if (resource instanceof JCVariableDecl) { + JCVariableDecl vdecl = (JCVariableDecl) resource; + visitVarDef(vdecl); + unrefdResources.put(vdecl.sym, vdecl); + } else if (resource instanceof JCExpression) { + scanExpr((JCExpression) resource); + } else { + throw new AssertionError(tree); // parser error + } + } + for (JCTree resource : tree.resources) { + MethodSymbol topCloseMethod = (MethodSymbol)syms.autoCloseableType.tsym.members().lookup(names.close).sym; + List<Type> closeableSupertypes = resource.type.isCompound() ? + types.interfaces(resource.type).prepend(types.supertype(resource.type)) : + List.of(resource.type); + for (Type sup : closeableSupertypes) { + if (types.asSuper(sup, syms.autoCloseableType.tsym) != null) { + MethodSymbol closeMethod = types.implementation(topCloseMethod, sup.tsym, types, true); + for (Type t : closeMethod.getThrownTypes()) { + markThrown(tree.body, t); + } + } + } + } scanStat(tree.body); List<Type> thrownInTry = thrown; thrown = thrownPrev; @@ -986,6 +1019,14 @@ public class Flow extends TreeScanner { Bits initsEnd = inits; Bits uninitsEnd = uninits; int nextadrCatch = nextadr; + + if (!unrefdResources.isEmpty() && + lint.isEnabled(Lint.LintCategory.ARM)) { + for (Map.Entry<VarSymbol, JCVariableDecl> e : unrefdResources.entrySet()) { + log.warning(e.getValue().pos(), + "automatic.resource.not.referenced", e.getKey()); + } + } List<Type> caughtInTry = List.nil(); for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { @@ -1070,6 +1111,7 @@ public class Flow extends TreeScanner { while (exits.nonEmpty()) pendingExits.append(exits.next()); } uninitsTry.andSet(uninitsTryPrev).andSet(uninits); + unrefdResources = unrefdResourcesPrev; } public void visitConditional(JCConditional tree) { @@ -1293,8 +1335,16 @@ public class Flow extends TreeScanner { } + + + + + + public void visitIdent(JCIdent tree) { if (tree.sym.kind == VAR) if (tree.sym.kind == VAR) { checkInit(tree.pos(), (VarSymbol)tree.sym); referenced(tree.sym); } } void referenced(Symbol sym) { + + + if (unrefdResources != null && unrefdResources.containsKey(sym)) { unrefdResources.remove(sym); } } public void visitTypeCast(JCTypeCast tree) { --- a/src/share/classes/com/sun/tools/javac/comp/Lower.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/comp/Lower.java! Fri Jul 16 19:35:24 2010 -0700 @@ -603,6 +603,23 @@ public class Lower extends TreeTranslato */ private void enterSynthetic(DiagnosticPosition pos, Symbol sym, Scope s) { s.enter(sym); + } + + /** Create a fresh synthetic name within a given scope - the unique name is + * obtained by appending '$' chars at the end of the name until no match + * is found. + * + * @param name base name + * @param s scope in which the name has to be unique + * @return fresh synthetic name + */ + private Name makeSyntheticName(Name name, Scope s) { + do { + name = name.append( + target.syntheticNameChar(), + names.empty); + } while (lookupSynthetic(name, s) != null); + return name; } /** Check whether synthetic symbols generated during lowering conflict @@ -1299,6 +1316,11 @@ public class Lower extends TreeTranslato */ Scope proxies; + + + + + /** A scope containing all unnamed resource variables/saved * exception variables for translated TWR blocks */ Scope twrVars; /** A stack containing the this$n field of the currently translated * classes (if needed) in innermost first order. * Inside a constructor, proxies and any this$n symbol are duplicated @@ -1398,6 +1420,122 @@ public class Lower extends TreeTranslato // need to go via this$n return makeOuterThis(pos, c); } + } + + /** Optionally replace a try statement with an automatic resource + * management (ARM) block. + * @param tree The try statement to inspect. + * @return An ARM block, or the original try block if there are no + * resources to manage. + */ + JCTree makeArmTry(JCTry tree) { + make_at(tree.pos()); + twrVars = twrVars.dup(); + JCBlock armBlock = makeArmBlock(tree.resources, tree.body, 0); + if (tree.catchers.isEmpty() && tree.finalizer == null) + result = translate(armBlock); + else + result = translate(make.Try(armBlock, tree.catchers, tree.finalizer)); + twrVars = twrVars.leave(); + return result; + } + + private JCBlock makeArmBlock(List<JCTree> resources, JCBlock block, int depth) { + if (resources.isEmpty()) + return block; + + // Add resource declaration or expression to block statements + ListBuffer<JCStatement> stats = new ListBuffer<JCStatement>(); + JCTree resource = resources.head; + JCExpression expr = null; + if (resource instanceof JCVariableDecl) { + JCVariableDecl var = (JCVariableDecl) resource; + expr = make.Ident(var.sym).setType(resource.type); + stats.add(var); + } else { + assert resource instanceof JCExpression; + VarSymbol syntheticTwrVar = + new VarSymbol(SYNTHETIC | FINAL, + makeSyntheticName(names.fromString("twrVar" + + depth), twrVars), + (resource.type.tag == TypeTags.BOT) ? + syms.autoCloseableType : resource.type, + currentMethodSym); + twrVars.enter(syntheticTwrVar); + JCVariableDecl syntheticTwrVarDecl = + make.VarDef(syntheticTwrVar, (JCExpression)resource); + expr = (JCExpression)make.Ident(syntheticTwrVar); + stats.add(syntheticTwrVarDecl); + } + + // Add primaryException declaration + VarSymbol primaryException = + new VarSymbol(SYNTHETIC, + makeSyntheticName(names.fromString("primaryException" + + depth), twrVars), + syms.throwableType, + currentMethodSym); + twrVars.enter(primaryException); + JCVariableDecl primaryExceptionTreeDecl = make.VarDef(primaryException, makeNull()); + stats.add(primaryExceptionTreeDecl); + + // Create catch clause that saves exception and then rethrows it + VarSymbol param = + new VarSymbol(FINAL|SYNTHETIC, + names.fromString("t" + + target.syntheticNameChar()), + syms.throwableType, + currentMethodSym); + JCVariableDecl paramTree = make.VarDef(param, null); + JCStatement assign = make.Assignment(primaryException, make.Ident(param)); + JCStatement rethrowStat = make.Throw(make.Ident(param)); + JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(assign, rethrowStat)); + JCCatch catchClause = make.Catch(paramTree, catchBlock); + + int oldPos = make.pos; + make.at(TreeInfo.endPos(block)); + JCBlock finallyClause = makeArmFinallyClause(primaryException, expr); + make.at(oldPos); + JCTry outerTry = make.Try(makeArmBlock(resources.tail, block, depth + 1), + List.<JCCatch>of(catchClause), + finallyClause); + stats.add(outerTry); + return make.Block(0L, stats.toList()); + } + + private JCBlock makeArmFinallyClause(Symbol primaryException, JCExpression resource) { + // primaryException.addSuppressedException(catchException); + VarSymbol catchException = + new VarSymbol(0, make.paramName(2), + syms.throwableType, + currentMethodSym); + JCStatement addSuppressionStatement = + make.Exec(makeCall(make.Ident(primaryException), + names.fromString("addSuppressedException"), + List.<JCExpression>of(make.Ident(catchException)))); + + // try { resource.close(); } catch (e) { primaryException.addSuppressedException(e); } + JCBlock tryBlock = + make.Block(0L, List.<JCStatement>of(makeResourceCloseInvocation(resource))); + JCVariableDecl catchExceptionDecl = make.VarDef(catchException, null); + JCBlock catchBlock = make.Block(0L, List.<JCStatement>of(addSuppressionStatement)); + List<JCCatch> catchClauses = List.<JCCatch>of(make.Catch(catchExceptionDecl, catchBlock)); + JCTry tryTree = make.Try(tryBlock, catchClauses, null); + + // if (resource != null) resourceClose; + JCExpression nullCheck = makeBinary(JCTree.NE, + make.Ident(primaryException), + makeNull()); + JCIf closeIfStatement = make.If(nullCheck, + tryTree, + makeResourceCloseInvocation(resource)); + return make.Block(0L, List.<JCStatement>of(closeIfStatement)); + } + + private JCStatement makeResourceCloseInvocation(JCExpression resource) { + // create resource.close() method invocation + JCExpression resourceClose = makeCall(resource, names.close, List.<JCExpression>nil()); + return make.Exec(resourceClose); } /** Construct a tree that represents the outer instance @@ -3405,6 +3543,15 @@ public class Lower extends TreeTranslato result = tree; } + + + + + + + + + @Override public void visitTry(JCTry tree) { if (tree.resources.isEmpty()) { super.visitTry(tree); } else { result = makeArmTry(tree); } } /************************************************************************** * main method *************************************************************************/ @@ -3430,6 +3577,7 @@ public class Lower extends TreeTranslato actualSymbols = new HashMap<Symbol,Symbol>(); freevarCache = new HashMap<ClassSymbol,List<VarSymbol>>(); proxies = new Scope(syms.noSymbol); + twrVars = new Scope(syms.noSymbol); outerThisStack = List.nil(); accessNums = new HashMap<Symbol,Integer>(); accessSyms = new HashMap<Symbol,MethodSymbol[]>(); --- a/src/share/classes/com/sun/tools/javac/comp/TransTypes.java! +++ b/src/share/classes/com/sun/tools/javac/comp/TransTypes.java! @@ -535,6 +535,14 @@ public class TransTypes extends TreeTran result = tree; } + + + + + + + + Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 public void visitTry(JCTry tree) { tree.resources = translate(tree.resources, syms.autoCloseableType); tree.body = translate(tree.body); tree.catchers = translateCatchers(tree.catchers); tree.finalizer = translate(tree.finalizer); result = tree; } public void visitConditional(JCConditional tree) { tree.cond = translate(tree.cond, syms.booleanType); tree.truepart = translate(tree.truepart, erasure(tree.type)); --- a/src/share/classes/com/sun/tools/javac/jvm/CRTable.java! +++ b/src/share/classes/com/sun/tools/javac/jvm/CRTable.java! @@ -325,6 +325,7 @@ implements CRTFlags { public void visitTry(JCTry tree) { SourceRange sr = new SourceRange(startPos(tree), endPos(tree)); sr.mergeWith(csp(tree.resources)); sr.mergeWith(csp(tree.body)); sr.mergeWith(cspCatchers(tree.catchers)); sr.mergeWith(csp(tree.finalizer)); --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java! +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java! @@ -131,6 +131,7 @@ public class JavacParser implements Pars this.allowForeach = source.allowForeach(); this.allowStaticImport = source.allowStaticImport(); this.allowAnnotations = source.allowAnnotations(); + this.allowTWR = source.allowTryWithResources(); this.allowDiamond = source.allowDiamond(); this.allowMulticatch = source.allowMulticatch(); this.allowTypeAnnotations = source.allowTypeAnnotations(); @@ -185,6 +186,10 @@ public class JavacParser implements Pars /** Switch: should we recognize type annotations? */ boolean allowTypeAnnotations; + + /** Switch: should we recognize automatic resource management? + */ + boolean allowTWR; Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 + Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 /** Switch: should we keep docComments? */ @@ -1846,6 +1851,7 @@ public class JavacParser implements Pars * | WHILE ParExpression Statement * | DO Statement WHILE ParExpression ";" * | TRY Block ( Catches | [Catches] FinallyPart ) + * | TRY "(" ResourceSpecification ")" Block [Catches] [FinallyPart] * | SWITCH ParExpression "{" SwitchBlockStatementGroups "}" * | SYNCHRONIZED ParExpression Block * | RETURN [Expression] ";" @@ -1916,6 +1922,13 @@ public class JavacParser implements Pars } case TRY: { S.nextToken(); + List<JCTree> resources = List.<JCTree>nil(); + if (S.token() == LPAREN) { + checkAutomaticResourceManagement(); + S.nextToken(); + resources = resources(); + accept(RPAREN); + } JCBlock body = block(); ListBuffer<JCCatch> catchers = new ListBuffer<JCCatch>(); JCBlock finalizer = null; @@ -1926,9 +1939,13 @@ public class JavacParser implements Pars finalizer = block(); } } else { log.error(pos, "try.without.catch.or.finally"); } return F.at(pos).Try(body, catchers.toList(), finalizer); + if (allowTWR) { + if (resources.isEmpty()) + log.error(pos, "try.without.catch.finally.or.resource.decls"); + } else + log.error(pos, "try.without.catch.or.finally"); + } + return F.at(pos).Try(resources, body, catchers.toList(), finalizer); } case SWITCH: { S.nextToken(); @@ -2387,6 +2404,39 @@ public class JavacParser implements Pars if ((mods.flags & Flags.VARARGS) == 0) type = bracketsOpt(type); return toP(F.at(pos).VarDef(mods, name, type, null)); + } + + /** Resources = Resource { ";" Resources } + */ + List<JCTree> resources() { + ListBuffer<JCTree> defs = new ListBuffer<JCTree>(); + defs.append(resource()); + while (S.token() == SEMI) { + // All but last of multiple declarators subsume a semicolon + storeEnd(defs.elems.last(), S.endPos()); + S.nextToken(); + defs.append(resource()); + } + return defs.toList(); + } + + /** Resource = + * VariableModifiers Type VariableDeclaratorId = Expression + * | Expression + */ + JCTree resource() { + int pos = S.pos(); + if (S.token() == FINAL || S.token() == MONKEYS_AT) { + return variableDeclaratorRest(pos, optFinal(0), parseType(), + ident(), true, null); + } else { + JCExpression t = term(EXPR | TYPE); + if ((lastmode & TYPE) != 0 && S.token() == IDENTIFIER) + return variableDeclaratorRest(pos, toP(F.at(pos).Modifiers(Flags.FINAL)), t, + ident(), true, null); + else + return t; + } } /** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration} @@ -3220,6 +3270,12 @@ public class JavacParser implements Pars if (!allowMulticatch) { log.error(S.pos(), "multicatch.not.supported.in.source", source.name); allowMulticatch = true; } + } + } + void checkAutomaticResourceManagement() { + if (!allowTWR) { + log.error(S.pos(), "automatic.resource.management.not.supported.in.source", source.name); + allowTWR = true; + } } } --- a/src/share/classes/com/sun/tools/javac/resources/compiler.properties! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/resources/compiler.properties! Fri Jul 16 19:35:24 2010 -0700 @@ -61,6 +61,8 @@ compiler.err.anon.class.impl.intf.no.typ anonymous class implements interface; cannot have type arguments compiler.err.anon.class.impl.intf.no.qual.for.new=\ anonymous class implements interface; cannot have qualifier for new +compiler.misc.twr.not.applicable.to.type=\ + automatic resource management not applicable to variable type compiler.err.array.and.varargs=\ cannot declare both {0} and {1} in {2} compiler.err.array.dimension.missing=\ @@ -172,6 +174,8 @@ compiler.err.except.never.thrown.in.try= compiler.err.final.parameter.may.not.be.assigned=\ final parameter {0} may not be assigned +compiler.err.twr.resource.may.not.be.assigned=\ + automatic resource {0} may not be assigned compiler.err.multicatch.parameter.may.not.be.assigned=\ multi-catch parameter {0} may not be assigned compiler.err.multicatch.param.must.be.final=\ @@ -448,6 +452,8 @@ compiler.err.throws.not.allowed.in.intf. throws clause not allowed in @interface members compiler.err.try.without.catch.or.finally=\ ''try'' without ''catch'' or ''finally'' +compiler.err.try.without.catch.finally.or.resource.decls=\ + ''try'' without ''catch'', ''finally'' or resource declarations compiler.err.type.doesnt.take.params=\ type {0} does not take parameters compiler.err.type.var.cant.be.deref=\ @@ -797,6 +803,10 @@ compiler.warn.proc.unmatched.processor.o compiler.warn.proc.unmatched.processor.options=\ The following options were not recognized by any processor: ''{0}'' +compiler.warn.twr.explicit.close.call=\ + [arm] explicit call to close() on an automatic resource +compiler.warn.automatic.resource.not.referenced=\ + [arm] automatic resource {0} is never referenced in body of corresponding try statement compiler.warn.unchecked.assign=\ [unchecked] unchecked assignment: {0} to {1} compiler.warn.unchecked.assign.to.var=\ @@ -1217,6 +1227,10 @@ compiler.err.unsupported.underscore.lit= underscores in literals are not supported in -source {0}\n\ 1 (use -source 7 or higher to enable underscores in literals) +compiler.err.automatic.resource.management.not.supported.in.source=\ + automatic resource management is not supported in -source {0}\n\ +(use -source 7 or higher to enable automatic resource management) + compiler.warn.enum.as.identifier=\ as of release 5, ''enum'' is a keyword, and may not be used as an identifier\n\ (use -source 5 or higher to use ''enum'' as a keyword) --- a/src/share/classes/com/sun/tools/javac/tree/JCTree.java! Thu Jul +++ b/src/share/classes/com/sun/tools/javac/tree/JCTree.java! Fri Jul @@ -1021,10 +1021,15 @@ public abstract class JCTree implements public JCBlock body; public List<JCCatch> catchers; public JCBlock finalizer; protected JCTry(JCBlock body, List<JCCatch> catchers, JCBlock finalizer) { + public List<JCTree> resources; + protected JCTry(List<JCTree> resources, + JCBlock body, + List<JCCatch> catchers, + JCBlock finalizer) { this.body = body; this.catchers = catchers; this.finalizer = finalizer; + this.resources = resources; } @Override public void accept(Visitor v) { v.visitTry(this); } @@ -1038,6 +1043,10 @@ public abstract class JCTree implements @Override public <R,D> R accept(TreeVisitor<R,D> v, D d) { return v.visitTry(this, d); + } + @Override + public List<? extends JCTree> getResources() { + return resources; } @Override public int getTag() { @@ -2162,6 +2171,10 @@ public abstract class JCTree implements JCCase Case(JCExpression pat, List<JCStatement> stats); JCSynchronized Synchronized(JCExpression lock, JCBlock body); JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer); + JCTry Try(List<JCTree> resources, + JCBlock body, + List<JCCatch> catchers, + JCBlock finalizer); JCCatch Catch(JCVariableDecl param, JCBlock body); JCConditional Conditional(JCExpression cond, JCExpression thenpart, --- a/src/share/classes/com/sun/tools/javac/tree/Pretty.java! Thu Jul +++ b/src/share/classes/com/sun/tools/javac/tree/Pretty.java! Fri Jul @@ -691,6 +691,19 @@ public class Pretty extends JCTree.Visit public void visitTry(JCTry tree) { try { print("try "); + if (tree.resources.nonEmpty()) { + print("("); + boolean first = true; + for (JCTree var : tree.resources) { + if (!first) { + println(); + indent(); + } + printStat(var); + first = false; + } + print(") "); + } printStat(tree.body); for (List<JCCatch> l = tree.catchers; l.nonEmpty(); l = l.tail) { printStat(l.head); --- a/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java! Thu Jul +++ b/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java! Fri Jul @@ -332,10 +332,11 @@ public class TreeCopier<P> implements Tr + + 15 16:31:56 2010 +0100 16 19:35:24 2010 -0700 15 16:31:56 2010 +0100 16 19:35:24 2010 -0700 public JCTree visitTry(TryTree node, P p) { JCTry t = (JCTry) node; List<JCTree> resources = copy(t.resources, p); JCBlock body = copy(t.body, p); List<JCCatch> catchers = copy(t.catchers, p); JCBlock finalizer = copy(t.finalizer, p); return M.at(t.pos).Try(body, catchers, finalizer); return M.at(t.pos).Try(resources, body, catchers, finalizer); } public JCTree visitParameterizedType(ParameterizedTypeTree node, P p) { --- a/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java! +++ b/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java! @@ -269,7 +269,14 @@ public class TreeMaker implements JCTree } + + + + + + + + 15 16:31:56 2010 +0100 16 19:35:24 2010 -0700 Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 public JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer) { JCTry tree = new JCTry(body, catchers, finalizer); return Try(List.<JCTree>nil(), body, catchers, finalizer); } public JCTry Try(List<JCTree> resources, JCBlock body, List<JCCatch> catchers, JCBlock finalizer) { JCTry tree = new JCTry(resources, body, catchers, finalizer); tree.pos = pos; return tree; } --- a/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java! +++ b/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java! @@ -147,6 +147,7 @@ public class TreeScanner extends Visitor } public void visitTry(JCTry tree) { scan(tree.resources); scan(tree.body); scan(tree.catchers); scan(tree.finalizer); --- a/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java! +++ b/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java! @@ -212,6 +212,7 @@ public class TreeTranslator extends JCTr } Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 + Thu Jul 15 16:31:56 2010 +0100 Fri Jul 16 19:35:24 2010 -0700 public void visitTry(JCTry tree) { tree.resources = translate(tree.resources); tree.body = translate(tree.body); tree.catchers = translateCatchers(tree.catchers); tree.finalizer = translate(tree.finalizer); --- a/src/share/classes/com/sun/tools/javac/util/Names.java! Thu Jul 15 16:31:56 2010 +0100 +++ b/src/share/classes/com/sun/tools/javac/util/Names.java! Fri Jul 16 19:35:24 2010 -0700 @@ -148,6 +148,8 @@ public class Names { public final Name getDeclaringClass; public final Name ex; public final Name finalize; + public final Name java_lang_AutoCloseable; + public final Name close; + public final Name.Table table; @@ -263,6 +265,9 @@ public class Names { getDeclaringClass = fromString("getDeclaringClass"); ex = fromString("ex"); finalize = fromString("finalize"); + + java_lang_AutoCloseable = fromString("java.lang.AutoCloseable"); + close = fromString("close"); } protected Name.Table createTable(Options options) { --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ArmLint.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,55 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 6967065 + * @author Joseph D. Darcy + * @summary Check that -Xlint:arm warnings are generated as expected + * @compile/ref=ArmLint.out -Xlint:arm,deprecation -XDrawDiagnostics ArmLint.java + */ + +class ArmLint implements AutoCloseable { + private static void test1() { + try(ArmLint r1 = new ArmLint(); + ArmLint r2 = new ArmLint(); + ArmLint r3 = new ArmLint()) { + r1.close(); // The resource's close + r2.close(42); // *Not* the resource's close + // r3 not referenced + } + + } + + @SuppressWarnings("arm") + private static void test2() { + try(@SuppressWarnings("deprecation") AutoCloseable r4 = + new DeprecatedAutoCloseable()) { + // r4 not referenced + } catch(Exception e) { + ; + } + } + + /** + * The AutoCloseable method of a resource. + */ + @Override + public void close () { + return; + } + + /** + * <em>Not</em> the AutoCloseable method of a resource. Freitag, 10. Januar 14 + */ + public void close (int arg) { + return; + } +} + +@Deprecated +class DeprecatedAutoCloseable implements AutoCloseable { + public DeprecatedAutoCloseable(){super();} + + @Override + public void close () { + return; + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ArmLint.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,3 @@ +ArmLint.java:14:15: compiler.warn.twr.explicit.close.call +ArmLint.java:13:13: compiler.warn.automatic.resource.not.referenced: r3 +2 warnings --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/BadTwr.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,36 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify bad TWRs don't compile + * @compile/fail -source 6 TwrFlow.java + * @compile/fail/ref=BadTwr.out -XDrawDiagnostics BadTwr.java + */ + +public class BadTwr implements AutoCloseable { + public static void main(String... args) { + // illegal repeated name + try(BadTwr r1 = new BadTwr(); BadTwr r1 = new BadTwr()) { + System.out.println(r1.toString()); + } + + // illegal duplicate name of method argument + try(BadTwr args = new BadTwr()) { + System.out.println(args.toString()); + final BadTwr thatsIt = new BadTwr(); + thatsIt = null; + } + + try(BadTwr name = new BadTwr()) { + // illegal duplicate name of enclosing try + try(BadTwr name = new BadTwr()) { + System.out.println(name.toString()); + } + } + + } + + public void close() { + ; + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/BadTwr.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,5 @@ +BadTwr.java:13:39: compiler.err.already.defined: r1, main(java.lang.String...) +BadTwr.java:18:13: compiler.err.already.defined: args, main(java.lang.String...) +BadTwr.java:21:13: compiler.err.cant.assign.val.to.final.var: thatsIt +BadTwr.java:26:17: compiler.err.already.defined: name, main(java.lang.String...) +4 errors --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/BadTwrSyntax.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify bad TWRs don't compile + * @compile/fail -source 6 BadTwrSyntax.java + * @compile/fail/ref=BadTwrSyntax.out -XDrawDiagnostics BadTwrSyntax.java + */ + +import java.io.IOException; +public class BadTwrSyntax implements AutoCloseable { + public static void main(String... args) throws Exception { + // illegal semicolon ending resources + try(BadTwr twrflow = new BadTwr();) { + System.out.println(twrflow.toString()); + } + } + + public void close() { + ; + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/BadTwrSyntax.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,2 @@ +BadTwrSyntax.java:14:43: compiler.err.illegal.start.of.expr +1 error --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/DuplicateResource.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Check that lowered arm block does not end up creating resource twice + */ + +import java.util.ArrayList; + +public class DuplicateResource { + + static class TestResource implements AutoCloseable { + TestResource() { + resources.add(this); + } + boolean isClosed = false; + public void close() throws Exception { + isClosed = true; + } + } + + static ArrayList<TestResource> resources = new ArrayList<TestResource>(); + + public static void main(String[] args) { + try(new TestResource()) { + //do something + } catch (Exception e) { + throw new AssertionError("Shouldn't reach here", e); + } + check(); + } + + public static void check() { + if (resources.size() != 1) { + throw new AssertionError("Expected one resource, found: " + resources.size()); + } + TestResource resource = resources.get(0); + if (!resource.isClosed) { + throw new AssertionError("Resource used in ARM block has not been automatically closed"); + } + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/DuplicateResourceDecl.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,20 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Check that resource variable is not accessible from catch/finally clause + * @compile/fail/ref=DuplicateResourceDecl.out -XDrawDiagnostics DuplicateResourceDecl.java + */ + +class DuplicateResourceDecl { + + public static void main(String[] args) { + try(MyResource c = new MyResource();MyResource c = new MyResource()) { + //do something + } catch (Exception e) { } + } + + static class MyResource implements AutoCloseable { + public void close() throws Exception {} + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/DuplicateResourceDecl.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,2 @@ +DuplicateResourceDecl.java:12:45: compiler.err.already.defined: c, main(java.lang.String[]) +1 error --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ImplicitFinal.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,27 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Test that resource variables are implicitly final + * @compile/fail/ref=ImplicitFinal.out -XDrawDiagnostics ImplicitFinal.java + */ + +import java.io.IOException; + +class ImplicitFinal implements AutoCloseable { + public static void main(String... args) { + try(ImplicitFinal r = new ImplicitFinal()) { + r = null; //disallowed + } catch (IOException ioe) { // Not reachable + throw new AssertionError("Shouldn't reach here", ioe); + } + } + + + // A close method, but the class is <em>not</em> Closeable or + // AutoCloseable. + + public void close() throws IOException { + throw new IOException(); + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ImplicitFinal.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,2 @@ +ImplicitFinal.java:14:13: compiler.err.twr.resource.may.not.be.assigned: r +1 error --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/PlainTry.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,15 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Test error messages for an unadorned try + * @compile/fail/ref=PlainTry6.out -XDrawDiagnostics -source 6 PlainTry.java + * @compile/fail/ref=PlainTry.out -XDrawDiagnostics PlainTry.java + */ +public class PlainTry { + public static void main(String... args) { + try { + ; + } + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/PlainTry.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,2 @@ +PlainTry.java:11:9: compiler.err.try.without.catch.finally.or.resource.decls +1 error --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/PlainTry6.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,2 @@ +PlainTry.java:11:9: compiler.err.try.without.catch.or.finally +1 error --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ResourceOutsideTry.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,23 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Check that resource variable is not accessible from catch/finally clause + * @compile/fail/ref=ResourceOutsideTry.out -XDrawDiagnostics ResourceOutsideTry.java + */ + +class ResourceOutsideTry { + void test() { + try(MyResource c = new MyResource()) { + //do something + } catch (Exception e) { + c.test(); + } finally { + c.test(); + } + } + static class MyResource implements AutoCloseable { + public void close() throws Exception {} + void test() {} + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ResourceOutsideTry.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,3 @@ +ResourceOutsideTry.java:14:13: compiler.err.cant.resolve.location: kindname.variable, c, , , kindname.class, ResourceOutsideTry +ResourceOutsideTry.java:16:13: compiler.err.cant.resolve.location: kindname.variable, c, , , kindname.class, ResourceOutsideTry +2 errors --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/ResourceTypeVar.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Resource of a type-variable type crashes Flow + * @compile ResourceTypeVar.java + */ + +class ResourceTypeVar<X extends AutoCloseable> { + + public void test() { + try(X armflow = getX()) { + //do something + } catch (Exception e) { // Not reachable + throw new AssertionError("Shouldn't reach here", e); + } + } + + X getX() { return null; } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrFlow.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,39 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Test exception analysis of ARM blocks + * @compile/fail/ref=TwrFlow.out -XDrawDiagnostics TwrFlow.java + */ + +import java.io.IOException; +public class TwrFlow implements AutoCloseable { + public static void main(String... args) { + try(TwrFlow armflow = new TwrFlow()) { + System.out.println(armflow.toString()); + } catch (IOException ioe) { // Not reachable + throw new AssertionError("Shouldn't reach here", ioe); + } + // CustomCloseException should be caught or added to throws clause + + // Also check behavior on a resource expression rather than a + // declaration. + TwrFlow armflowexpr = new TwrFlow(); + try(armflowexpr) { + System.out.println(armflowexpr.toString()); + } catch (IOException ioe) { // Not reachable + throw new AssertionError("Shouldn't reach here", ioe); + } + // CustomCloseException should be caught or added to throws clause + } + + /* + * A close method, but the class is <em>not</em> Closeable or + * AutoCloseable. + */ + public void close() throws CustomCloseException { + throw new CustomCloseException(); + } +} + +class CustomCloseException extends Exception {} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrFlow.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,5 @@ +TwrFlow.java:14:11: compiler.err.except.never.thrown.in.try: java.io.IOException +TwrFlow.java:24:11: compiler.err.except.never.thrown.in.try: java.io.IOException +TwrFlow.java:12:46: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException +TwrFlow.java:22:26: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException +4 errors --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrInference.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Verify that method type-inference works as expected in TWR context + * @compile TwrInference.java + */ + +class TwrInference { + + public void test() { + try(getX()) { + //do something + } catch (Exception e) { // Not reachable + throw new AssertionError("Shouldn't reach here", e); + } + } + + <X> X getX() { return null; } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrIntersection.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Resource of an intersection type crashes Flow + * @compile TwrIntersection.java + */ + +interface MyCloseable extends AutoCloseable { + void close() throws java.io.IOException; +} + +class ResourceTypeVar { + + public void test() { + try(getX()) { + //do something + } catch (java.io.IOException e) { // Not reachable + throw new AssertionError("Shouldn't reach here", e); + } + } + + <X extends Number & MyCloseable> X getX() { return null; } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrIntersection02.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,37 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Check that resources of an intersection type forces union of exception types + * to be caught outside twr block + * @compile/fail/ref=TwrIntersection02.out -XDrawDiagnostics TwrIntersection02.java + */ + +class TwrIntersection02 { + + static class Exception1 extends Exception {} + static class Exception2 extends Exception {} + + + interface MyResource1 extends AutoCloseable { + void close() throws Exception1; + } + + interface MyResource2 extends AutoCloseable { + void close() throws Exception2; + } + + public void test1() throws Exception1 { + try(getX()) { + //do something + } + } + + public void test2() throws Exception2 { + try(getX()) { + //do something + } + } + + <X extends MyResource1 & MyResource2> X getX() { return null; } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrIntersection02.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,3 @@ +TwrIntersection02.java:25:21: compiler.err.unreported.exception.need.to.catch.or.throw: TwrIntersection02.Exception2 +TwrIntersection02.java:31:21: compiler.err.unreported.exception.need.to.catch.or.throw: TwrIntersection02.Exception1 +2 errors --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrMultiCatch.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Test that TWR and multi-catch play well together + * @compile TwrMultiCatch.java + * @run main TwrMultiCatch + */ + 2 +import java.io.IOException; +public class TwrMultiCatch implements AutoCloseable { + private final Class<? extends Exception> exceptionClass; + + private TwrMultiCatch(Class<? extends Exception> exceptionClass) { + this.exceptionClass = exceptionClass; + } + + public static void main(String... args) { + test(new TwrMultiCatch(CustomCloseException1.class), + CustomCloseException1.class); + + test(new TwrMultiCatch(CustomCloseException2.class), + CustomCloseException2.class); + } + + private static void test(TwrMultiCatch twrMultiCatch, + Class<? extends Exception> expected) { + try(twrMultiCatch) { + System.out.println(twrMultiCatch.toString()); + } catch (final CustomCloseException1 | + CustomCloseException2 exception) { + if (!exception.getClass().equals(expected) ) { + throw new RuntimeException("Unexpected catch!"); + } + } + } + + public void close() throws CustomCloseException1, CustomCloseException2 { + Throwable t; + try { + t = exceptionClass.newInstance(); + } catch(ReflectiveOperationException rfe) { + throw new RuntimeException(rfe); + } + + try { + throw t; + } catch (final CustomCloseException1 | + CustomCloseException2 exception) { + throw exception; + } catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } +} + +class CustomCloseException1 extends Exception {} +class CustomCloseException2 extends Exception {} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrOnNonResource.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,42 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify invalid TWR block is not accepted. + * @compile/fail -source 6 TwrOnNonResource.java + * @compile/fail/ref=TwrOnNonResource.out -XDrawDiagnostics TwrOnNonResource.java + */ + +class TwrOnNonResource { + public static void main(String... args) { + try(TwrOnNonResource aonr = new TwrOnNonResource()) { + System.out.println(aonr.toString()); + } + try(TwrOnNonResource aonr = new TwrOnNonResource()) { + System.out.println(aonr.toString()); + } finally {;} + try(TwrOnNonResource aonr = new TwrOnNonResource()) { + System.out.println(aonr.toString()); + } catch (Exception e) {;} + + // Also check expression form + TwrOnNonResource aonr = new TwrOnNonResource(); + try(aonr) { + System.out.println(aonr.toString()); + } + try(aonr) { + System.out.println(aonr.toString()); + } finally {;} + try(aonr) { + System.out.println(aonr.toString()); + } catch (Exception e) {;} + } + + /* + * A close method, but the class is <em>not</em> Closeable or + * AutoCloseable. + */ + public void close() { + throw new AssertionError("I'm not Closable!"); + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrOnNonResource.out! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,7 @@ +TwrOnNonResource.java:12:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +TwrOnNonResource.java:15:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +TwrOnNonResource.java:18:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +TwrOnNonResource.java:24:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +TwrOnNonResource.java:27:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +TwrOnNonResource.java:30:13: compiler.err.prob.found.req: (compiler.misc.twr.not.applicable.to.type), TwrOnNonResource, +6 errors --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/TwrTests.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 + * @summary Tests of generated TWR code. + */ + +import java.util.List; +import java.util.ArrayList; + +public class TwrTests { + public static void main(String[] args) { + testCreateFailure1(); + testCreateFailure2(); + testCreateFailure2Nested(); + testCreateFailure3(); + testCreateFailure3Nested(); + testCreateFailure4(); + testCreateFailure4Nested(); + testCreateFailure5(); + testCreateFailure5Nested(); + + testCreateSuccess1(); + testCreateSuccess2(); + testCreateSuccess2Nested(); + testCreateSuccess3(); + testCreateSuccess3Nested(); + testCreateSuccess4(); + testCreateSuccess4Nested(); + testCreateSuccess5(); + testCreateSuccess5Nested(); + } + + /* + * The following tests simulate a creation failure of every possible + * resource in an TWR block, and check to make sure that the failure + * prevents creation of subsequent resources, and that all created + * resources are properly closed, even if one or more of the close + * attempts fails. + */ + + public static void testCreateFailure1() { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(0); + try (Resource r0 = createResource(0, 0, 0, closedList)) { + throw new AssertionError("Resource creation succeeded"); + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + if (e.resourceId() != 0) { + throw new AssertionError("Wrong resource creation " + + e.resourceId() + " failed"); + } + } catch (Resource.CloseFailException e) { + throw new AssertionError("Unexpected CloseFailException: " + e.resourceId()); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, 0); + } + Freitag, 10. Januar 14 + public static void testCreateFailure2() { + for (int createFailureId = 0; createFailureId < 2; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList); + Resource r1 = createResource(1, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed"); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure2Nested() { + for (int createFailureId = 0; createFailureId < 2; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) { + try(Resource r1 = createResource(1, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed"); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure3() { + for (int createFailureId = 0; createFailureId < 3; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList); + Resource r1 = createResource(1, createFailureId, bitMap, closedList); + Resource r2 = createResource(2, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure3Nested() { + for (int createFailureId = 0; createFailureId < 3; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) { + try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) { + try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } + } + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure4() { + for (int createFailureId = 0; createFailureId < 4; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList); + Resource r1 = createResource(1, createFailureId, bitMap, closedList); + Resource r2 = createResource(2, createFailureId, bitMap, closedList); + Resource r3 = createResource(3, createFailureId, bitMap, closedList)) { java.lang.AutoCloseable + throw new AssertionError("Entire resource creation succeeded"); java.lang.AutoCloseable + } catch (Resource.CreateFailException e) { java.lang.AutoCloseable + creationFailuresDetected++; java.lang.AutoCloseable + checkCreateFailureId(e.resourceId(), createFailureId); java.lang.AutoCloseable + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); java.lang.AutoCloseable + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure4Nested() { + for (int createFailureId = 0; createFailureId < 4; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) { + try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) { + try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) { + try (Resource r3 = createResource(3, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } + } + } + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure5() { + for (int createFailureId = 0; createFailureId < 5; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList); + Resource r1 = createResource(1, createFailureId, bitMap, closedList); + Resource r2 = createResource(2, createFailureId, bitMap, closedList); + Resource r3 = createResource(3, createFailureId, bitMap, closedList); + Resource r4 = createResource(4, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + public static void testCreateFailure5Nested() { + for (int createFailureId = 0; createFailureId < 5; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, createFailureId, bitMap, closedList)) { + try (Resource r1 = createResource(1, createFailureId, bitMap, closedList)) { + try (Resource r2 = createResource(2, createFailureId, bitMap, closedList)) { + try (Resource r3 = createResource(3, createFailureId, bitMap, closedList)) { + try (Resource r4 = createResource(4, createFailureId, bitMap, closedList)) { + throw new AssertionError("Entire resource creation succeeded"); + } + } + } + } + } catch (Resource.CreateFailException e) { + creationFailuresDetected++; + checkCreateFailureId(e.resourceId(), createFailureId); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + throw new AssertionError("Secondary exception suppression failed:" + e); + } + checkForSingleCreationFailure(creationFailuresDetected); + checkClosedList(closedList, createFailureId); + } + } + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /** * Create a resource with the specified ID. The ID must be less than createFailureId. * A subsequent attempt to close the resource will fail iff the corresponding bit * is set in closeFailureBitMap. When an attempt is made to close this resource, * its ID will be added to closedList, regardless of whether the attempt succeeds. * * @param id the ID of this resource * @param createFailureId the ID of the resource whose creation will fail * @param closeFailureBitMap a bit vector describing which resources should throw an * exception when close is attempted * @param closedList a list on which to record resource close attempts * @throws AssertionError if no attempt should be made to create this resource */ private static Resource createResource(int id, int createFailureId, int closeFailureBitMap, List<Integer> closedList) throws Resource.CreateFailException { if (id > createFailureId) throw new AssertionError("Resource " + id + " shouldn't be created"); boolean createSucceeds = id != createFailureId; boolean closeSucceeds = (closeFailureBitMap & (1 << id)) == 0; return new Resource(id, createSucceeds, closeSucceeds, closedList); } /** * Check that an observed creation failure has the expected resource ID. * * @param foundId the ID of the resource whose creation failed * @param expectedId the ID of the resource whose creation should have failed */ private static void checkCreateFailureId(int foundId, int expectedId) { if (foundId != expectedId) throw new AssertionError("Wrong resource creation failed. Found ID " + foundId + " expected " + expectedId); } /** * Check for proper suppressed exceptions in proper order. * * @param suppressedExceptions the suppressed exceptions array returned by * getSuppressedExceptions() * @bitmap a bitmap indicating which suppressed exceptions are expected. * Bit i is set iff id should throw a CloseFailException. */ private static void checkSuppressedExceptions(Throwable[] suppressedExceptions, int bitMap) { if (suppressedExceptions.length != Integer.bitCount(bitMap)) throw new AssertionError("Expected " + Integer.bitCount(bitMap) + " suppressed exceptions, got " + suppressedExceptions.length); int prevCloseFailExceptionId = Integer.MAX_VALUE; for (Throwable t : suppressedExceptions) { int id = ((Resource.CloseFailException) t).resourceId(); if ((1 << id & bitMap) == 0) throw new AssertionError("Unexpected suppressed CloseFailException: " + id); if (id > prevCloseFailExceptionId) throw new AssertionError("Suppressed CloseFailException" + id + " followed " + prevCloseFailExceptionId); } } /** * Check that exactly one resource creation failed. * * @param numCreationFailuresDetected the number of creation failures detected */ private static void checkForSingleCreationFailure(int numCreationFailuresDetected) { if (numCreationFailuresDetected != 1) throw new AssertionError("Wrong number of creation failures: " + numCreationFailuresDetected); } /** * Check that a close was attempted on every resourced that was successfully opened, * and that the close attempts occurred in the proper order. * * @param closedList the resource IDs of the close attempts, in the order they occurred * @param the ID of the resource whose creation failed. Close attempts should occur * for all previous resources, in reverse order. */ private static void checkClosedList(List<Integer> closedList, int createFailureId) { List<Integer> expectedList = new ArrayList<Integer>(createFailureId); for (int i = createFailureId - 1; i >= 0; i--) expectedList.add(i); if (!closedList.equals(expectedList)) throw new AssertionError("Closing sequence " + closedList + " != " + expectedList); } /* * The following tests simulate the creation of several resources, followed * by success or failure of forward processing. They test that all resources * are properly closed, even if one or more of the close attempts fails. */ public static void testCreateSuccess1() { for (int bitMap = 0, n = 1 << 1; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 1); } } } public static void testCreateSuccess2() { for (int bitMap = 0, n = 1 << 2; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList); Resource r1 = createResource(1, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 2); } } } public static void testCreateSuccess2Nested() { for (int bitMap = 0, n = 1 << 2; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList)) { try (Resource r1 = createResource(1, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 2); } } } public static void testCreateSuccess3() { for (int bitMap = 0, n = 1 << 3; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList); Resource r1 = createResource(1, bitMap, closedList); Resource r2 = createResource(2, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 3); } } } public static void testCreateSuccess3Nested() { for (int bitMap = 0, n = 1 << 3; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList)) { try (Resource r1 = createResource(1, bitMap, closedList)) { try (Resource r2 = createResource(2, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } } } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 3); } } } public static void testCreateSuccess4() { for (int bitMap = 0, n = 1 << 4; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList); Resource r1 = createResource(1, bitMap, closedList); Resource r2 = createResource(2, bitMap, closedList); Resource r3 = createResource(3, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 4); } } } public static void testCreateSuccess4Nested() { for (int bitMap = 0, n = 1 << 4; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList)) { try (Resource r1 = createResource(1, bitMap, closedList)) { try (Resource r2 = createResource(2, bitMap, closedList)) { try (Resource r3 = createResource(3, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } } } } catch (Resource.CreateFailException e) { throw new AssertionError( "Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 4); } } } public static void testCreateSuccess5() { for (int bitMap = 0, n = 1 << 5; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { List<Integer> closedList = new ArrayList<Integer>(); try (Resource r0 = createResource(0, bitMap, closedList); Resource r1 = createResource(1, bitMap, closedList); Resource r2 = createResource(2, bitMap, closedList); Resource r3 = createResource(3, bitMap, closedList); Resource r4 = createResource(4, bitMap, closedList)) { if (failure != 0) throw new MyKindOfException(); } catch (Resource.CreateFailException e) { throw new AssertionError("Resource creation failed: " + e.resourceId()); } catch (MyKindOfException e) { if (failure == 0) throw new AssertionError("Unexpected MyKindOfException"); checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); } catch (Resource.CloseFailException e) { if (failure == 1) throw new AssertionError("Secondary exception suppression failed"); int id = e.resourceId(); if (bitMap == 0) throw new AssertionError("Unexpected CloseFailException: " + id); int highestCloseFailBit = Integer.highestOneBit(bitMap); if (1 << id != highestCloseFailBit) { throw new AssertionError("CloseFailException: got id " + id + ", expected lg(" + highestCloseFailBit +")"); } checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); } checkClosedList(closedList, 5); } } } public static void testCreateSuccess5Nested() { for (int bitMap = 0, n = 1 << 5; bitMap < n; bitMap++) { for (int failure = 0; failure < 2; failure++) { 3 + List<Integer> closedList = new ArrayList<Integer>(); + try (Resource r0 = createResource(0, bitMap, closedList)) { + try (Resource r1 = createResource(1, bitMap, closedList)) { + try (Resource r2 = createResource(2, bitMap, closedList)) { + try (Resource r3 = createResource(3, bitMap, closedList)) { + try (Resource r4 = createResource(4, bitMap, closedList)) { + if (failure != 0) + throw new MyKindOfException(); + } + } + } + } + } catch (Resource.CreateFailException e) { + throw new AssertionError("Resource creation failed: " + e.resourceId()); + } catch (MyKindOfException e) { + if (failure == 0) + throw new AssertionError("Unexpected MyKindOfException"); + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap); + } catch (Resource.CloseFailException e) { + if (failure == 1) + throw new AssertionError("Secondary exception suppression failed"); + int id = e.resourceId(); + if (bitMap == 0) + throw new AssertionError("Unexpected CloseFailException: " + id); + int highestCloseFailBit = Integer.highestOneBit(bitMap); + if (1 << id != highestCloseFailBit) { + throw new AssertionError("CloseFailException: got id " + id + + ", expected lg(" + highestCloseFailBit +")"); + } + checkSuppressedExceptions(e.getSuppressedExceptions(), bitMap & ~highestCloseFailBit); + } + checkClosedList(closedList, 5); + } + } + } + + private static Resource createResource(int id, + int closeFailureBitMap, + List<Integer> closedList) throws Resource.CreateFailException { + boolean closeSucceeds = (closeFailureBitMap & (1 << id)) == 0; + return new Resource(id, true, closeSucceeds, closedList); + } + + private static class MyKindOfException extends Exception { + } +} + +class Resource implements AutoCloseable { + /** A number identifying this resource */ + private final int resourceId; + + /** Whether the close call on this resource should succeed or fail */ + private final boolean closeSucceeds; + + /** When resource is closed, it records its ID in this list */ + private final List<Integer> closedList; + + Resource(int resourceId, boolean createSucceeds, boolean closeSucceeds, + List<Integer> closedList) throws CreateFailException { + if (!createSucceeds) + throw new CreateFailException(resourceId); + this.resourceId = resourceId; + this.closeSucceeds = closeSucceeds; + this.closedList = closedList; + } + + public void close() throws CloseFailException { + closedList.add(resourceId); + if (!closeSucceeds) + throw new CloseFailException(resourceId); + } + + public static class ResourceException extends RuntimeException { + private final int resourceId; + + public ResourceException(int resourceId) { + super("Resource ID = " + resourceId); + this.resourceId = resourceId; + } + + public int resourceId() { + return resourceId; + } + } + + public static class CreateFailException extends ResourceException { + public CreateFailException(int resourceId) { + super(resourceId); + } + } + + public static class CloseFailException extends ResourceException { + public CloseFailException(int resourceId) { + super(resourceId); + } + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/TryWithResources/WeirdTwr.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Strange TWRs + * @compile/fail -source 6 WeirdTwr.java + * @compile WeirdTwr.java + * @run main WeirdTwr + */ + +public class WeirdTwr implements AutoCloseable { + private static int closeCount = 0; + public static void main(String... args) { + try(WeirdTwr r1 = new WeirdTwr(); WeirdTwr r2 = r1) { + if (r1 != r2) + throw new RuntimeException("Unexpected inequality."); + } + if (closeCount != 2) + throw new RuntimeException("bad closeCount" + closeCount); + } + + public void close() { + closeCount++; + } +} --- /dev/null! Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javac/processing/model/element/TestResourceVariable.java! Fri Jul 16 19:35:24 2010 -0700 @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 6911256 6964740 + * @summary Test that the resource variable kind is appropriately set + * @author Joseph D. Darcy + * @build TestResourceVariable + * @compile/fail -processor TestResourceVariable -proc:only TestResourceVariable.java + */ + +// Bug should be filed for this misbehavior + + Freitag, 10. Januar 14 import java.io.*; +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.lang.model.type.*; +import javax.lang.model.util.*; +import java.util.*; +import com.sun.source.tree.*; +import com.sun.source.util.*; +import static javax.tools.Diagnostic.Kind.*; + +/** + * Using the tree API, retrieve element representations of the + * resource of an ARM block and verify their kind tags are set + * appropriately. + */ +@SupportedAnnotationTypes("*") +public class TestResourceVariable extends AbstractProcessor implements AutoCloseable { + int resourceVariableCount = 0; + + public boolean process(Set<? extends TypeElement> annotations, + RoundEnvironment roundEnv) { + if (!roundEnv.processingOver()) { + Trees trees = Trees.instance(processingEnv); + + for(Element rootElement : roundEnv.getRootElements()) { + TreePath treePath = trees.getPath(rootElement); + + (new ResourceVariableScanner(trees)). + scan(trees.getTree(rootElement), + treePath.getCompilationUnit()); + } + if (resourceVariableCount != 3) + throw new RuntimeException("Bad resource variable count " + + resourceVariableCount); + } + return true; + } + + @Override + public void close() {} + + private void test1() { + try(TestResourceVariable trv = this) {} + } + + private void test2() { + try(TestResourceVariable trv1 = this; TestResourceVariable trv2 = trv1) {} + } + + class ResourceVariableScanner extends TreeScanner<Void, CompilationUnitTree> { + private Trees trees; + + public ResourceVariableScanner(Trees trees) { + super(); + this.trees = trees; + } + @Override + public Void visitVariable(VariableTree node, CompilationUnitTree cu) { + Element element = trees.getElement(trees.getPath(cu, node)); + if (element == null) { + System.out.println("Null variable element: " + node); + } else { + System.out.println("Name: " + element.getSimpleName() + + "\tKind: " + element.getKind()); + } + if (element != null && + element.getKind() == ElementKind.RESOURCE_VARIABLE) { + resourceVariableCount++; + } + return super.visitVariable(node, cu); + } + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } +} 4 So you want to change the Clojure Programming Language... By Jens on Jan 9, 2013 Step 1: Write a Macro Step 2: There is no Step 2 Freitag, 10. Januar 14 Clojure (defmacro with-resource [binding close-fn & body] `(let ~binding (try (do ~@body) (finally (~close-fn ~(binding 0)))))) Freitag, 10. Januar 14 Deep walking • Bisher: "Oberflächen Macros" • Modifikation der Top Form • Es gibt auch Deep Walking Macros (DWM), die die Sprache radikaler ändern • In Clojure kann man Dinge als Bibliothek ausliefern, die in anderen Sprachen nur durch Sprachänderung möglich sind Freitag, 10. Januar 14 Deep Walking Macros Beispiel: Incanter Infix user=> (use 'incanter.core) nil user=> ($= (2 + 3) * 4) 20 user=> ($= 4 * (2 + 3)) 20 user=> ($= 2 + 3 * 4) 14 user=> (macroexpand-1 '($= (2 + 3) * 4)) (incanter.core/mult (incanter.core/plus 2 3) 4) user=> (macroexpand-1 '($= 4 * (2 + 3))) (incanter.core/mult 4 (incanter.core/plus 2 3)) user=> (macroexpand-1 '($= 2 + 3 * 4)) (incanter.core/plus 2 (incanter.core/mult 3 4)) Freitag, 10. Januar 14 Deep Walking Macros Beispiel: core.async • CSP Implementierung • Von der Go Sprache adaptiert • Idee: Prozesse kommunizieren über Channel user=> (def c (chan)) #'user/c user=> (go (while true (let [v (<! c)] (println "got" v)))) #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@1416e4cd> user=> (go (>! c 10)) #<ManyToManyChannel clojure.core.async.impl.channels.ManyToManyChannel@42e80f7> got 10 Freitag, 10. Januar 14 Deep Walking Macros • Vorteile von core.async - Leichtgewichtige Threads - Funktioniert auch in ClojureScript (d.h. ohne OS Threads) - Eliminiert Callback Hell in ClojureScript - Ermöglicht simplere Architektur durch Trennung von Komponenten • Versucht das als Bibliothek in einer anderen Sprache zu schreiben! Freitag, 10. Januar 14 Deep Walking Macros Das go Macro expandiert: user=> (pprint (macroexpand-1 '(go (>! c 10)))) Freitag, 10. Januar 14 Deep Walking Macros user=> (pprint (macroexpand-1 '(go (>! c 10)))) (clojure.core/let [c__2197__auto__ (clojure.core.async/chan 1) captured-bindings__2198__auto__ (clojure.lang.Var/getThreadBindingFrame)] (clojure.core.async.impl.dispatch/run (clojure.core/fn [] (clojure.core/let [f__2199__auto__ (clojure.core/fn state-machine__2062__auto__ ([] (clojure.core.async.impl.ioc-macros/aset-all! (java.util.concurrent.atomic.AtomicReferenceArray. 7) 0 state-machine__2062__auto__ 1 1)) ([state_4309] (clojure.core/let [old-frame__2063__auto__ (clojure.lang.Var/getThreadBindingFrame) ret-value__2064__auto__ (try (clojure.lang.Var/resetThreadBindingFrame (clojure.core.async.impl.ioc-macros/aget-object state_4309 3)) (clojure.core/loop [] (clojure.core/let [result__2065__auto__ (clojure.core/case (clojure.core/int (clojure.core.async.impl.ioc-macros/aget-object state_4309 1)) (clojure.core.async.impl.ioc-macros/aset-all! clojure.core.async.impl.ioc-macros/USER-START-IDX c__2197__auto__ clojure.core.async.impl.ioc-macros/BINDINGS-IDX captured-bindings__2198__auto__))] (clojure.core.async.impl.ioc-macros/run-state-machine-wrapped state__2200__auto__)))) c__2197__auto__) Freitag, 10. Januar 14 2 (clojure.core/let [inst_4307 (clojure.core.async.impl.ioc-macros/aget-object state_4309 2) state_4309 state_4309] (clojure.core.async.impl.ioc-macros/return-chan state_4309 inst_4307)) 1 (clojure.core/let [state_4309 state_4309] (clojure.core.async.impl.ioc-macros/put! state_4309 2 c 10)))] (if (clojure.core/identical? result__2065__auto__ :recur) (recur) result__2065__auto__))) (catch java.lang.Throwable ex__2066__auto__ (clojure.core.async.impl.ioc-macros/aset-all! state_4309 clojure.core.async.impl.ioc-macros/CURRENT-EXCEPTION ex__2066__auto__) (clojure.core.async.impl.ioc-macros/process-exception state_4309) :recur) (finally (clojure.lang.Var/resetThreadBindingFrame old-frame__2063__auto__)))] (if (clojure.core/identical? ret-value__2064__auto__ :recur) (recur state_4309) ret-value__2064__auto__)))) state__2200__auto__ (clojure.core/-> (f__2199__auto__) DWM Implementierung • Macros sind hart, DWM sind härter • Es ist schwer DWM korrekt zu implementieren • DWM erfordern Erfahrung DWM: http://blog.fogus.me/2013/07/17/an-introduction-to-deep-code-walking-macros-with-clojure/ http://www.youtube.com/watch?v=HXfDK1OYpco core.async DWM: http://www.youtube.com/watch?v=R3PZMIwXN_g http://www.youtube.com/watch?v=SI7qtuuahhU Freitag, 10. Januar 14 Macros • Macros erlauben es die Sprache zu modifizieren • ... in potentiell radikaler Weise • ... ohne auf Rich Hickey warten zu müssen • Macros werden durch Homoikonizität viel einfacher! Freitag, 10. Januar 14 Freitag, 10. Januar 14 Haskell Freitag, 10. Januar 14 Noch eine Sprache? • Für die Klausur: Es werden keine Aufgaben kommen in denen Haskell Code geschrieben werden muss • Das heisst nicht, dass nichts über Haskell in der Klausur drankommt! Freitag, 10. Januar 14 Freitag, 10. Januar 14 Freitag, 10. Januar 14 Agenda • Typisierung • Currying • Pattern Matching • Lazyness • Functors, Applicatives & Monads Freitag, 10. Januar 14 Typisierung Freitag, 10. Januar 14 Typsysteme Freitag, 10. Januar 14 stark schwach dynamisch Clojure, Python PHP statisch Scala, Haskell, Java C Statische Typisierung "Static Typing is an academical experiment only suitable for small projects." (Stuart Halloway, Euroclojure 2013) Freitag, 10. Januar 14 Dynamic vs. Static user=> (defn bumm! [x] (+ "ab" 3 x)) #'user/bumm! user=> (bumm! 1) ClassCastException java.lang.String cannot be cast to java.lang.Number clojure.lang.Numbers.add (Numbers.java:126) Prelude> let bumm! x = "ab" + 3 + x <interactive>:35:24: No instance for (Num [Char]) arising from a use of `+' Possible fix: add an instance declaration for (Num [Char]) In the expression: "ab" + 3 + x In an equation for `!': bumm ! x = "ab" + 3 + x Freitag, 10. Januar 14 Static Typing • Vorteil: Eine gewisse Klasse von Fehlern werden vom Compiler erkannt • Gegenargument: • Die Fehler sind einfach durch Testing zu vermeiden (Python Fans) • Es sind nur triviale Fehler (Stu Halloway) Freitag, 10. Januar 14 Haskell Types Prelude> :t 'a' 'a' :: Char Prelude> :t ('1',True,"hallo") ('1',True,"hallo") :: (Char, Bool, [Char]) Prelude> :t head head :: [a] -> a Prelude> :t (+) (+) :: Num a => a -> a -> a primitives composites polymorphic typeclass Prelude> :t 5 5 :: Num a => a Prelude> :t putStr putStr :: String -> IO () Prelude Data.Maybe> :t listToMaybe listToMaybe :: [a] -> Maybe a Freitag, 10. Januar 14 monadic Typinferenz • In Haskell muss man bis auf wenige Ausnahmen keine Typen angeben • Typen werden vom Compiler inferiert • Es gehört aber besonders bei Bibliotheken zum guten Ton eine Typdeklaration zu schreiben myPlus :: Num a => a -> a -> a myPlus = (+) Freitag, 10. Januar 14 Algebraische Datentypen • Definition eigener Datentypen geht mit data • Bsp: data MyBool = T | F :t F F :: MyBool • MyBool ist ein Typ • T und F nennt man value constructor Freitag, 10. Januar 14 Algebraische Datentypen • Es gibt auch algebraische Datentypen, die Parameter haben • Bsp: data MyInt = Wert Int | Unendlich Prelude> :t Wert 4 Wert 4 :: MyInt Prelude> :t Wert Wert :: Int -> MyInt Prelude> :t Unendlich Unendlich :: MyInt Freitag, 10. Januar 14 Polymorphismus Prelude> :t head head :: [a] -> a Prelude> head [1,2,3] 1 Prelude> head ["1","2","3"] "1" Prelude> head [1,2,"3"] user=> (first [1 2 3]) 1 user=> (first ["1" "2" "3"]) "1" user=> (first [1 2 "3"]) 1 <interactive>:10:7: No instance for (Num [Char]) arising from the literal `1' Possible fix: add an instance declaration for (Num [Char]) In the expression: 1 In the first argument of `head', namely `[1, 2, "3"]' In the expression: head [1, 2, "3"] Freitag, 10. Januar 14 Polymorphismus • Die head Funktion ist polymorph • Aber die Elemente der Liste müssen einen gemeinsamen Typ haben Freitag, 10. Januar 14 Übung Welche Funktionen haben die Signaturen: • (a,b) -> b • [a] -> [a] Freitag, 10. Januar 14 Algebraische Datentypen Freitag, 10. Januar 14 • Zweistellige Vektoren • Hier bräuchte man eine Art polymorphen Vektor data VectorB = Vector Bool Bool data VectorI = Vector Int Int data VectorC = Vector Char Char data Vector a = Vector a a *Main> let l = Vector 1 2 *Main> :t l l :: Vector Integer Algebraische Datentypen data Vector a = Vector a a *Main> let l = Vector 1 2 *Main> :t l l :: Vector Integer • • • • Freitag, 10. Januar 14 Vector auf der rechten Seite ist ein Wertkonstruktor, Vector 1 3 konstruiert einen Wert Vector iauf der linken Seite ist ein Typkonstruktor, Vector Integer konstruiert einen Typ Man kann die beiden nicht verwechseln, Typkonstruktoren und Wertkonstruktoren kommen nicht an den gleichen Stellen vor Sogar data Vector a b = Vector a b geht! Algebraische Datentypen Beispiel: Berechnungen, die fehlschlagen können data Maybe a = Just a | Nothing mydiv :: Int -> Int -> Maybe Int mydiv a b | b == 0 = Nothing | otherwise = Just (a `div` b) *Main> mydiv 4 2 Just 2 *Main> mydiv 4 0 Nothing *Main> :t mydiv 4 3 mydiv 4 3 :: Maybe Int Freitag, 10. Januar 14 Typklassen Prelude> :t (+) (+) :: Num a => a -> a -> a Einschränkung des Typs (aka Typeclass) Hier kommen die Clojure Protokolle her! Freitag, 10. Januar 14 Typklassen • Beispiel: Eq - Dinge, die man vergleichen kann • Was gibt Vector Freitag, 10. Januar 14 1 2 == Vector 1 3 ? Typklassen *Main> Vector 1 2 == Vector 1 3 <interactive>:137:12: No instance for (Eq (Vector a0)) arising from a use of `==' Possible fix: add an instance declaration for (Eq (Vector a0)) In the expression: Vector 1 2 == Vector 1 3 In an equation for `it': it = Vector 1 2 == Vector 1 3 Uns fehlt die passende type class um Vektoren zu vergleichen Freitag, 10. Januar 14 Eq class Eq (==) (/=) x == x /= a where :: a -> :: a -> y = not y = not a -> Bool a -> Bool (x /= y) (x == y) Default Implementierung "extend-protocol" instance (Eq a) => Eq (Vector a) where (Vector x1 y1) == (Vector x2 y2) = x1==x2 && y1 == y2 Freitag, 10. Januar 14 "Eq" in Clojure data MyVector a = MyVector a a class Eq (==) (/=) x == x /= a where :: a -> :: a -> y = not y = not a -> Bool a -> Bool (x /= y) (x == y) (defrecord MyVector [x y]) (defprotocol Eq (eq [this that])) instance (Eq a) => Eq (Vector a) where (Vector x1 y1) == (Vector x2 y2) = x1==x2 && y1 == y2 (extend-protocol Eq MyVector (eq [this that] (and (= (:x this) (:x that)) (= (:y this) (:y that))))))) Freitag, 10. Januar 14 Seiteneffekte • Statische Typisierung in Haskell ist anders als in anderen statisch typisierten Sprachen • Haskell ist pure • In Haskell kann man am Typ erkennen, ob eine Funktion einen Seiteneffekt hat • Haskell ist pure und hat trotzdem Seiteneffekte ?!? Freitag, 10. Januar 14 Seiteneffekte echo x = do n <- getLine putStrLn (x ++" " ++ n) return n *Main> echo "Hallo" Welt Eingabe in der Shell Hallo Welt "Welt" *Main> :t echo echo :: [Char] -> IO String Freitag, 10. Januar 14 Seiteneffekte echo x = do n <- getLine putStrLn (x ++" " ++ n) return n *Main> echo "Hallo" Welt Hallo Welt "Welt" *Main> :t echo echo :: [Char] -> IO String *Main> :t putStr putStr :: String -> IO () *Main> :t getLine getLine :: IO String Freitag, 10. Januar 14 IO markiert Seiteneffekte Was ist IO String? Freitag, 10. Januar 14 Clojure Alternativen • Ersatz für Static typing in Clojure? • Optional Typing mit core.typed • Prismatic Schemas (→REPL) • Contracts • Tests • Alles nur geeignet zur Fehlervermeidung, nicht zur Erkennung von Seiteneffekten Freitag, 10. Januar 14 Currying Freitag, 10. Januar 14 Currying • Benannt nach Haskell Curry • Unabhängig auch von Moses Schönfinkel entdeckt Freitag, 10. Januar 14 (+) Prelude> :t (+) (+) :: Num a => a -> a -> a Warum ist die Signatur nicht (+) :: Num a => a,a -> a Freitag, 10. Januar 14 Currying Jede Funktion in Haskell nimmt genau einen Parameter Prelude> :t (+) (+) :: Num a => a -> a -> a Prelude> let inc = (+) 1 Prelude> :t inc inc :: Integer -> Integer Eine n-stellige Funktion schreibt man als Funktion, die einen Parameter nimmt und eine n-1 stellige Funktion liefert Freitag, 10. Januar 14 Currying Haskell Typsignaturen sind also rechtsassoziativ (+) :: Num a => a -> (a -> a) Freitag, 10. Januar 14 Currying Prelude> let foo x y z = (x + y) * z Prelude> :t foo foo :: Num a => a -> a -> a -> a foo(1)(2)(3) Prelude> foo 1 2 3 (((foo 1) 2) 3) 9 Prelude> let f1 = foo 1 Prelude> f2 2 3 9 Prelude> let f2 = f1 2 Prelude> f2 3 9 Prelude> let f2 = foo 1 2 Prelude> f2 3 9 Prelude> let f3 = foo 1 2 3 Prelude> f3 9 Freitag, 10. Januar 14 Partial Application Prelude> :t take take :: Int -> [a] -> [a] Prelude> let take3 = take 3 Prelude> :t take3 take3 :: [a] -> [a] Prelude> take3 [1,2,3,4,5] [1,2,3] Freitag, 10. Januar 14 Partial Application Man kann das Verhalten in Clojure simulieren: user=> (def take' (fn [n] (fn [c] (take n c)))) #'user/take' user=> (def take3 (take' 3)) #'user/take3 user=> (take3 (range 5)) (0 1 2) Freitag, 10. Januar 14 Partial Application Es gibt eine weitere Möglichkeit take3 zu schreiben user=> (def take3 (partial take 3)) #'user/take3 user=> (take3 [1 2 3 4 5]) (1 2 3) partial = currying? Freitag, 10. Januar 14 Currying • Allgemeines Currying ist in Clojure nicht direkt realisierbar • Grund: variable Anzahl an Argumenten • Was ist das Resultat von (+ 2 3)? • 5? • Eine Funktion, die +5 rechnet? Currying geht nur wenn wir die Anzahl der Parameter fixieren Freitag, 10. Januar 14 Curry Macro (defmacro curry ([n f] `(curry ~n [] ~f)) ([n p f] (if (= 0 n) `(~f ~@p) (let [x (gensym)] `(fn [~x] (curry ~(dec n) ~(conj p x) ~f)))))) user=> (def +c2 (curry 2 +)) #'user/+c2 user=> (+c2 1) #<PLUS_c2$fn... PLUS_c2$fn...> user=> ((+c2 1) 4) 5 user=> (macroexpand-all '(curry 2 +)) (fn* ([G__3943] (fn* ([G__3944] (+ G__3943 G__3944))))) Freitag, 10. Januar 14