/** * 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; } }