All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jruby.truffle.language.SnippetNode Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016 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.language;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.Source;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.methods.DeclarationContext;
import org.jruby.truffle.parser.ParserContext;

public class SnippetNode extends RubyBaseNode {

    @CompilationFinal private String expression;
    @CompilationFinal private String[] parameters;
    @CompilationFinal private FrameDescriptor frameDescriptor;
    @CompilationFinal private FrameSlot[] parameterFrameSlots;

    @Child private DirectCallNode directCallNode;

    public SnippetNode() {
    }

    public Object execute(VirtualFrame frame, String expression, Object... arguments) {
        if (directCallNode == null) {
            CompilerDirectives.transferToInterpreterAndInvalidate();

            this.expression = expression;
            assert arguments.length % 2 == 0;
            parameters = new String[arguments.length / 2];
            for (int n = 0; n < parameters.length; n++) {
                parameters[n] = (String) arguments[2 * n];
            }

            frameDescriptor = new FrameDescriptor(nil());
            parameterFrameSlots = new FrameSlot[parameters.length];

            for (int n = 0; n < parameters.length; n++) {
                parameterFrameSlots[n] = frameDescriptor.findOrAddFrameSlot(parameters[n]);
            }

            final Source source = getContext().getSourceLoader().loadFragment(this.expression, "(snippet)");

            final RubyRootNode rootNode = getContext().getCodeLoader().parse(
                    source,
                    UTF8Encoding.INSTANCE,
                    ParserContext.INLINE,
                    frameDescriptor,
                    null,
                    true,
                    this);

            directCallNode = insert(Truffle.getRuntime().createDirectCallNode(
                    Truffle.getRuntime().createCallTarget(rootNode)));

            if (directCallNode.isInlinable()) {
                directCallNode.forceInlining();
            }
        }

        if (arguments.length != parameters.length * 2) {
            CompilerDirectives.transferToInterpreterAndInvalidate();
            throw new UnsupportedOperationException(
                    "number of arguments doesn't match number of parameters");
        }

        assert ensureConstantExpressionParameters(expression, arguments);

        final Object[] callArguments = RubyArguments.pack(
                parentFrame(frame, arguments),
                null,
                RubyArguments.getMethod(frame),
                DeclarationContext.INSTANCE_EVAL,
                null,
                RubyArguments.getSelf(frame),
                null,
                new Object[]{});

        return directCallNode.call(frame, callArguments);
    }

    private boolean ensureConstantExpressionParameters(String expression, Object[] arguments) {
        assert this.expression == expression :
                "has to be always called with same expression: " + this.expression + " != " + expression;
        for (int n = 0; n < parameters.length; n++) {
            assert parameters[n] == arguments[2 * n] :
                    "has to be always called with same parameter name at " + n + ": " + parameters[n] + " != " + arguments[2 * n];
        }
        return true;
    }

    @ExplodeLoop
    private MaterializedFrame parentFrame(VirtualFrame frame, Object[] arguments) {
        final Object[] parentFrameArguments = RubyArguments.pack(
                null,
                null,
                RubyArguments.getMethod(frame),
                DeclarationContext.INSTANCE_EVAL,
                null,
                RubyArguments.getSelf(frame),
                null,
                new Object[]{});

        final MaterializedFrame parentFrame = Truffle.getRuntime().createMaterializedFrame(
                parentFrameArguments,
                frameDescriptor);

        for (int n = 0; n < parameters.length; n++) {
            parentFrame.setObject(parameterFrameSlots[n], arguments[2 * n + 1]);
        }
        return parentFrame;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy