001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package org.eclipse.golo.runtime;
012
013import java.lang.invoke.CallSite;
014import java.lang.invoke.ConstantCallSite;
015import java.lang.invoke.MethodHandles.Lookup;
016import java.lang.invoke.MethodType;
017
018import static gololang.Messages.message;
019import static java.lang.invoke.MethodHandles.constant;
020import static org.eclipse.golo.runtime.Module.imports;
021
022public final class ClassReferenceSupport {
023
024  private ClassReferenceSupport() {
025    throw new UnsupportedOperationException("Don't instantiate invokedynamic bootstrap class");
026  }
027
028  public static CallSite bootstrap(Lookup caller, String name, MethodType type) throws ClassNotFoundException {
029    String className = name.replaceAll("#", "\\.");
030    Class<?> callerClass = caller.lookupClass();
031    ClassLoader classLoader = callerClass.getClassLoader();
032
033    Class<?> classRef = tryLoadingFromPrimitiveType(className);
034    if (classRef != null) {
035      return createCallSite(classRef);
036    }
037    classRef = tryLoadingFromName(className, classLoader, callerClass.getName());
038    if (classRef != null) {
039      return createCallSite(classRef);
040    }
041    classRef = tryLoadingFromImports(className, callerClass, classLoader);
042    if (classRef != null) {
043      return createCallSite(classRef);
044    }
045    throw new ClassNotFoundException(message("class_not_resolved", className));
046  }
047
048  private static Class<?> tryLoadingFromName(String name, ClassLoader classLoader, String callerName) {
049    try {
050      return Class.forName(name, true, classLoader);
051    } catch (ClassNotFoundException e) {
052      Warnings.unavailableClass(name, callerName);
053      return null;
054    }
055  }
056
057  private static Class<?> tryLoadingFromImports(String className, Class<?> callerClass, ClassLoader classLoader) {
058    for (String importedClassName : imports(callerClass)) {
059      Class<?> classRef = tryLoadingFromName(importedClassName + "." + className, classLoader, callerClass.getName());
060      if (classRef != null) {
061        return classRef;
062      } else {
063        if (importedClassName.endsWith(className)) {
064          classRef = tryLoadingFromName(importedClassName, classLoader, callerClass.getName());
065          if (classRef != null) {
066            return classRef;
067          }
068        }
069      }
070    }
071    return null;
072  }
073
074  private static Class<?> tryLoadingFromPrimitiveType(String name) {
075    switch (name) {
076      case "byte"    : return byte.class;
077      case "char"    : return char.class;
078      case "int"     : return int.class;
079      case "long"    : return long.class;
080      case "double"  : return double.class;
081      case "short"   : return short.class;
082      case "float"   : return float.class;
083      case "boolean" : return boolean.class;
084      default: return null;
085    }
086  }
087
088  private static CallSite createCallSite(Class<?> classRef) {
089    return new ConstantCallSite(constant(Class.class, classRef));
090  }
091
092}