KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > runtime > Block


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-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
15  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
17  * Copyright (C) 2004-2007 Thomas E Enebo <enebo@acm.org>
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 CPL, 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 CPL, the GPL or the LGPL.
31  ***** END LICENSE BLOCK *****/

32 package org.jruby.runtime;
33
34 import org.jruby.Ruby;
35 import org.jruby.RubyArray;
36 import org.jruby.RubyModule;
37 import org.jruby.RubyProc;
38 import org.jruby.ast.MultipleAsgnNode;
39 import org.jruby.ast.Node;
40 import org.jruby.ast.NodeTypes;
41 import org.jruby.ast.util.ArgsUtil;
42 import org.jruby.evaluator.AssignmentVisitor;
43 import org.jruby.exceptions.JumpException;
44 import org.jruby.parser.BlockStaticScope;
45 import org.jruby.runtime.builtin.IRubyObject;
46 import org.jruby.internal.runtime.methods.EvaluateCallable;
47 import org.jruby.util.collections.SinglyLinkedList;
48
49 /**
50  * Internal live representation of a block ({...} or do ... end).
51  */

52 public class Block {
53     /**
54      * All Block variables should either refer to a real block or this NULL_BLOCK.
55      */

56     public static Block NULL_BLOCK = new Block() {
57         public boolean isGiven() {
58             return false;
59         }
60         
61         public IRubyObject yield(ThreadContext context, IRubyObject value, IRubyObject self,
62                 RubyModule klass, boolean aValue) {
63             throw context.getRuntime().newLocalJumpError("yield called out of block");
64         }
65         
66         public Block cloneBlock() {
67             return this;
68         }
69     };
70
71     /**
72      * 'self' at point when the block is defined
73      */

74     private IRubyObject self;
75
76     /**
77      * body of the block (wrapped in an ICallable)
78      */

79     private ICallable method;
80
81     /**
82      * AST Node representing the parameter (VARiable) list to the block.
83      */

84     private Node varNode;
85     
86     /**
87      * frame of method which defined this block
88      */

89     private Frame frame;
90     private SinglyLinkedList cref;
91     private Visibility visibility;
92     private RubyModule klass;
93     
94     /**
95      * A reference to all variable values (and names) that are in-scope for this block.
96      */

97     private DynamicScope dynamicScope;
98     
99     /**
100      * The Proc that this block is associated with. When we reference blocks via variable
101      * reference they are converted to Proc objects. We store a reference of the associated
102      * Proc object for easy conversion.
103      */

104     private RubyProc proc = null;
105     
106     public boolean isLambda = false;
107
108     public static Block createBlock(ThreadContext context, Node varNode, DynamicScope dynamicScope,
109             ICallable method, IRubyObject self) {
110         return new Block(varNode,
111                          method,
112                          self,
113                          context.getCurrentFrame(),
114                          context.peekCRef(),
115                          context.getCurrentFrame().getVisibility(),
116                          context.getRubyClass(),
117                          dynamicScope);
118     }
119     
120     protected Block() {
121         this(null, null, null, null, null, null, null, null);
122     }
123
124     public Block(Node varNode, ICallable method, IRubyObject self, Frame frame,
125         SinglyLinkedList cref, Visibility visibility, RubyModule klass,
126         DynamicScope dynamicScope) {
127         
128         //assert method != null;
129

130         this.varNode = varNode;
131         this.method = method;
132         this.self = self;
133         this.frame = frame;
134         this.visibility = visibility;
135         this.klass = klass;
136         this.cref = cref;
137         this.dynamicScope = dynamicScope;
138     }
139     
140     public static Block createBinding(RubyModule wrapper, Frame frame, DynamicScope dynamicScope) {
141         ThreadContext context = frame.getSelf().getRuntime().getCurrentContext();
142         
143         // We create one extra dynamicScope on a binding so that when we 'eval "b=1", binding' the
144
// 'b' will get put into this new dynamic scope. The original scope does not see the new
145
// 'b' and successive evals with this binding will. I take it having the ability to have
146
// succesive binding evals be able to share same scope makes sense from a programmers
147
// perspective. One crappy outcome of this design is it requires Dynamic and Static
148
// scopes to be mutable for this one case.
149

150         // Note: In Ruby 1.9 all of this logic can go away since they will require explicit
151
// bindings for evals.
152

153         // We only define one special dynamic scope per 'logical' binding. So all bindings for
154
// the same scope should share the same dynamic scope. This allows multiple evals with
155
// different different bindings in the same scope to see the same stuff.
156
DynamicScope extraScope = dynamicScope.getBindingScope();
157         
158         // No binding scope so we should create one
159
if (extraScope == null) {
160             // If the next scope out has the same binding scope as this scope it means
161
// we are evaling within an eval and in that case we should be sharing the same
162
// binding scope.
163
DynamicScope parent = dynamicScope.getNextCapturedScope();
164             if (parent != null && parent.getBindingScope() == dynamicScope) {
165                 extraScope = dynamicScope;
166             } else {
167                 extraScope = new DynamicScope(new BlockStaticScope(dynamicScope.getStaticScope()), dynamicScope);
168                 dynamicScope.setBindingScope(extraScope);
169             }
170         }
171         
172         // FIXME: Ruby also saves wrapper, which we do not
173
return new Block(null, null, frame.getSelf(), frame, context.peekCRef(), frame.getVisibility(),
174                 context.getBindingRubyClass(), extraScope);
175     }
176
177     public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject replacementSelf) {
178         Block newBlock = this.cloneBlock();
179             
180         if (replacementSelf != null) newBlock.self = replacementSelf;
181
182         return newBlock.yield(context, context.getRuntime().newArrayNoCopy(args), null, null, true);
183     }
184     
185     protected void pre(ThreadContext context, RubyModule klass) {
186         context.preYieldSpecificBlock(this, klass);
187     }
188     
189     protected void post(ThreadContext context) {
190         context.postYield();
191     }
192
193     /**
194      * Yield to this block, usually passed to the current call.
195      *
196      * @param context represents the current thread-specific data
197      * @param value The value to yield, either a single value or an array of values
198      * @param self The current self
199      * @param klass
200      * @param aValue Should value be arrayified or not?
201      * @return
202      */

