RubyObjectSpace.java

  1. /***** BEGIN LICENSE BLOCK *****
  2.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Eclipse 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/epl-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) 2001 Alan Moore <alan_moore@gmx.net>
  15.  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  16.  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  17.  * Copyright (C) 2004 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 EPL, 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 EPL, the GPL or the LGPL.
  31.  ***** END LICENSE BLOCK *****/
  32. package org.jruby;

  33. import java.util.ArrayList;
  34. import java.util.Collection;
  35. import static org.jruby.RubyEnumerator.enumeratorize;

  36. import java.util.Iterator;

  37. import org.jruby.anno.JRubyMethod;
  38. import org.jruby.anno.JRubyModule;
  39. import org.jruby.runtime.Block;
  40. import org.jruby.runtime.ObjectAllocator;
  41. import org.jruby.runtime.ThreadContext;
  42. import static org.jruby.runtime.Visibility.*;
  43. import org.jruby.runtime.builtin.IRubyObject;
  44. import org.jruby.util.collections.WeakValuedIdentityMap;
  45. import org.jruby.util.func.Function1;

  46. @JRubyModule(name="ObjectSpace")
  47. public class RubyObjectSpace {

  48.     /** Create the ObjectSpace module and add it to the Ruby runtime.
  49.      *
  50.      */
  51.     public static RubyModule createObjectSpaceModule(Ruby runtime) {
  52.         RubyModule objectSpaceModule = runtime.defineModule("ObjectSpace");
  53.         runtime.setObjectSpaceModule(objectSpaceModule);
  54.        
  55.         objectSpaceModule.defineAnnotatedMethods(RubyObjectSpace.class);

  56.         WeakMap.createWeakMap(runtime);

  57.         return objectSpaceModule;
  58.     }

  59.     @JRubyMethod(required = 1, optional = 1, module = true, visibility = PRIVATE)
  60.     public static IRubyObject define_finalizer(IRubyObject recv, IRubyObject[] args, Block block) {
  61.         Ruby runtime = recv.getRuntime();
  62.         IRubyObject finalizer = null;
  63.         if (args.length == 2) {
  64.             finalizer = args[1];
  65.             if (!finalizer.respondsTo("call")) {
  66.                 throw runtime.newArgumentError("wrong type argument "
  67.                         + finalizer.getType() + " (should be callable)");
  68.             }
  69.         } else {
  70.             finalizer = runtime.newProc(Block.Type.PROC, block);
  71.         }
  72.         IRubyObject obj = args[0];
  73.         runtime.getObjectSpace().addFinalizer(obj, finalizer);
  74.         return runtime.newArray(RubyFixnum.zero(runtime), finalizer);
  75.     }

  76.     @JRubyMethod(required = 1, module = true, visibility = PRIVATE)
  77.     public static IRubyObject undefine_finalizer(IRubyObject recv, IRubyObject arg1, Block block) {
  78.         recv.getRuntime().getObjectSpace().removeFinalizers(RubyNumeric.fix2long(arg1.id()));
  79.         return recv;
  80.     }

  81.     @JRubyMethod(name = "_id2ref", required = 1, module = true, visibility = PRIVATE)
  82.     public static IRubyObject id2ref(IRubyObject recv, IRubyObject id) {
  83.         Ruby runtime = id.getRuntime();
  84.         if (!(id instanceof RubyFixnum)) {
  85.             throw recv.getRuntime().newTypeError(id, recv.getRuntime().getFixnum());
  86.         }
  87.         RubyFixnum idFixnum = (RubyFixnum) id;
  88.         long longId = idFixnum.getLongValue();
  89.         if (longId == 0) {
  90.             return runtime.getFalse();
  91.         } else if (longId == 20) {
  92.             return runtime.getTrue();
  93.         } else if (longId == 8) {
  94.             return runtime.getNil();
  95.         } else if (longId % 2 != 0) {
  96.             // odd
  97.             return runtime.newFixnum((longId - 1) / 2);
  98.         } else {
  99.             if (runtime.isObjectSpaceEnabled()) {
  100.                 IRubyObject object = runtime.getObjectSpace().id2ref(longId);
  101.                 if (object == null) {
  102.                     return runtime.getNil();
  103.                 }
  104.                 return object;
  105.             } else {
  106.                 runtime.getWarnings().warn("ObjectSpace is disabled; _id2ref only supports immediates, pass -X+O to enable");
  107.                 return runtime.getNil();
  108.             }
  109.         }
  110.     }
  111.    
  112.     public static IRubyObject each_objectInternal(final ThreadContext context, IRubyObject recv, IRubyObject[] args, final Block block) {
  113.         RubyModule tmpClass;
  114.         if (args.length == 0) {
  115.             tmpClass = recv.getRuntime().getObject();
  116.         } else {
  117.             if (!(args[0] instanceof RubyModule)) throw recv.getRuntime().newTypeError("class or module required");
  118.             tmpClass = (RubyModule) args[0];
  119.         }
  120.         final RubyModule rubyClass = tmpClass;
  121.         Ruby runtime = recv.getRuntime();
  122.         final int[] count = {0};
  123.         if (rubyClass == runtime.getClassClass() ||
  124.                 rubyClass == runtime.getModule()) {
  125.             final Collection<IRubyObject> modules = new ArrayList<IRubyObject>();
  126.             runtime.eachModule(new Function1<Object, IRubyObject>() {
  127.                 public Object apply(IRubyObject arg1) {
  128.                     if (rubyClass.isInstance(arg1)) {
  129.                         if (arg1 instanceof IncludedModule ||
  130.                                 (arg1 instanceof RubyClass && ((RubyClass)arg1).isSingleton())) {
  131.                             // do nothing for included wrappers or singleton classes
  132.                         } else {
  133.                             count[0]++;
  134.                             modules.add(arg1); // store the module to avoid concurrent modification exceptions
  135.                         }
  136.                     }
  137.                     return null;
  138.                 }
  139.             });

  140.             for (IRubyObject arg : modules) {
  141.                 block.yield(context, arg);
  142.             }
  143.         } else {
  144.             if (!runtime.isObjectSpaceEnabled()) {
  145.                 throw runtime.newRuntimeError("ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable");
  146.             }
  147.             Iterator iter = recv.getRuntime().getObjectSpace().iterator(rubyClass);

  148.             IRubyObject obj = null;
  149.             while ((obj = (IRubyObject)iter.next()) != null) {
  150.                 count[0]++;
  151.                 block.yield(context, obj);
  152.             }
  153.         }
  154.         return recv.getRuntime().newFixnum(count[0]);
  155.     }

  156.     @JRubyMethod(name = "each_object", optional = 1, module = true, visibility = PRIVATE)
  157.     public static IRubyObject each_object(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  158.         return block.isGiven() ? each_objectInternal(context, recv, args, block) : enumeratorize(context.runtime, recv, "each_object", args);
  159.     }

  160.     @JRubyMethod(name = "garbage_collect", module = true, visibility = PRIVATE)
  161.     public static IRubyObject garbage_collect(ThreadContext context, IRubyObject recv) {
  162.         return RubyGC.start(context, recv);
  163.     }

  164.     public static class WeakMap extends RubyObject {
  165.         static void createWeakMap(Ruby runtime) {
  166.             RubyClass weakMap = runtime.getObjectSpaceModule().defineClassUnder("WeakMap", runtime.getObject(), new ObjectAllocator() {
  167.                 public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
  168.                     return new WeakMap(runtime, klazz);
  169.                 }
  170.             });

  171.             weakMap.defineAnnotatedMethods(WeakMap.class);
  172.         }

  173.         public WeakMap(Ruby runtime, RubyClass cls) {
  174.             super(runtime, cls);
  175.         }

  176.         @JRubyMethod(name = "[]")
  177.         public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
  178.             IRubyObject value = map.get(key);
  179.             if (value != null) return value;
  180.             return context.nil;
  181.         }

  182.         @JRubyMethod(name = "[]=")
  183.         public IRubyObject op_aref(ThreadContext context, IRubyObject key, IRubyObject value) {
  184.             map.put(key, value);
  185.             return context.runtime.newFixnum(System.identityHashCode(value));
  186.         }

  187.         private final WeakValuedIdentityMap<IRubyObject, IRubyObject> map = new WeakValuedIdentityMap<IRubyObject, IRubyObject>();
  188.     }
  189. }