001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
015: * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
016: * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
017: * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
018: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
019: *
020: * Alternatively, the contents of this file may be used under the terms of
021: * either of the GNU General Public License Version 2 or later (the "GPL"),
022: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
023: * in which case the provisions of the GPL or the LGPL are applicable instead
024: * of those above. If you wish to allow use of your version of this file only
025: * under the terms of either the GPL or the LGPL, and not to allow others to
026: * use your version of this file under the terms of the CPL, indicate your
027: * decision by deleting the provisions above and replace them with the notice
028: * and other provisions required by the GPL or the LGPL. If you do not delete
029: * the provisions above, a recipient may use your version of this file under
030: * the terms of any one of the CPL, the GPL or the LGPL.
031: ***** END LICENSE BLOCK *****/package org.jruby;
032:
033: import org.jruby.exceptions.JumpException;
034: import org.jruby.internal.runtime.methods.DynamicMethod;
035: import org.jruby.runtime.Block;
036: import org.jruby.runtime.CallbackFactory;
037: import org.jruby.runtime.MethodBlock;
038: import org.jruby.runtime.ObjectAllocator;
039: import org.jruby.runtime.ThreadContext;
040: import org.jruby.runtime.builtin.IRubyObject;
041:
042: /**
043: * The RubyMethod class represents a RubyMethod object.
044: *
045: * You can get such a method by calling the "method" method of an object.
046: *
047: * Note: This was renamed from Method.java
048: *
049: * @author jpetersen
050: * @since 0.2.3
051: */
052: public class RubyMethod extends RubyObject {
053: protected RubyModule implementationModule;
054: protected String methodName;
055: protected RubyModule originModule;
056: protected String originName;
057: protected DynamicMethod method;
058: protected IRubyObject receiver;
059:
060: protected RubyMethod(Ruby runtime, RubyClass rubyClass) {
061: super (runtime, rubyClass);
062: }
063:
064: /** Create the RubyMethod class and add it to the Ruby runtime.
065: *
066: */
067: public static RubyClass createMethodClass(Ruby runtime) {
068: // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
069: RubyClass methodClass = runtime
070: .defineClass("Method", runtime.getObject(),
071: ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
072:
073: CallbackFactory callbackFactory = runtime
074: .callbackFactory(RubyMethod.class);
075:
076: methodClass.defineFastMethod("arity", callbackFactory
077: .getFastMethod("arity"));
078: methodClass.defineMethod("to_proc", callbackFactory
079: .getMethod("to_proc"));
080: methodClass.defineMethod("unbind", callbackFactory
081: .getMethod("unbind"));
082: methodClass.defineMethod("call", callbackFactory
083: .getOptMethod("call"));
084: methodClass.defineMethod("[]", callbackFactory
085: .getOptMethod("call"));
086: methodClass.defineFastMethod("inspect", callbackFactory
087: .getFastMethod("inspect"));
088: methodClass.defineFastMethod("to_s", callbackFactory
089: .getFastMethod("inspect"));
090:
091: return methodClass;
092: }
093:
094: public static RubyMethod newMethod(RubyModule implementationModule,
095: String methodName, RubyModule originModule,
096: String originName, DynamicMethod method,
097: IRubyObject receiver) {
098: Ruby runtime = implementationModule.getRuntime();
099: RubyMethod newMethod = new RubyMethod(runtime, runtime
100: .getClass("Method"));
101:
102: newMethod.implementationModule = implementationModule;
103: newMethod.methodName = methodName;
104: newMethod.originModule = originModule;
105: newMethod.originName = originName;
106: newMethod.method = method;
107: newMethod.receiver = receiver;
108:
109: return newMethod;
110: }
111:
112: /** Call the method.
113: *
114: */
115: public IRubyObject call(IRubyObject[] args, Block block) {
116: assert args != null;
117: ThreadContext tc = getRuntime().getCurrentContext();
118:
119: method.getArity().checkArity(getRuntime(), args);
120:
121: // FIXME: should lastClass be implementation module for a Method?
122: return method.call(tc, receiver, implementationModule,
123: methodName, args, false, block);
124: }
125:
126: /** Returns the number of arguments a method accepted.
127: *
128: * @return the number of arguments of a method.
129: */
130: public RubyFixnum arity() {
131: return getRuntime().newFixnum(method.getArity().getValue());
132: }
133:
134: /** Create a Proc object.
135: *
136: */
137: public IRubyObject to_proc(Block unusedBlock) {
138: CallbackFactory f = getRuntime().callbackFactory(
139: RubyMethod.class);
140: Ruby r = getRuntime();
141: ThreadContext tc = r.getCurrentContext();
142: Block block = MethodBlock.createMethodBlock(tc, tc
143: .getCurrentScope().cloneScope(), f
144: .getBlockMethod("bmcall"), this , r.getTopSelf());
145:
146: while (true) {
147: try {
148: // FIXME: We should not be regenerating this over and over
149: return f.getSingletonMethod("mproc").execute(
150: getRuntime().getNil(), IRubyObject.NULL_ARRAY,
151: block);
152: } catch (JumpException je) {
153: if (je.getJumpType() == JumpException.JumpType.BreakJump) {
154: return (IRubyObject) je.getValue();
155: } else if (je.getJumpType() == JumpException.JumpType.ReturnJump) {
156: return (IRubyObject) je.getValue();
157: } else if (je.getJumpType() == JumpException.JumpType.RetryJump) {
158: // Execute iterateMethod again.
159: } else {
160: throw je;
161: }
162: }
163: }
164: }
165:
166: /** Create a Proc object which is called like a ruby method.
167: *
168: * Used by the RubyMethod#to_proc method.
169: *
170: */
171: public static IRubyObject mproc(IRubyObject recv, Block block) {
172: Ruby runtime = recv.getRuntime();
173: ThreadContext tc = runtime.getCurrentContext();
174:
175: tc.preMproc();
176:
177: try {
178: return RubyKernel.proc(recv, block);
179: } finally {
180: tc.postMproc();
181: }
182: }
183:
184: /** Delegate a block call to a bound method call.
185: *
186: * Used by the RubyMethod#to_proc method.
187: *
188: */
189: public static IRubyObject bmcall(IRubyObject blockArg,
190: IRubyObject arg1, IRubyObject self, Block unusedBlock) {
191: if (blockArg instanceof RubyArray) {
192: // ENEBO: Very wrong
193: return ((RubyMethod) arg1).call(((RubyArray) blockArg)
194: .toJavaArray(), Block.NULL_BLOCK);
195: }
196: // ENEBO: Very wrong
197: return ((RubyMethod) arg1).call(new IRubyObject[] { blockArg },
198: Block.NULL_BLOCK);
199: }
200:
201: public RubyUnboundMethod unbind(Block unusedBlock) {
202: RubyUnboundMethod unboundMethod = RubyUnboundMethod
203: .newUnboundMethod(implementationModule, methodName,
204: originModule, originName, method);
205: unboundMethod.receiver = this ;
206: unboundMethod.infectBy(this );
207:
208: return unboundMethod;
209: }
210:
211: public IRubyObject inspect() {
212: String cname = getMetaClass().getRealClass().getName();
213: RubyString str = getRuntime().newString(
214: "#<" + cname + ": " + originModule.getName() + "#"
215: + methodName + ">");
216: str.setTaint(isTaint());
217: return str;
218: }
219: }
|