1 /*
   2  * Copyright (c) 2019, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 import java.lang.StackWalker.StackFrame;
  25 import java.util.List;
  26 import java.util.stream.Collectors;
  27 import java.util.Set;
  28 import sun.hotspot.WhiteBox;
  29 
  30 public class LambdaVerification {
  31     static void verifyCallerIsArchivedLambda(boolean isRuntime) {
  32         // getCallerClass(0) = verifyCallerIsArchivedLambda
  33         // getCallerClass(1) = testNonCapturingLambda or testCapturingLambda
  34         // getCallerClass(2) = the lambda proxy class that should be archived by CDS.
  35         Class<?> c = getCallerClass(2);
  36         System.out.println("Lambda proxy class = " + c);
  37         String cn = c.getName();
  38         System.out.println(" cn = " + cn);
  39         String hiddenClassName = cn.replace('/', '+');
  40         String lambdaClassName = cn.substring(0, cn.lastIndexOf('/'));
  41         System.out.println(" lambda name = " + lambdaClassName);
  42         WhiteBox wb = WhiteBox.getWhiteBox();
  43         if (isRuntime) {
  44             // check that c is a shared class
  45             if (wb.isSharedClass(c)) {
  46                 System.out.println("As expected, " + c + " is in shared space.");
  47             } else {
  48                 throw new java.lang.RuntimeException(c + " must be in shared space.");
  49             }
  50             // check that lambda class cannot be found manually
  51             try {
  52                 Class.forName(hiddenClassName);
  53             } catch (ClassNotFoundException cnfe) {
  54                 cnfe.printStackTrace(System.out);
  55                 System.out.println("As expected, loading of " + hiddenClassName + " should result in ClassNotFoundException.");
  56             } catch (Exception ex) {
  57                 throw ex;
  58             }
  59             // check that lambda class is alive
  60             if (wb.isClassAlive(hiddenClassName)) {
  61                 System.out.println("As expected, " + cn + " is alive.");
  62             } else {
  63                 throw new java.lang.RuntimeException(cn + " should be alive.");
  64             }
  65         } else {
  66             if (wb.isSharedClass(c)) {
  67                 throw new java.lang.RuntimeException(c + " must not be in shared space.");
  68             } else {
  69                 System.out.println("As expected, " + c + " is not in shared space.");
  70             }
  71         }
  72         //System.out.println("=== Here's the call stack");
  73         //(new Throwable()).printStackTrace(System.out);
  74         //System.out.println("===");
  75         System.out.println("Succeeded");            
  76     }
  77 
  78     // depth is 0-based -- i.e., depth==0 returns the class of the immediate caller of getCallerClass
  79     static Class<?> getCallerClass(int depth) {
  80         // Need to add the frame of the getCallerClass -- so the immediate caller (depth==0) of this method
  81         // is at stack.get(1) == stack.get(depth+1);
  82         StackWalker walker = StackWalker.getInstance(
  83             Set.of(StackWalker.Option.RETAIN_CLASS_REFERENCE,
  84                    StackWalker.Option.SHOW_HIDDEN_FRAMES));
  85         List<StackFrame> stack = walker.walk(s -> s.limit(depth+2).collect(Collectors.toList()));
  86         return stack.get(depth+1).getDeclaringClass();
  87     }
  88 }