001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
015: * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
016: * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
017: * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
018: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
019: * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * either of the GNU General Public License Version 2 or later (the "GPL"),
023: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
024: * in which case the provisions of the GPL or the LGPL are applicable instead
025: * of those above. If you wish to allow use of your version of this file only
026: * under the terms of either the GPL or the LGPL, and not to allow others to
027: * use your version of this file under the terms of the CPL, indicate your
028: * decision by deleting the provisions above and replace them with the notice
029: * and other provisions required by the GPL or the LGPL. If you do not delete
030: * the provisions above, a recipient may use your version of this file under
031: * the terms of any one of the CPL, the GPL or the LGPL.
032: ***** END LICENSE BLOCK *****/package org.jruby;
033:
034: import java.util.List;
035:
036: import org.jruby.runtime.Arity;
037: import org.jruby.runtime.Block;
038: import org.jruby.runtime.CallbackFactory;
039: import org.jruby.runtime.MethodIndex;
040: import org.jruby.runtime.ObjectAllocator;
041: import org.jruby.runtime.ThreadContext;
042: import org.jruby.runtime.builtin.IRubyObject;
043: import org.jruby.runtime.marshal.MarshalStream;
044: import org.jruby.runtime.marshal.UnmarshalStream;
045: import org.jruby.util.IdUtil;
046: import org.jruby.exceptions.RaiseException;
047: import org.jruby.runtime.ClassIndex;
048:
049: /**
050: * @author jpetersen
051: */
052: public class RubyStruct extends RubyObject {
053: private IRubyObject[] values;
054:
055: /**
056: * Constructor for RubyStruct.
057: * @param runtime
058: * @param rubyClass
059: */
060: public RubyStruct(Ruby runtime, RubyClass rubyClass) {
061: super (runtime, rubyClass);
062: }
063:
064: public static RubyClass createStructClass(Ruby runtime) {
065: // TODO: NOT_ALLOCATABLE_ALLOCATOR may be ok here, but it's unclear how Structs
066: // work with marshalling. Confirm behavior and ensure we're doing this correctly. JRUBY-415
067: RubyClass structClass = runtime
068: .defineClass("Struct", runtime.getObject(),
069: ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
070: structClass.index = ClassIndex.STRUCT;
071:
072: CallbackFactory callbackFactory = runtime
073: .callbackFactory(RubyStruct.class);
074: structClass.includeModule(runtime.getModule("Enumerable"));
075:
076: structClass.getMetaClass().defineMethod("new",
077: callbackFactory.getOptSingletonMethod("newInstance"));
078:
079: structClass.defineMethod("initialize", callbackFactory
080: .getOptMethod("initialize"));
081: structClass.defineFastMethod("initialize_copy", callbackFactory
082: .getFastMethod("initialize_copy",
083: RubyKernel.IRUBY_OBJECT));
084: structClass.defineMethod("clone", callbackFactory
085: .getMethod("rbClone"));
086:
087: structClass.defineFastMethod("==", callbackFactory
088: .getFastMethod("equal", RubyKernel.IRUBY_OBJECT));
089: structClass.defineFastMethod("eql?", callbackFactory
090: .getFastMethod("eql_p", RubyKernel.IRUBY_OBJECT));
091:
092: structClass.defineFastMethod("to_s", callbackFactory
093: .getFastMethod("to_s"));
094: structClass.defineFastMethod("inspect", callbackFactory
095: .getFastMethod("inspect"));
096: structClass.defineFastMethod("to_a", callbackFactory
097: .getFastMethod("to_a"));
098: structClass.defineFastMethod("values", callbackFactory
099: .getFastMethod("to_a"));
100: structClass.defineFastMethod("size", callbackFactory
101: .getFastMethod("size"));
102: structClass.defineFastMethod("length", callbackFactory
103: .getFastMethod("size"));
104: structClass.defineFastMethod("hash", callbackFactory
105: .getFastMethod("hash"));
106:
107: structClass.defineMethod("each", callbackFactory
108: .getMethod("each"));
109: structClass.defineMethod("each_pair", callbackFactory
110: .getMethod("each_pair"));
111: structClass.defineFastMethod("[]", callbackFactory
112: .getFastMethod("aref", RubyKernel.IRUBY_OBJECT));
113: structClass.defineFastMethod("[]=", callbackFactory
114: .getFastMethod("aset", RubyKernel.IRUBY_OBJECT,
115: RubyKernel.IRUBY_OBJECT));
116: structClass.defineFastMethod("values_at", callbackFactory
117: .getFastOptMethod("values_at"));
118:
119: structClass.defineFastMethod("members", callbackFactory
120: .getFastMethod("members"));
121:
122: return structClass;
123: }
124:
125: public int getNativeTypeIndex() {
126: return ClassIndex.STRUCT;
127: }
128:
129: private static IRubyObject getInstanceVariable(RubyClass type,
130: String name) {
131: RubyClass structClass = type.getRuntime().getClass("Struct");
132:
133: while (type != null && type != structClass) {
134: IRubyObject variable = type.getInstanceVariable(name);
135: if (variable != null) {
136: return variable;
137: }
138:
139: type = type.getSuperClass();
140: }
141:
142: return type.getRuntime().getNil();
143: }
144:
145: private RubyClass classOf() {
146: return getMetaClass() instanceof MetaClass ? getMetaClass()
147: .getSuperClass() : getMetaClass();
148: }
149:
150: private void modify() {
151: testFrozen("Struct is frozen");
152:
153: if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
154: throw getRuntime().newSecurityError(
155: "Insecure: can't modify struct");
156: }
157: }
158:
159: public RubyFixnum hash() {
160: Ruby runtime = getRuntime();
161: ThreadContext context = runtime.getCurrentContext();
162: int h = getMetaClass().getRealClass().hashCode();
163:
164: for (int i = 0; i < values.length; i++) {
165: h = (h << 1) | (h < 0 ? 1 : 0);
166: h ^= RubyNumeric.num2long(values[i].callMethod(context,
167: MethodIndex.HASH, "hash"));
168: }
169:
170: return runtime.newFixnum(h);
171: }
172:
173: private IRubyObject setByName(String name, IRubyObject value) {
174: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
175: "__member__");
176:
177: assert !member.isNil() : "uninitialized struct";
178:
179: modify();
180:
181: for (int i = 0, k = member.getLength(); i < k; i++) {
182: if (member.eltInternal(i).asSymbol().equals(name)) {
183: return values[i] = value;
184: }
185: }
186:
187: throw notStructMemberError(name);
188: }
189:
190: private IRubyObject getByName(String name) {
191: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
192: "__member__");
193:
194: assert !member.isNil() : "uninitialized struct";
195:
196: for (int i = 0, k = member.getLength(); i < k; i++) {
197: if (member.eltInternal(i).asSymbol().equals(name)) {
198: return values[i];
199: }
200: }
201:
202: throw notStructMemberError(name);
203: }
204:
205: // Struct methods
206:
207: /** Create new Struct class.
208: *
209: * MRI: rb_struct_s_def / make_struct
210: *
211: */
212: public static RubyClass newInstance(IRubyObject recv,
213: IRubyObject[] args, Block block) {
214: String name = null;
215: Ruby runtime = recv.getRuntime();
216: Arity.checkArgumentCount(runtime, args, 1, -1);
217:
218: if (args.length > 0 && args[0] instanceof RubyString) {
219: name = args[0].toString();
220: }
221:
222: RubyArray member = runtime.newArray();
223:
224: for (int i = name == null ? 0 : 1; i < args.length; i++) {
225: member.append(RubySymbol.newSymbol(runtime, args[i]
226: .asSymbol()));
227: }
228:
229: RubyClass newStruct;
230: RubyClass super Class = (RubyClass) recv;
231:
232: if (name == null) {
233: newStruct = new RubyClass(super Class,
234: STRUCT_INSTANCE_ALLOCATOR);
235: } else {
236: if (!IdUtil.isConstant(name)) {
237: throw runtime.newNameError("identifier " + name
238: + " needs to be constant", name);
239: }
240:
241: IRubyObject type = super Class.getConstantAt(name);
242:
243: if (type != null) {
244: runtime.getWarnings().warn(
245: runtime.getCurrentContext().getFramePosition(),
246: "redefining constant Struct::" + name);
247: }
248: newStruct = super Class.newSubClass(name,
249: STRUCT_INSTANCE_ALLOCATOR, super Class.getCRef(),
250: false);
251: }
252:
253: newStruct.index = ClassIndex.STRUCT;
254:
255: newStruct.setInstanceVariable("__size__", member.length());
256: newStruct.setInstanceVariable("__member__", member);
257:
258: CallbackFactory callbackFactory = runtime
259: .callbackFactory(RubyStruct.class);
260: newStruct.getSingletonClass().defineMethod("new",
261: callbackFactory.getOptSingletonMethod("newStruct"));
262: newStruct.getSingletonClass().defineMethod("[]",
263: callbackFactory.getOptSingletonMethod("newStruct"));
264: newStruct.getSingletonClass().defineMethod("members",
265: callbackFactory.getSingletonMethod("members"));
266:
267: // define access methods.
268: for (int i = name == null ? 0 : 1; i < args.length; i++) {
269: String memberName = args[i].asSymbol();
270: newStruct.defineMethod(memberName, callbackFactory
271: .getMethod("get"));
272: newStruct.defineMethod(memberName + "=", callbackFactory
273: .getMethod("set", RubyKernel.IRUBY_OBJECT));
274: }
275:
276: if (block.isGiven()) {
277: block.yield(runtime.getCurrentContext(), null, newStruct,
278: newStruct, false);
279: }
280:
281: return newStruct;
282: }
283:
284: /** Create new Structure.
285: *
286: * MRI: struct_alloc
287: *
288: */
289: public static RubyStruct newStruct(IRubyObject recv,
290: IRubyObject[] args, Block block) {
291: RubyStruct struct = new RubyStruct(recv.getRuntime(),
292: (RubyClass) recv);
293:
294: int size = RubyNumeric.fix2int(getInstanceVariable(
295: (RubyClass) recv, "__size__"));
296:
297: struct.values = new IRubyObject[size];
298:
299: struct.callInit(args, block);
300:
301: return struct;
302: }
303:
304: public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
305: modify();
306:
307: int size = RubyNumeric.fix2int(getInstanceVariable(
308: getMetaClass(), "__size__"));
309:
310: if (args.length > size) {
311: throw getRuntime().newArgumentError(
312: "struct size differs (" + args.length + " for "
313: + size + ")");
314: }
315:
316: for (int i = 0; i < args.length; i++) {
317: values[i] = args[i];
318: }
319:
320: for (int i = args.length; i < size; i++) {
321: values[i] = getRuntime().getNil();
322: }
323:
324: return getRuntime().getNil();
325: }
326:
327: public static RubyArray members(IRubyObject recv, Block block) {
328: RubyArray member = (RubyArray) getInstanceVariable(
329: (RubyClass) recv, "__member__");
330:
331: assert !member.isNil() : "uninitialized struct";
332:
333: RubyArray result = recv.getRuntime().newArray(
334: member.getLength());
335: for (int i = 0, k = member.getLength(); i < k; i++) {
336: result.append(recv.getRuntime().newString(
337: member.eltInternal(i).asSymbol()));
338: }
339:
340: return result;
341: }
342:
343: public RubyArray members() {
344: return members(classOf(), Block.NULL_BLOCK);
345: }
346:
347: public IRubyObject set(IRubyObject value, Block block) {
348: String name = getRuntime().getCurrentContext().getFrameName();
349: if (name.endsWith("=")) {
350: name = name.substring(0, name.length() - 1);
351: }
352:
353: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
354: "__member__");
355:
356: assert !member.isNil() : "uninitialized struct";
357:
358: modify();
359:
360: for (int i = 0, k = member.getLength(); i < k; i++) {
361: if (member.eltInternal(i).asSymbol().equals(name)) {
362: return values[i] = value;
363: }
364: }
365:
366: throw notStructMemberError(name);
367: }
368:
369: private RaiseException notStructMemberError(String name) {
370: return getRuntime().newNameError(
371: name + " is not struct member", name);
372: }
373:
374: public IRubyObject get(Block block) {
375: String name = getRuntime().getCurrentContext().getFrameName();
376:
377: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
378: "__member__");
379:
380: assert !member.isNil() : "uninitialized struct";
381:
382: for (int i = 0, k = member.getLength(); i < k; i++) {
383: if (member.eltInternal(i).asSymbol().equals(name)) {
384: return values[i];
385: }
386: }
387:
388: throw notStructMemberError(name);
389: }
390:
391: public IRubyObject rbClone(Block block) {
392: RubyStruct clone = new RubyStruct(getRuntime(), getMetaClass());
393:
394: clone.values = new IRubyObject[values.length];
395: System.arraycopy(values, 0, clone.values, 0, values.length);
396:
397: clone.setFrozen(this .isFrozen());
398: clone.setTaint(this .isTaint());
399:
400: return clone;
401: }
402:
403: public IRubyObject equal(IRubyObject other) {
404: if (this == other)
405: return getRuntime().getTrue();
406: if (!(other instanceof RubyStruct))
407: return getRuntime().getFalse();
408: if (getMetaClass().getRealClass() != other.getMetaClass()
409: .getRealClass())
410: return getRuntime().getFalse();
411:
412: Ruby runtime = getRuntime();
413: ThreadContext context = runtime.getCurrentContext();
414: RubyStruct otherStruct = (RubyStruct) other;
415: for (int i = 0; i < values.length; i++) {
416: if (!values[i]
417: .equalInternal(context, otherStruct.values[i])
418: .isTrue()) {
419: return runtime.getFalse();
420: }
421: }
422: return runtime.getTrue();
423: }
424:
425: public IRubyObject eql_p(IRubyObject other) {
426: if (this == other)
427: return getRuntime().getTrue();
428: if (!(other instanceof RubyStruct))
429: return getRuntime().getFalse();
430: if (getMetaClass() != other.getMetaClass())
431: return getRuntime().getFalse();
432:
433: Ruby runtime = getRuntime();
434: ThreadContext context = runtime.getCurrentContext();
435: RubyStruct otherStruct = (RubyStruct) other;
436: for (int i = 0; i < values.length; i++) {
437: if (!values[i].eqlInternal(context, otherStruct.values[i])) {
438: return runtime.getFalse();
439: }
440: }
441: return runtime.getTrue();
442: }
443:
444: public IRubyObject to_s() {
445: return inspect();
446: }
447:
448: public IRubyObject inspect() {
449: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
450: "__member__");
451:
452: assert !member.isNil() : "uninitialized struct";
453:
454: StringBuffer sb = new StringBuffer(100);
455:
456: sb.append("#<struct ").append(
457: getMetaClass().getRealClass().getName()).append(' ');
458:
459: for (int i = 0, k = member.getLength(); i < k; i++) {
460: if (i > 0) {
461: sb.append(", ");
462: }
463:
464: sb.append(member.eltInternal(i).asSymbol()).append("=");
465: sb.append(values[i].callMethod(getRuntime()
466: .getCurrentContext(), "inspect"));
467: }
468:
469: sb.append('>');
470:
471: return getRuntime().newString(sb.toString()); // OBJ_INFECT
472: }
473:
474: public RubyArray to_a() {
475: return getRuntime().newArray(values);
476: }
477:
478: public RubyFixnum size() {
479: return getRuntime().newFixnum(values.length);
480: }
481:
482: public IRubyObject each(Block block) {
483: ThreadContext context = getRuntime().getCurrentContext();
484: for (int i = 0; i < values.length; i++) {
485: block.yield(context, values[i]);
486: }
487:
488: return this ;
489: }
490:
491: public IRubyObject each_pair(Block block) {
492: RubyArray member = (RubyArray) getInstanceVariable(classOf(),
493: "__member__");
494:
495: assert !member.isNil() : "uninitialized struct";
496:
497: ThreadContext context = getRuntime().getCurrentContext();
498: for (int i = 0; i < values.length; i++) {
499: block.yield(context, getRuntime().newArrayNoCopy(
500: new IRubyObject[] { member.eltInternal(i),
501: values[i] }));
502: }
503:
504: return this ;
505: }
506:
507: public IRubyObject aref(IRubyObject key) {
508: if (key instanceof RubyString || key instanceof RubySymbol) {
509: return getByName(key.asSymbol());
510: }
511:
512: int idx = RubyNumeric.fix2int(key);
513:
514: idx = idx < 0 ? values.length + idx : idx;
515:
516: if (idx < 0) {
517: throw getRuntime().newIndexError(
518: "offset " + idx + " too large for struct (size:"
519: + values.length + ")");
520: } else if (idx >= values.length) {
521: throw getRuntime().newIndexError(
522: "offset " + idx + " too large for struct (size:"
523: + values.length + ")");
524: }
525:
526: return values[idx];
527: }
528:
529: public IRubyObject aset(IRubyObject key, IRubyObject value) {
530: if (key instanceof RubyString || key instanceof RubySymbol) {
531: return setByName(key.asSymbol(), value);
532: }
533:
534: int idx = RubyNumeric.fix2int(key);
535:
536: idx = idx < 0 ? values.length + idx : idx;
537:
538: if (idx < 0) {
539: throw getRuntime().newIndexError(
540: "offset " + idx + " too large for struct (size:"
541: + values.length + ")");
542: } else if (idx >= values.length) {
543: throw getRuntime().newIndexError(
544: "offset " + idx + " too large for struct (size:"
545: + values.length + ")");
546: }
547:
548: modify();
549: return values[idx] = value;
550: }
551:
552: // FIXME: This is copied code from RubyArray. Both RE, Struct, and Array should share one impl
553: // This is also hacky since I construct ruby objects to access ruby arrays through aref instead
554: // of something lower.
555: public IRubyObject values_at(IRubyObject[] args) {
556: long olen = values.length;
557: RubyArray result = getRuntime().newArray(args.length);
558:
559: for (int i = 0; i < args.length; i++) {
560: if (args[i] instanceof RubyFixnum) {
561: result.append(aref(args[i]));
562: continue;
563: }
564:
565: long beglen[];
566: if (!(args[i] instanceof RubyRange)) {
567: } else if ((beglen = ((RubyRange) args[i]).begLen(olen, 0)) == null) {
568: continue;
569: } else {
570: int beg = (int) beglen[0];
571: int len = (int) beglen[1];
572: int end = len;
573: for (int j = 0; j < end; j++) {
574: result
575: .append(aref(getRuntime()
576: .newFixnum(j + beg)));
577: }
578: continue;
579: }
580: result.append(aref(getRuntime().newFixnum(
581: RubyNumeric.num2long(args[i]))));
582: }
583:
584: return result;
585: }
586:
587: public static void marshalTo(RubyStruct struct, MarshalStream output)
588: throws java.io.IOException {
589: output.dumpDefaultObjectHeader('S', struct.getMetaClass());
590:
591: List members = ((RubyArray) getInstanceVariable(struct
592: .classOf(), "__member__")).getList();
593: output.writeInt(members.size());
594:
595: for (int i = 0; i < members.size(); i++) {
596: RubySymbol name = (RubySymbol) members.get(i);
597: output.dumpObject(name);
598: output.dumpObject(struct.values[i]);
599: }
600: }
601:
602: public static RubyStruct unmarshalFrom(UnmarshalStream input)
603: throws java.io.IOException {
604: Ruby runtime = input.getRuntime();
605:
606: RubySymbol className = (RubySymbol) input.unmarshalObject();
607: RubyClass rbClass = pathToClass(runtime, className.asSymbol());
608: if (rbClass == null) {
609: throw runtime.newNameError("uninitialized constant "
610: + className, className.asSymbol());
611: }
612:
613: RubyArray mem = members(rbClass, Block.NULL_BLOCK);
614:
615: int len = input.unmarshalInt();
616: IRubyObject[] values = new IRubyObject[len];
617: for (int i = 0; i < len; i++) {
618: values[i] = runtime.getNil();
619: }
620: RubyStruct result = newStruct(rbClass, values, Block.NULL_BLOCK);
621: input.registerLinkTarget(result);
622: for (int i = 0; i < len; i++) {
623: IRubyObject slot = input.unmarshalObject();
624: if (!mem.eltInternal(i).toString().equals(slot.toString())) {
625: throw runtime.newTypeError("struct "
626: + rbClass.getName() + " not compatible (:"
627: + slot + " for :" + mem.eltInternal(i) + ")");
628: }
629: result.aset(runtime.newFixnum(i), input.unmarshalObject());
630: }
631: return result;
632: }
633:
634: private static RubyClass pathToClass(Ruby runtime, String path) {
635: // FIXME: Throw the right ArgumentError's if the class is missing
636: // or if it's a module.
637: return (RubyClass) runtime.getClassFromPath(path);
638: }
639:
640: private static ObjectAllocator STRUCT_INSTANCE_ALLOCATOR = new ObjectAllocator() {
641: public IRubyObject allocate(Ruby runtime, RubyClass klass) {
642: RubyStruct instance = new RubyStruct(runtime, klass);
643:
644: instance.setMetaClass(klass);
645:
646: return instance;
647: }
648: };
649:
650: public IRubyObject initialize_copy(IRubyObject arg) {
651: if (this == arg)
652: return this ;
653: RubyStruct original = (RubyStruct) arg;
654:
655: values = new IRubyObject[original.values.length];
656: System.arraycopy(original.values, 0, values, 0,
657: original.values.length);
658:
659: return this;
660: }
661:
662: }
|