001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.compilers.opt.inlining;
014    
015    import java.io.FileOutputStream;
016    import java.io.IOException;
017    import java.io.PrintStream;
018    import java.util.Iterator;
019    
020    import org.jikesrvm.VM;
021    import org.jikesrvm.classloader.RVMClass;
022    import org.jikesrvm.classloader.ClassLoadingListener;
023    import org.jikesrvm.classloader.RVMMethod;
024    import org.jikesrvm.compilers.common.CompiledMethod;
025    import org.jikesrvm.compilers.common.CompiledMethods;
026    import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
027    
028    /**
029     * This class acts as an intermediary between RVMClassLoader and the
030     * optimizing compiler's dependency database.  Just before a class
031     * is marked as INITIALIZED, RVMClass.initialize() invokes
032     * ClassLoadingDependencyManager.classInitialized(), which is responsible
033     * for identifying and performing all necessary invalidations of
034     * opt compiler code.
035     */
036    public final class ClassLoadingDependencyManager implements ClassLoadingListener {
037    
038      /** Database holding information on class loading */
039      private final InvalidationDatabase db = new InvalidationDatabase();
040    
041      /** Debug execution */
042      static final boolean DEBUG = false;
043      /** Trace execution */
044      static final boolean TRACE = false;
045      /** Stream used in debug tracing */
046      private static PrintStream log;
047    
048      ////////////////////////
049      // Entrypoints from RVMClass
050      ////////////////////////
051      public synchronized void classInitialized(RVMClass c, boolean writingBootImage) {
052        // Process any dependencies on methods not being overridden.
053        if (!writingBootImage) {
054          if (DEBUG) {
055            report("CLDM: " + c + " is about to be marked as initialized.\n");
056          }
057          handleOverriddenMethods(c);
058          handleSubclassing(c);
059        }
060        InterfaceHierarchy.notifyClassInitialized(c);
061      }
062    
063      /////////////////////////
064      // Entrypoints for the opt compiler to record dependencies
065      /////////////////////////
066    
067      /**
068       * Record that the code currently being compiled (cm) must be
069       * invalidated if source is overridden.
070       */
071      public synchronized void addNotOverriddenDependency(RVMMethod source, CompiledMethod cm) {
072        int cmid = cm.getId();
073        if (TRACE || DEBUG) {
074          report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not being overridden\n");
075        }
076        db.addNotOverriddenDependency(source, cmid);
077      }
078    
079      /**
080       * Record that the code currently being compiled (cm) must be
081       * invalidated if source ever has a subclass.
082       */
083      public synchronized void addNoSubclassDependency(RVMClass source, CompiledMethod cm) {
084        int cmid = cm.getId();
085        if (TRACE || DEBUG) {
086          report("CLDM: " + cmid + "(" + cm.getMethod() + ") is dependent on " + source + " not having a subclass\n");
087        }
088        db.addNoSubclassDependency(source, cmid);
089      }
090    
091      ////////////////////////
092      // Implementation
093      ////////////////////////
094    
095      /**
096       * Take action when a method is overridden.
097       * @param c a class that has just been loaded.
098       */
099      private void handleOverriddenMethods(RVMClass c) {
100        if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do.
101        RVMClass sc = c.getSuperClass();
102        // for each virtual method of sc, if it is overriden by
103        // a virtual method declared by c, then handle any required invalidations.
104        RVMMethod[] sc_methods = sc.getVirtualMethods();
105        RVMMethod[] c_methods = c.getVirtualMethods();
106        for (int i = 0; i < sc_methods.length; i++) {
107          if (sc_methods[i] != c_methods[i]) {
108            processOverride(sc_methods[i]);
109          }
110        }
111        // for each interface implmented by c, note that c provides an overridding
112        // implementation
113        for (RVMClass intf : c.getAllImplementedInterfaces()) {
114          for (RVMMethod m : intf.getVirtualMethods()) {
115            processOverride(m);
116          }
117        }
118      }
119    
120      private void processOverride(RVMMethod overridden) {
121        Iterator<Integer> invalidatedMethods = db.invalidatedByOverriddenMethod(overridden);
122        if (invalidatedMethods != null) {
123          while (invalidatedMethods.hasNext()) {
124            int cmid = invalidatedMethods.next();
125            CompiledMethod im = CompiledMethods.getCompiledMethod(cmid);
126            if (im != null) { // im == null implies that the code has been GCed already
127              invalidate(im);
128            }
129          }
130          db.removeNotOverriddenDependency(overridden);
131        }
132      }
133    
134      private void handleSubclassing(RVMClass c) {
135        if (c.isJavaLangObjectType() || c.isInterface()) return; // nothing to do
136        RVMClass sc = c.getSuperClass();
137        Iterator<Integer> invalidatedMethods = db.invalidatedBySubclass(sc);
138        if (invalidatedMethods != null) {
139          while (invalidatedMethods.hasNext()) {
140            int cmid = invalidatedMethods.next();
141            CompiledMethod im = CompiledMethods.getCompiledMethod(cmid);
142            if (im != null) { // im == null implies that the code has been GCed already
143              invalidate(im);
144            }
145          }
146          db.removeNoSubclassDependency(sc);
147        }
148      }
149    
150      /**
151       * helper method to invalidate a particular compiled method
152       */
153      private void invalidate(CompiledMethod cm) {
154        RVMMethod m = cm.getMethod();
155        if (TRACE || DEBUG) {
156          report("CLDM: Invalidating compiled method " + cm.getId() + "(" + m + ")\n");
157        }
158    
159        // (1) Mark the compiled method as invalid.
160        synchronized (cm) {
161          if (cm.isInvalid()) {
162            if (TRACE || DEBUG) report("\tcmid was alrady invalid; nothing more to do\n");
163            return;
164          }
165    
166          // (2) Apply any code patches to protect invocations already executing
167          //     in the soon to be invalid code.
168          ((OptCompiledMethod)cm).applyCodePatches(cm);
169    
170          cm.setInvalid();
171        }
172    
173        // (3) Inform its RVMMethod that cm is invalid;
174        //     This will update all the dispatching entries (TIB, JTOC, IMTs)
175        //     so that no new invocations will reach the invalid compiled code.
176        //     It also marks cm as obsolete so it can eventually be reclaimed by GC.
177        m.invalidateCompiledMethod(cm);
178      }
179    
180      void report(String s) {
181        if (VM.runningVM) {
182          if (log == null) {
183            if (true || !VM.fullyBooted) {
184              VM.sysWriteln("CLDM: VM not fully booted ", s);
185              return;
186            }
187            try {
188              log = new PrintStream(new FileOutputStream("PREEX_OPTS.TRACE"));
189            } catch (IOException e) {
190              VM.sysWrite("\n\nCLDM: Error opening logging file!!\n\n");
191            }
192          }
193        } else {
194          System.out.print(s);
195        }
196      }
197    }