1 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 52 public class Block { 53 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 74 private IRubyObject self; 75 76 79 private ICallable method; 80 81 84 private Node varNode; 85 86 89 private Frame frame; 90 private SinglyLinkedList cref; 91 private Visibility visibility; 92 private RubyModule klass; 93 94 97 private DynamicScope dynamicScope; 98 99 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 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 150 153 DynamicScope extraScope = dynamicScope.getBindingScope(); 157 158 if (extraScope == null) { 160 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 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 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 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 } 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 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 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 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 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 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 375 public RubyProc getProcObject() { 376 return proc; 377 } 378 379 384 public void setProcObject(RubyProc procObject) { 385 this.proc = procObject; 386 } 387 388 394 public DynamicScope getDynamicScope() { 395 return dynamicScope; 396 } 397 398 403 public Frame getFrame() { 404 return frame; 405 } 406 407 411 public RubyModule getKlass() { 412 return klass; 413 } 414 415 420 public boolean isGiven() { 421 return true; 422 } 423 } 424 | Popular Tags |