Package org.jruby

Source Code of org.jruby.RubyProc

/***** BEGIN LICENSE BLOCK *****
* Version: CPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
* Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
* Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the CPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the CPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.JumpException;
import org.jruby.internal.runtime.JumpTarget;
import org.jruby.java.MiniJava;
import org.jruby.runtime.Block;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

/**
* @author  jpetersen
*/
@JRubyClass(name="Proc")
public class RubyProc extends RubyObject implements JumpTarget {
    private Block block = Block.NULL_BLOCK;
    private Block.Type type;
    private String file;
    private int line;

    public RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type) {
        super(runtime, rubyClass);
       
        this.type = type;
    }
   
    private static ObjectAllocator PROC_ALLOCATOR = new ObjectAllocator() {
        public IRubyObject allocate(Ruby runtime, RubyClass klass) {
            RubyProc instance = RubyProc.newProc(runtime, Block.Type.PROC);

            instance.setMetaClass(klass);

            return instance;
        }
    };

    public static RubyClass createProcClass(Ruby runtime) {
        RubyClass procClass = runtime.defineClass("Proc", runtime.getObject(), PROC_ALLOCATOR);
        runtime.setProc(procClass);
       
        procClass.defineAnnotatedMethods(RubyProc.class);
       
        return procClass;
    }

    public Block getBlock() {
        return block;
    }

    // Proc class

    public static RubyProc newProc(Ruby runtime, Block.Type type) {
        return new RubyProc(runtime, runtime.getProc(), type);
    }
    public static RubyProc newProc(Ruby runtime, Block block, Block.Type type) {
        RubyProc proc = new RubyProc(runtime, runtime.getProc(), type);
        proc.callInit(NULL_ARRAY, block);
       
        return proc;
    }
   
    /**
     * Create a new instance of a Proc object.  We override this method (from RubyClass)
     * since we need to deal with special case of Proc.new with no arguments or block arg.  In
     * this case, we need to check previous frame for a block to consume.
     */
    @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
    public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
        // No passed in block, lets check next outer frame for one ('Proc.new')
        if (!block.isGiven()) {
            block = context.getPreviousFrame().getBlock();
        }
       
        if (block.isGiven() && block.getProcObject() != null) {
            return block.getProcObject();
        }
       
        IRubyObject obj = ((RubyClass) recv).allocate();
       
        obj.callMethod(context, "initialize", args, block);
        return obj;
    }
   
    @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
    public IRubyObject initialize(ThreadContext context, Block procBlock) {
        if (!procBlock.isGiven()) {
            throw getRuntime().newArgumentError("tried to create Proc object without a block");
        }
       
        if (type == Block.Type.LAMBDA && procBlock == null) {
            // TODO: warn "tried to create Proc object without a block"
        }
       
        block = procBlock.cloneBlock();
        block.type = type;
        block.setProcObject(this);

        file = context.getFile();
        line = context.getLine();
        return this;
    }
   
    @JRubyMethod(name = "clone")
    public IRubyObject rbClone() {
      RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
      newProc.block = getBlock();
      newProc.file = file;
      newProc.line = line;
      // TODO: CLONE_SETUP here
      return newProc;
    }

    @JRubyMethod(name = "dup")
    public IRubyObject dup() {
        RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
        newProc.block = getBlock();
        newProc.file = file;
        newProc.line = line;
        return newProc;
    }
   
    @JRubyMethod(name = "==", required = 1)
    public IRubyObject op_equal(IRubyObject other) {
        if (!(other instanceof RubyProc)) return getRuntime().getFalse();
       
        if (this == other || this.block == ((RubyProc)other).block) {
            return getRuntime().getTrue();
        }
       
        return getRuntime().getFalse();
    }
   
    @JRubyMethod(name = "to_s")
    public IRubyObject to_s() {
        return RubyString.newString(getRuntime(),
                "#<Proc:0x" + Integer.toString(block.hashCode(), 16) + "@" +
                file + ":" + (line + 1) + ">");
    }

    @JRubyMethod(name = "binding")
    public IRubyObject binding() {
        return getRuntime().newBinding(block.getBinding());
    }

    @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
    public IRubyObject call(ThreadContext context, IRubyObject[] args) {
        return call(context, args, null);
    }
   
    public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self) {
        assert args != null;
       
        Ruby runtime = getRuntime();
        Block newBlock = block.cloneBlock();
        JumpTarget jumpTarget = newBlock.getBinding().getFrame().getJumpTarget();
       
        try {
            if (self != null) newBlock.getBinding().setSelf(self);
           
            return newBlock.call(context, args);
        } catch (JumpException.BreakJump bj) {
            switch(block.type) {
            case LAMBDA: if (bj.getTarget() == jumpTarget) {
                return (IRubyObject) bj.getValue();
            } else {
                throw runtime.newLocalJumpError(RubyLocalJumpError.Reason.BREAK, (IRubyObject)bj.getValue(), "unexpected break");
            }
            case PROC:
                if (newBlock.isEscaped()) {
                    throw runtime.newLocalJumpError(RubyLocalJumpError.Reason.BREAK, (IRubyObject)bj.getValue(), "break from proc-closure");
                } else {
                    throw bj;
                }
            default: throw bj;
            }
        } catch (JumpException.ReturnJump rj) {
            Object target = rj.getTarget();
           
            if (target == jumpTarget && block.type == Block.Type.LAMBDA) return (IRubyObject) rj.getValue();

            if (type == Block.Type.THREAD) {
                throw runtime.newThreadError("return can't jump across threads");
            }
            throw rj;
        } catch (JumpException.RetryJump rj) {
            throw runtime.newLocalJumpError(RubyLocalJumpError.Reason.RETRY, (IRubyObject)rj.getValue(), "retry not supported outside rescue");
        }
    }

    @JRubyMethod(name = "arity")
    public RubyFixnum arity() {
        return getRuntime().newFixnum(block.arity().getValue());
    }
   
    @JRubyMethod(name = "to_proc")
    public RubyProc to_proc() {
      return this;
    }
   
    public IRubyObject as(Class asClass) {
        final Ruby ruby = getRuntime();
        if (!asClass.isInterface()) {
            throw ruby.newTypeError(asClass.getCanonicalName() + " is not an interface");
        }

        return MiniJava.javaToRuby(ruby, Proxy.newProxyInstance(Ruby.getClassLoader(), new Class[] {asClass}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                IRubyObject[] rubyArgs = new IRubyObject[args.length + 1];
                rubyArgs[0] = RubySymbol.newSymbol(ruby, method.getName());
                for (int i = 1; i < rubyArgs.length; i++) {
                    rubyArgs[i] = MiniJava.javaToRuby(ruby, args[i - 1]);
                }
                return MiniJava.rubyToJava(call(ruby.getCurrentContext(), rubyArgs));
            }
        }));
    }
}
TOP

Related Classes of org.jruby.RubyProc

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.