Package org.jruby.runtime.invokedynamic

Source Code of org.jruby.runtime.invokedynamic.InvokeDynamicSupport

package org.jruby.runtime.invokedynamic;

import java.dyn.CallSite;
import java.dyn.Linkage;
import java.dyn.MethodHandle;
import java.dyn.MethodHandles;
import java.dyn.MethodType;
import org.jruby.RubyClass;
import org.jruby.RubyLocalJumpError;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.exceptions.JumpException;
import org.jruby.exceptions.RaiseException;
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.javasupport.util.RuntimeHelpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.util.CodegenUtils.*;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

public class InvokeDynamicSupport {
    public static Object bootstrap(final CallSite site, Object... args) {
        // dynamic call
        IRubyObject self = (IRubyObject) args[0];
        ThreadContext context = (ThreadContext) args[1];
        String methodName = (String) args[2];
        CallType callType = CallType.NORMAL;
        String siteName = site.name();
        boolean iterator = siteName.length() == 2 && siteName.charAt(1) == 'b';
       
        switch (siteName.charAt(0)) {
        case 'c':
            callType = CallType.NORMAL;
            break;
        case 'f':
            callType = CallType.FUNCTIONAL;
            break;
        case 'v':
            callType = CallType.VARIABLE;
            break;
        }

        DynamicMethod method = self.getMetaClass().searchMethod(methodName);
        IRubyObject caller = context.getFrameSelf();
        if (shouldCallMethodMissing(method, methodName, caller, callType)) {
            return RuntimeHelpers.callMethodMissing(context, self, method, methodName, callType, Block.NULL_BLOCK);
        }

        String dispatcherName = iterator ? "invokeDynamicIter" : "invokeDynamic";
        MethodHandle outHandle = MethodHandles.findStatic(
                InvokeDynamicSupport.class,
                dispatcherName,
                MethodType.make(IRubyObject.class, DynamicMethod.class, site.type().parameterArray()));
        MethodHandle inHandle = MethodHandles.insertArgument(outHandle, method);
        site.setTarget(inHandle);

        // do normal invocation this time
        int length = args.length;
        if (args[length - 1] instanceof Block) {
            if (iterator) {
                switch (length) {
                case 4:
                    return invokeDynamicIter(method, self, context, methodName, (Block) args[length - 1]);
                case 5:
                    if (args[length - 2] instanceof IRubyObject[]) {
                        return invokeDynamicIter(method, self, context, methodName, (IRubyObject[]) args[length - 2], (Block) args[length - 1]);
                    } else {
                        return invokeDynamicIter(method, self, context, methodName, (IRubyObject) args[length - 2], (Block) args[length - 1]);
                    }
                case 6:
                    return invokeDynamicIter(method, self, context, methodName, (IRubyObject) args[length - 3], (IRubyObject) args[length - 2], (Block) args[length - 1]);
                case 7:
                    return invokeDynamicIter(method, self, context, methodName, (IRubyObject) args[length - 4], (IRubyObject) args[length - 3], (IRubyObject) args[length - 2], (Block) args[length - 1]);
                }
            } else {
                switch (length) {
                case 4:
                    return invokeDynamic(method, self, context, methodName, (Block) args[length - 1]);
                case 5:
                    if (args[length - 2] instanceof IRubyObject[]) {
                        return invokeDynamic(method, self, context, methodName, (IRubyObject[]) args[length - 2], (Block) args[length - 1]);
                    } else {
                        return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 2], (Block) args[length - 1]);
                    }
                case 6:
                    return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 3], (IRubyObject) args[length - 2], (Block) args[length - 1]);
                case 7:
                    return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 4], (IRubyObject) args[length - 3], (IRubyObject) args[length - 2], (Block) args[length - 1]);
                }
            }
        } else {
            switch (length) {
            case 3:
                return invokeDynamic(method, self, context, methodName);
            case 4:
                if (args[length - 1] instanceof IRubyObject[]) {
                    return invokeDynamic(method, self, context, methodName, (IRubyObject[]) args[length - 1]);
                } else {
                    return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 1]);
                }
            case 5:
                return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 2], (IRubyObject) args[length - 1]);
            case 6:
                return invokeDynamic(method, self, context, methodName, (IRubyObject) args[length - 3], (IRubyObject) args[length - 2], (IRubyObject) args[length - 1]);
            }
        }

        throw new RuntimeException("Unsupported method signature for dynamic call: " + site.type());
    }
   
    public static void installBytecode(MethodVisitor method, String classname) {
        SkinnyMethodAdapter clinitMethod = new SkinnyMethodAdapter(method);
        clinitMethod.ldc(c(classname));
        clinitMethod.invokestatic(p(Class.class), "forName", sig(Class.class, params(String.class)));
        clinitMethod.ldc(Type.getType(InvokeDynamicSupport.class));
        clinitMethod.ldc("bootstrap");
        clinitMethod.getstatic(p(Linkage.class), "BOOTSTRAP_METHOD_TYPE", ci(MethodType.class));
        clinitMethod.invokestatic(p(MethodHandles.class), "findStatic", sig(MethodHandle.class, Class.class, String.class, MethodType.class));
        clinitMethod.invokestatic(p(Linkage.class), "registerBootstrapMethod", sig(void.class, Class.class, MethodHandle.class));
    }

    private static boolean shouldCallMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
        return method.isUndefined() || notVisibleAndNotMethodMissing(method, name, caller, callType);
    }

    private static boolean notVisibleAndNotMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
        return !method.isCallableFrom(caller, callType) && !name.equals("method_missing");
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name) {
        IRubyObject self = (IRubyObject) selfObj;
        return method.call(context, self, self.getMetaClass(), name);
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        }
    }

    public static IRubyObject invokeDynamicIter(DynamicMethod method, Object selfObj, ThreadContext context, String name, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        } finally {
            block.escape();
        }
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg) {
        IRubyObject self = (IRubyObject) selfObj;
        return method.call(context, self, self.getMetaClass(), name, arg);
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        }
    }

    public static IRubyObject invokeDynamicIter(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        } finally {
            block.escape();
        }
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1) {
        IRubyObject self = (IRubyObject) selfObj;
        return method.call(context, self, self.getMetaClass(), name, arg0, arg1);
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg0, arg1, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        }
    }

    public static IRubyObject invokeDynamicIter(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg0, arg1, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        } finally {
            block.escape();
        }
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
        IRubyObject self = (IRubyObject) selfObj;
        return method.call(context, self, self.getMetaClass(), name, arg0, arg1, arg2);
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg0, arg1, arg2, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        }
    }

    public static IRubyObject invokeDynamicIter(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, arg0, arg1, arg2, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        } finally {
            block.escape();
        }
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject[] args) {
        IRubyObject self = (IRubyObject) selfObj;
        return method.call(context, self, self.getMetaClass(), name, args);
    }

    public static IRubyObject invokeDynamic(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject[] args, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, args, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        }
    }

    public static IRubyObject invokeDynamicIter(DynamicMethod method, Object selfObj, ThreadContext context, String name, IRubyObject[] args, Block block) {
        IRubyObject self = (IRubyObject) selfObj;
        RubyClass selfType = pollAndGetClass(context, self);
        try {
            return method.call(context, self, selfType, name, args, block);
        } catch (JumpException.BreakJump bj) {
            return handleBreakJump(context, bj);
        } catch (JumpException.RetryJump rj) {
            throw retryJumpError(context);
        } finally {
            block.escape();
        }
    }

    private static RubyClass pollAndGetClass(ThreadContext context, IRubyObject self) {
        context.callThreadPoll();

        RubyClass selfType = self.getMetaClass();

        return selfType;
    }

    private static IRubyObject handleBreakJump(ThreadContext context, JumpException.BreakJump bj) throws JumpException.BreakJump {
        // consume and rethrow or just keep rethrowing?
        if (context.getFrameJumpTarget() == bj.getTarget()) {
            return (IRubyObject) bj.getValue();
        }
        throw bj;
    }

    private static RaiseException retryJumpError(ThreadContext context) {
        return context.getRuntime().newLocalJumpError(RubyLocalJumpError.Reason.RETRY, context.getRuntime().getNil(), "retry outside of rescue not supported");
    }
}
TOP

Related Classes of org.jruby.runtime.invokedynamic.InvokeDynamicSupport

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.