KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > internal > runtime > methods > DefaultMethod


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common 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/cpl-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) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
15  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
16  * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
17  * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
18  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
19  * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
20  *
21  * Alternatively, the contents of this file may be used under the terms of
22  * either of the GNU General Public License Version 2 or later (the "GPL"),
23  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
24  * in which case the provisions of the GPL or the LGPL are applicable instead
25  * of those above. If you wish to allow use of your version of this file only
26  * under the terms of either the GPL or the LGPL, and not to allow others to
27  * use your version of this file under the terms of the CPL, indicate your
28  * decision by deleting the provisions above and replace them with the notice
29  * and other provisions required by the GPL or the LGPL. If you do not delete
30  * the provisions above, a recipient may use your version of this file under
31  * the terms of any one of the CPL, the GPL or the LGPL.
32  ***** END LICENSE BLOCK *****/

33 package org.jruby.internal.runtime.methods;
34
35 import java.util.ArrayList JavaDoc;
36 import java.util.Iterator JavaDoc;
37
38 import org.jruby.Ruby;
39 import org.jruby.RubyArray;
40 import org.jruby.RubyModule;
41 import org.jruby.RubyProc;
42 import org.jruby.ast.ArgsNode;
43 import org.jruby.ast.ListNode;
44 import org.jruby.ast.Node;
45 import org.jruby.ast.executable.Script;
46 import org.jruby.compiler.NodeCompilerFactory;
47 import org.jruby.compiler.impl.StandardASMCompiler;
48 import org.jruby.evaluator.AssignmentVisitor;
49 import org.jruby.evaluator.CreateJumpTargetVisitor;
50 import org.jruby.evaluator.EvaluationState;
51 import org.jruby.exceptions.JumpException;
52 import org.jruby.lexer.yacc.ISourcePosition;
53 import org.jruby.parser.StaticScope;
54 import org.jruby.runtime.Arity;
55 import org.jruby.runtime.Block;
56 import org.jruby.runtime.ThreadContext;
57 import org.jruby.runtime.Visibility;
58 import org.jruby.runtime.builtin.IRubyObject;
59 import org.jruby.util.collections.SinglyLinkedList;
60
61 /**
62  *
63  */

