RubyMethod.java

  1. /***** BEGIN LICENSE BLOCK *****
  2.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Eclipse Public
  5.  * License Version 1.0 (the "License"); you may not use this file
  6.  * except in compliance with the License. You may obtain a copy of
  7.  * the License at http://www.eclipse.org/legal/epl-v10.html
  8.  *
  9.  * Software distributed under the License is distributed on an "AS
  10.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11.  * implied. See the License for the specific language governing
  12.  * rights and limitations under the License.
  13.  *
  14.  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  15.  * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  16.  * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  17.  * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  18.  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  19.  *
  20.  * Alternatively, the contents of this file may be used under the terms of
  21.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  22.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  23.  * in which case the provisions of the GPL or the LGPL are applicable instead
  24.  * of those above. If you wish to allow use of your version of this file only
  25.  * under the terms of either the GPL or the LGPL, and not to allow others to
  26.  * use your version of this file under the terms of the EPL, indicate your
  27.  * decision by deleting the provisions above and replace them with the notice
  28.  * and other provisions required by the GPL or the LGPL. If you do not delete
  29.  * the provisions above, a recipient may use your version of this file under
  30.  * the terms of any one of the EPL, the GPL or the LGPL.
  31.  ***** END LICENSE BLOCK *****/
  32. package org.jruby;

  33. import org.jruby.ext.jruby.JRubyLibrary;
  34. import org.jruby.anno.JRubyMethod;
  35. import org.jruby.anno.JRubyClass;
  36. import org.jruby.internal.runtime.methods.DynamicMethod;
  37. import org.jruby.internal.runtime.methods.ProcMethod;
  38. import org.jruby.internal.runtime.methods.UndefinedMethod;
  39. import org.jruby.runtime.Block;
  40. import org.jruby.runtime.BlockBody;
  41. import org.jruby.runtime.ClassIndex;
  42. import org.jruby.runtime.CompiledBlockCallback19;
  43. import org.jruby.runtime.CompiledBlockLight19;
  44. import org.jruby.runtime.Helpers;
  45. import org.jruby.runtime.ObjectAllocator;
  46. import org.jruby.runtime.PositionAware;
  47. import org.jruby.runtime.ThreadContext;
  48. import org.jruby.runtime.Visibility;
  49. import org.jruby.runtime.builtin.IRubyObject;
  50. import org.jruby.runtime.marshal.DataType;

  51. /**
  52.  * The RubyMethod class represents a RubyMethod object.
  53.  *
  54.  * You can get such a method by calling the "method" method of an object.
  55.  *
  56.  * Note: This was renamed from Method.java
  57.  *
  58.  * @author  jpetersen
  59.  * @since 0.2.3
  60.  */
  61. @JRubyClass(name="Method")
  62. public class RubyMethod extends RubyObject implements DataType {
  63.     protected RubyModule implementationModule;
  64.     protected String methodName;
  65.     protected RubyModule originModule;
  66.     protected String originName;
  67.     protected DynamicMethod method;
  68.     protected IRubyObject receiver;

  69.     protected RubyMethod(Ruby runtime, RubyClass rubyClass) {
  70.         super(runtime, rubyClass);
  71.     }

  72.     /** Create the RubyMethod class and add it to the Ruby runtime.
  73.      *
  74.      */
  75.     public static RubyClass createMethodClass(Ruby runtime) {
  76.         // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
  77.         RubyClass methodClass = runtime.defineClass("Method", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  78.         runtime.setMethod(methodClass);

  79.         methodClass.setClassIndex(ClassIndex.METHOD);
  80.         methodClass.setReifiedClass(RubyMethod.class);
  81.        
  82.         methodClass.defineAnnotatedMethods(RubyMethod.class);
  83.        
  84.         return methodClass;
  85.     }

  86.     public static RubyMethod newMethod(
  87.         RubyModule implementationModule,
  88.         String methodName,
  89.         RubyModule originModule,
  90.         String originName,
  91.         DynamicMethod method,
  92.         IRubyObject receiver) {
  93.         Ruby runtime = implementationModule.getRuntime();
  94.         RubyMethod newMethod = new RubyMethod(runtime, runtime.getMethod());

  95.         newMethod.implementationModule = implementationModule;
  96.         newMethod.methodName = methodName;
  97.         newMethod.originModule = originModule;
  98.         newMethod.originName = originName;
  99.         newMethod.method = method;
  100.         newMethod.receiver = receiver;

  101.         return newMethod;
  102.     }

  103.     public DynamicMethod getMethod() {
  104.         return method;
  105.     }

  106.     /** Call the method.
  107.      *
  108.      */
  109.     @JRubyMethod(name = {"call", "[]"})
  110.     public IRubyObject call(ThreadContext context, Block block) {
  111.         return method.call(context, receiver, implementationModule, methodName, block);
  112.     }
  113.     @JRubyMethod(name = {"call", "[]"})
  114.     public IRubyObject call(ThreadContext context, IRubyObject arg, Block block) {
  115.         return method.call(context, receiver, implementationModule, methodName, arg, block);
  116.     }
  117.     @JRubyMethod(name = {"call", "[]"})
  118.     public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  119.         return method.call(context, receiver, implementationModule, methodName, arg0, arg1, block);
  120.     }
  121.     @JRubyMethod(name = {"call", "[]"})
  122.     public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  123.         return method.call(context, receiver, implementationModule, methodName, arg0, arg1, arg2, block);
  124.     }
  125.     @JRubyMethod(name = {"call", "[]"}, rest = true)
  126.     public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
  127.         return method.call(context, receiver, implementationModule, methodName, args, block);
  128.     }

  129.     /** Returns the number of arguments a method accepted.
  130.      *
  131.      * @return the number of arguments of a method.
  132.      */
  133.     @JRubyMethod
  134.     public RubyFixnum arity() {
  135.         return getRuntime().newFixnum(method.getArity().getValue());
  136.     }

  137.     @JRubyMethod(name = "==", required = 1)
  138.     @Override
  139.     public RubyBoolean op_equal(ThreadContext context, IRubyObject other) {
  140.         if (!(other instanceof RubyMethod)) {
  141.             return context.runtime.getFalse();
  142.         }
  143.         if (method instanceof ProcMethod) {
  144.             return context.runtime.newBoolean(((ProcMethod) method).isSame(((RubyMethod) other).getMethod()));
  145.         }
  146.         RubyMethod otherMethod = (RubyMethod)other;
  147.         return context.runtime.newBoolean(receiver == otherMethod.receiver &&
  148.                 originModule == otherMethod.originModule &&
  149.                 (isMethodMissingMatch(otherMethod.getMethod().getRealMethod()) || isSerialMatch(otherMethod.getMethod().getRealMethod()))
  150.         );
  151.     }

  152.     private boolean isMethodMissingMatch(DynamicMethod other) {
  153.         return (method.getRealMethod() instanceof RubyModule.RespondToMissingMethod) &&
  154.                 ((RubyModule.RespondToMissingMethod)method.getRealMethod()).equals(other);
  155.     }

  156.     private boolean isSerialMatch(DynamicMethod otherMethod) {
  157.         return method.getRealMethod().getSerialNumber() == otherMethod.getRealMethod().getSerialNumber();
  158.     }

  159.     @JRubyMethod(name = "eql?", required = 1)
  160.     public IRubyObject op_eql19(ThreadContext context, IRubyObject other) {
  161.         return op_equal(context, other);
  162.     }

  163.     @JRubyMethod(name = "clone")
  164.     @Override
  165.     public RubyMethod rbClone() {
  166.         return newMethod(implementationModule, methodName, originModule, originName, method, receiver);
  167.     }

  168.     /** Create a Proc object.
  169.      *
  170.      */
  171.     @JRubyMethod
  172.     public IRubyObject to_proc(ThreadContext context, Block unusedBlock) {
  173.         Ruby runtime = context.runtime;
  174.         CompiledBlockCallback19 callback = new CompiledBlockCallback19() {
  175.             @Override
  176.             public IRubyObject call(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
  177.                 return method.call(context, receiver, originModule, originName, args, block);
  178.             }

  179.             @Override
  180.             public String getFile() {
  181.                 return getFilename();
  182.             }

  183.             @Override
  184.             public int getLine() {
  185.                 return RubyMethod.this.getLine();
  186.             }
  187.         };
  188.         BlockBody body = CompiledBlockLight19.newCompiledBlockLight(method.getArity(), runtime.getStaticScopeFactory().getDummyScope(), callback, false, 0, JRubyLibrary.MethodExtensions.methodParameters(runtime, method));
  189.         Block b = new Block(body, context.currentBinding(receiver, Visibility.PUBLIC));
  190.        
  191.         return RubyProc.newProc(runtime, b, Block.Type.LAMBDA);
  192.     }

  193.     @JRubyMethod
  194.     public RubyUnboundMethod unbind() {
  195.         RubyUnboundMethod unboundMethod =
  196.             RubyUnboundMethod.newUnboundMethod(implementationModule, methodName, originModule, originName, method);
  197.         unboundMethod.infectBy(this);
  198.        
  199.         return unboundMethod;
  200.     }
  201.    
  202.     @JRubyMethod(name = {"inspect", "to_s"})
  203.     @Override
  204.     public IRubyObject inspect() {
  205.         StringBuilder buf = new StringBuilder("#<");
  206.         char delimeter = '#';
  207.        
  208.         buf.append(getMetaClass().getRealClass().getName()).append(": ");
  209.        
  210.         if (implementationModule.isSingleton()) {
  211.             IRubyObject attached = ((MetaClass) implementationModule).getAttached();
  212.             if (receiver == null) {
  213.                 buf.append(implementationModule.inspect().toString());
  214.             } else if (receiver == attached) {
  215.                 buf.append(attached.inspect().toString());
  216.                 delimeter = '.';
  217.             } else {
  218.                 buf.append(receiver.inspect().toString());
  219.                 buf.append('(').append(attached.inspect().toString()).append(')');
  220.                 delimeter = '.';
  221.             }
  222.         } else {
  223.             buf.append(originModule.getName());
  224.            
  225.             if (implementationModule != originModule) {
  226.                 buf.append('(').append(implementationModule.getName()).append(')');
  227.             }
  228.         }
  229.        
  230.         buf.append(delimeter).append(methodName).append('>');
  231.        
  232.         RubyString str = getRuntime().newString(buf.toString());
  233.         str.setTaint(isTaint());
  234.         return str;
  235.     }

  236.     public IRubyObject name(ThreadContext context) {
  237.         return name19(context);
  238.     }

  239.     @JRubyMethod(name = "name")
  240.     public IRubyObject name19(ThreadContext context) {
  241.         return context.runtime.newSymbol(methodName);
  242.     }

  243.     public String getMethodName() {
  244.         return methodName;
  245.     }

  246.     @JRubyMethod
  247.     public IRubyObject receiver(ThreadContext context) {
  248.         return receiver;
  249.     }

  250.     @JRubyMethod
  251.     public IRubyObject owner(ThreadContext context) {
  252.         return implementationModule;
  253.     }

  254.     @JRubyMethod
  255.     public IRubyObject source_location(ThreadContext context) {
  256.         Ruby runtime = context.runtime;

  257.         String filename = getFilename();
  258.         if (filename != null) {
  259.             return runtime.newArray(runtime.newString(filename), runtime.newFixnum(getLine()));
  260.         }

  261.         return context.runtime.getNil();
  262.     }

  263.     public String getFilename() {
  264.         DynamicMethod realMethod = method.getRealMethod(); // Follow Aliases
  265.         if (realMethod instanceof PositionAware) {
  266.             PositionAware poser = (PositionAware) realMethod;
  267.             return poser.getFile();
  268.         }
  269.         return null;
  270.     }

  271.     public int getLine() {
  272.         DynamicMethod realMethod = method.getRealMethod(); // Follow Aliases
  273.         if (realMethod instanceof PositionAware) {
  274.             PositionAware poser = (PositionAware) realMethod;
  275.             return poser.getLine() + 1;
  276.         }
  277.         return -1;
  278.     }

  279.     @JRubyMethod
  280.     public IRubyObject parameters(ThreadContext context) {
  281.         return JRubyLibrary.MethodExtensions.methodArgs(this);
  282.     }

  283.     @JRubyMethod(optional = 1)
  284.     public IRubyObject curry(ThreadContext context, IRubyObject[] args) {
  285.         return to_proc(context, Block.NULL_BLOCK).callMethod(context, "curry", args);
  286.     }

  287.     @JRubyMethod
  288.     public IRubyObject super_method(ThreadContext context) {
  289.         RubyModule superClass = Helpers.findImplementerIfNecessary(receiver.getMetaClass(), implementationModule).getSuperClass();
  290.         return super_method(context, receiver, superClass);
  291.     }

  292.     protected IRubyObject super_method(ThreadContext context, IRubyObject receiver, RubyModule superClass) {
  293.         if (superClass == null) return context.runtime.getNil();

  294.         DynamicMethod newMethod = superClass.searchMethod(methodName);
  295.         if (newMethod == UndefinedMethod.INSTANCE) return context.runtime.getNil();

  296.         if (receiver == null) {
  297.             return RubyUnboundMethod.newUnboundMethod(superClass, methodName, superClass, originName, newMethod);
  298.         } else {
  299.             return newMethod(superClass, methodName, superClass, originName, newMethod, receiver);
  300.         }
  301.     }
  302. }