1 /*
   2  * Copyright (c) 2016, 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 jdk.jshell;
  26 
  27 import java.util.Arrays;
  28 import java.util.HashMap;
  29 import java.util.Objects;
  30 import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
  31 
  32 /**
  33  * Tracks the state of a class.
  34  */
  35 class ClassTracker {
  36 
  37     private final HashMap<String, ClassInfo> map;
  38 
  39     ClassTracker() {
  40         this.map = new HashMap<>();
  41     }
  42 
  43     /**
  44      * Associates a class name, class bytes (current and loaded).
  45      */
  46     class ClassInfo {
  47 
  48         // The name of the class
  49         private final String className;
  50 
  51         // The corresponding compiled class bytes when a load or redefine
  52         // is started.  May not be the loaded bytes.  May be null.
  53         private byte[] currentBytes;
  54 
  55         // The class bytes successfully loaded/redefined into the remote VM.
  56         private byte[] loadedBytes;
  57 
  58         private ClassInfo(String className) {
  59             this.className = className;
  60         }
  61 
  62         String getClassName() {
  63             return className;
  64         }
  65 
  66         byte[] getLoadedBytes() {
  67             return loadedBytes;
  68         }
  69 
  70         byte[] getCurrentBytes() {
  71             return currentBytes;
  72         }
  73 
  74         void setCurrentBytes(byte[] bytes) {
  75             this.currentBytes = bytes;
  76         }
  77 
  78         void setLoadedBytes(byte[] bytes) {
  79             this.loadedBytes = bytes;
  80         }
  81         
  82         boolean isLoaded() {
  83             return loadedBytes != null;
  84         }
  85             
  86         boolean isCurrent() {
  87             return Arrays.equals(currentBytes, loadedBytes);
  88         }
  89         
  90         ClassBytecodes toClassBytecodes() {
  91             return new ClassBytecodes(className, currentBytes);
  92         }
  93             
  94             @Override
  95         public boolean equals(Object o) {
  96             return o instanceof ClassInfo
  97                     && ((ClassInfo) o).className.equals(className);
  98         }
  99 
 100         @Override
 101         public int hashCode() {
 102             return Objects.hashCode(this.className);
 103         }
 104     }
 105 
 106     void markLoaded(ClassBytecodes[] cbcs) {
 107         for (ClassBytecodes cbc : cbcs) {
 108             get(cbc.name).setLoadedBytes(cbc.bytecodes);
 109         }
 110     }
 111 
 112     void markLoaded(ClassBytecodes[] cbcs, boolean[] isLoaded) {
 113         for (int i = 0; i < cbcs.length; ++i) {
 114             if (isLoaded[i]) {
 115                 ClassBytecodes cbc = cbcs[i];
 116                 get(cbc.name).setLoadedBytes(cbc.bytecodes);
 117             }
 118         }
 119     }
 120 
 121     // Map a class name to the current compiled class bytes.
 122     void setCurrentBytes(String className, byte[] bytes) {
 123         ClassInfo ci = get(className);
 124         ci.setCurrentBytes(bytes);
 125     }
 126 
 127     // Lookup the ClassInfo by class name, create if it does not exist.
 128     ClassInfo get(String className) {
 129         return map.computeIfAbsent(className, k -> new ClassInfo(k));
 130     }
 131 }