1 33 package org.jruby; 34 35 import java.util.List ; 36 import org.jruby.runtime.Block; 37 import org.jruby.runtime.CallbackFactory; 38 import org.jruby.runtime.ObjectAllocator; 39 import org.jruby.runtime.ThreadContext; 40 import org.jruby.runtime.builtin.IRubyObject; 41 import org.jruby.runtime.marshal.MarshalStream; 42 import org.jruby.runtime.marshal.UnmarshalStream; 43 import org.jruby.util.IdUtil; 44 import org.jruby.exceptions.RaiseException; 45 import org.jruby.runtime.ClassIndex; 46 47 50 public class RubyStruct extends RubyObject { 51 private IRubyObject[] values; 52 53 58 public RubyStruct(Ruby runtime, RubyClass rubyClass) { 59 super(runtime, rubyClass); 60 } 61 62 public static RubyClass createStructClass(Ruby runtime) { 63 RubyClass structClass = runtime.defineClass("Struct", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR); 66 structClass.index = ClassIndex.STRUCT; 67 68 CallbackFactory callbackFactory = runtime.callbackFactory(RubyStruct.class); 69 structClass.includeModule(runtime.getModule("Enumerable")); 70 71 structClass.getMetaClass().defineMethod("new", callbackFactory.getOptSingletonMethod("newInstance")); 72 73 structClass.defineMethod("initialize", callbackFactory.getOptMethod("initialize")); 74 structClass.defineMethod("clone", callbackFactory.getMethod("rbClone")); 75 76 structClass.defineFastMethod("==", callbackFactory.getFastMethod("equal", RubyKernel.IRUBY_OBJECT)); 77 78 structClass.defineFastMethod("to_s", callbackFactory.getFastMethod("to_s")); 79 structClass.defineFastMethod("inspect", callbackFactory.getFastMethod("inspect")); 80 structClass.defineFastMethod("to_a", callbackFactory.getFastMethod("to_a")); 81 structClass.defineFastMethod("values", callbackFactory.getFastMethod("to_a")); 82 structClass.defineFastMethod("size", callbackFactory.getFastMethod("size")); 83 structClass.defineFastMethod("length", callbackFactory.getFastMethod("size")); 84 85 structClass.defineMethod("each", callbackFactory.getMethod("each")); 86 structClass.defineMethod("each_pair", callbackFactory.getMethod("each_pair")); 87 structClass.defineFastMethod("[]", callbackFactory.getFastMethod("aref", RubyKernel.IRUBY_OBJECT)); 88 structClass.defineFastMethod("[]=", callbackFactory.getFastMethod("aset", RubyKernel.IRUBY_OBJECT, RubyKernel.IRUBY_OBJECT)); 89 90 structClass.defineFastMethod("members", callbackFactory.getFastMethod("members")); 91 92 return structClass; 93 } 94 95 public int getNativeTypeIndex() { 96 return ClassIndex.STRUCT; 97 } 98 99 private static IRubyObject getInstanceVariable(RubyClass type, String name) { 100 RubyClass structClass = type.getRuntime().getClass("Struct"); 101 102 while (type != null && type != structClass) { 103 IRubyObject variable = type.getInstanceVariable(name); 104 if (variable != null) { 105 return variable; 106 } 107 108 type = type.getSuperClass(); 109 } 110 111 return type.getRuntime().getNil(); 112 } 113 114 private RubyClass classOf() { 115 return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass(); 116 } 117 118 private void modify() { 119 testFrozen("Struct is frozen"); 120 121 if (!isTaint() && getRuntime().getSafeLevel() >= 4) { 122 throw getRuntime().newSecurityError("Insecure: can't modify struct"); 123 } 124 } 125 126 private IRubyObject setByName(String name, IRubyObject value) { 127 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 128 129 assert !member.isNil() : "uninitialized struct"; 130 131 modify(); 132 133 for (int i = 0,k=member.getLength(); i < k; i++) { 134 if (member.eltInternal(i).asSymbol().equals(name)) { 135 return values[i] = value; 136 } 137 } 138 139 throw notStructMemberError(name); 140 } 141 142 private IRubyObject getByName(String name) { 143 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 144 145 assert !member.isNil() : "uninitialized struct"; 146 147 for (int i = 0,k=member.getLength(); i < k; i++) { 148 if (member.eltInternal(i).asSymbol().equals(name)) { 149 return values[i]; 150 } 151 } 152 153 throw notStructMemberError(name); 154 } 155 156 158 163 public static RubyClass newInstance(IRubyObject recv, IRubyObject[] args, Block block) { 164 String name = null; 165 Ruby runtime = recv.getRuntime(); 166 167 if (args.length > 0 && args[0] instanceof RubyString) { 168 name = args[0].toString(); 169 } 170 171 RubyArray member = recv.getRuntime().newArray(); 172 173 for (int i = name == null ? 0 : 1; i < args.length; i++) { 174 member.append(RubySymbol.newSymbol(recv.getRuntime(), args[i].asSymbol())); 175 } 176 177 RubyClass newStruct; 178 RubyClass superClass = (RubyClass)recv; 179 180 if (name == null) { 181 newStruct = new RubyClass(superClass, superClass.getAllocator()); 182 } else { 183 if (!IdUtil.isConstant(name)) { 184 throw runtime.newNameError("identifier " + name + " needs to be constant", name); 185 } 186 187 IRubyObject type = superClass.getConstantAt(name); 188 189 if (type != null) { 190 runtime.getWarnings().warn(runtime.getCurrentContext().getFramePosition(), "redefining constant Struct::" + name); 191 } 192 newStruct = superClass.newSubClass(name, superClass.getAllocator(), superClass.getCRef()); 193 } 194 195 newStruct.index = ClassIndex.STRUCT; 196 197 newStruct.setInstanceVariable("__size__", member.length()); 198 newStruct.setInstanceVariable("__member__", member); 199 200 CallbackFactory callbackFactory = recv.getRuntime().callbackFactory(RubyStruct.class); 201 newStruct.defineSingletonMethod("new", callbackFactory.getOptSingletonMethod("newStruct")); 202 newStruct.defineSingletonMethod("[]", callbackFactory.getOptSingletonMethod("newStruct")); 203 newStruct.defineSingletonMethod("members", callbackFactory.getSingletonMethod("members")); 204 205 for (int i = name == null ? 0 : 1; i < args.length; i++) { 207 String memberName = args[i].asSymbol(); 208 newStruct.defineMethod(memberName, callbackFactory.getMethod("get")); 209 newStruct.defineMethod(memberName + "=", callbackFactory.getMethod("set", RubyKernel.IRUBY_OBJECT)); 210 } 211 212 if (block.isGiven()) { 213 block.yield(recv.getRuntime().getCurrentContext(), null, newStruct, newStruct, false); 214 } 215 216 return newStruct; 217 } 218 219 224 public static RubyStruct newStruct(IRubyObject recv, IRubyObject[] args, Block block) { 225 RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClass) recv); 226 227 int size = RubyNumeric.fix2int(getInstanceVariable((RubyClass) recv, "__size__")); 228 229 struct.values = new IRubyObject[size]; 230 231 struct.callInit(args, block); 232 233 return struct; 234 } 235 236 public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) { 237 modify(); 238 239 int size = RubyNumeric.fix2int(getInstanceVariable(getMetaClass(), "__size__")); 240 241 if (args.length > size) { 242 throw getRuntime().newArgumentError("struct size differs (" + args.length +" for " + size + ")"); 243 } 244 245 for (int i = 0; i < args.length; i++) { 246 values[i] = args[i]; 247 } 248 249 for (int i = args.length; i < size; i++) { 250 values[i] = getRuntime().getNil(); 251 } 252 253 return getRuntime().getNil(); 254 } 255 256 public static RubyArray members(IRubyObject recv, Block block) { 257 RubyArray member = (RubyArray) getInstanceVariable((RubyClass) recv, "__member__"); 258 259 assert !member.isNil() : "uninitialized struct"; 260 261 RubyArray result = recv.getRuntime().newArray(member.getLength()); 262 for (int i = 0,k=member.getLength(); i < k; i++) { 263 result.append(recv.getRuntime().newString(member.eltInternal(i).asSymbol())); 264 } 265 266 return result; 267 } 268 269 public RubyArray members() { 270 return members(classOf(), Block.NULL_BLOCK); 271 } 272 273 public IRubyObject set(IRubyObject value, Block block) { 274 String name = getRuntime().getCurrentContext().getFrameName(); 275 if (name.endsWith("=")) { 276 name = name.substring(0, name.length() - 1); 277 } 278 279 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 280 281 assert !member.isNil() : "uninitialized struct"; 282 283 modify(); 284 285 for (int i = 0,k=member.getLength(); i < k; i++) { 286 if (member.eltInternal(i).asSymbol().equals(name)) { 287 return values[i] = value; 288 } 289 } 290 291 throw notStructMemberError(name); 292 } 293 294 private RaiseException notStructMemberError(String name) { 295 return getRuntime().newNameError(name + " is not struct member", name); 296 } 297 298 public IRubyObject get(Block block) { 299 String name = getRuntime().getCurrentContext().getFrameName(); 300 301 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 302 303 assert !member.isNil() : "uninitialized struct"; 304 305 for (int i = 0,k=member.getLength(); i < k; i++) { 306 if (member.eltInternal(i).asSymbol().equals(name)) { 307 return values[i]; 308 } 309 } 310 311 throw notStructMemberError(name); 312 } 313 314 public IRubyObject rbClone(Block block) { 315 RubyStruct clone = new RubyStruct(getRuntime(), getMetaClass()); 316 317 clone.values = new IRubyObject[values.length]; 318 System.arraycopy(values, 0, clone.values, 0, values.length); 319 320 clone.setFrozen(this.isFrozen()); 321 clone.setTaint(this.isTaint()); 322 323 return clone; 324 } 325 326 public IRubyObject equal(IRubyObject other) { 327 if (this == other) { 328 return getRuntime().getTrue(); 329 } else if (!(other instanceof RubyStruct)) { 330 return getRuntime().getFalse(); 331 } else if (getMetaClass() != other.getMetaClass()) { 332 return getRuntime().getFalse(); 333 } else { 334 for (int i = 0; i < values.length; i++) { 335 if (!values[i].equals(((RubyStruct) other).values[i])) { 336 return getRuntime().getFalse(); 337 } 338 } 339 return getRuntime().getTrue(); 340 } 341 } 342 343 public IRubyObject to_s() { 344 return inspect(); 345 } 346 347 public IRubyObject inspect() { 348 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 349 350 assert !member.isNil() : "uninitialized struct"; 351 352 StringBuffer sb = new StringBuffer (100); 353 354 sb.append("#<struct ").append(getMetaClass().getName()).append(' '); 355 356 for (int i = 0,k=member.getLength(); i < k; i++) { 357 if (i > 0) { 358 sb.append(", "); 359 } 360 361 sb.append(member.eltInternal(i).asSymbol()).append("="); 362 sb.append(values[i].callMethod(getRuntime().getCurrentContext(), "inspect")); 363 } 364 365 sb.append('>'); 366 367 return getRuntime().newString(sb.toString()); } 369 370 public RubyArray to_a() { 371 return getRuntime().newArray(values); 372 } 373 374 public RubyFixnum size() { 375 return getRuntime().newFixnum(values.length); 376 } 377 378 public IRubyObject each(Block block) { 379 ThreadContext context = getRuntime().getCurrentContext(); 380 for (int i = 0; i < values.length; i++) { 381 context.yield(values[i], block); 382 } 383 384 return this; 385 } 386 387 public IRubyObject each_pair(Block block) { 388 RubyArray member = (RubyArray) getInstanceVariable(classOf(), "__member__"); 389 390 assert !member.isNil() : "uninitialized struct"; 391 392 ThreadContext context = getRuntime().getCurrentContext(); 393 for (int i = 0; i < values.length; i++) { 394 context.yield(getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), values[i]}), block); 395 } 396 397 return this; 398 } 399 400 public IRubyObject aref(IRubyObject key) { 401 if (key instanceof RubyString || key instanceof RubySymbol) { 402 return getByName(key.asSymbol()); 403 } 404 405 int idx = RubyNumeric.fix2int(key); 406 407 idx = idx < 0 ? values.length + idx : idx; 408 409 if (idx < 0) { 410 throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")"); 411 } else if (idx >= values.length) { 412 throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")"); 413 } 414 415 return values[idx]; 416 } 417 418 public IRubyObject aset(IRubyObject key, IRubyObject value) { 419 if (key instanceof RubyString || key instanceof RubySymbol) { 420 return setByName(key.asSymbol(), value); 421 } 422 423 int idx = RubyNumeric.fix2int(key); 424 425 idx = idx < 0 ? values.length + idx : idx; 426 427 if (idx < 0) { 428 throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")"); 429 } else if (idx >= values.length) { 430 throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")"); 431 } 432 433 modify(); 434 return values[idx] = value; 435 } 436 437 public static void marshalTo(RubyStruct struct, MarshalStream output) throws java.io.IOException { 438 String className = struct.getMetaClass().getName(); 439 if (className == null) { 440 throw struct.getRuntime().newArgumentError("can't dump anonymous class"); 441 } 442 output.dumpObject(RubySymbol.newSymbol(struct.getRuntime(), className)); 443 444 List members = ((RubyArray) getInstanceVariable(struct.classOf(), "__member__")).getList(); 445 output.writeInt(members.size()); 446 447 for (int i = 0; i < members.size(); i++) { 448 RubySymbol name = (RubySymbol) members.get(i); 449 output.dumpObject(name); 450 output.dumpObject(struct.values[i]); 451 } 452 } 453 454 public static RubyStruct unmarshalFrom(UnmarshalStream input) throws java.io.IOException { 455 Ruby runtime = input.getRuntime(); 456 457 RubySymbol className = (RubySymbol) input.unmarshalObject(); 458 RubyClass rbClass = pathToClass(runtime, className.asSymbol()); 459 if (rbClass == null) { 460 throw runtime.newNameError("uninitialized constant " + className, className.asSymbol()); 461 } 462 463 int size = input.unmarshalInt(); 464 465 IRubyObject[] values = new IRubyObject[size]; 466 for (int i = 0; i < size; i++) { 467 input.unmarshalObject(); values[i] = input.unmarshalObject(); 469 } 470 471 RubyStruct result = newStruct(rbClass, values, Block.NULL_BLOCK); 472 input.registerLinkTarget(result); 473 return result; 474 } 475 476 private static RubyClass pathToClass(Ruby runtime, String path) { 477 return (RubyClass) runtime.getClassFromPath(path); 480 } 481 } 482 | Popular Tags |