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.Map;
  28 import java.util.WeakHashMap;
  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.ClassSymbol;
  33 import com.sun.tools.javac.code.Symbol.Completer;
  34 import com.sun.tools.javac.code.Symbol.CompletionFailure;
  35 import com.sun.tools.javac.util.Context;
  36 
  37 /** When a CompletionFailure is thrown when user code is running, it shouldn't be
  38  *  thrown out to the client code, but rather skipped, and then rethrown later if javac
  39  *  itself will complete the Symbol.
  40  *
  41  *  On all places where javac invokes client code (e.g. TaskListeners, annotation
  42  *  Processors), the {@code userCodeHandler} should be set using
  43  *  {@link DeferredCompletionFailureHandler#setHandler}, and the original handler
  44  *  should be restored when the control returns back to javac.
  45  *
  46  *  Implementations of API methods should use {@link Symbol#apiComplete()} instead of
  47  *  {@link Symbol#complete}, as the {@code apiComplete} method will invoke
  48  *  {@link DeferredCompletionFailureHandler#handleAPICompletionFailure }, which will
  49  *  catch the CompletionFailure and will either rethrow it or skip it, depending on
  50  *  the context.
  51  */
  52 public class DeferredCompletionFailureHandler {
  53 
  54     protected static final Context.Key<DeferredCompletionFailureHandler> deferredCompletionFailureHandlerKey = new Context.Key<>();
  55 
  56     public static DeferredCompletionFailureHandler instance(Context context) {
  57         DeferredCompletionFailureHandler instance = context.get(deferredCompletionFailureHandlerKey);
  58         if (instance == null)
  59             instance = new DeferredCompletionFailureHandler(context);
  60         return instance;
  61     }
  62 
  63     public final Handler userCodeHandler = new Handler() {
  64         public void install() {
  65             class2Flip.values().forEach(f -> f.flip());
  66         }
  67         public void handleAPICompletionFailure(CompletionFailure cf) {
  68             //ignore
  69         }
  70         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
  71             class2Flip.put(sym, new FlipSymbolDescription(sym, new DeferredCompleter(origCompleter)));
  72         }
  73         public void uninstall() {
  74             class2Flip.values().forEach(f -> f.flip());
  75         }
  76     };
  77 
  78     public final Handler javacCodeHandler = new Handler() {
  79         public void install() {
  80         }
  81         public void handleAPICompletionFailure(CompletionFailure cf) {
  82             throw cf;
  83         }
  84         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {}
  85         public void uninstall() {
  86         }
  87     };
  88 
  89     private Handler handler = javacCodeHandler;
  90 
  91     private final Map<ClassSymbol, FlipSymbolDescription> class2Flip = new WeakHashMap<>();
  92 
  93     protected DeferredCompletionFailureHandler(Context context) {
  94         context.put(deferredCompletionFailureHandlerKey, this);
  95     }
  96 
  97     public Handler setHandler(Handler h) {
  98         if (h == handler) return handler;
  99 
 100         handler.uninstall();
 101         Handler prev = handler;
 102         handler = h;
 103         handler.install();
 104         return prev;
 105     }
 106 
 107     public void handleAPICompletionFailure(CompletionFailure cf) {
 108         handler.handleAPICompletionFailure(cf);
 109     }
 110 
 111     public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
 112         handler.classSymbolCompleteFailed(sym, origCompleter);
 113     }
 114 
 115     public boolean isDeferredCompleter(Completer c) {
 116         return c instanceof DeferredCompleter;
 117     }
 118 
 119     public interface Handler {
 120         public void install();
 121         public void handleAPICompletionFailure(CompletionFailure cf);
 122         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter);
 123         public void uninstall();
 124     }
 125 
 126     private class DeferredCompleter implements Completer {
 127 
 128         private final Completer origCompleter;
 129 
 130         public DeferredCompleter(Completer origCompleter) {
 131             this.origCompleter = origCompleter;
 132         }
 133 
 134         @Override
 135         public void complete(Symbol sym) throws CompletionFailure {
 136             class2Flip.remove(sym);
 137             origCompleter.complete(sym);
 138         }
 139     }
 140 
 141     private static class FlipSymbolDescription {
 142         public final ClassSymbol sym;
 143         public Type type;
 144         public Kind kind;
 145         public WriteableScope members;
 146         public Completer completer;
 147 
 148         public FlipSymbolDescription(ClassSymbol sym, Completer completer) {
 149             this.sym = sym;
 150             this.type = sym.type;
 151             this.kind = sym.kind;
 152             this.members = null;
 153             this.completer = completer;
 154         }
 155 
 156         public void flip() {
 157             Type prevType = sym.type;
 158             sym.type = type;
 159             this.type = prevType;
 160             Kind prevKind = sym.kind;
 161             sym.kind = kind;
 162             this.kind = prevKind;
 163             Completer prevCompleter = sym.completer;
 164             sym.completer = completer;
 165             this.completer = prevCompleter;
 166             WriteableScope prevMembers = sym.members_field;
 167             sym.members_field = members;
 168             this.members = prevMembers;
 169         }
 170 
 171     }
 172 }