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 speculativeCodeHandler = new Handler() {
  90         private final Map<ClassSymbol, FlipSymbolDescription> class2Flip = new HashMap<>();
  91 
  92         public void install() {
  93         }
  94         public void handleAPICompletionFailure(CompletionFailure cf) {
  95             throw cf;
  96         }
  97         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
  98             class2Flip.put(sym, new FlipSymbolDescription(sym, new DeferredCompleter(origCompleter)));
  99         }
 100         public void classSymbolRemoved(ClassSymbol sym) {
 101             class2Flip.remove(sym);
 102         }
 103         public void uninstall() {
 104             class2Flip.values().forEach(f -> f.flip());
 105             class2Flip.clear();
 106         }
 107     };
 108 
 109     public final Handler javacCodeHandler = new Handler() {
 110         public void install() {
 111         }
 112         public void handleAPICompletionFailure(CompletionFailure cf) {
 113             throw cf;
 114         }
 115         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {}
 116         public void classSymbolRemoved(ClassSymbol sym) {}
 117         public void uninstall() {
 118         }
 119     };
 120 
 121     private Handler handler = javacCodeHandler;
 122 
 123     protected DeferredCompletionFailureHandler(Context context) {
 124         context.put(deferredCompletionFailureHandlerKey, this);
 125     }
 126 
 127     public Handler setHandler(Handler h) {
 128         if (h == handler) return handler;
 129 
 130         handler.uninstall();
 131         Handler prev = handler;
 132         handler = h;
 133         handler.install();
 134         return prev;
 135     }
 136 
 137     public void handleAPICompletionFailure(CompletionFailure cf) {
 138         handler.handleAPICompletionFailure(cf);
 139     }
 140 
 141     public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter) {
 142         handler.classSymbolCompleteFailed(sym, origCompleter);
 143     }
 144 
 145     public void classSymbolRemoved(ClassSymbol sym) {
 146         handler.classSymbolRemoved(sym);
 147     }
 148 
 149     public boolean isDeferredCompleter(Completer c) {
 150         return c instanceof DeferredCompleter;
 151     }
 152 
 153     public interface Handler {
 154         public void install();
 155         public void handleAPICompletionFailure(CompletionFailure cf);
 156         public void classSymbolCompleteFailed(ClassSymbol sym, Completer origCompleter);
 157         public void classSymbolRemoved(ClassSymbol sym);
 158         public void uninstall();
 159     }
 160 
 161     private class DeferredCompleter implements Completer {
 162 
 163         private final Completer origCompleter;
 164 
 165         public DeferredCompleter(Completer origCompleter) {
 166             this.origCompleter = origCompleter;
 167         }
 168 
 169         @Override
 170         public void complete(Symbol sym) throws CompletionFailure {
 171             origCompleter.complete(sym);
 172         }
 173     }
 174 
 175     private static class FlipSymbolDescription {
 176         public final ClassSymbol sym;
 177         public Type type;
 178         public Kind kind;
 179         public WriteableScope members;
 180         public Completer completer;
 181 
 182         public FlipSymbolDescription(ClassSymbol sym, Completer completer) {
 183             this.sym = sym;
 184             this.type = sym.type;
 185             this.kind = sym.kind;
 186             this.members = null;
 187             this.completer = completer;
 188         }
 189 
 190         public void flip() {
 191             Type prevType = sym.type;
 192             sym.type = type;
 193             this.type = prevType;
 194             Kind prevKind = sym.kind;
 195             sym.kind = kind;
 196             this.kind = prevKind;
 197             Completer prevCompleter = sym.completer;
 198             sym.completer = completer;
 199             this.completer = prevCompleter;
 200             WriteableScope prevMembers = sym.members_field;
 201             sym.members_field = members;
 202             this.members = prevMembers;
 203         }
 204 
 205     }
 206 }