1 28 package org.jruby; 29 30 import org.jruby.internal.runtime.methods.MultiStubMethod; 31 import org.jruby.internal.runtime.methods.NoopMultiStub; 32 33 import org.jruby.runtime.Arity; 34 import org.jruby.runtime.Block; 35 import org.jruby.runtime.BlockCallback; 36 import org.jruby.runtime.ObjectAllocator; 37 import org.jruby.runtime.ThreadContext; 38 import org.jruby.runtime.Visibility; 39 40 import org.jruby.runtime.builtin.IRubyObject; 41 42 45 public class RubyEnumerator extends RubyObject { 46 47 private IRubyObject object; 48 49 50 private IRubyObject method; 51 52 53 private IRubyObject[] methodArgs; 54 55 private static ObjectAllocator ENUMERATOR_ALLOCATOR = new ObjectAllocator() { 56 public IRubyObject allocate(Ruby runtime, RubyClass klass) { 57 return new RubyEnumerator(runtime, klass); 58 } 59 }; 60 61 public static void defineEnumerator(Ruby runtime) { 62 RubyModule enumerableModule = runtime.getModule("Enumerable"); 63 RubyClass object = runtime.getObject(); 64 RubyClass enumeratorClass = enumerableModule.defineClassUnder("Enumerator", object, ENUMERATOR_ALLOCATOR); 65 RubyEnumeratorStub0 enumeratorStub = RubyEnumeratorStub0.createStub(enumeratorClass, object, enumerableModule); 66 67 enumeratorClass.includeModule(enumerableModule); 68 enumeratorClass.getMetaClass().addMethod("new", enumeratorStub.enumerator__new); 69 enumeratorClass.addMethod("initialize", enumeratorStub.enumerator__initialize); 70 enumeratorClass.addMethod("each", enumeratorStub.enumerator__each); 71 72 object.addMethod("to_enum", enumeratorStub.object__to_enum); 73 object.addMethod("enum_for", enumeratorStub.object__to_enum); 74 75 enumerableModule.addMethod("enum_with_index", enumeratorStub.enumerable__enum_with_index); 76 enumerableModule.addMethod("each_slice", enumeratorStub.enumerable__each_slice); 77 enumerableModule.addMethod("enum_slice", enumeratorStub.enumerable__enum_slice); 78 enumerableModule.addMethod("each_cons", enumeratorStub.enumerable__each_cons); 79 enumerableModule.addMethod("enum_cons", enumeratorStub.enumerable__enum_cons); 80 } 81 82 private RubyEnumerator(Ruby runtime, RubyClass type) { 83 super(runtime, type); 84 } 85 86 87 private IRubyObject initialize(ThreadContext tc, IRubyObject[] args, Block block) { 88 checkArgumentCount(args, 1, -1); 89 90 object = args[0]; 91 methodArgs = new IRubyObject[Math.max(0, args.length - 2)]; 92 93 if (args.length >= 2) { 94 method = args[1]; 95 } else { 96 method = RubySymbol.newSymbol(tc.getRuntime(), "each"); 97 } 98 99 if (args.length >= 3) { 100 System.arraycopy(args, 2, methodArgs, 0, args.length - 2); 101 } else { 102 methodArgs = new IRubyObject[0]; 103 } 104 105 return this; 106 } 107 108 113 private IRubyObject each(ThreadContext tc, IRubyObject[] args, Block block) { 114 checkArgumentCount(args, 0, 0); 115 116 return object.callMethod(tc, method.asSymbol(), methodArgs, block); 117 } 118 119 120 public static class SlicedBlockCallback implements BlockCallback { 121 protected RubyArray slice; 122 protected final long sliceSize; 123 protected final Block clientBlock; 124 protected final Ruby runtime; 125 126 public SlicedBlockCallback(Ruby runtime, Block clientBlock, long sliceSize) { 127 this.runtime = runtime; 128 this.clientBlock = clientBlock; 129 this.sliceSize = sliceSize; 130 this.slice = RubyArray.newArray(runtime, sliceSize); 131 } 132 133 public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject replacementSelf, Block block) { 134 if (args.length > 1) { 135 slice.append(RubyArray.newArray(runtime, args)); 136 } else { 137 slice.append(args[0]); 138 } 139 140 if (slice.getLength() == sliceSize) { 141 clientBlock.call(context, new IRubyObject[] { slice }, null); 143 144 slice = RubyArray.newArray(runtime, sliceSize); 145 } 146 147 return runtime.getNil(); 148 } 149 150 151 152 public boolean hasLeftovers() { 153 return (slice.getLength() > 0) && (slice.getLength() < sliceSize); 154 } 155 156 157 public void yieldLeftovers(ThreadContext context) { 158 clientBlock.call(context, new IRubyObject[] { slice }, null); 159 } 160 } 161 162 163 public static class ConsecutiveBlockCallback implements BlockCallback { 164 protected final RubyArray cont; 165 protected final long contSize; 166 protected final Block clientBlock; 167 protected final Ruby runtime; 168 169 170 public ConsecutiveBlockCallback(Ruby runtime, Block clientBlock, long contSize) { 171 this.runtime = runtime; 172 this.clientBlock = clientBlock; 173 this.contSize = contSize; 174 this.cont = RubyArray.newArray(runtime, contSize); 175 } 176 177 public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject replacementSelf, Block block) { 178 if (cont.getLength() == contSize) { 179 cont.shift(); 180 } 181 182 if (args.length > 1) { 183 cont.append(RubyArray.newArray(runtime, args)); 184 } else { 185 cont.append(args[0]); 186 } 187 188 if (cont.getLength() == contSize) { 189 clientBlock.call(context, new IRubyObject[] { cont.dup() }, null); 191 } 192 193 return runtime.getNil(); 194 } 195 } 196 197 198 public static class RubyEnumeratorStub0 extends NoopMultiStub { 199 private final RubyModule enumerator; 200 private final Ruby runtime; 201 202 public static RubyEnumeratorStub0 createStub(RubyClass enumeratorClass, 203 RubyClass objectClass, RubyModule enumerableModule) { 204 return new RubyEnumeratorStub0(enumeratorClass, objectClass, enumerableModule); 205 } 206 207 public final MultiStubMethod enumerator__new; 208 public final MultiStubMethod enumerator__initialize; 209 public final MultiStubMethod enumerator__each; 210 public final MultiStubMethod object__to_enum; 211 public final MultiStubMethod enumerable__each_slice; 212 public final MultiStubMethod enumerable__each_cons; 213 public final MultiStubMethod enumerable__enum_with_index; 214 public final MultiStubMethod enumerable__enum_slice; 215 public final MultiStubMethod enumerable__enum_cons; 216 217 private RubyEnumeratorStub0(RubyClass enumeratorClass, 218 RubyClass objectClass, RubyModule enumerableModule) { 219 enumerator = enumeratorClass; 220 runtime = enumeratorClass.getRuntime(); 221 enumerator__new = new MultiStubMethod(RubyEnumeratorStub0.this, 0, 222 enumeratorClass, Arity.required(1), Visibility.PUBLIC); 223 enumerator__initialize = new MultiStubMethod(RubyEnumeratorStub0.this, 1, 224 enumeratorClass, Arity.required(1), Visibility.PRIVATE); 225 enumerator__each = new MultiStubMethod(RubyEnumeratorStub0.this, 2, 226 enumeratorClass, Arity.optional(), Visibility.PUBLIC); 227 object__to_enum = new MultiStubMethod(RubyEnumeratorStub0.this, 3, 228 objectClass, Arity.optional(), Visibility.PUBLIC); 229 enumerable__each_slice = new MultiStubMethod(RubyEnumeratorStub0.this, 4, 230 enumerableModule, Arity.singleArgument(), Visibility.PUBLIC); 231 enumerable__each_cons = new MultiStubMethod(RubyEnumeratorStub0.this, 5, 232 enumerableModule, Arity.singleArgument(), Visibility.PUBLIC); 233 enumerable__enum_with_index = new MultiStubMethod(RubyEnumeratorStub0.this, 6, 234 enumerableModule, Arity.noArguments(), Visibility.PUBLIC); 235 enumerable__enum_slice = new MultiStubMethod(RubyEnumeratorStub0.this, 7, 236 enumerableModule, Arity.singleArgument(), Visibility.PUBLIC); 237 enumerable__enum_cons = new MultiStubMethod(RubyEnumeratorStub0.this, 8, 238 enumerableModule, Arity.singleArgument(), Visibility.PUBLIC); 239 } 240 241 242 public IRubyObject method0(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 243 RubyClass klass = (RubyClass)self; 244 245 RubyEnumerator result = (RubyEnumerator) klass.allocate(); 246 247 result.callInit(args, block); 248 249 return result; 250 } 251 252 253 public IRubyObject method1(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 254 return ((RubyEnumerator) self).initialize(tc, args, block); 255 } 256 257 258 public IRubyObject method2(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 259 return ((RubyEnumerator) self).each(tc, args, block); 260 } 261 262 263 public IRubyObject method3(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 264 IRubyObject[] newArgs = new IRubyObject[args.length + 1]; 265 newArgs[0] = self; 266 System.arraycopy(args, 0, newArgs, 1, args.length); 267 268 return enumerator.callMethod(tc, "new", newArgs); 269 } 270 271 272 public IRubyObject method4(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 273 self.checkArgumentCount(args, 1, 1); 274 275 long sliceSize = args[0].convertToInteger().getLongValue(); 276 277 if (sliceSize <= 0L) { 278 throw runtime.newArgumentError("invalid slice size"); 279 } 280 281 SlicedBlockCallback sliceBlock = new SlicedBlockCallback(runtime, block, sliceSize); 282 283 RubyEnumerable.callEach(tc, self, self.getMetaClass(), sliceBlock); 284 285 if (sliceBlock.hasLeftovers()) { 286 sliceBlock.yieldLeftovers(tc); 287 } 288 289 return runtime.getNil(); 290 } 291 292 293 public IRubyObject method5(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 294 self.checkArgumentCount(args, 1, 1); 295 296 long consecutiveSize = args[0].convertToInteger().getLongValue(); 297 298 if (consecutiveSize <= 0L) { 299 throw runtime.newArgumentError("invalid size"); 300 } 301 302 RubyEnumerable.callEach(tc, self, self.getMetaClass(), 303 new ConsecutiveBlockCallback(runtime, block, consecutiveSize)); 304 305 return runtime.getNil(); 306 } 307 308 309 public IRubyObject method6(final ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 310 self.checkArgumentCount(args, 0, 0); 311 312 return enumerator.callMethod(tc, "new", 313 new IRubyObject[] { self, runtime.newSymbol("each_with_index") }); 314 } 315 316 317 public IRubyObject method7(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 318 self.checkArgumentCount(args, 1, 1); 319 320 return enumerator.callMethod(tc, "new", 321 new IRubyObject[] { self, runtime.newSymbol("each_slice"), args[0] }); 322 } 323 324 325 public IRubyObject method8(ThreadContext tc, IRubyObject self, IRubyObject[] args, Block block) { 326 self.checkArgumentCount(args, 1, 1); 327 328 return enumerator.callMethod(tc, "new", 329 new IRubyObject[] { self, runtime.newSymbol("each_cons"), args[0] }); 330 } 331 } 332 } 333 | Popular Tags |