1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package com.sun.tools.javac.code;
  26 
  27 import java.util.HashMap;
  28 import java.util.Map;
  29 
  30 import com.sun.tools.javac.code.Kinds.Kind;
  31 import com.sun.tools.javac.code.Scope.WriteableScope;
  32 import com.sun.tools.javac.code.Symbol;
  33 import com.sun.tools.javac.code.Symbol.ClassSymbol;
  34 import com.sun.tools.javac.code.Symbol.Completer;
  35 import com.sun.tools.javac.code.Symbol.CompletionFailure;
  36 import com.sun.tools.javac.util.Context;
  37 
  38 /** When a CompletionFailure is thrown when user code is running, it shouldn't be
  39  *  thrown out to the client code, but rather skipped, and then rethrown later if javac
  40  *  itself will complete the Symbol.
  41  *
  42  *  On all places where javac invokes client code (e.g. TaskListeners, annotation
  43  *  Processors), the {@code userCodeHandler} should be set using
  44  *  {@link DeferredCompletionFailureHandler#setHandler}, and the original handler
  45  *  should be restored when the control returns back to javac.
  46  *
  47  *  Implementations of API methods should use {@link Symbol#apiComplete()} instead of
  48  *  {@link Symbol#complete}, as the {@code apiComplete} method will invoke
  49  *  {@link DeferredCompletionFailureHandler#handleAPICompletionFailure }, which will
  50  *  catch the CompletionFailure and will either rethrow it or skip it, depending on
  51  *  the context.
  52  */
  53 public class DeferredCompletionFailureHandler {
  54 
  55     protected static final Context.Key<DeferredCompletionFailureHandler> deferredCompletionFailureHandlerKey = new Context.Key<>();
  56 
  57     public static DeferredCompletionFailureHandler instance(Context context) {
  58         DeferredCompletionFailureHandler instance = context.get(deferredCompletionFailureHandlerKey);
  59         if (instance == null)
  60             instance = new DeferredCompletionFailureHandler(context);
  61         return instance;
  62     }
  63 
  64     public final Handler userCodeHandler = new Handler() {
  65         private final Map<ClassSymbol, FlipSymbolDescription> class2Flip = new HashMap<>();
  66 
  67         public void install() {
  68             class2Flip.values().forEach(f -> f.flip());
  69         }
  70         public void handleAPICompletionFailure(CompletionFailure cf) {
  71             //ignore
  72         }
  73         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
  74             class2Flip.put(sym, new FlipSymbolDescription(sym, new DeferredCompleter(origCompleter) {
  75                 @Override public void complete(Symbol sym) throws CompletionFailure {
  76                     class2Flip.remove(sym);
  77                     super.complete(sym);
  78                 }
  79             }));
  80         }
  81         public void classSymbolRemoved(ClassSymbol sym) {
  82             class2Flip.remove(sym);
  83         }
  84         public void uninstall() {
  85             class2Flip.values().forEach(f -> f.flip());
  86         }
  87     };
  88 
  89     public final Handler javacCodeHandler = new Handler() {
  90         public void install() {
  91         }
  92         public void handleAPICompletionFailure(CompletionFailure cf) {
  93             throw cf;
  94         }
  95         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {}
  96         public void classSymbolRemoved(ClassSymbol sym) {}
  97         public void uninstall() {
  98         }
  99     };
 100 
 101     private Handler handler = javacCodeHandler;
 102 
 103     protected DeferredCompletionFailureHandler(Context context) {
 104         context.put(deferredCompletionFailureHandlerKey, this);
 105     }
 106 
 107     public Handler setHandler(Handler h) {
 108         if (h == handler) return handler;
 109 
 110         handler.uninstall();
 111         Handler prev = handler;
 112         handler = h;
 113         handler.install();
 114         return prev;
 115     }
 116 
 117     public void handleAPICompletionFailure(CompletionFailure cf) {
 118         handler.handleAPICompletionFailure(cf);
 119     }
 120 
 121     public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
 122         handler.classSymbolCompleteFailed(sym, origCompleter);
 123     }
 124 
 125     public void classSymbolRemoved(ClassSymbol sym) {
 126         handler.classSymbolRemoved(sym);
 127     }
 128 
 129     public boolean isDeferredCompleter(Completer c) {
 130         return c instanceof DeferredCompleter;
 131     }
 132 
 133     public interface Handler {
 134         public void install();
 135         public void handleAPICompletionFailure(CompletionFailure cf);
 136         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter);
 137         public void classSymbolRemoved(ClassSymbol sym);
 138         public void uninstall();
 139     }
 140 
 141     private class DeferredCompleter implements Completer {
 142 
 143         private final Completer origCompleter;
 144 
 145         public DeferredCompleter(Completer origCompleter) {
 146             this.origCompleter = origCompleter;
 147         }
 148 
 149         @Override
 150         public void complete(Symbol sym) throws CompletionFailure {
 151             origCompleter.complete(sym);
 152         }
 153     }
 154 
 155     private static class FlipSymbolDescription {
 156         public final ClassSymbol sym;
 157         public Type type;
 158         public Kind kind;
 159         public WriteableScope members;
 160         public Completer completer;
 161 
 162         public FlipSymbolDescription(ClassSymbol sym, Completer completer) {
 163             this.sym = sym;
 164             this.type = sym.type;
 165             this.kind = sym.kind;
 166             this.members = null;
 167             this.completer = completer;
 168         }
 169 
 170         public void flip() {
 171             Type prevType = sym.type;
 172             sym.type = type;
 173             this.type = prevType;
 174             Kind prevKind = sym.kind;
 175             sym.kind = kind;
 176             this.kind = prevKind;
 177             Completer prevCompleter = sym.completer;
 178             sym.completer = completer;
 179             this.completer = prevCompleter;
 180             WriteableScope prevMembers = sym.members_field;
 181             sym.members_field = members;
 182             this.members = prevMembers;
 183         }
 184 
 185     }
 186 }