package org.dynjs.runtime.linker.js.primitive; import org.dynjs.codegen.DereferencedReference; import org.dynjs.exception.ThrowException; import org.dynjs.runtime.ExecutionContext; import org.dynjs.runtime.Reference; import org.dynjs.runtime.Types; import org.projectodd.rephract.SmartLink; import org.projectodd.rephract.builder.LinkBuilder; import org.projectodd.rephract.guards.Guard; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import static java.lang.invoke.MethodHandles.lookup; import static java.lang.invoke.MethodType.methodType; /** * @author Bob McWhirter */ public class PrimitiveCallLink extends SmartLink implements Guard { public PrimitiveCallLink(LinkBuilder builder) throws Exception { super(builder); this.builder = this.builder.guardWith( this ); } public boolean guard(Object receiver, Object context, Object self, Object[] args) { if ( !(receiver instanceof DereferencedReference) ) { return false; } Object base = ((DereferencedReference) receiver).getValue(); return base instanceof String || base instanceof Number || base instanceof Boolean; } @Override public MethodHandle guardMethodHandle(MethodType inputType) throws Exception { return lookup() .findVirtual(PrimitiveCallLink.class, "guard", methodType(boolean.class, Object.class, Object.class, Object.class, Object[].class)) .bindTo(this); } public MethodHandle guard() throws Exception { return this.builder.getGuard(); } protected static Object primitiveReferenceToObject(ExecutionContext context, Reference reference) { return Types.toObject( context, reference.getBase() ); } public static ThrowException typeError(ExecutionContext context, Object nonCallable) { return new ThrowException(context, context.createTypeError("not callable: " + nonCallable)); } public MethodHandle target() throws Exception { return builder .permute(1, 2) .convert(Object.class, ExecutionContext.class, Object.class) .fold(lookup().findStatic(PrimitiveCallLink.class, "typeError", MethodType.methodType(ThrowException.class, ExecutionContext.class, Object.class))) .drop(1, 2) .invoke(MethodHandles.throwException(Object.class, ThrowException.class)) .target(); } }