RubyProc.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.core;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.MaterializedFrame;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.methods.MethodLike;
import org.jruby.truffle.runtime.methods.SharedMethodInfo;
import org.jruby.truffle.runtime.subsystems.ObjectSpaceManager;
import org.jruby.util.cli.Options;

/**
 * Represents the Ruby {@code Proc} class.
 */
public class RubyProc extends RubyBasicObject implements MethodLike {

    public static final boolean PROC_BINDING = Options.TRUFFLE_PROC_BINDING.load();

    public static enum Type {
        PROC, LAMBDA
    }

    private final Type type;
    @CompilationFinal private SharedMethodInfo sharedMethodInfo;
    /** Call target for procs, which have special arguments destructuring */
    @CompilationFinal private CallTarget callTarget;
    /** Call target for lambdas and methods, which have strict arguments destructuring */
    @CompilationFinal private CallTarget callTargetForMethods;
    @CompilationFinal private MaterializedFrame declarationFrame;
    @CompilationFinal private RubyModule declaringModule;
    @CompilationFinal private MethodLike method;
    @CompilationFinal private Object self;
    @CompilationFinal private RubyProc block;

    public RubyProc(RubyClass procClass, Type type) {
        super(procClass);
        this.type = type;
    }

    public RubyProc(RubyClass procClass, Type type, SharedMethodInfo sharedMethodInfo, CallTarget callTarget,
                    CallTarget callTargetForMethods, MaterializedFrame declarationFrame, RubyModule declaringModule, MethodLike method, Object self, RubyProc block) {
        this(procClass, type);
        initialize(sharedMethodInfo, callTarget, callTargetForMethods, declarationFrame, declaringModule, method, self, block);
    }

    public void initialize(SharedMethodInfo sharedMethodInfo, CallTarget callTarget, CallTarget callTargetForMethods,
                           MaterializedFrame declarationFrame, RubyModule declaringModule, MethodLike method, Object self, RubyProc block) {
        this.sharedMethodInfo = sharedMethodInfo;
        this.callTarget = callTarget;
        this.callTargetForMethods = callTargetForMethods;
        this.declarationFrame = declarationFrame;
        this.declaringModule = declaringModule;
        this.method = method;
        this.self = self;
        this.block = block;
    }

    public CallTarget getCallTargetForType() {
        switch (type) {
            case PROC:
                return callTarget;
            case LAMBDA:
                return callTargetForMethods;
        }

        throw new UnsupportedOperationException(type.toString());
    }

    public Object rootCall(Object... args) {
        RubyNode.notDesignedForCompilation();

        // TODO(CS): handle exceptions in here?

        return getCallTargetForType().call(RubyArguments.pack(this, declarationFrame, self, block, args));
    }

    public Type getType() {
        return type;
    }

    public SharedMethodInfo getSharedMethodInfo() {
        return sharedMethodInfo;
    }

    public CallTarget getCallTarget() {
        return callTarget;
    }

    public CallTarget getCallTargetForMethods() {
        return callTargetForMethods;
    }

    public MaterializedFrame getDeclarationFrame() {
        return declarationFrame;
    }

    @Override
    public RubyModule getDeclaringModule() {
        return declaringModule;
    }

    public MethodLike getMethod() {
        return method;
    }

    public Object getSelfCapturedInScope() {
        return self;
    }

    public RubyProc getBlockCapturedInScope() {
        return block;
    }

    @Override
    public void visitObjectGraphChildren(ObjectSpaceManager.ObjectGraphVisitor visitor) {
        getContext().getObjectSpaceManager().visitFrame(declarationFrame, visitor);
    }

    public static class ProcAllocator implements Allocator {

        @Override
        public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, RubyNode currentNode) {
            return new RubyProc(rubyClass, Type.PROC);
        }

    }

}