RubyArguments.java

/*
 * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
 * code is released under a tri EPL/GPL/LGPL license. You can use it,
 * redistribute it and/or modify it under the terms of the:
 *
 * Eclipse Public License version 1.0
 * GNU General Public License version 2
 * GNU Lesser General Public License version 2.1
 */
package org.jruby.truffle.runtime;

import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.methods.MethodLike;

import java.util.Arrays;

/**
 * Pack and unpack Ruby method arguments to and from an array of objects.
 */
public final class RubyArguments {

    public static final int METHOD_INDEX = 0;
    public static final int DECLARATION_FRAME_INDEX = 1;
    public static final int SELF_INDEX = 2;
    public static final int BLOCK_INDEX = 3;
    public static final int RUNTIME_ARGUMENT_COUNT = 4;

    public static Object[] pack(MethodLike method, MaterializedFrame declarationFrame, Object self, RubyProc block, Object[] arguments) {
        final Object[] packed = new Object[arguments.length + RUNTIME_ARGUMENT_COUNT];

        packed[METHOD_INDEX] = method;
        packed[DECLARATION_FRAME_INDEX] = declarationFrame;
        packed[SELF_INDEX] = self;
        packed[BLOCK_INDEX] = block;
        arraycopy(arguments, 0, packed, RUNTIME_ARGUMENT_COUNT, arguments.length);

        return packed;
    }

    public static MethodLike getMethod(Object[] arguments) {
        return (MethodLike) arguments[METHOD_INDEX];
    }

    public static Object getSelf(Object[] arguments) {
        return arguments[SELF_INDEX];
    }

    public static RubyProc getBlock(Object[] arguments) {
        return (RubyProc) arguments[BLOCK_INDEX];
    }

    public static Object[] extractUserArguments(Object[] arguments) {
        return Arrays.copyOfRange(arguments, RUNTIME_ARGUMENT_COUNT, arguments.length);
    }

    public static Object[] concatUserArguments(Object o, Object[] arguments) {
        final Object[] concatenatedArguments;

        if (o instanceof Object[]) {
            Object[] concatArray = (Object[]) o;
            concatenatedArguments = new Object[concatArray.length + arguments.length - RUNTIME_ARGUMENT_COUNT];

            arraycopy(concatArray, 0, concatenatedArguments, 0, concatArray.length);
            arraycopy(arguments, RUNTIME_ARGUMENT_COUNT, concatenatedArguments, concatArray.length, getUserArgumentsCount(arguments));
        } else {
            concatenatedArguments = new Object[1 + arguments.length - RUNTIME_ARGUMENT_COUNT];

            concatenatedArguments[0] = o;
            arraycopy(arguments, RUNTIME_ARGUMENT_COUNT, concatenatedArguments, 1, getUserArgumentsCount(arguments));
        }

        return concatenatedArguments;
    }

    public static int getUserArgumentsCount(Object[] internalArguments) {
        return internalArguments.length - RUNTIME_ARGUMENT_COUNT;
    }

    public static Object getUserArgument(Object[] internalArguments, int index) {
        return internalArguments[RUNTIME_ARGUMENT_COUNT + index];
    }

    public static MaterializedFrame getDeclarationFrame(Object[] arguments) {
        return (MaterializedFrame) arguments[DECLARATION_FRAME_INDEX];
    }

    @ExplodeLoop
    public static void arraycopy(Object[] src, int srcPos, Object[] dest, int destPos, int length) {
        for (int i = 0; i < length; i++) {
            dest[destPos + i] = src[srcPos + i];
        }
    }

    /**
     * Get the declaration frame a certain number of levels up from the current frame, where the
     * current frame is 0.
     */
    public static MaterializedFrame getDeclarationFrame(VirtualFrame frame, int level) {
        assert level > 0;

        MaterializedFrame parentFrame = RubyArguments.getDeclarationFrame(frame.getArguments());
        return getDeclarationFrame(parentFrame, level - 1);
    }

    /**
     * Get the declaration frame a certain number of levels up from the current frame, where the
     * current frame is 0.
     */
    @ExplodeLoop
    private static MaterializedFrame getDeclarationFrame(MaterializedFrame frame, int level) {
        assert frame != null;
        assert level >= 0;

        MaterializedFrame parentFrame = frame;

        for (int n = 0; n < level; n++) {
            parentFrame = RubyArguments.getDeclarationFrame(parentFrame.getArguments());
        }

        return parentFrame;
    }

}