--- old/make/build.properties 2010-07-15 23:37:52.000000000 -0700 +++ new/make/build.properties 2010-07-15 23:37:52.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-15 23:37:53.000000000 -0700 +++ new/src/share/classes/com/sun/source/tree/TryTree.java 2010-07-15 23:37:53.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-15 23:37:53.000000000 -0700 +++ new/src/share/classes/com/sun/source/util/TreeScanner.java 2010-07-15 23:37:53.000000000 -0700 @@ -209,7 +209,8 @@ } 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; --- old/src/share/classes/com/sun/tools/javac/code/Lint.java 2010-07-15 23:37:54.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Lint.java 2010-07-15 23:37:54.000000000 -0700 @@ -208,7 +208,12 @@ /** * Warn about potentially unsafe vararg methods */ - VARARGS("varargs"); + VARARGS("varargs"), + + /** + * Warn about arm resources + */ + ARM("arm"); LintCategory(String option) { this(option, false); --- old/src/share/classes/com/sun/tools/javac/code/Source.java 2010-07-15 23:37:54.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Source.java 2010-07-15 23:37:54.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-15 23:37:55.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Symbol.java 2010-07-15 23:37:55.000000000 -0700 @@ -993,12 +993,17 @@ return data == ElementKind.EXCEPTION_PARAMETER; } + public boolean isResourceVariable() { + return data == ElementKind.RESOURCE_VARIABLE; + } + 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 + // 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. --- old/src/share/classes/com/sun/tools/javac/code/Symtab.java 2010-07-15 23:37:55.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/code/Symtab.java 2010-07-15 23:37:55.000000000 -0700 @@ -148,6 +148,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. */ @@ -159,6 +160,9 @@ /** The symbol representing the final finalize method on enums */ public final MethodSymbol enumFinalFinalize; + /** The symbol representing the close method on ARM AutoCloseable type */ + public final MethodSymbol autoCloseableClose; + /** The predefined type that belongs to a tag. */ public final Type[] typeOfTag = new Type[TypeTags.TypeTagCount]; @@ -444,6 +448,12 @@ 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.nil(), voidType, + List.of(exceptionType), methodClass), + autoCloseableType.tsym); synthesizeEmptyInterfaceIfMissing(cloneableType); synthesizeEmptyInterfaceIfMissing(serializableType); --- old/src/share/classes/com/sun/tools/javac/comp/Attr.java 2010-07-15 23:37:56.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Attr.java 2010-07-15 23:37:56.000000000 -0700 @@ -192,7 +192,7 @@ 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 @@ !((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()) { //ARM resource + log.error(pos, "arm.resource.may.not.be.assigned", v); + } else { + log.error(pos, "cant.assign.val.to.final.var", v); + } } } @@ -372,6 +376,10 @@ */ 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 @@ * @param pt The prototype visitor argument. */ Type attribTree(JCTree tree, Env env, int pkind, Type pt) { + return attribTree(tree, env, pkind, pt, "incompatible.types"); + } + + Type attribTree(JCTree tree, Env env, int pkind, Type pt, String errKey) { Env 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 @@ this.env = prevEnv; this.pkind = prevPkind; this.pt = prevPt; + this.errKey = prevErrKey; } } @@ -412,6 +427,10 @@ return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType); } + public Type attribExpr(JCTree tree, Env env, Type pt, String key) { + return attribTree(tree, env, VAL, pt.tag != ERROR ? pt : Type.noType, key); + } + /** Derived visitor method: attribute an expression tree with * no constraints on the computed type. */ @@ -976,14 +995,34 @@ } public void visitTry(JCTry tree) { + // Create a new local environment with a local + Env 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 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, "arm.not.applicable.to.type"); + VarSymbol var = (VarSymbol)TreeInfo.symbolFor(resource); + var.setData(ElementKind.RESOURCE_VARIABLE); + } else { + attribExpr(resource, tryEnv, syms.autoCloseableType, "arm.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 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 @@ -1003,7 +1042,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; } @@ -2139,6 +2180,15 @@ checkAssignable(tree.pos(), v, tree.selected, env); } + 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, "arm.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, --- old/src/share/classes/com/sun/tools/javac/comp/Check.java 2010-07-15 23:37:56.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Check.java 2010-07-15 23:37:56.000000000 -0700 @@ -393,6 +393,10 @@ * @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 @@ 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 --- old/src/share/classes/com/sun/tools/javac/comp/Flow.java 2010-07-15 23:37:57.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Flow.java 2010-07-15 23:37:57.000000000 -0700 @@ -28,6 +28,8 @@ 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 @@ */ List caught; + /** The list of unreferenced automatic resources. + */ + Map unrefdResources; + /** Set when processing a loop body the second time for DU analysis. */ boolean loopPassTwo = false; @@ -963,6 +969,7 @@ public void visitTry(JCTry tree) { List caughtPrev = caught; List thrownPrev = thrown; + Map unrefdResourcesPrev = unrefdResources; thrown = List.nil(); for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { List subClauses = TreeInfo.isMultiCatch(l.head) ? @@ -977,6 +984,32 @@ pendingExits = new ListBuffer(); Bits initsTry = inits.dup(); uninitsTry = uninits.dup(); + unrefdResources = new LinkedHashMap(); + 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 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 thrownInTry = thrown; thrown = thrownPrev; @@ -987,6 +1020,14 @@ Bits uninitsEnd = uninits; int nextadrCatch = nextadr; + if (!unrefdResources.isEmpty() && + lint.isEnabled(Lint.LintCategory.ARM)) { + for (Map.Entry e : unrefdResources.entrySet()) { + log.warning(e.getValue().pos(), + "automatic.resource.not.referenced", e.getKey()); + } + } + List caughtInTry = List.nil(); for (List l = tree.catchers; l.nonEmpty(); l = l.tail) { alive = true; @@ -1070,6 +1111,7 @@ while (exits.nonEmpty()) pendingExits.append(exits.next()); } uninitsTry.andSet(uninitsTryPrev).andSet(uninits); + unrefdResources = unrefdResourcesPrev; } public void visitConditional(JCConditional tree) { @@ -1293,8 +1335,16 @@ } 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) { --- old/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-07-15 23:37:57.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/Lower.java 2010-07-15 23:37:57.000000000 -0700 @@ -605,6 +605,23 @@ 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 * with user-defined symbols. * @@ -1299,6 +1316,11 @@ */ Scope proxies; + /** A scope containing all unnamed resource variables/saved exception variables + * for translated ARM blocks + */ + Scope armVars; + /** 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 @@ -1400,6 +1422,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 makeArmTry(JCTry tree) { + make_at(tree.pos()); + armVars = armVars.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)); + armVars = armVars.leave(); + 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, + makeSyntheticName(names.fromString("armVar" + + depth), armVars), + (resource.type.tag == TypeTags.BOT) ? + syms.autoCloseableType : resource.type, + currentMethodSym); + armVars.enter(syntheticArmVar); + JCVariableDecl syntheticArmVarDecl = + make.VarDef(syntheticArmVar, (JCExpression)resource); + expr = (JCExpression)make.Ident(syntheticArmVar); + stats.add(syntheticArmVarDecl); + } + + // Add primaryException declaration + VarSymbol primaryException = + new VarSymbol(SYNTHETIC, + makeSyntheticName(names.fromString("primaryException" + + depth), armVars), + syms.throwableType, + currentMethodSym); + armVars.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.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 +3543,15 @@ 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 @@ actualSymbols = new HashMap(); freevarCache = new HashMap>(); proxies = new Scope(syms.noSymbol); + armVars = new Scope(syms.noSymbol); outerThisStack = List.nil(); accessNums = new HashMap(); accessSyms = new HashMap(); --- old/src/share/classes/com/sun/tools/javac/comp/TransTypes.java 2010-07-15 23:37:58.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/comp/TransTypes.java 2010-07-15 23:37:58.000000000 -0700 @@ -535,6 +535,14 @@ result = tree; } + 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)); --- old/src/share/classes/com/sun/tools/javac/jvm/CRTable.java 2010-07-15 23:37:58.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/jvm/CRTable.java 2010-07-15 23:37:58.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-15 23:37:59.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/parser/JavacParser.java 2010-07-15 23:37:59.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; @@ -1846,6 +1851,7 @@ * | 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 @@ } case TRY: { S.nextToken(); + List resources = List.nil(); + if (S.token() == LPAREN) { + checkAutomaticResourceManagement(); + S.nextToken(); + resources = resources(); + accept(RPAREN); + } JCBlock body = block(); ListBuffer catchers = new ListBuffer(); JCBlock finalizer = null; @@ -1926,9 +1939,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(resources, body, catchers.toList(), finalizer); } case SWITCH: { S.nextToken(); @@ -2389,6 +2406,39 @@ return toP(F.at(pos).VarDef(mods, name, type, null)); } + /** Resources = Resource { ";" Resources } + */ + List resources() { + ListBuffer defs = new ListBuffer(); + 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} */ public JCTree.JCCompilationUnit parseCompilationUnit() { @@ -3220,6 +3270,12 @@ if (!allowMulticatch) { log.error(S.pos(), "multicatch.not.supported.in.source", source.name); 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-15 23:37:59.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/resources/compiler.properties 2010-07-15 23:37:59.000000000 -0700 @@ -61,6 +61,8 @@ 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 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.final.parameter.may.not.be.assigned=\ final parameter {0} may not be assigned +compiler.err.arm.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 @@ 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.options=\ The following options were not recognized by any processor: ''{0}'' +compiler.warn.arm.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 @@ 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.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-15 23:38:00.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/JCTree.java 2010-07-15 23:38:00.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(List resources, + JCBlock body, + List catchers, + JCBlock finalizer) { 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(List resources, + JCBlock body, + List catchers, + JCBlock finalizer); 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-15 23:38:00.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/Pretty.java 2010-07-15 23:38:00.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-15 23:38:01.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeCopier.java 2010-07-15 23:38:01.000000000 -0700 @@ -332,10 +332,11 @@ public JCTree visitTry(TryTree node, P p) { JCTry t = (JCTry) node; + List resources = copy(t.resources, p); 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); + return M.at(t.pos).Try(resources, body, catchers, finalizer); } public JCTree visitParameterizedType(ParameterizedTypeTree node, P p) { --- old/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java 2010-07-15 23:38:01.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeMaker.java 2010-07-15 23:38:01.000000000 -0700 @@ -269,7 +269,14 @@ } public JCTry Try(JCBlock body, List catchers, JCBlock finalizer) { - JCTry tree = new JCTry(body, catchers, finalizer); + return Try(List.nil(), body, catchers, finalizer); + } + + public JCTry Try(List resources, + JCBlock body, + List catchers, + JCBlock finalizer) { + JCTry tree = new JCTry(resources, body, catchers, finalizer); tree.pos = pos; return tree; } --- old/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java 2010-07-15 23:38:02.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeScanner.java 2010-07-15 23:38:02.000000000 -0700 @@ -147,6 +147,7 @@ } public void visitTry(JCTry tree) { + scan(tree.resources); scan(tree.body); scan(tree.catchers); scan(tree.finalizer); --- old/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java 2010-07-15 23:38:02.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/tree/TreeTranslator.java 2010-07-15 23:38:02.000000000 -0700 @@ -212,6 +212,7 @@ } 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); --- old/src/share/classes/com/sun/tools/javac/util/Names.java 2010-07-15 23:38:03.000000000 -0700 +++ new/src/share/classes/com/sun/tools/javac/util/Names.java 2010-07-15 23:38:02.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/TryWithResource/ArmFlow.java 2010-07-15 23:38:03.000000000 -0700 @@ -0,0 +1,39 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 + * @author Joseph D. Darcy + * @summary Test exception analysis of ARM blocks + * @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/TryWithResource/ArmFlow.out 2010-07-15 23:38:03.000000000 -0700 @@ -0,0 +1,5 @@ +ArmFlow.java:14:11: compiler.err.except.never.thrown.in.try: java.io.IOException +ArmFlow.java:24:11: compiler.err.except.never.thrown.in.try: java.io.IOException +ArmFlow.java:12:46: compiler.err.unreported.exception.need.to.catch.or.throw: CustomCloseException +ArmFlow.java:22: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/TryWithResource/ArmInference.java 2010-07-15 23:38:04.000000000 -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 ARM context + * @compile ArmInference.java + */ + +class ArmInference { + + public void test() { + try(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/TryWithResource/ArmIntersection.java 2010-07-15 23:38:04.000000000 -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 ArmIntersection.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 getX() { return null; } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmIntersection02.java 2010-07-15 23:38:05.000000000 -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 arm block + * @compile/fail/ref=ArmIntersection02.out -XDrawDiagnostics ArmIntersection02.java + */ + +class ArmIntersection02 { + + 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 getX() { return null; } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmIntersection02.out 2010-07-15 23:38:05.000000000 -0700 @@ -0,0 +1,3 @@ +ArmIntersection02.java:25:21: compiler.err.unreported.exception.need.to.catch.or.throw: ArmIntersection02.Exception2 +ArmIntersection02.java:31:21: compiler.err.unreported.exception.need.to.catch.or.throw: ArmIntersection02.Exception1 +2 errors --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmLint.java 2010-07-15 23:38:05.000000000 -0700 @@ -0,0 +1,55 @@ +/* + * @test /nodynamiccopyright/ + * @bug 6911256 6964740 6965277 + * @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; + } + + /** + * Not the AutoCloseable method of a resource. + */ + public void close (int arg) { + return; + } +} + +@Deprecated +class DeprecatedAutoCloseable implements AutoCloseable { + public DeprecatedAutoCloseable(){super();} + + @Override + public void close () { + return; + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmLint.out 2010-07-15 23:38:06.000000000 -0700 @@ -0,0 +1,3 @@ +ArmLint.java:14:15: compiler.warn.arm.explicit.close.call +ArmLint.java:13:13: compiler.warn.automatic.resource.not.referenced: r3 +2 warnings --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmMultiCatch.java 2010-07-15 23:38:06.000000000 -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 ARM and multi-catch play well together + * @compile ArmMultiCatch.java + * @run main ArmMultiCatch + */ + +import java.io.IOException; +public class ArmMultiCatch implements AutoCloseable { + private final Class exceptionClass; + + private ArmMultiCatch(Class exceptionClass) { + this.exceptionClass = exceptionClass; + } + + public static void main(String... args) { + test(new ArmMultiCatch(CustomCloseException1.class), + CustomCloseException1.class); + + test(new ArmMultiCatch(CustomCloseException2.class), + CustomCloseException2.class); + } + + private static void test(ArmMultiCatch armMultiCatch, + Class expected) { + try(armMultiCatch) { + System.out.println(armMultiCatch.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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/ArmOnNonResource.java 2010-07-15 23:38:07.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/TryWithResource/ArmOnNonResource.out 2010-07-15 23:38:07.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/TryWithResource/ArmTests.java 2010-07-15 23:38:07.000000000 -0700 @@ -0,0 +1,743 @@ +/* + * 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 ARM code. + */ + +import java.util.List; +import java.util.ArrayList; + +public class ArmTests { + 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 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 testCreateFailure2Nested() { + 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)) { + 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 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 testCreateFailure3Nested() { + 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)) { + 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 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 testCreateFailure4Nested() { + 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)) { + 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 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); + } + } + } + + 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 closedList = new ArrayList(); + 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 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 testCreateSuccess2Nested() { + 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)) { + 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 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 testCreateSuccess3Nested() { + 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)) { + 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 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 testCreateSuccess4Nested() { + 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)) { + 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 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); + } + } + } + + public static void testCreateSuccess5Nested() { + 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)) { + 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 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/TryWithResource/BadArm.java 2010-07-15 23:38:08.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/TryWithResource/BadArm.out 2010-07-15 23:38:08.000000000 -0700 @@ -0,0 +1,5 @@ +BadArm.java:13:39: compiler.err.already.defined: r1, main(java.lang.String...) +BadArm.java:18:13: 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:17: 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/TryWithResource/BadArmSyntax.java 2010-07-15 23:38:08.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/TryWithResource/BadArmSyntax.out 2010-07-15 23:38:09.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/TryWithResource/DuplicateResource.java 2010-07-15 23:38:09.000000000 -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 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/TryWithResource/DuplicateResourceDecl.java 2010-07-15 23:38:10.000000000 -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 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/TryWithResource/DuplicateResourceDecl.out 2010-07-15 23:38:10.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/TryWithResource/ImplicitFinal.java 2010-07-15 23:38:10.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/TryWithResource/ImplicitFinal.out 2010-07-15 23:38:11.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/TryWithResource/PlainTry.java 2010-07-15 23:38:11.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/TryWithResource/PlainTry.out 2010-07-15 23:38:12.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/TryWithResource/PlainTry6.out 2010-07-15 23:38:12.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/TryWithResource/ResourceOutsideTry.java 2010-07-15 23:38:12.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/TryWithResource/ResourceOutsideTry.out 2010-07-15 23:38:13.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/TryWithResource/ResourceTypeVar.java 2010-07-15 23:38:13.000000000 -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 { + + 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/TryWithResource/WeirdArm.java 2010-07-15 23:38:13.000000000 -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 ARMs + * @compile/fail -source 6 WeirdArm.java + * @compile WeirdArm.java + * @run main WeirdArm + */ + +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++; + } +} --- /dev/null 2009-07-06 20:02:10.000000000 -0700 +++ new/test/tools/javac/processing/model/element/TestResourceVariable.java 2010-07-15 23:38:14.000000000 -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 + +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 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 { + 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(); + } +}