/**
   * Returns if the given object is a "callable"
   *
   * @param obj object to be checked for callability
   * @return true if the obj is callable
   */
  public static boolean isCallable(final Object obj) {
    if (obj == ScriptRuntime.UNDEFINED || obj == null) {
      return false;
    }

    return obj instanceof ScriptFunction
        || isJSObjectFunction(obj)
        || BeansLinker.isDynamicMethod(obj)
        || obj instanceof BoundCallable
        || isFunctionalInterfaceObject(obj)
        || obj instanceof StaticClass;
  }
 /**
  * Returns true if the given object is a strict callable
  *
  * @param callable the callable object to be checked for strictness
  * @return true if the obj is a strict callable, false if it is a non-strict callable.
  * @throws ECMAException with {@code TypeError} if the object is not a callable.
  */
 public static boolean isStrictCallable(final Object callable) {
   if (callable instanceof ScriptFunction) {
     return ((ScriptFunction) callable).isStrict();
   } else if (isJSObjectFunction(callable)) {
     return ((JSObject) callable).isStrictFunction();
   } else if (callable instanceof BoundCallable) {
     return isStrictCallable(((BoundCallable) callable).getCallable());
   } else if (BeansLinker.isDynamicMethod(callable)
       || callable instanceof StaticClass
       || isFunctionalInterfaceObject(callable)) {
     return false;
   }
   throw notFunction(callable);
 }
 /**
  * Returns if the given object is a dynalink Dynamic method
  *
  * @param obj object to be checked
  * @return true if the obj is a dynamic method
  */
 public static boolean isDynamicMethod(final Object obj) {
   return BeansLinker.isDynamicMethod(
       obj instanceof BoundCallable ? ((BoundCallable) obj).getCallable() : obj);
 }
/**
 * Internal linker for {@link StaticClass} objects, only ever used by Nashorn engine and not exposed
 * to other engines. It is used for extending the "new" operator on StaticClass in order to be able
 * to instantiate interfaces and abstract classes by passing a ScriptObject or ScriptFunction as
 * their implementation, e.g.:
 *
 * <pre>
 *   var r = new Runnable() { run: function() { print("Hello World" } }
 * </pre>
 *
 * or for SAM types, even just passing a function:
 *
 * <pre>
 *   var r = new Runnable(function() { print("Hello World" })
 * </pre>
 */
final class NashornStaticClassLinker implements TypeBasedGuardingDynamicLinker {
  private static final GuardingDynamicLinker staticClassLinker =
      BeansLinker.getLinkerForClass(StaticClass.class);

  @Override
  public boolean canLinkType(final Class<?> type) {
    return type == StaticClass.class;
  }

  @Override
  public GuardedInvocation getGuardedInvocation(
      final LinkRequest linkRequest, final LinkerServices linkerServices) throws Exception {
    final LinkRequest request =
        linkRequest.withoutRuntimeContext(); // Nashorn has no runtime context
    final Object self = request.getReceiver();
    if (self.getClass() != StaticClass.class) {
      return null;
    }
    final Class<?> receiverClass = ((StaticClass) self).getRepresentedClass();

    Bootstrap.checkReflectionAccess(receiverClass, true);
    final CallSiteDescriptor desc = request.getCallSiteDescriptor();
    // We intercept "new" on StaticClass instances to provide additional capabilities
    if ("new".equals(desc.getNameToken(CallSiteDescriptor.OPERATOR))) {
      if (!Modifier.isPublic(receiverClass.getModifiers())) {
        throw ECMAErrors.typeError("new.on.nonpublic.javatype", receiverClass.getName());
      }

      // make sure new is on accessible Class
      Context.checkPackageAccess(receiverClass);

      // Is the class abstract? (This includes interfaces.)
      if (NashornLinker.isAbstractClass(receiverClass)) {
        // Change this link request into a link request on the adapter class.
        final Object[] args = request.getArguments();
        args[0] =
            JavaAdapterFactory.getAdapterClassFor(
                new Class<?>[] {receiverClass},
                null,
                linkRequest.getCallSiteDescriptor().getLookup());
        final LinkRequest adapterRequest =
            request.replaceArguments(request.getCallSiteDescriptor(), args);
        final GuardedInvocation gi =
            checkNullConstructor(delegate(linkerServices, adapterRequest), receiverClass);
        // Finally, modify the guard to test for the original abstract class.
        return gi.replaceMethods(gi.getInvocation(), Guards.getIdentityGuard(self));
      }
      // If the class was not abstract, just delegate linking to the standard StaticClass linker.
      // Make an
      // additional check to ensure we have a constructor. We could just fall through to the next
      // "return"
      // statement, except we also insert a call to checkNullConstructor() which throws an
      // ECMAScript TypeError
      // with a more intuitive message when no suitable constructor is found.
      return checkNullConstructor(delegate(linkerServices, request), receiverClass);
    }
    // In case this was not a "new" operation, just delegate to the the standard StaticClass linker.
    return delegate(linkerServices, request);
  }

  private static GuardedInvocation delegate(
      final LinkerServices linkerServices, final LinkRequest request) throws Exception {
    return NashornBeansLinker.getGuardedInvocation(staticClassLinker, request, linkerServices);
  }

  private static GuardedInvocation checkNullConstructor(
      final GuardedInvocation ctorInvocation, final Class<?> receiverClass) {
    if (ctorInvocation == null) {
      throw ECMAErrors.typeError("no.constructor.matches.args", receiverClass.getName());
    }
    return ctorInvocation;
  }
}