64 public final class DefaultMethod extends DynamicMethod {
65     private StaticScope staticScope;
66     private Node body;
67     private ArgsNode argsNode;
68     private SinglyLinkedList cref;
69     private boolean hasBeenTargeted = false;
70     private int callCount = 0;
71     private static final int COMPILE_COUNT = 50;
72     private Script jitCompiledScript;
73
74     // change to true to enable JIT compilation
75
private static final boolean JIT_ENABLED = Boolean.getBoolean("jruby.jit.enabled");
76     
77     public DefaultMethod(RubyModule implementationClass, StaticScope staticScope, Node body,
78             ArgsNode argsNode, Visibility visibility, SinglyLinkedList cref) {
79         super(implementationClass, visibility);
80         this.body = body;
81         this.staticScope = staticScope;
82         this.argsNode = argsNode;
83         this.cref = cref;
84         
85         assert argsNode != null;
86     }
87     
88     public void preMethod(ThreadContext context, RubyModule clazz, IRubyObject self, String JavaDoc name,
89             IRubyObject[] args, boolean noSuper, Block block) {
90         context.preDefMethodInternalCall(clazz, name, self, args, block, noSuper, cref, staticScope);
91     }
92     
93     public void postMethod(ThreadContext context) {
94         context.postDefMethodInternalCall();
95     }
96
97     /**
98      * @see AbstractCallable#call(Ruby, IRubyObject, String, IRubyObject[], boolean)
99      */

100     // FIXME: This is commented out because problems were found compiling methods that call protected code.
101
// because eliminating the pre/post does not change the "self" on the current frame, this caused
102
// visibility to be a larger problem. We must revisit this to examine how to avoid this trap for visibility checks.
103
public IRubyObject call(ThreadContext context, IRubyObject self,
104             RubyModule clazz, String JavaDoc name, IRubyObject[] args, boolean noSuper, Block block) {
105         if (jitCompiledScript != null) {
106             try {
107                 context.preCompiledMethod(implementationClass, cref);
108                 // FIXME: pass block when available
109
return jitCompiledScript.run(context, self, args, Block.NULL_BLOCK);
110             } finally {
111                 context.postCompiledMethod();
112             }
113         }
114           
115         return super.call(context, self, clazz, name, args, noSuper, block);
116     }
117
118     /**
119      * @see AbstractCallable#call(Ruby, IRubyObject, String, IRubyObject[], boolean)
120      */

121     public IRubyObject internalCall(ThreadContext context, RubyModule clazz,
122             IRubyObject self, String JavaDoc name, IRubyObject[] args, boolean noSuper, Block block) {
123             assert args != null;
124         if (JIT_ENABLED && jitCompiledScript != null) {
125             return jitCompiledScript.run(context, self, args, Block.NULL_BLOCK);
126         }
127         
128         Ruby runtime = context.getRuntime();
129         
130         if (!hasBeenTargeted) {
131             CreateJumpTargetVisitor.setJumpTarget(this, body);
132             hasBeenTargeted = true;
133         }
134
135         if (argsNode.getBlockArgNode() != null && block.isGiven()) {
136             RubyProc blockArg;
137             
138             if (block.getProcObject() != null) {
139                 blockArg = (RubyProc) block.getProcObject();
140             } else {
141                 blockArg = runtime.newProc(false, block);
142                 blockArg.getBlock().isLambda = block.isLambda;
143             }
144             // We pass depth zero since we know this only applies to newly created local scope
145
context.getCurrentScope().setValue(argsNode.getBlockArgNode().getCount(), blockArg, 0);
146         }
147
148         try {
149             prepareArguments(context, runtime, args);
150             
151             getArity().checkArity(runtime, args);
152
153             traceCall(context, runtime, self, name);
154
155             if (JIT_ENABLED) runJIT(runtime, name);
156                     
157             return EvaluationState.eval(runtime, context, body, self, block);
158         } catch (JumpException je) {
159             if (je.getJumpType() == JumpException.JumpType.ReturnJump && je.getTarget() == this) {
160                     return (IRubyObject) je.getValue();
161             }
162             
163             throw je;
164         } finally {
165             traceReturn(context, runtime, self, name);
166         }
167     }
168
169     private void runJIT(Ruby runtime, String JavaDoc name) {
170         if (callCount >= 0 && getArity().isFixed()) {
171             callCount++;
172             if (callCount >= COMPILE_COUNT) {
173                 // System.err.println("trying to compile: " + getImplementationClass().getBaseName() + "." + name);
174
try {
175                     String JavaDoc cleanName = cleanJavaIdentifier(name);
176                     StandardASMCompiler compiler = new StandardASMCompiler(cleanName + hashCode(), body.getPosition().getFile());
177                     compiler.startScript();
178                     Object JavaDoc methodToken = compiler.beginMethod("__file__", getArity().getValue(), staticScope.getNumberOfVariables());
179                     NodeCompilerFactory.getCompiler(body).compile(body, compiler);
180                     compiler.endMethod(methodToken);
181                     compiler.endScript();
182                     Class JavaDoc sourceClass = compiler.loadClass(runtime);
183                     jitCompiledScript = (Script)sourceClass.newInstance();
184                     
185                     String JavaDoc className = getImplementationClass().getBaseName();
186                     if (className == null) {
187                         className = "<anon class>";
188                     }
189                     System.out.println("compiled: " + className + "." + name);
190                 } catch (Exception JavaDoc e) {
191                     // e.printStackTrace();
192
} finally {
193                     callCount = -1;
194                 }
195             }
196         }
197     }
198
199     private void prepareArguments(ThreadContext context, Ruby runtime, IRubyObject[] args) {
200         int expectedArgsCount = argsNode.getArgsCount();
201
202         int restArg = argsNode.getRestArg();
203         boolean hasOptArgs = argsNode.getOptArgs() != null;
204
205         // FIXME: This seems redundant with the arity check in internalCall...is it actually different?
206
if (expectedArgsCount > args.length) {
207             throw runtime.newArgumentError("Wrong # of arguments(" + args.length + " for " + expectedArgsCount + ")");
208         }
209
210         // Bind 'normal' parameter values to the local scope for this method.
211
if (expectedArgsCount > 0) {
212             context.getCurrentScope().setArgValues(args, expectedArgsCount);
213         }
214
215         // optArgs and restArgs require more work, so isolate them and ArrayList creation here
216
if (hasOptArgs || restArg != -1) {
217             args = prepareOptOrRestArgs(context, runtime, args, expectedArgsCount, restArg, hasOptArgs);
218         }
219         
220         context.setFrameArgs(args);
221     }
222
223     private IRubyObject[] prepareOptOrRestArgs(ThreadContext context, Ruby runtime, IRubyObject[] args, int expectedArgsCount, int restArg, boolean hasOptArgs) {
224         if (restArg == -1 && hasOptArgs) {
225             int opt = expectedArgsCount + argsNode.getOptArgs().size();
226
227             if (opt < args.length) {
228                 throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for " + opt + ")");
229             }
230         }
231         
232         int count = expectedArgsCount;
233         if (argsNode.getOptArgs() != null) {
234             count += argsNode.getOptArgs().size();
235         }
236
237         ArrayList JavaDoc allArgs = new ArrayList JavaDoc();
238         
239         // Combine static and optional args into a single list allArgs
240
for (int i = 0; i < count && i < args.length; i++) {
241             allArgs.add(args[i]);
242         }
243         
244         if (hasOptArgs) {
245             ListNode optArgs = argsNode.getOptArgs();
246    
247             Iterator JavaDoc iter = optArgs.iterator();
248             for (int i = expectedArgsCount; i < args.length && iter.hasNext(); i++) {
249                 // in-frame EvalState should already have receiver set as self, continue to use it
250
AssignmentVisitor.assign(runtime, context, context.getFrameSelf(), (Node)iter.next(), args[i], Block.NULL_BLOCK, true);
251                 expectedArgsCount++;
252             }
253    
254             // assign the default values, adding to the end of allArgs
255
while (iter.hasNext()) {
256                 // in-frame EvalState should already have receiver set as self, continue to use it
257
allArgs.add(EvaluationState.eval(runtime, context, (Node) iter.next(), context.getFrameSelf(), Block.NULL_BLOCK));
258             }
259         }
260         
261         // build an array from *rest type args, also adding to allArgs
262

263         // ENEBO: Does this next comment still need to be done since I killed hasLocalVars:
264
// move this out of the scope.hasLocalVariables() condition to deal
265
// with anonymous restargs (* versus *rest)
266

267         
268         // none present ==> -1
269
// named restarg ==> >=0
270
// anonymous restarg ==> -2
271
if (restArg != -1) {
272             for (int i = expectedArgsCount; i < args.length; i++) {
273                 allArgs.add(args[i]);
274             }
275
276             // only set in scope if named
277
if (restArg >= 0) {
278                 RubyArray array = runtime.newArray(args.length - expectedArgsCount);
279                 for (int i = expectedArgsCount; i < args.length; i++) {
280                     array.append(args[i]);
281                 }
282
283                 context.getCurrentScope().setValue(restArg, array, 0);
284             }
285         }
286         
287         args = (IRubyObject[])allArgs.toArray(new IRubyObject[allArgs.size()]);
288         return args;
289     }
290
291     private void traceReturn(ThreadContext context, Ruby runtime, IRubyObject self, String JavaDoc name) {
292         if (runtime.getTraceFunction() == null) {
293             return;
294         }
295
296         ISourcePosition position = context.getPreviousFramePosition();
297         runtime.callTraceFunction(context, "return", position, self, name, getImplementationClass());
298     }
299
300     private void traceCall(ThreadContext context, Ruby runtime, IRubyObject self, String JavaDoc name) {
301         if (runtime.getTraceFunction() == null) return;
302
303         ISourcePosition position = body != null ? body.getPosition() : context.getPosition();
304
305         runtime.callTraceFunction(context, "call", position, self, name, getImplementationClass());
306     }
307
308     public Arity getArity() {
309         return argsNode.getArity();
310     }
311     
312     public DynamicMethod dup() {
313         return new DefaultMethod(getImplementationClass(), staticScope, body, argsNode, getVisibility(), cref);
314     }
315     
316     private String JavaDoc cleanJavaIdentifier(String JavaDoc name) {
317         char[] characters = name.toCharArray();
318         StringBuffer JavaDoc cleanBuffer = new StringBuffer JavaDoc();
319         boolean prevWasReplaced = false;
320         for (int i = 0; i < characters.length; i++) {
321             if (Character.isJavaIdentifierStart(characters[i])) {
322                 cleanBuffer.append(characters[i]);
323                 prevWasReplaced = false;
324             } else {
325                 if (!prevWasReplaced) {
326                     cleanBuffer.append("_");
327                 }
328                 prevWasReplaced = true;
329                 switch (characters[i]) {
330                 case '?':
331                     cleanBuffer.append("p_");
332                     continue;
333                 case '!':
334                     cleanBuffer.append("b_");
335                     continue;
336                 case '<':
337                     cleanBuffer.append("lt_");
338                     continue;
339                 case '>':
340                     cleanBuffer.append("gt_");
341                     continue;
342                 case '=':
343                     cleanBuffer.append("equal_");
344                     continue;
345                 case '[':
346                     if ((i + 1) < characters.length && characters[i + 1] == ']') {
347                         cleanBuffer.append("aref_");
348                         i++;
349                     } else {
350                         // can this ever happen?
351
cleanBuffer.append("lbracket_");
352                     }
353                     continue;
354                 case ']':
355                     // given [ logic above, can this ever happen?
356
cleanBuffer.append("rbracket_");
357                     continue;
358                 case '+':
359                     cleanBuffer.append("plus_");
360                     continue;
361                 case '-':
362                     cleanBuffer.append("minus_");
363                     continue;
364                 case '*':
365                     cleanBuffer.append("times_");
366                     continue;
367                 case '/':
368                     cleanBuffer.append("div_");
369                     continue;
370                 case '&':
371                     cleanBuffer.append("and_");
372                     continue;
373                 default:
374                     cleanBuffer.append(Integer.toHexString(characters[i])).append("_");
375                 }
376             }
377         }
378         return cleanBuffer.toString();
379     }
380 }
381
Popular Tags