203     public IRubyObject yield(ThreadContext context, IRubyObject value, IRubyObject self,
204             RubyModule klass, boolean aValue) {
205         if (klass == null) {
206             self = this.self;
207             frame.setSelf(self);
208         }
209         
210         pre(context, klass);
211
212         try {
213             IRubyObject[] args = (method instanceof EvaluateCallable) ? getBlockArgsEvaluate(context, value, self, aValue) : getBlockArgs(context, value, self, aValue);
214             // This while loop is for restarting the block call in case a 'redo' fires.
215
while (true) {
216                 try {
217                     return method.call(context, self, args, NULL_BLOCK);
218                 } catch (JumpException je) {
219                     if (je.getJumpType() == JumpException.JumpType.RedoJump) {
220                         // do nothing, allow loop to redo
221
} else {
222                         if (je.getJumpType() == JumpException.JumpType.BreakJump && je.getTarget() == null) {
223                             je.setTarget(this);
224                         }
225                         throw je;
226                     }
227                 }
228             }
229             
230         } catch (JumpException je) {
231             // A 'next' is like a local return from the block, ending this call or yield.
232
if (je.getJumpType() == JumpException.JumpType.NextJump) return (IRubyObject) je.getValue();
233
234             throw je;
235         } finally {
236             post(context);
237         }
238     }
239
240     private IRubyObject[] getBlockArgs(ThreadContext context, IRubyObject value, IRubyObject self, boolean valueIsArray) {
241         //FIXME: block arg handling is mucked up in strange ways and NEED to
242
// be fixed. Especially with regard to Enumerable. See RubyEnumerable#eachToList too.
243
if (varNode == null) {
244             return new IRubyObject[]{value};
245         }
246         
247         Ruby runtime = self.getRuntime();
248         
249         switch (varNode.nodeId) {
250             case NodeTypes.ZEROARGNODE:
251                 break;
252             case NodeTypes.MULTIPLEASGNNODE:
253                 if (!valueIsArray) {
254                     value = ArgsUtil.convertToRubyArray(runtime, value, ((MultipleAsgnNode)varNode).getHeadNode() != null);
255                 }
256
257                 value = AssignmentVisitor.multiAssign(runtime, context, self, (MultipleAsgnNode)varNode, (RubyArray)value, false);
258                 break;
259             default:
260                 if (valueIsArray) {
261                     int length = arrayLength(value);
262
263                     switch (length) {
264                         case 0:
265                             value = runtime.getNil();
266                             break;
267                         case 1:
268                             value = ((RubyArray)value).eltInternal(0);
269                             break;
270                         default:
271                             runtime.getWarnings().warn("multiple values for a block parameter (" + length + " for 1)");
272                     }
273                 } else if (value == null) {
274                     runtime.getWarnings().warn("multiple values for a block parameter (0 for 1)");
275                 }
276
277                 AssignmentVisitor.assign(runtime, context, self, varNode, value, Block.NULL_BLOCK, false);
278         }
279         return ArgsUtil.convertToJavaArray(value);
280     }
281
282     private IRubyObject[] getBlockArgsEvaluate(ThreadContext context, IRubyObject value, IRubyObject self, boolean valueIsArray) {
283         //FIXME: block arg handling is mucked up in strange ways and NEED to
284
// be fixed. Especially with regard to Enumerable. See RubyEnumerable#eachToList too.
285
if (varNode == null) {
286             return IRubyObject.NULL_ARRAY;
287         }
288         
289         Ruby runtime = self.getRuntime();
290
291
292         if(valueIsArray) {
293             switch (varNode.nodeId) {
294             case NodeTypes.ZEROARGNODE:
295                 break;
296             case NodeTypes.MULTIPLEASGNNODE:
297                 value = AssignmentVisitor.multiAssign(runtime, context, self, (MultipleAsgnNode)varNode, (RubyArray)value, false);
298                 break;
299             default:
300                 int length = arrayLength(value);
301                 switch (length) {
302                 case 0:
303                     value = runtime.getNil();
304                     break;
305                 case 1:
306                     value = ((RubyArray)value).eltInternal(0);
307                     break;
308                 default:
309                     runtime.getWarnings().warn("multiple values for a block parameter (" + length + " for 1)");
310                 }
311                 AssignmentVisitor.assign(runtime, context, self, varNode, value, Block.NULL_BLOCK, false);
312             }
313         } else {
314             switch (varNode.nodeId) {
315             case NodeTypes.ZEROARGNODE:
316                 return IRubyObject.NULL_ARRAY;
317             case NodeTypes.MULTIPLEASGNNODE:
318                 value = AssignmentVisitor.multiAssign(runtime, context, self, (MultipleAsgnNode)varNode,
319                                                       ArgsUtil.convertToRubyArray(runtime, value, ((MultipleAsgnNode)varNode).getHeadNode() != null)
320                                                       , false);
321                 break;
322             default:
323                 if (value == null) {
324                     runtime.getWarnings().warn("multiple values for a block parameter (0 for 1)");
325                 }
326                 AssignmentVisitor.assign(runtime, context, self, varNode, value, Block.NULL_BLOCK, false);
327             }
328         }
329         return IRubyObject.NULL_ARRAY;
330     }
331     
332     private int arrayLength(IRubyObject node) {
333         return node instanceof RubyArray ? ((RubyArray)node).getLength() : 0;
334     }
335
336     public Block cloneBlock() {
337         // We clone dynamic scope because this will be a new instance of a block. Any previously
338
// captured instances of this block may still be around and we do not want to start
339
// overwriting those values when we create a new one.
340
// ENEBO: Once we make self, lastClass, and lastMethod immutable we can remove duplicate
341
Block newBlock = new Block(varNode, method, self, frame.duplicate(), cref, visibility, klass,
342                 dynamicScope.cloneScope());
343         
344         newBlock.isLambda = isLambda;
345
346         return newBlock;
347     }
348
349     /**
350      * What is the arity of this block?
351      *
352      * @return the arity
353      */

