--- old/make/build.properties 2010-07-01 10:48:35.000000000 -0700 +++ new/make/build.properties 2010-07-01 10:48:35.000000000 -0700 @@ -107,7 +107,8 @@ javax/annotation/processing/ \ javax/lang/model/ \ javax/tools/ \ - com/sun/source/ com/sun/tools/javac/ + com/sun/source/ \ + com/sun/tools/javac/ javac.tests = \ tools/javac --- old/src/share/classes/com/sun/source/tree/TryTree.java 2010-07-01 10:48:36.000000000 -0700 +++ new/src/share/classes/com/sun/source/tree/TryTree.java 2010-07-01 10:48:36.000000000 -0700 @@ -49,4 +49,5 @@ BlockTree getBlock(); List getCatches(); BlockTree getFinallyBlock(); + List getResources(); } --- old/src/share/classes/com/sun/source/util/TreeScanner.java 2010-07-01 10:48:36.000000000 -0700 +++ new/src/share/classes/com/sun/source/util/TreeScanner.java 2010-07-01 10:48:36.000000000 -0700 @@ -212,6 +212,7 @@ R r = scan(node.getBlock(), p); r = scanAndReduce(node.getCatches(), p, r); r = scanAndReduce(node.getFinallyBlock(), p, r); + r = scanAndReduce(node.getResources(), p, r); return r; } --- old/src/share/classes/com/sun/tools/javac/code/Source.java 2010-07-01 10:48:36.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Source.java 2010-07-01 10:48:36.000000000 -0700 @@ -159,6 +159,9 @@ public boolean enforceMandatoryWarnings() { return compareTo(JDK1_5) >= 0; } + public boolean allowAutomaticResourceManagement() { + return compareTo(JDK1_7) >= 0; + } public boolean allowTypeAnnotations() { return compareTo(JDK1_7) >= 0; } --- old/src/share/classes/com/sun/tools/javac/code/Symbol.java 2010-07-01 10:48:37.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Symbol.java 2010-07-01 10:48:37.000000000 -0700 @@ -995,7 +995,8 @@ public Object getConstValue() { // TODO: Consider if getConstValue and getConstantValue can be collapsed - if (data == ElementKind.EXCEPTION_PARAMETER) { + if (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 --- old/src/share/classes/com/sun/tools/javac/code/Symtab.java 2010-07-01 10:48:37.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Symtab.java 2010-07-01 10:48:37.000000000 -0700 @@ -147,6 +147,7 @@ public final Type inheritedType; public final Type proprietaryType; public final Type systemType; + public final Type autoCloseableType; /** The symbol representing the length field of an array. */ @@ -460,6 +461,7 @@ suppressWarningsType = enterClass("java.lang.SuppressWarnings"); inheritedType = enterClass("java.lang.annotation.Inherited"); systemType = enterClass("java.lang.System"); + autoCloseableType = enterClass("java.lang.AutoCloseable"); synthesizeEmptyInterfaceIfMissing(cloneableType); synthesizeEmptyInterfaceIfMissing(serializableType); --- old/src/share/classes/com/sun/tools/javac/comp/Attr.java 2010-07-01 10:48:38.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Attr.java 2010-07-01 10:48:38.000000000 -0700 @@ -244,7 +244,11 @@ !((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 (types.asSuper(v.type, syms.autoCloseableType.tsym) != null) { //ARM resource + log.error(pos, "arm.resource.may.not.be.assigned", v); + } else { + log.error(pos, "cant.assign.val.to.final.var", v); + } } } @@ -981,14 +985,33 @@ } public void visitTry(JCTry tree) { + // Create a new local environment with a local scope. + Env localEnv = env.dup(tree, env.info.dup(env.info.scope.dup())); + // Attribute resource declarations + ListBuffer resourceVars = ListBuffer.lb(); + for (JCTree resource : tree.resources) { + attribExpr(resource, localEnv); + chk.checkArmResource(resource); + if (resource.getTag() == JCTree.VARDEF) { + VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource); + var.setData(ElementKind.RESOURCE_VARIABLE); + resourceVars.append(var); + } + } // Attribute body - attribStat(tree.body, env.dup(tree, env.info.dup())); + attribStat(tree.body, localEnv.dup(tree, localEnv.info.dup())); + + //remove arm resource vars from local scope - such variables cannot be + //accessed from catch/finally clauses + for (VarSymbol resourceVar : resourceVars) { + localEnv.info.scope.remove(resourceVar); + } // Attribute catch clauses for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { JCCatch c = l.head; Env 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 @@ -1008,7 +1031,9 @@ } // Attribute finalizer - if (tree.finalizer != null) attribStat(tree.finalizer, env); + if (tree.finalizer != null) attribStat(tree.finalizer, localEnv); + + localEnv.info.scope.leave(); result = null; } --- old/src/share/classes/com/sun/tools/javac/comp/Check.java 2010-07-01 10:48:38.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Check.java 2010-07-01 10:48:38.000000000 -0700 @@ -1603,6 +1603,22 @@ return false; } + /** + * Check that a resource is a valid arm resource (e.g. the resource type must be + * a subtype of java.lang.autoCloseable). + * + * @param resource resource tree + */ + void checkArmResource(JCTree resource) { + if (types.asSuper(resource.type, syms.autoCloseableType.tsym) == null) { + typeError(resource.pos(), + diags.fragment("arm.not.applicable.to.type"), + resource.type, + syms.autoCloseableType); + resource.type = types.createErrorType(resource.type); + } + } + /** Check that a given method conforms with any method it overrides. * @param tree The tree from which positions are extracted * for errors. --- old/src/share/classes/com/sun/tools/javac/comp/Flow.java 2010-07-01 10:48:39.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Flow.java 2010-07-01 10:48:39.000000000 -0700 @@ -265,6 +265,10 @@ */ List caught; + /** The list of unreferenced automatic resources. + */ + List unrefdResources; + /** Set when processing a loop body the second time for DU analysis. */ boolean loopPassTwo = false; @@ -961,6 +965,16 @@ } public void visitTry(JCTry tree) { + unrefdResources = List.nil(); + for (JCTree resource : tree.resources) { + if (resource instanceof JCVariableDecl) { + visitVarDef((JCVariableDecl) resource); + } else if (resource instanceof JCExpression) { + scanExpr((JCExpression) resource); + } else { + throw new AssertionError(tree); // parser error + } + } List caughtPrev = caught; List thrownPrev = thrown; thrown = List.nil(); @@ -977,6 +991,14 @@ pendingExits = new ListBuffer(); Bits initsTry = inits.dup(); uninitsTry = uninits.dup(); + for (JCTree resource : tree.resources) { + MethodSymbol topCloseMethod = (MethodSymbol)syms.autoCloseableType.tsym.members().lookup(names.close).sym; + MethodSymbol closeMethod = types.implementation(topCloseMethod, resource.type.tsym, types, true); + + for (Type thrownType : closeMethod.getThrownTypes()) { + markThrown(tree.body, thrownType); + } + } scanStat(tree.body); List thrownInTry = thrown; thrown = thrownPrev; @@ -987,6 +1009,18 @@ Bits uninitsEnd = uninits; int nextadrCatch = nextadr; + if (unrefdResources.nonEmpty()) { + for (List l = tree.resources; l.nonEmpty(); l = l.tail) { + if (l.head instanceof JCVariableDecl) { + JCVariableDecl v = (JCVariableDecl) l.head; + if (unrefdResources.contains(v.sym)) { + log.warning(v.pos(), + "automatic.resource.not.referenced", v.sym); + } + } + } + } + List caughtInTry = List.nil(); for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { alive = true; @@ -1293,8 +1327,20 @@ } 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.contains(sym)) { + ListBuffer lb = new ListBuffer(); + for (List l = unrefdResources; l.nonEmpty(); l = l.tail) + if (l.head != sym) + lb.add(l.head); + unrefdResources = lb.toList(); + } } public void visitTypeCast(JCTypeCast tree) { --- old/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-07-01 10:48:39.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-07-01 10:48:39.000000000 -0700 @@ -1400,6 +1400,122 @@ } } + /** 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 makeTry(JCTry tree) { + if (tree.resources.isEmpty()) { + return tree; + } + make_at(tree.pos()); + 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)); + return result; + } + + private JCBlock makeArmBlock(List resources, JCBlock block, int depth) { + if (resources.isEmpty()) + return block; + + // Add resource declaration or expression to block statements + ListBuffer stats = new ListBuffer(); + 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 syntheticArmVar = + new VarSymbol(SYNTHETIC | FINAL, + names.fromString("armVar" + + resource.pos + + target.syntheticNameChar()), + resource.type, + currentMethodSym); + JCVariableDecl syntheticArmVarDecl = make.VarDef(syntheticArmVar, (JCExpression)resource); + expr = (JCExpression)make.Ident(syntheticArmVar); + stats.add(syntheticArmVarDecl); + } + + // Add primaryException declaration + VarSymbol primaryException = + new VarSymbol(SYNTHETIC, + names.fromString("primaryException" + + resource.pos + + target.syntheticNameChar()), + syms.throwableType, + currentMethodSym); + 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" + + resource.pos + + 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.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.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.of(make.Ident(catchException)))); + + // try { resource.close(); } catch (e) { primaryException.addSuppressedException(e); } + JCBlock tryBlock = + make.Block(0L, List.of(makeResourceCloseInvocation(resource))); + JCVariableDecl catchExceptionDecl = make.VarDef(catchException, null); + JCBlock catchBlock = make.Block(0L, List.of(addSuppressionStatement)); + List catchClauses = List.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.of(closeIfStatement)); + } + + private JCStatement makeResourceCloseInvocation(JCExpression resource) { + // create resource.close() method invocation + JCExpression resourceClose = makeCall(resource, names.close, List.nil()); + return make.Exec(resourceClose); + } + /** Construct a tree that represents the outer instance * . Never pick the current `this'. * @param pos The source code position to be used for the tree. @@ -3405,6 +3521,12 @@ result = tree; } + @Override + public void visitTry(JCTry tree) { + super.visitTry(tree); + result = makeTry(tree); + } + /************************************************************************** * main method *************************************************************************/ --- old/src/share/classes/com/sun/tools/javac/comp/TransTypes.java 2010-07-01 10:48:40.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/TransTypes.java 2010-07-01 10:48:40.000000000 -0700 @@ -535,6 +535,14 @@ result = tree; } + public void visitTry(JCTry tree) { + tree.body = translate(tree.body); + tree.catchers = translateCatchers(tree.catchers); + tree.finalizer = translate(tree.finalizer); + tree.resources = translate(tree.resources, syms.autoCloseableType); + result = tree; + } + public void visitConditional(JCConditional tree) { tree.cond = translate(tree.cond, syms.booleanType); tree.truepart = translate(tree.truepart, erasure(tree.type)); --- old/src/share/classes/com/sun/tools/javac/jvm/CRTable.java 2010-07-01 10:48:40.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/jvm/CRTable.java 2010-07-01 10:48:40.000000000 -0700 @@ -325,6 +325,7 @@ 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)); --- old/src/share/classes/com/sun/tools/javac/parser/JavacParser.java 2010-07-01 10:48:41.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/parser/JavacParser.java 2010-07-01 10:48:41.000000000 -0700 @@ -131,6 +131,7 @@ this.allowForeach = source.allowForeach(); this.allowStaticImport = source.allowStaticImport(); this.allowAnnotations = source.allowAnnotations(); + this.allowARM = source.allowAutomaticResourceManagement(); this.allowDiamond = source.allowDiamond(); this.allowMulticatch = source.allowMulticatch(); this.allowTypeAnnotations = source.allowTypeAnnotations(); @@ -186,6 +187,10 @@ */ boolean allowTypeAnnotations; + /** Switch: should we recognize automatic resource management? + */ + boolean allowARM; + /** Switch: should we keep docComments? */ boolean keepDocComments; @@ -1912,6 +1917,13 @@ } case TRY: { S.nextToken(); + List resources = List.nil(); + if (S.token() == LPAREN) { + checkAutomaticResourceManagement(); + S.nextToken(); + resources = resourceDeclarators(); + accept(RPAREN); + } JCBlock body = block(); ListBuffer catchers = new ListBuffer(); JCBlock finalizer = null; @@ -1922,9 +1934,13 @@ finalizer = block(); } } else { - log.error(pos, "try.without.catch.or.finally"); + if (allowARM) { + 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(body, catchers.toList(), finalizer); + return F.at(pos).Try(body, catchers.toList(), finalizer, resources); } case SWITCH: { S.nextToken(); @@ -2385,6 +2401,36 @@ return toP(F.at(pos).VarDef(mods, name, type, null)); } + /** ResourceDeclarators = ResourceDeclarator { "," ResourceDeclarator } + */ + List resourceDeclarators() { + ListBuffer defs = new ListBuffer(); + defs.append(resourceDeclarator()); + while (S.token() == SEMI) { + // All but last of multiple declarators subsume a semicolon + storeEnd(defs.elems.last(), S.endPos()); + S.nextToken(); + defs.append(resourceDeclarator()); + } + return defs.toList(); + } + + /** ResourceDeclarator = LocalVariableDeclaration */ + JCTree resourceDeclarator() { + 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} */ public JCTree.JCCompilationUnit parseCompilationUnit() { @@ -3218,4 +3264,10 @@ allowMulticatch = true; } } + void checkAutomaticResourceManagement() { + if (!allowARM) { + log.error(S.pos(), "automatic.resource.management.not.supported.in.source", source.name); + allowARM = true; + } + } } --- old/src/share/classes/com/sun/tools/javac/resources/compiler.properties 2010-07-01 10:48:41.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/resources/compiler.properties 2010-07-01 10:48:41.000000000 -0700 @@ -61,6 +61,10 @@ 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.arm.not.applicable.to.type=\ + automatic resource management not applicable to value of type {0} +compiler.err.arm.resource.may.not.be.assigned=\ + cannot assign another value to automatic resource variable {0} compiler.err.array.and.varargs=\ cannot declare both {0} and {1} in {2} compiler.err.array.dimension.missing=\ @@ -448,6 +452,8 @@ 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=\ @@ -1212,6 +1218,13 @@ underscores in literals are not supported in -source {0}\n\ (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.automatic.resource.not.referenced=\ + automatic resource {0} is never referenced in body of corresponding try statement + 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) --- old/src/share/classes/com/sun/tools/javac/tree/JCTree.java 2010-07-01 10:48:42.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/JCTree.java 2010-07-01 10:48:42.000000000 -0700 @@ -1021,10 +1021,15 @@ public JCBlock body; public List catchers; public JCBlock finalizer; - protected JCTry(JCBlock body, List catchers, JCBlock finalizer) { + public List resources; + protected JCTry(JCBlock body, + List catchers, + JCBlock finalizer, + List resources) { this.body = body; this.catchers = catchers; this.finalizer = finalizer; + this.resources = resources; } @Override public void accept(Visitor v) { v.visitTry(this); } @@ -1040,6 +1045,10 @@ return v.visitTry(this, d); } @Override + public List getResources() { + return resources; + } + @Override public int getTag() { return TRY; } @@ -2162,6 +2171,10 @@ JCCase Case(JCExpression pat, List stats); JCSynchronized Synchronized(JCExpression lock, JCBlock body); JCTry Try(JCBlock body, List catchers, JCBlock finalizer); + JCTry Try(JCBlock body, + List catchers, + JCBlock finalizer, + List resources); JCCatch Catch(JCVariableDecl param, JCBlock body); JCConditional Conditional(JCExpression cond, JCExpression thenpart, --- old/src/share/classes/com/sun/tools/javac/tree/Pretty.java 2010-07-01 10:48:42.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/Pretty.java 2010-07-01 10:48:42.000000000 -0700 @@ -691,6 +691,19 @@ 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 l = tree.catchers; l.nonEmpty(); l = l.tail) { printStat(l.head); --- old/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java 2010-07-01 10:48:43.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java 2010-07-01 10:48:43.000000000 -0700 @@ -335,7 +335,8 @@ JCBlock body = copy(t.body, p); List catchers = copy(t.catchers, p); JCBlock finalizer = copy(t.finalizer, p); - return M.at(t.pos).Try(body, catchers, finalizer); + List resources = copy(t.resources, p); + return M.at(t.pos).Try(body, catchers, finalizer, resources); } public JCTree visitParameterizedType(ParameterizedTypeTree node, P p) { --- old/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java 2010-07-01 10:48:43.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java 2010-07-01 10:48:43.000000000 -0700 @@ -269,7 +269,14 @@ } public JCTry Try(JCBlock body, List catchers, JCBlock finalizer) { - JCTry tree = new JCTry(body, catchers, finalizer); + return Try(body, catchers, finalizer, List.nil()); + } + + public JCTry Try(JCBlock body, + List catchers, + JCBlock finalizer, + List resources) { + JCTry tree = new JCTry(body, catchers, finalizer, resources); tree.pos = pos; return tree; } --- old/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java 2010-07-01 10:48:44.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java 2010-07-01 10:48:44.000000000 -0700 @@ -150,6 +150,7 @@ scan(tree.body); scan(tree.catchers); scan(tree.finalizer); + scan(tree.resources); } public void visitCatch(JCCatch tree) { --- old/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java 2010-07-01 10:48:44.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java 2010-07-01 10:48:44.000000000 -0700 @@ -215,6 +215,7 @@ tree.body = translate(tree.body); tree.catchers = translateCatchers(tree.catchers); tree.finalizer = translate(tree.finalizer); + tree.resources = translate(tree.resources); result = tree; } --- old/src/share/classes/com/sun/tools/javac/util/Names.java 2010-07-01 10:48:44.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/util/Names.java 2010-07-01 10:48:44.000000000 -0700 @@ -148,6 +148,8 @@ 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 @@ 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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ArmFlow.java 2010-07-01 10:48:45.000000000 -0700 @@ -0,0 +1,40 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Test exception analysis of ARM blocks + * @compile/fail -source 6 ArmFlow.java + * @compile/fail/ref=ArmFlow.out -XDrawDiagnostics ArmFlow.java + */ + +import java.io.IOException; +public class ArmFlow implements AutoCloseable { + public static void main(String... args) { + try(ArmFlow armflow = new ArmFlow()) { + 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. + ArmFlow armflowexpr = new ArmFlow(); + 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 not Closeable or + * AutoCloseable. + */ + public void close() throws CustomCloseException { + throw new CustomCloseException(); + } +} + +class CustomCloseException extends Exception {} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ArmFlow.out 2010-07-01 10:48:45.000000000 -0700 @@ -0,0 +1,5 @@ +ArmFlow.java:15:11: compiler.err.except.never.thrown.in.try: java.io.IOException +ArmFlow.java:25:11: compiler.err.except.never.thrown.in.try: java.io.IOException +ArmFlow.java:13:46: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException +ArmFlow.java:23:26: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException +4 errors --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ArmOnNonResource.java 2010-07-01 10:48:46.000000000 -0700 @@ -0,0 +1,42 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify invalid ARM block is not accepted. + * @compile/fail -source 6 ArmOnNonResource.java + * @compile/fail/ref=ArmOnNonResource.out -XDrawDiagnostics ArmOnNonResource.java + */ + +class ArmOnNonResource { + public static void main(String... args) { + try(ArmOnNonResource aonr = new ArmOnNonResource()) { + System.out.println(aonr.toString()); + } + try(ArmOnNonResource aonr = new ArmOnNonResource()) { + System.out.println(aonr.toString()); + } finally {;} + try(ArmOnNonResource aonr = new ArmOnNonResource()) { + System.out.println(aonr.toString()); + } catch (Exception e) {;} + + // Also check expression form + ArmOnNonResource aonr = new ArmOnNonResource(); + 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 not Closeable or + * AutoCloseable. + */ + public void close() { + throw new AssertionError("I'm not Closable!"); + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ArmOnNonResource.out 2010-07-01 10:48:46.000000000 -0700 @@ -0,0 +1,7 @@ +ArmOnNonResource.java:12:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +ArmOnNonResource.java:15:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +ArmOnNonResource.java:18:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +ArmOnNonResource.java:24:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +ArmOnNonResource.java:27:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +ArmOnNonResource.java:30:13: compiler.err.prob.found.req: (compiler.misc.arm.not.applicable.to.type), ArmOnNonResource, java.lang.AutoCloseable +6 errors --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ArmTests.java 2010-07-01 10:48:46.000000000 -0700 @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2009, 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 + * @summary Tests of generated ARM code. + */ + +import java.util.List; +import java.util.ArrayList; + +public class ArmTests { + public static void main(String[] args) { + testCreateFailure1(); + testCreateFailure2(); + testCreateFailure3(); + testCreateFailure4(); + testCreateFailure5(); + + testCreateSuccess1(); + testCreateSuccess2(); + testCreateSuccess3(); + testCreateSuccess4(); + testCreateSuccess5(); + } + + /* + * The following tests simulate a creation failure of every possible + * resource in an ARM 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 closedList = new ArrayList(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); + } + + 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 closedList = new ArrayList(); + 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 testCreateFailure3() { + for (int createFailureId = 0; createFailureId < 3; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List closedList = new ArrayList(); + 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 testCreateFailure4() { + for (int createFailureId = 0; createFailureId < 4; createFailureId++) { + for (int bitMap = 0, n = 1 << createFailureId; bitMap < n; bitMap++) { + int creationFailuresDetected = 0; + List closedList = new ArrayList(); + 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)) { + 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 closedList = new ArrayList(); + 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); + } + } + } + + /** + * 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 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 closedList, int createFailureId) { + List expectedList = new ArrayList(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 closedList = new ArrayList(); + 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 closedList = new ArrayList(); + 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 testCreateSuccess3() { + for (int bitMap = 0, n = 1 << 3; bitMap < n; bitMap++) { + for (int failure = 0; failure < 2; failure++) { + List closedList = new ArrayList(); + 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 testCreateSuccess4() { + for (int bitMap = 0, n = 1 << 4; bitMap < n; bitMap++) { + for (int failure = 0; failure < 2; failure++) { + List closedList = new ArrayList(); + 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 testCreateSuccess5() { + for (int bitMap = 0, n = 1 << 5; bitMap < n; bitMap++) { + for (int failure = 0; failure < 2; failure++) { + List closedList = new ArrayList(); + 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); + } + } + } + + private static Resource createResource(int id, + int closeFailureBitMap, + List 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 closedList; + + Resource(int resourceId, boolean createSucceeds, boolean closeSucceeds, + List 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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/BadArm.java 2010-07-01 10:48:47.000000000 -0700 @@ -0,0 +1,36 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify bad ARMs don't compile + * @compile/fail -source 6 ArmFlow.java + * @compile/fail/ref=BadArm.out -XDrawDiagnostics BadArm.java + */ + +public class BadArm implements AutoCloseable { + public static void main(String... args) { + // illegal repeated name + try(BadArm r1 = new BadArm(); BadArm r1 = new BadArm()) { + System.out.println(r1.toString()); + } + + // illegal duplicate name of method argument + try(BadArm args = new BadArm()) { + System.out.println(args.toString()); + final BadArm thatsIt = new BadArm(); + thatsIt = null; + } + + try(BadArm name = new BadArm()) { + // illegal duplicate name of enclosing try + try(BadArm name = new BadArm()) { + System.out.println(name.toString()); + } + } + + } + + public void close() { + ; + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/BadArm.out 2010-07-01 10:48:47.000000000 -0700 @@ -0,0 +1,5 @@ +BadArm.java:13:46: compiler.err.already.defined: r1, main(java.lang.String...) +BadArm.java:18:20: compiler.err.already.defined: args, main(java.lang.String...) +BadArm.java:21:13: compiler.err.cant.assign.val.to.final.var: thatsIt +BadArm.java:26:24: compiler.err.already.defined: name, main(java.lang.String...) +4 errors --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/BadArmSyntax.java 2010-07-01 10:48:47.000000000 -0700 @@ -0,0 +1,22 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Verify bad ARMs don't compile + * @compile/fail -source 6 BadArmSyntax.java + * @compile/fail/ref=BadArmSyntax.out -XDrawDiagnostics BadArmSyntax.java + */ + +import java.io.IOException; +public class BadArmSyntax implements AutoCloseable { + public static void main(String... args) throws Exception { + // illegal semicolon ending resources + try(BadArm armflow = new BadArm();) { + System.out.println(armflow.toString()); + } + } + + public void close() { + ; + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/BadArmSyntax.out 2010-07-01 10:48:48.000000000 -0700 @@ -0,0 +1,2 @@ +BadArmSyntax.java:14:43: compiler.err.illegal.start.of.expr +1 error --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/DuplicateResource.java 2010-07-01 10:48:48.000000000 -0700 @@ -0,0 +1,42 @@ +/* + * @test /nodynamiccopyright/ + * @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 resources = new ArrayList(); + + 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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/DuplicateResourceDecl.java 2010-07-01 10:48:48.000000000 -0700 @@ -0,0 +1,22 @@ +/* + * @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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/DuplicateResourceDecl.out 2010-07-01 10:48:49.000000000 -0700 @@ -0,0 +1,2 @@ +DuplicateResourceDecl.java:12:45: compiler.err.already.defined: c, main(java.lang.String[]) +1 error --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ImplicitFinal.java 2010-07-01 10:48:49.000000000 -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 not Closeable or + // AutoCloseable. + + public void close() throws IOException { + throw new IOException(); + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ImplicitFinal.out 2010-07-01 10:48:50.000000000 -0700 @@ -0,0 +1,2 @@ +ImplicitFinal.java:14:13: compiler.err.arm.resource.may.not.be.assigned: r +1 error --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/NestedArm.java 2010-07-01 10:48:50.000000000 -0700 @@ -0,0 +1,37 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Joseph D. Darcy + * @summary Verify nested ARM blocks have proper semantics + * @compile/fail -source 6 NestedArm.java + * @compile NestedArm.java + * @run main NestedArm + */ + +import java.io.IOException; +public class NestedArm implements AutoCloseable { + private final Class exceptionOnClose; + public NestedArm(Class exception) { + exceptionOnClose = exception; + } + + public static void main(String... args) throws Exception { + try(NestedArm armflow = new NestedArm(AnotherCustomCloseException.class)) { + try(NestedArm armflow2 = new NestedArm(YetAnotherCustomCloseException.class)) { + ; + } + } catch (AnotherCustomCloseException e) { + e.printStackTrace(); + } + } + + /* + * A close method. + */ + public void close() throws Exception { + throw exceptionOnClose.newInstance(); + } +} + +class AnotherCustomCloseException extends Exception {} +class YetAnotherCustomCloseException extends Exception {} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/PlainTry.java 2010-07-01 10:48:50.000000000 -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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/PlainTry.out 2010-07-01 10:48:51.000000000 -0700 @@ -0,0 +1,2 @@ +PlainTry.java:11:9: compiler.err.try.without.catch.finally.or.resource.decls +1 error --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/PlainTry6.out 2010-07-01 10:48:51.000000000 -0700 @@ -0,0 +1,2 @@ +PlainTry.java:11:9: compiler.err.try.without.catch.or.finally +1 error --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ResourceOutsideTry.java 2010-07-01 10:48:51.000000000 -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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ResourceOutsideTry.out 2010-07-01 10:48:52.000000000 -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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/ResourceTypeVar.java 2010-07-01 10:48:52.000000000 -0700 @@ -0,0 +1,20 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @author Maurizio Cimadamore + * @summary Resource of a type-variable type crashes Flow + * @compile ResourceTypeVar.java + */ + +class ResourceTypeVar { + + 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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/AutomaticResourceManagement/WeirdArm.java 2010-07-01 10:48:52.000000000 -0700 @@ -0,0 +1,25 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Strangs ARMs + * @compile/fail -source 6 WeirdArm.java + * @compile WeirdArm.java + * @run main WeirdArm + */ +// /fail/ref=BadArm.out -XDrawDiagnostics +public class WeirdArm implements AutoCloseable { + private static int closeCount = 0; + public static void main(String... args) { + try(WeirdArm r1 = new WeirdArm(); WeirdArm r2 = r1) { + if (r1 != r2) + throw new RuntimeException("Unexpected inequality."); + } + if (closeCount != 2) + throw new RuntimeException("bad closeCount" + closeCount); + } + + public void close() { + closeCount++; + } +}