354     public Arity arity() {
355         return method.getArity();
356     }
357
358     public Visibility getVisibility() {
359         return visibility;
360     }
361
362     public void setVisibility(Visibility visibility) {
363         this.visibility = visibility;
364     }
365
366     public SinglyLinkedList getCRef() {
367         return cref;
368     }
369
370     /**
371      * Retrieve the proc object associated with this block
372      *
373      * @return the proc or null if this has no proc associated with it
374      */

375     public RubyProc getProcObject() {
376         return proc;
377     }
378     
379     /**
380      * Set the proc object associated with this block
381      *
382      * @param procObject
383      */

384     public void setProcObject(RubyProc procObject) {
385         this.proc = procObject;
386     }
387
388     /**
389      * Gets the dynamicVariables that are local to this block. Parent dynamic scopes are also
390      * accessible via the current dynamic scope.
391      *
392      * @return Returns all relevent variable scoping information
393      */

394     public DynamicScope getDynamicScope() {
395         return dynamicScope;
396     }
397
398     /**
399      * Gets the frame.
400      *
401      * @return Returns a RubyFrame
402      */

403     public Frame getFrame() {
404         return frame;
405     }
406
407     /**
408      * Gets the klass.
409      * @return Returns a RubyModule
410      */

411     public RubyModule getKlass() {
412         return klass;
413     }
414     
415     /**
416      * Is the current block a real yield'able block instead a null one
417      *
418      * @return true if this is a valid block or false otherwise
419      */

420     public boolean isGiven() {
421         return true;
422     }
423 }
424
Popular Tags