/text/src/test/resources/examples/java/jruby.in.java
Java | 13963 lines | 12550 code | 421 blank | 992 comment | 360 complexity | 177e0295a7887cd9ab9c9ef722d9455d MD5 | raw file
Possible License(s): EPL-1.0, MPL-2.0-no-copyleft-exception
- package org.jruby;
- public enum CompatVersion {
- RUBY1_8, RUBY1_9, BOTH;
- public static CompatVersion getVersionFromString(String compatString) {
- if (compatString.equalsIgnoreCase("RUBY1_8")) {
- return CompatVersion.RUBY1_8;
- } else if (compatString.equalsIgnoreCase("RUBY1_9")) {
- return CompatVersion.RUBY1_9;
- } else {
- return null;
- }
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Damian Steer <pldms@mac.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- /**
- * An almost entirely useless interface for those objects that we _really_ want
- * to finalise.
- *
- * @author pldms
- *
- */
- public interface Finalizable {
- public void finalize();
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- /**
- * Error numbers.
- * @fixme
- * this interface is a big hack defining a bunch of arbitrary valor as system call error numbers
- * this is actually because I need them but will probably need to be changed to something smarter
- * sooner or later.
- * The purpose of this class it to help implement the Errno module which in turn in needed by rubicon.
- * @author Benoit Cerrina
- **/
- public interface IErrno
- {
- int EPERM = 1;
- int ENOENT = 2;
- int ESRCH = 3;
- int EINTR = 4;
- int EIO = 5;
- int ENXIO = 6;
- int E2BIG = 7;
- int ENOEXEC = 8;
- int EBADF = 9;
- int ECHILD = 10;
- int EDEADLK = 11;
- int ENOMEM = 12;
- int EACCES = 13;
- int EFAULT = 14;
- int ENOTBLK = 15;
- int EBUSY = 16;
- int EEXIST = 17;
- int EXDEV = 18;
- int ENODEV = 19;
- int ENOTDIR = 20;
- int EISDIR = 21;
- int EINVAL = 22;
- int ENFILE = 23;
- int EMFILE = 24;
- int ENOTTY = 25;
- int ETXTBSY = 26;
- int EFBIG = 27;
- int ENOSPC = 28;
- int ESPIPE = 29;
- int EROFS = 30;
- int EMLINK = 31;
- int EPIPE = 32;
- int EDOM = 33;
- int ERANGE = 34;
- int EWOULDBLOCK = 35;
- int EAGAIN = 35;
- int EINPROGRESS = 36;
- int EALREADY = 37;
- int ENOTSOCK = 38;
- int EDESTADDRREQ = 39;
- int EMSGSIZE = 40;
- int EPROTOTYPE = 41;
- int ENOPROTOOPT = 42;
- int EPROTONOSUPPORT = 43;
- int ESOCKTNOSUPPORT = 44;
- int EOPNOTSUPP = 45;
- int EPFNOSUPPORT = 46;
- int EAFNOSUPPORT = 47;
- int EADDRINUSE = 48;
- int EADDRNOTAVAIL = 49;
- int ENETDOWN = 50;
- int ENETUNREACH = 51;
- int ENETRESET = 52;
- int ECONNABORTED = 53;
- int ECONNRESET = 54;
- int ENOBUFS = 55;
- int EISCONN = 56;
- int ENOTCONN = 57;
- int ESHUTDOWN = 58;
- int ETOOMANYREFS = 59;
- int ETIMEDOUT = 60;
- int ECONNREFUSED = 61;
- int ELOOP = 62;
- int ENAMETOOLONG = 63;
- int EHOSTDOWN = 64;
- int EHOSTUNREACH = 65;
- int ENOTEMPTY = 66;
- int EUSERS = 68;
- int EDQUOT = 69;
- int ESTALE = 70;
- int EREMOTE = 71;
- int ENOLCK = 77;
- int ENOSYS = 78;
- int EOVERFLOW = 84;
- int EIDRM = 90;
- int ENOMSG = 91;
- int EILSEQ = 92;
- int EBADMSG = 94;
- int EMULTIHOP = 95;
- int ENODATA = 96;
- int ENOLINK = 97;
- int ENOSR = 98;
- int ENOSTR = 99;
- int EPROTO = 100;
- int ETIME = 101;
- int EOPNOTSUPP_DARWIN = 102;
- }
- /*
- ***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.util.List;
- import java.util.Map;
- import org.jruby.internal.runtime.methods.DynamicMethod;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.builtin.Variable;
- /**
- * This class is used to provide an intermediate superclass for modules and classes that include
- * other modules. It inserts itself as the immediate superClass of the includer, but defers all
- * module methods to the actual superclass. Multiple of these intermediate superclasses can be
- * added for multiple included modules.
- *
- * This allows the normal superclass-based searches (searchMethod, getConstant, etc) to traverse
- * the superclass ancestors as normal while the included modules do not actually show up in
- * direct inheritance traversal.
- *
- * @see org.jruby.RubyModule
- */
- public final class IncludedModuleWrapper extends RubyClass {
- private final RubyModule delegate;
- public IncludedModuleWrapper(Ruby runtime, RubyClass superClass, RubyModule delegate) {
- super(runtime, superClass, false);
- this.delegate = delegate;
- this.metaClass = delegate.metaClass;
- }
- /**
- * Overridden newIncludeClass implementation to allow attaching future includes to the correct module
- * (i.e. the one to which this is attached)
- *
- * @see org.jruby.RubyModule#newIncludeClass(RubyClass)
- */
- @Override
- public IncludedModuleWrapper newIncludeClass(RubyClass superClass) {
- IncludedModuleWrapper includedModule = new IncludedModuleWrapper(getRuntime(), superClass, getNonIncludedClass());
-
- // include its parent (and in turn that module's parents)
- if (getSuperClass() != null) {
- includedModule.includeModule(getSuperClass());
- }
-
- return includedModule;
- }
- @Override
- public boolean isModule() {
- return false;
- }
- @Override
- public boolean isClass() {
- return false;
- }
- @Override
- public boolean isIncluded() {
- return true;
- }
-
- @Override
- public boolean isImmediate() {
- return true;
- }
- @Override
- public void setMetaClass(RubyClass newRubyClass) {
- throw new UnsupportedOperationException("An included class is only a wrapper for a module");
- }
- @Override
- public Map<String, DynamicMethod> getMethods() {
- return delegate.getMethods();
- }
- @Override
- public void addMethod(String name, DynamicMethod method) {
- throw new UnsupportedOperationException("An included class is only a wrapper for a module");
- }
- public void setMethods(Map newMethods) {
- throw new UnsupportedOperationException("An included class is only a wrapper for a module");
- }
- @Override
- public String getName() {
- return delegate.getName();
- }
- @Override
- public RubyModule getNonIncludedClass() {
- return delegate;
- }
-
- @Override
- public RubyClass getRealClass() {
- return getSuperClass().getRealClass();
- }
- @Override
- protected boolean isSame(RubyModule module) {
- return delegate.isSame(module);
- }
-
- /**
- * We don't want to reveal ourselves to Ruby code, so delegate this
- * operation.
- */
- @Override
- public IRubyObject id() {
- return delegate.id();
- }
- //
- // VARIABLE TABLE METHODS - pass to delegate
- //
- @Override
- protected boolean variableTableContains(String name) {
- return delegate.variableTableContains(name);
- }
- @Override
- protected boolean variableTableFastContains(String internedName) {
- return delegate.variableTableFastContains(internedName);
- }
- @Override
- protected IRubyObject variableTableFetch(String name) {
- return delegate.variableTableFetch(name);
- }
- @Override
- protected IRubyObject variableTableFastFetch(String internedName) {
- return delegate.variableTableFastFetch(internedName);
- }
- @Override
- protected IRubyObject variableTableStore(String name, IRubyObject value) {
- return delegate.variableTableStore(name, value);
- }
- @Override
- protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
- return delegate.variableTableFastStore(internedName, value);
- }
- @Override
- protected IRubyObject variableTableRemove(String name) {
- return delegate.variableTableRemove(name);
- }
- @Override
- protected VariableTableEntry[] variableTableGetTable() {
- return delegate.variableTableGetTable();
- }
- @Override
- protected int variableTableGetSize() {
- return delegate.variableTableGetSize();
- }
- @Override
- protected void variableTableSync(List<Variable<IRubyObject>> vars) {
- delegate.variableTableSync(vars);
- }
- @Override
- protected IRubyObject variableTableReadLocked(VariableTableEntry entry) {
- return delegate.variableTableReadLocked(entry);
- }
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- @Override
- @Deprecated // born deprecated
- protected Map variableTableGetMap() {
- return delegate.variableTableGetMap();
- }
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- @Override
- @Deprecated // born deprecated
- protected Map variableTableGetMap(Map map) {
- return delegate.variableTableGetMap(map);
- }
- //
- // CONSTANT TABLE METHODS - pass to delegate
- //
- @Override
- protected boolean constantTableContains(String name) {
- return delegate.constantTableContains(name);
- }
- @Override
- protected boolean constantTableFastContains(String internedName) {
- return delegate.constantTableFastContains(internedName);
- }
- @Override
- protected IRubyObject constantTableFetch(String name) {
- return delegate.constantTableFetch(name);
- }
- @Override
- protected IRubyObject constantTableFastFetch(String internedName) {
- return delegate.constantTableFastFetch(internedName);
- }
- @Override
- protected IRubyObject constantTableStore(String name, IRubyObject value) {
- // FIXME: legal here? may want UnsupportedOperationException
- return delegate.constantTableStore(name, value);
- }
- @Override
- protected IRubyObject constantTableFastStore(String internedName, IRubyObject value) {
- // FIXME: legal here? may want UnsupportedOperationException
- return delegate.constantTableFastStore(internedName, value);
- }
- @Override
- protected IRubyObject constantTableRemove(String name) {
- // this _is_ legal (when removing an undef)
- return delegate.constantTableRemove(name);
- }
- @Override
- protected ConstantTableEntry[] constantTableGetTable() {
- return delegate.constantTableGetTable();
- }
- @Override
- protected int constantTableGetSize() {
- return delegate.constantTableGetSize();
- }
- @Override
- protected void constantTableSync(List<Variable<IRubyObject>> vars) {
- // FIXME: legal here? may want UnsupportedOperationException
- delegate.constantTableSync(vars);
- }
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- @Override
- @Deprecated // born deprecated
- protected Map constantTableGetMap() {
- return delegate.constantTableGetMap();
- }
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- @Override
- @Deprecated // born deprecated
- protected Map constantTableGetMap(Map map) {
- return delegate.constantTableGetMap(map);
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Charles Nutter <charles.o.nutter@sun.com>
- * Copyright (C) 2008 MenTaLguY <mental@rydia.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.applet.Applet;
- import java.awt.BorderLayout;
- import java.awt.Color;
- import java.awt.Container;
- import java.awt.EventQueue;
- import java.awt.Font;
- import java.awt.Insets;
- import java.awt.Graphics;
- import java.awt.GraphicsConfiguration;
- import java.awt.GraphicsEnvironment;
- import java.awt.image.VolatileImage;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintStream;
- import java.net.URL;
- import java.util.Arrays;
- import java.lang.reflect.InvocationTargetException;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.demo.TextAreaReadline;
- import org.jruby.javasupport.JavaUtil;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import javax.swing.JScrollPane;
- import javax.swing.JTextPane;
- /**
- * @author <a href="mailto:mental@rydia.net">MenTaLguY</a>
- *
- * The JRubyApplet class provides a simple way to write Java applets using
- * JRuby without needing to create a custom Java applet class. At applet
- * initialization time, JRubyApplet starts up a JRuby runtime, then evaluates
- * the scriptlet given as the "eval" applet parameter.
- *
- * The Java applet instance is available to the Ruby script as
- * JRUBY_APPLET; the script can define callbacks for applet start, stop,
- * and destroy by passing blocks to JRUBY_APPLET.on_start,
- * JRUBY_APPLET.on_stop, and JRUBY_APPLET.on_destroy, respectively.
- *
- * Ruby code can install a custom paint callback using JRUBY_APPLET.on_paint
- * (the Graphics2D object is passed as an argument to the callback). By
- * default, JRubyApplet painting is double-buffered, but you can select
- * single-buffered painting via JRUBY_APPLET.double_buffered = false.
- *
- * The applet's background color can be set via JRUBY_APPLET.background_color=.
- * You may want to set it to nil if you're not using double-buffering, so that
- * no background color will be drawn (your own paint code is then responsible
- * for filling the area).
- *
- * Beyond these things, you should be able to use JRuby's Java integration
- * to do whatever you would do in Java with the applet instance.
- *
- */
- public class JRubyApplet extends Applet {
- private Ruby runtime;
- private boolean doubleBuffered = true;
- private Color backgroundColor = Color.WHITE;
- private RubyProc startProc;
- private RubyProc stopProc;
- private RubyProc destroyProc;
- private RubyProc paintProc;
- private Graphics priorGraphics;
- private IRubyObject wrappedGraphics;
- private VolatileImage backBuffer;
- private Graphics backBufferGraphics;
- private Facade facade;
- private interface Facade {
- public InputStream getInputStream();
- public PrintStream getOutputStream();
- public PrintStream getErrorStream();
- public void attach(Ruby runtime, Applet applet);
- public void destroy();
- }
- private static RubyProc blockToProc(Ruby runtime, Block block) {
- if (block.isGiven()) {
- RubyProc proc = block.getProcObject();
- if (proc == null) {
- proc = RubyProc.newProc(runtime, block, block.type);
- }
- return proc;
- } else {
- return null;
- }
- }
- private boolean getBooleanParameter(String name, boolean defaultValue) {
- String value = getParameter(name);
- if ( value != null ) {
- return value.equals("true");
- } else {
- return defaultValue;
- }
- }
- private InputStream getCodeResourceAsStream(String name) {
- if (name == null) {
- return null;
- }
- try {
- final URL directURL = new URL(getCodeBase(), name);
- return directURL.openStream();
- } catch (IOException e) {
- }
- return JRubyApplet.class.getClassLoader().getResourceAsStream(name);
- }
- private static void safeInvokeAndWait(Runnable runnable) throws InvocationTargetException, InterruptedException {
- if (EventQueue.isDispatchThread()) {
- try {
- runnable.run();
- } catch (Exception e) {
- throw new InvocationTargetException(e);
- }
- } else {
- EventQueue.invokeAndWait(runnable);
- }
- }
- public static class RubyMethods {
- @JRubyMethod
- public static IRubyObject on_start(IRubyObject recv, Block block) {
- JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
- synchronized (applet) {
- applet.startProc = blockToProc(applet.runtime, block);
- }
- return recv;
- }
- @JRubyMethod
- public static IRubyObject on_stop(IRubyObject recv, Block block) {
- JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
- synchronized (applet) {
- applet.stopProc = blockToProc(applet.runtime, block);
- }
- return recv;
- }
- @JRubyMethod
- public static IRubyObject on_destroy(IRubyObject recv, Block block) {
- JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
- synchronized (applet) {
- applet.destroyProc = blockToProc(applet.runtime, block);
- }
- return recv;
- }
- @JRubyMethod
- public static IRubyObject on_paint(IRubyObject recv, Block block) {
- JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
- synchronized (applet) {
- applet.paintProc = blockToProc(applet.runtime, block);
- applet.repaint();
- }
- return recv;
- }
- }
- @Override
- public void init() {
- super.init();
- if (getBooleanParameter("jruby.console", false)) {
- facade = new ConsoleFacade(getParameter("jruby.banner"));
- } else {
- facade = new TrivialFacade();
- }
- synchronized (this) {
- if (runtime != null) {
- return;
- }
- final RubyInstanceConfig config = new RubyInstanceConfig() {{
- setInput(facade.getInputStream());
- setOutput(facade.getOutputStream());
- setError(facade.getErrorStream());
- setObjectSpaceEnabled(getBooleanParameter("jruby.objectspace", false));
- }};
- Ruby.setSecurityRestricted(true);
- runtime = Ruby.newInstance(config);
- }
- final String scriptName = getParameter("jruby.script");
- final InputStream scriptStream = getCodeResourceAsStream(scriptName);
- final String evalString = getParameter("jruby.eval");
- try {
- final JRubyApplet applet = this;
- safeInvokeAndWait(new Runnable() {
- public void run() {
- applet.setLayout(new BorderLayout());
- applet.facade.attach(applet.runtime, applet);
- if (scriptStream != null) {
- applet.runtime.runFromMain(scriptStream, scriptName);
- }
- if (evalString != null) {
- applet.runtime.evalScriptlet(evalString);
- }
- }
- });
- } catch (InterruptedException e) {
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Error running script", e.getCause());
- }
- }
- private void invokeCallback(final RubyProc proc, final IRubyObject[] args) {
- if (proc == null) {
- return;
- }
- final Ruby runtime = this.runtime;
- try {
- safeInvokeAndWait(new Runnable() {
- public void run() {
- ThreadContext context = runtime.getCurrentContext();
- proc.call(context, args);
- }
- });
- } catch (InterruptedException e) {
- } catch (InvocationTargetException e) {
- throw new RuntimeException("Ruby callback failed", e.getCause());
- }
- }
- public synchronized void setBackgroundColor(Color color) {
- backgroundColor = color;
- repaint();
- }
- public synchronized Color getBackgroundColor() {
- return backgroundColor;
- }
- public synchronized boolean isDoubleBuffered() {
- return doubleBuffered;
- }
- public synchronized void setDoubleBuffered(boolean shouldBuffer) {
- doubleBuffered = shouldBuffer;
- repaint();
- }
- @Override
- public synchronized void start() {
- super.start();
- invokeCallback(startProc, new IRubyObject[] {});
- }
- @Override
- public synchronized void stop() {
- invokeCallback(stopProc, new IRubyObject[] {});
- super.stop();
- }
- @Override
- public synchronized void destroy() {
- try {
- invokeCallback(destroyProc, new IRubyObject[] {});
- } finally {
- facade.destroy();
- final Ruby runtime = this.runtime;
- this.runtime = null;
- startProc = null;
- stopProc = null;
- destroyProc = null;
- paintProc = null;
- priorGraphics = null;
- wrappedGraphics = null;
- runtime.tearDown();
- super.destroy();
- }
- }
- @Override
- public void update(Graphics g) {
- paint(g);
- }
- @Override
- public synchronized void paint(Graphics g) {
- if (doubleBuffered) {
- paintBuffered(g);
- } else {
- paintUnbuffered(g);
- }
- }
- private synchronized void paintBuffered(Graphics g) {
- do {
- GraphicsConfiguration config = getGraphicsConfiguration();
- int width = getWidth();
- int height = getHeight();
- if (backBuffer == null || width != backBuffer.getWidth() || height != backBuffer.getHeight() || backBuffer.validate(config) == VolatileImage.IMAGE_INCOMPATIBLE) {
- if (backBuffer != null) {
- backBufferGraphics.dispose();
- backBufferGraphics = null;
- backBuffer.flush();
- backBuffer = null;
- }
- backBuffer = config.createCompatibleVolatileImage(width, height);
- backBufferGraphics = backBuffer.createGraphics();
- }
- backBufferGraphics.setClip(g.getClip());
- paintUnbuffered(backBufferGraphics);
- g.drawImage(backBuffer, 0, 0, this);
- } while (backBuffer.contentsLost());
- }
- private synchronized void paintUnbuffered(Graphics g) {
- if (backgroundColor != null) {
- g.setColor(backgroundColor);
- g.fillRect(0, 0, getWidth(), getHeight());
- }
- if (paintProc != null) {
- if (priorGraphics != g) {
- wrappedGraphics = JavaUtil.convertJavaToUsableRubyObject(runtime, g);
- priorGraphics = g;
- }
- ThreadContext context = runtime.getCurrentContext();
- paintProc.call(context, new IRubyObject[] {wrappedGraphics});
- }
- super.paint(g);
- }
- private static class TrivialFacade implements Facade {
- public TrivialFacade() {}
- public InputStream getInputStream() { return System.in; }
- public PrintStream getOutputStream() { return System.out; }
- public PrintStream getErrorStream() { return System.err; }
- public void attach(Ruby runtime, Applet applet) {
- final IRubyObject wrappedApplet = JavaUtil.convertJavaToUsableRubyObject(runtime, applet);
- wrappedApplet.dataWrapStruct(applet);
- runtime.defineGlobalConstant("JRUBY_APPLET", wrappedApplet);
- wrappedApplet.getMetaClass().defineAnnotatedMethods(RubyMethods.class);
- }
- public void destroy() {}
- }
- private static class ConsoleFacade implements Facade {
- private JTextPane textPane;
- private JScrollPane scrollPane;
- private TextAreaReadline adaptor;
- private InputStream inputStream;
- private PrintStream outputStream;
- private PrintStream errorStream;
-
- public ConsoleFacade(String bannerText) {
- textPane = new JTextPane();
- textPane.setMargin(new Insets(4, 4, 0, 4));
- textPane.setCaretColor(new Color(0xa4, 0x00, 0x00));
- textPane.setBackground(new Color(0xf2, 0xf2, 0xf2));
- textPane.setForeground(new Color(0xa4, 0x00, 0x00));
- Font font = findFont("Monospaced", Font.PLAIN, 14,
- new String[] {"Monaco", "Andale Mono"});
- textPane.setFont(font);
- scrollPane = new JScrollPane(textPane);
- scrollPane.setDoubleBuffered(true);
- if ( bannerText != null ) {
- bannerText = " " + bannerText + " \n\n";
- }
- adaptor = new TextAreaReadline(textPane, bannerText);
- inputStream = adaptor.getInputStream();
- outputStream = new PrintStream(adaptor.getOutputStream());
- errorStream = new PrintStream(adaptor.getOutputStream());
- }
- public InputStream getInputStream() { return inputStream; }
- public PrintStream getOutputStream() { return outputStream; }
- public PrintStream getErrorStream() { return errorStream; }
- public void attach(Ruby runtime, Applet applet) {
- adaptor.hookIntoRuntime(runtime);
- applet.add(scrollPane);
- applet.validate();
- }
- public void destroy() {
- Container parent = scrollPane.getParent();
- adaptor.shutdown();
- if (parent != null) {
- parent.remove(scrollPane);
- }
- }
- private Font findFont(String otherwise, int style, int size, String[] families) {
- String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
- Arrays.sort(fonts);
- for (int i = 0; i < families.length; i++) {
- if (Arrays.binarySearch(fonts, families[i]) >= 0) {
- return new Font(families[i], style, size);
- }
- }
- return new Font(otherwise, style, size);
- }
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.BufferedWriter;
- import java.io.OutputStreamWriter;
- import java.net.InetAddress;
- import java.net.Socket;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- public class JRubyClient extends JRubyService {
- public JRubyClient(String[] args) throws Exception {
- Configuration conf = new Configuration(args[0]);
- if(conf.isDebug()) {
- System.err.println("Starting client with port " + conf.getPort() + ", key " + conf.getKey() + " and command " + conf.getCommand());
- }
- Socket socket = new Socket(InetAddress.getLocalHost(), conf.getPort());
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
- if(conf.terminate()) {
- writer.write(CMD_TERM + " " + conf.getKey() + "\n");
- } else if(conf.noMore()) {
- writer.write(CMD_NO_MORE + " " + conf.getKey() + "\n");
- } else {
- writer.write(CMD_START + " " + conf.getKey() + " " + conf.getCommand() + "\n");
- }
- writer.flush();
- writer.close();
- socket.close();
- }
- public static void main(String[] args) throws Exception {
- new JRubyClient(args);
- }
- }// JRubyClient
- /*
- ***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.BufferedReader;
- import java.io.InputStreamReader;
- import java.net.InetAddress;
- import java.net.InetSocketAddress;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.List;
- import java.util.ArrayList;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- public class JRubyServer extends JRubyService {
- private Configuration conf;
- private boolean stillStarting = true;
- private JRubyServer(String[] args) throws Exception {
- conf = new Configuration(args[0]);
- if(conf.isDebug()) {
- System.err.println("Starting server with port " + conf.getPort() + " and key " + conf.getKey());
- }
- ServerSocket server = new ServerSocket();
- server.bind(new InetSocketAddress(InetAddress.getLocalHost(),conf.getPort()));
- while(true) {
- Thread t1 = new Thread(new Handler(server.accept()));
- t1.setDaemon(true);
- t1.start();
- }
- }
- private class Handler implements Runnable {
- private Socket socket;
- public Handler(Socket socket) {
- this.socket = socket;
- }
- public void run() {
- try {
- BufferedReader rr = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
- String command = rr.readLine();
- rr.close();
- this.socket.close();
- this.socket = null;
- if(conf.isDebug()) {
- System.err.println("Got command: " + command);
- }
- String[] cmds = command.split(" ", 3);
- if(cmds[1].equals(conf.getKey())) {
- if(cmds[0].equals(CMD_TERM)) {
- if(conf.isDebug()) {
- System.err.println("Terminating hard");
- }
- System.exit(0);
- } else if(cmds[0].equals(CMD_NO_MORE)) {
- if(conf.isDebug()) {
- System.err.println("Accepting no more START");
- }
- stillStarting = false;
- } else if(cmds[0].equals(CMD_START)) {
- if(stillStarting) {
- if(conf.isDebug()) {
- System.err.println("Doing START on command " + cmds[2]);
- }
- new Main().run(intoCommandArguments(cmds[2].trim()));
- } else {
- if(conf.isDebug()) {
- System.err.println("Not doing START anymore, invalid command");
- }
- }
- } else {
- if(conf.isDebug()) {
- System.err.println("Unrecognized command");
- }
- }
- } else {
- if(conf.isDebug()) {
- System.err.println("Invalid key");
- }
- }
- } catch(Exception e) {}
- }
- }
- protected static String[] intoCommandArguments(String str) {
- List<String> args = new ArrayList<String>();
- boolean inSingle = false;
- int contentStart = -1;
- for(int i=0,j=str.length();i<j;i++) {
- if(str.charAt(i) == ' ' && !inSingle && contentStart != -1) {
- args.add(str.substring(contentStart,i));
- contentStart = -1;
- continue;
- }
- if(str.charAt(i) == ' ') {
- continue;
- }
- if(str.charAt(i) == '\'' && !inSingle) {
- inSingle = true;
- contentStart = i+1;
- continue;
- }
- if(str.charAt(i) == '\'') {
- inSingle = false;
- args.add(str.substring(contentStart,i));
- contentStart = -1;
- continue;
- }
- if(contentStart == -1) {
- contentStart = i;
- }
- }
- if(contentStart != -1) {
- args.add(str.substring(contentStart));
- }
- return (String[])args.toArray(new String[0]);
- }
- public static void main(String[] args) throws Exception {
- new JRubyServer(args);
- }
- }// JRubyServer
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- public abstract class JRubyService {
- protected static class Configuration {
- private final static int DEFAULT_PORT = 19222;
- private String key;
- private int port = DEFAULT_PORT;
- private boolean terminate;
- private boolean noMore;
- private boolean debug;
- private String command;
- public Configuration(String args) {
- int i=0;
- int stop;
- loop: for(int j=args.length();i<j;i++) {
- if(args.charAt(i) == '-' && i+1 < j) {
- switch(args.charAt(++i)) {
- case 'k':
- stop = args.indexOf(" ", (++i) + 1);
- if(stop == -1) {
- stop = args.length();
- }
- key = args.substring(i, stop).trim();
- i = stop;
- break;
- case 'p':
- stop = args.indexOf(" ", (++i) + 1);
- if(stop == -1) {
- stop = args.length();
- }
- port = Integer.parseInt(args.substring(i, stop).trim());
- i = stop;
- break;
- case 't':
- terminate = true;
- i++;
- break;
- case 'n':
- noMore = true;
- i++;
- break;
- case 'd':
- debug = true;
- i++;
- break;
- case '-': // handle everything after -- as arguments to the jruby process
- i++;
- break loop;
- default:
- i--;
- break loop;
- }
- } else if(args.charAt(i) != ' ') {
- break loop;
- }
- }
- if(i<args.length()) {
- command = args.substring(i).trim();
- }
- }
-
- public String getKey() {
- return key;
- }
- public int getPort() {
- return port;
- }
- public boolean terminate() {
- return terminate;
- }
- public boolean noMore() {
- return noMore;
- }
- public boolean isDebug() {
- return debug;
- }
- public String getCommand() {
- return command;
- }
- }
- public static final String CMD_START = "START";
- public static final String CMD_NO_MORE = "NO_MORE";
- public static final String CMD_TERM = "TERM";
- }// JRubyService
- /*
- ***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 Kiel Hodges <jruby-devel@selfsosoft.com>
- * Copyright (C) 2005 Jason Voegele <jason@jvoegele.com>
- * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.InputStream;
- import java.io.PrintStream;
- import org.jruby.exceptions.MainExitException;
- import org.jruby.exceptions.RaiseException;
- import org.jruby.exceptions.ThreadKill;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.SafePropertyAccessor;
- import org.jruby.util.SimpleSampler;
- /**
- * Class used to launch the interpreter.
- * This is the main class as defined in the jruby.mf manifest.
- * It is very basic and does not support yet the same array of switches
- * as the C interpreter.
- * Usage: java -jar jruby.jar [switches] [rubyfile.rb] [arguments]
- * -e 'command' one line of script. Several -e's allowed. Omit [programfile]
- * @author jpetersen
- */
- public class Main {
- private boolean hasPrintedUsage = false;
- private final RubyInstanceConfig config;
- public Main(RubyInstanceConfig config) {
- this.config = config;
- }
- public Main(final InputStream in, final PrintStream out, final PrintStream err) {
- this(new RubyInstanceConfig(){{
- setInput(in);
- setOutput(out);
- setError(err);
- }});
- }
- public Main() {
- this(new RubyInstanceConfig());
- }
- public static void main(String[] args) {
- Main main = new Main();
-
- try {
- int status = main.run(args);
- if (status != 0) {
- System.exit(status);
- }
- } catch (RaiseException re) {
- throw re;
- } catch (Throwable t) {
- // print out as a nice Ruby backtrace
- System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t));
- System.exit(1);
- }
- }
- public int run(String[] args) {
- try {
- config.processArguments(args);
- return run();
- } catch (MainExitException mee) {
- if (!mee.isAborted()) {
- config.getOutput().println(mee.getMessage());
- if (mee.isUsageError()) {
- printUsage();
- }
- }
- return mee.getStatus();
- } catch (OutOfMemoryError oome) {
- // produce a nicer error since Rubyists aren't used to seeing this
- System.gc();
-
- String memoryMax = SafePropertyAccessor.getProperty("jruby.memory.max");
- String message = "";
- if (memoryMax != null) {
- message = " of " + memoryMax;
- }
- System.err.println("Error: Your application used more memory than the safety cap" + message + ".");
- System.err.println("Specify -J-Xmx####m to increase it (#### = cap size in MB).");
-
- if (config.getVerbose()) {
- System.err.println("Exception trace follows:");
- oome.printStackTrace();
- } else {
- System.err.println("Specify -w for full OutOfMemoryError stack trace");
- }
- return 1;
- } catch (StackOverflowError soe) {
- // produce a nicer error since Rubyists aren't used to seeing this
- System.gc();
-
- String stackMax = SafePropertyAccessor.getProperty("jruby.stack.max");
- String message = "";
- if (stackMax != null) {
- message = " of " + stackMax;
- }
- System.err.println("Error: Your application used more stack memory than the safety cap" + message + ".");
- System.err.println("Specify -J-Xss####k to increase it (#### = cap size in KB).");
-
- if (config.getVerbose()) {
- System.err.println("Exception trace follows:");
- soe.printStackTrace();
- } else {
- System.err.println("Specify -w for full StackOverflowError stack trace");
- }
- return 1;
- } catch (UnsupportedClassVersionError ucve) {
- System.err.println("Error: Some library (perhaps JRuby) was built with a later JVM version.");
- System.err.println("Please use libraries built with the version you intend to use or an earlier one.");
-
- if (config.getVerbose()) {
- System.err.println("Exception trace follows:");
- ucve.printStackTrace();
- } else {
- System.err.println("Specify -w for full UnsupportedClassVersionError stack trace");
- }
- return 1;
- } catch (ThreadKill kill) {
- return 0;
- }
- }
- public int run() {
- if (config.isShowVersion()) {
- showVersion();
- }
-
- if (config.isShowCopyright()) {
- showCopyright();
- }
- if (!config.shouldRunInterpreter() ) {
- if (config.shouldPrintUsage()) {
- printUsage();
- }
- if (config.shouldPrintProperties()) {
- printProperties();
- }
- return 0;
- }
- InputStream in = config.getScriptSource();
- String filename = config.displayedFileName();
- Ruby runtime = Ruby.newInstance(config);
-
- // set thread context JRuby classloader here, for the main thread
- try {
- Thread.currentThread().setContextClassLoader(runtime.getJRubyClassLoader());
- } catch (SecurityException se) {
- // can't set TC classloader
- if (runtime.getInstanceConfig().isVerbose()) {
- System.err.println("WARNING: Security restrictions disallowed setting context classloader for main thread.");
- }
- }
- if (in == null) {
- // no script to run, return success below
- } else if (config.isShouldCheckSyntax()) {
- runtime.parseFromMain(in, filename);
- config.getOutput().println("Syntax OK");
- } else {
- long now = -1;
- try {
- if (config.isBenchmarking()) {
- now = System.currentTimeMillis();
- }
- if (config.isSamplingEnabled()) {
- SimpleSampler.startSampleThread();
- }
- try {
- runtime.runFromMain(in, filename);
- } finally {
- runtime.tearDown();
- if (config.isBenchmarking()) {
- config.getOutput().println("Runtime: " + (System.currentTimeMillis() - now) + " ms");
- }
- if (config.isSamplingEnabled()) {
- org.jruby.util.SimpleSampler.report();
- }
- }
- } catch (RaiseException rj) {
- RubyException raisedException = rj.getException();
- if (runtime.getSystemExit().isInstance(raisedException)) {
- IRubyObject status = raisedException.callMethod(runtime.getCurrentContext(), "status");
- if (status != null && !status.isNil()) {
- return RubyNumeric.fix2int(status);
- }
- } else {
- runtime.printError(raisedException);
- return 1;
- }
- }
- }
- return 0;
- }
- private void showVersion() {
- config.getOutput().print(config.getVersionString());
- }
- private void showCopyright() {
- config.getOutput().print(config.getCopyrightString());
- }
- public void printUsage() {
- if (!hasPrintedUsage) {
- config.getOutput().print(config.getBasicUsageHelp());
- hasPrintedUsage = true;
- }
- }
-
- public void printProperties() {
- config.getOutput().print(config.getPropertyHelp());
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.lang.ref.SoftReference;
- import org.jruby.runtime.builtin.IRubyObject;
- public final class MetaClass extends RubyClass {
-
- private SoftReference<IRubyObject> attached = new SoftReference<IRubyObject>(null);
- /** NEWOBJ (in RubyObject#getSingletonClassClone())
- *
- */
- public MetaClass(Ruby runtime) {
- super(runtime, null, false);
- }
-
- /** rb_class_boot (for MetaClasses) (in makeMetaClass(RubyClass))
- *
- */
- public MetaClass(Ruby runtime, RubyClass superClass) {
- super(runtime, superClass, false);
- index = superClass.index; // use same ClassIndex as metaclass, since we're technically still of that type
- }
-
- public boolean isSingleton() {
- return true;
- }
- /**
- * If an object uses an anonymous class 'class << obj', then this grabs the original
- * metaclass and not the one that get injected as a result of 'class << obj'.
- */
- public RubyClass getRealClass() {
- return superClass.getRealClass();
- }
-
- public final IRubyObject allocate(){
- throw getRuntime().newTypeError("can't create instance of virtual class");
- }
- public IRubyObject getAttached() {
- return attached.get();
- }
- public void setAttached(IRubyObject attached) {
- this.attached = new SoftReference<IRubyObject>(attached);
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.PrintStream;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.javasupport.Java;
- import org.jruby.javasupport.JavaObject;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.builtin.IRubyObject;
- @JRubyClass(name = "NativeException", parent = "RuntimeError")
- public class NativeException extends RubyException {
- private final Throwable cause;
- public static final String CLASS_NAME = "NativeException";
- private final Ruby runtime;
- public NativeException(Ruby runtime, RubyClass rubyClass, Throwable cause) {
- super(runtime, rubyClass, cause.getClass().getName() + ": " + cause.getMessage());
- this.runtime = runtime;
- this.cause = cause;
- }
- public static RubyClass createClass(Ruby runtime, RubyClass baseClass) {
- // FIXME: If NativeException is expected to be used from Ruby code, it should provide
- // a real allocator to be used. Otherwise Class.new will fail, as will marshalling. JRUBY-415
- RubyClass exceptionClass = runtime.defineClass(CLASS_NAME, baseClass, ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- exceptionClass.defineAnnotatedMethods(NativeException.class);
- return exceptionClass;
- }
- @JRubyMethod(frame = true)
- public IRubyObject cause(Block unusedBlock) {
- return Java.wrap(getRuntime(), JavaObject.wrap(getRuntime(), cause));
- }
- public IRubyObject backtrace() {
- IRubyObject rubyTrace = super.backtrace();
- if (rubyTrace.isNil()) {
- return rubyTrace;
- }
- RubyArray array = (RubyArray) rubyTrace.dup();
- StackTraceElement[] stackTrace = cause.getStackTrace();
- for (int i = stackTrace.length - 1; i >= 0; i--) {
- StackTraceElement element = stackTrace[i];
- String className = element.getClassName();
- String line = null;
- if (element.getFileName() == null) {
- line = className + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'";
- } else {
- int index = className.lastIndexOf(".");
- String packageName = null;
- if (index == -1) {
- packageName = "";
- } else {
- packageName = className.substring(0, index) + "/";
- }
- line = packageName.replace(".", "/") + element.getFileName() + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'";
- }
- RubyString string = runtime.newString(line);
- array.unshift(string);
- }
- return array;
- }
- public void printBacktrace(PrintStream errorStream) {
- super.printBacktrace(errorStream);
- errorStream.println("Complete Java stackTrace");
- cause.printStackTrace(errorStream);
- }
- public Throwable getCause() {
- return cause;
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- public interface Profile {
- Profile ALL = new Profile() {
- public boolean allowBuiltin(String name) { return true; }
- public boolean allowClass(String name) { return true; }
- public boolean allowModule(String name) { return true; }
- public boolean allowLoad(String name) { return true; }
- public boolean allowRequire(String name) { return true; }
- };
- Profile DEBUG_ALLOW = new Profile() {
- public boolean allowBuiltin(String name) { System.err.println("allowBuiltin("+name+")"); return true; }
- public boolean allowClass(String name) { System.err.println("allowClass("+name+")"); return true; }
- public boolean allowModule(String name) { System.err.println("allowModule("+name+")"); return true; }
- public boolean allowLoad(String name) { System.err.println("allowLoad("+name+")"); return true; }
- public boolean allowRequire(String name) { System.err.println("allowRequire("+name+")"); return true; }
- };
- Profile NO_FILE_CLASS = new Profile() {
- public boolean allowBuiltin(String name) { return true; }
- public boolean allowClass(String name) { return !name.equals("File"); }
- public boolean allowModule(String name) { return true; }
- public boolean allowLoad(String name) { return true; }
- public boolean allowRequire(String name) { return true; }
- };
- Profile ANY = ALL;
- Profile DEFAULT = ALL;
-
- boolean allowBuiltin(String name);
- boolean allowClass(String name);
- boolean allowModule(String name);
- boolean allowLoad(String name);
- boolean allowRequire(String name);
- }// Profile
- /*
- **** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
- * Copyright (C) 2006 Ola Bini <ola@ologix.com>
- * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.ByteArrayInputStream;
- import java.io.File;
- import java.io.FileDescriptor;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PrintStream;
- import java.io.UnsupportedEncodingException;
- import java.lang.ref.WeakReference;
- import java.lang.reflect.Field;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Hashtable;
- import java.util.IdentityHashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.Random;
- import java.util.Set;
- import java.util.Stack;
- import java.util.Vector;
- import java.util.WeakHashMap;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.SynchronousQueue;
- import java.util.concurrent.ThreadFactory;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.joda.time.DateTimeZone;
- import org.jruby.ast.Node;
- import org.jruby.ast.executable.RubiniusRunner;
- import org.jruby.ast.executable.Script;
- import org.jruby.ast.executable.YARVCompiledRunner;
- import org.jruby.common.RubyWarnings;
- import org.jruby.common.IRubyWarnings.ID;
- import org.jruby.compiler.ASTCompiler;
- import org.jruby.compiler.ASTInspector;
- import org.jruby.compiler.JITCompiler;
- import org.jruby.compiler.NotCompilableException;
- import org.jruby.compiler.impl.StandardASMCompiler;
- import org.jruby.compiler.yarv.StandardYARVCompiler;
- import org.jruby.exceptions.JumpException;
- import org.jruby.exceptions.RaiseException;
- import org.jruby.ext.JRubyPOSIXHandler;
- import org.jruby.ext.LateLoadingLibrary;
- import org.jruby.ext.posix.POSIX;
- import org.jruby.ext.posix.POSIXFactory;
- import org.jruby.internal.runtime.GlobalVariables;
- import org.jruby.internal.runtime.ThreadService;
- import org.jruby.internal.runtime.ValueAccessor;
- import org.jruby.javasupport.JavaSupport;
- import org.jruby.management.BeanManager;
- import org.jruby.management.ClassCache;
- import org.jruby.management.Config;
- import org.jruby.parser.Parser;
- import org.jruby.parser.ParserConfiguration;
- import org.jruby.runtime.Binding;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.CacheMap;
- import org.jruby.runtime.CallSite;
- import org.jruby.runtime.CallbackFactory;
- import org.jruby.runtime.DynamicScope;
- import org.jruby.runtime.EventHook;
- import org.jruby.runtime.GlobalVariable;
- import org.jruby.runtime.IAccessor;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ObjectSpace;
- import org.jruby.runtime.RubyEvent;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.load.Library;
- import org.jruby.runtime.load.LoadService;
- import org.jruby.util.BuiltinScript;
- import org.jruby.util.ByteList;
- import org.jruby.util.IOInputStream;
- import org.jruby.util.IOOutputStream;
- import org.jruby.util.JRubyClassLoader;
- import org.jruby.util.JavaNameMangler;
- import org.jruby.util.KCode;
- import org.jruby.util.SafePropertyAccessor;
- import org.jruby.util.collections.WeakHashSet;
- import org.jruby.util.io.ChannelDescriptor;
- /**
- * The Ruby object represents the top-level of a JRuby "instance" in a given VM.
- * JRuby supports spawning multiple instances in the same JVM. Generally, objects
- * created under these instances are tied to a given runtime, for such details
- * as identity and type, because multiple Ruby instances means there are
- * multiple instances of each class. This means that in multi-runtime mode
- * (or really, multi-VM mode, where each JRuby instance is a ruby "VM"), objects
- * generally can't be transported across runtimes without marshaling.
- *
- * This class roots everything that makes the JRuby runtime function, and
- * provides a number of utility methods for constructing global types and
- * accessing global runtime structures.
- */
- public final class Ruby {
- /**
- * Returns a new instance of the JRuby runtime configured with defaults.
- *
- * @return the JRuby runtime
- * @see org.jruby.RubyInstanceConfig
- */
- public static Ruby newInstance() {
- return newInstance(new RubyInstanceConfig());
- }
- /**
- * Returns a new instance of the JRuby runtime configured as specified.
- *
- * @param config The instance configuration
- * @return The JRuby runtime
- * @see org.jruby.RubyInstanceConfig
- */
- public static Ruby newInstance(RubyInstanceConfig config) {
- Ruby ruby = new Ruby(config);
- ruby.init();
- return ruby;
- }
- /**
- * Returns a new instance of the JRuby runtime configured with the given
- * input, output and error streams and otherwise default configuration
- * (except where specified system properties alter defaults).
- *
- * @param in the custom input stream
- * @param out the custom output stream
- * @param err the custom error stream
- * @return the JRuby runtime
- * @see org.jruby.RubyInstanceConfig
- */
- public static Ruby newInstance(InputStream in, PrintStream out, PrintStream err) {
- RubyInstanceConfig config = new RubyInstanceConfig();
- config.setInput(in);
- config.setOutput(out);
- config.setError(err);
- return newInstance(config);
- }
-
- /**
- * Create and initialize a new JRuby runtime. The properties of the
- * specified RubyInstanceConfig will be used to determine various JRuby
- * runtime characteristics.
- *
- * @param config The configuration to use for the new instance
- * @see org.jruby.RubyInstanceConfig
- */
- private Ruby(RubyInstanceConfig config) {
- this.config = config;
- this.threadService = new ThreadService(this);
- if(config.isSamplingEnabled()) {
- org.jruby.util.SimpleSampler.registerThreadContext(threadService.getCurrentContext());
- }
- this.in = config.getInput();
- this.out = config.getOutput();
- this.err = config.getError();
- this.objectSpaceEnabled = config.isObjectSpaceEnabled();
- this.profile = config.getProfile();
- this.currentDirectory = config.getCurrentDirectory();
- this.kcode = config.getKCode();
- this.beanManager = new BeanManager(this, config.isManagementEnabled());
- this.jitCompiler = new JITCompiler(this);
-
- this.beanManager.register(new Config(this));
- this.beanManager.register(new ClassCache(this));
-
- this.cacheMap = new CacheMap(this);
- }
-
- /**
- * Evaluates a script under the current scope (perhaps the top-level
- * scope) and returns the result (generally the last value calculated).
- * This version goes straight into the interpreter, bypassing compilation
- * and runtime preparation typical to normal script runs.
- *
- * @param script The scriptlet to run
- * @returns The result of the eval
- */
- public IRubyObject evalScriptlet(String script) {
- ThreadContext context = getCurrentContext();
- Node node = parseEval(script, "<script>", context.getCurrentScope(), 0);
-
- try {
- return node.interpret(this, context, context.getFrameSelf(), Block.NULL_BLOCK);
- } catch (JumpException.ReturnJump rj) {
- throw newLocalJumpError("return", (IRubyObject)rj.getValue(), "unexpected return");
- } catch (JumpException.BreakJump bj) {
- throw newLocalJumpError("break", (IRubyObject)bj.getValue(), "unexpected break");
- } catch (JumpException.RedoJump rj) {
- throw newLocalJumpError("redo", (IRubyObject)rj.getValue(), "unexpected redo");
- }
- }
-
- /**
- * Parse and execute the specified script
- * This differs from the other methods in that it accepts a string-based script and
- * parses and runs it as though it were loaded at a command-line. This is the preferred
- * way to start up a new script when calling directly into the Ruby object (which is
- * generally *dis*couraged.
- *
- * @param script The contents of the script to run as a normal, root script
- * @return The last value of the script
- */
- public IRubyObject executeScript(String script, String filename) {
- byte[] bytes;
-
- try {
- bytes = script.getBytes(KCode.NONE.getKCode());
- } catch (UnsupportedEncodingException e) {
- bytes = script.getBytes();
- }
- Node node = parseInline(new ByteArrayInputStream(bytes), filename, null);
- ThreadContext context = getCurrentContext();
-
- String oldFile = context.getFile();
- int oldLine = context.getLine();
- try {
- context.setFile(node.getPosition().getFile());
- context.setLine(node.getPosition().getStartLine());
- return runNormally(node, false);
- } finally {
- context.setFile(oldFile);
- context.setLine(oldLine);
- }
- }
-
- /**
- * Run the script contained in the specified input stream, using the
- * specified filename as the name of the script being executed. The stream
- * will be read fully before being parsed and executed. The given filename
- * will be used for the ruby $PROGRAM_NAME and $0 global variables in this
- * runtime.
- *
- * This method is intended to be called once per runtime, generally from
- * Main or from main-like top-level entry points.
- *
- * As part of executing the script loaded from the input stream, various
- * RubyInstanceConfig properties will be used to determine whether to
- * compile the script before execution or run with various wrappers (for
- * looping, printing, and so on, see jruby -help).
- *
- * @param inputStream The InputStream from which to read the script contents
- * @param filename The filename to use when parsing, and for $PROGRAM_NAME
- * and $0 ruby global variables.
- */
- public void runFromMain(InputStream inputStream, String filename) {
- IAccessor d = new ValueAccessor(newString(filename));
- getGlobalVariables().define("$PROGRAM_NAME", d);
- getGlobalVariables().define("$0", d);
- for (Iterator i = config.getOptionGlobals().entrySet().iterator(); i.hasNext();) {
- Map.Entry entry = (Map.Entry) i.next();
- Object value = entry.getValue();
- IRubyObject varvalue;
- if (value != null) {
- varvalue = newString(value.toString());
- } else {
- varvalue = getTrue();
- }
- getGlobalVariables().set("$" + entry.getKey().toString(), varvalue);
- }
-
- if(config.isYARVEnabled()) {
- if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
- new YARVCompiledRunner(this, inputStream, filename).run();
- } else if(config.isRubiniusEnabled()) {
- if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
- new RubiniusRunner(this, inputStream, filename).run();
- } else {
- Node scriptNode = parseFromMain(inputStream, filename);
- ThreadContext context = getCurrentContext();
- String oldFile = context.getFile();
- int oldLine = context.getLine();
- try {
- context.setFile(scriptNode.getPosition().getFile());
- context.setLine(scriptNode.getPosition().getStartLine());
- if (config.isAssumePrinting() || config.isAssumeLoop()) {
- runWithGetsLoop(scriptNode, config.isAssumePrinting(), config.isProcessLineEnds(),
- config.isSplit(), config.isYARVCompileEnabled());
- } else {
- runNormally(scriptNode, config.isYARVCompileEnabled());
- }
- } finally {
- context.setFile(oldFile);
- context.setLine(oldLine);
- }
- }
- }
- /**
- * Parse the script contained in the given input stream, using the given
- * filename as the name of the script, and return the root Node. This
- * is used to verify that the script syntax is valid, for jruby -c. The
- * current scope (generally the top-level scope) is used as the parent
- * scope for parsing.
- *
- * @param inputStream The input stream from which to read the script
- * @param filename The filename to use for parsing
- * @returns The root node of the parsed script
- */
- public Node parseFromMain(InputStream inputStream, String filename) {
- if (config.isInlineScript()) {
- return parseInline(inputStream, filename, getCurrentContext().getCurrentScope());
- } else {
- return parseFile(inputStream, filename, getCurrentContext().getCurrentScope());
- }
- }
-
- /**
- * Run the given script with a "while gets; end" loop wrapped around it.
- * This is primarily used for the -n command-line flag, to allow writing
- * a short script that processes input lines using the specified code.
- *
- * @param scriptNode The root node of the script to execute
- * @param printing Whether $_ should be printed after each loop (as in the
- * -p command-line flag)
- * @param processLineEnds Whether line endings should be processed by
- * setting $\ to $/ and <code>chop!</code>ing every line read
- * @param split Whether to split each line read using <code>String#split</code>
- * @param yarvCompile Whether to compile the target script to YARV (Ruby 1.9)
- * bytecode before executing.
- * @return The result of executing the specified script
- */
- public IRubyObject runWithGetsLoop(Node scriptNode, boolean printing, boolean processLineEnds, boolean split, boolean yarvCompile) {
- ThreadContext context = getCurrentContext();
-
- Script script = null;
- YARVCompiledRunner runner = null;
- boolean compile = getInstanceConfig().getCompileMode().shouldPrecompileCLI();
- if (compile || !yarvCompile) {
- script = tryCompile(scriptNode);
- if (compile && script == null) {
- // terminate; tryCompile will have printed out an error and we're done
- return getNil();
- }
- } else if (yarvCompile) {
- runner = tryCompileYarv(scriptNode);
- }
-
- if (processLineEnds) {
- getGlobalVariables().set("$\\", getGlobalVariables().get("$/"));
- }
-
- while (RubyKernel.gets(context, getTopSelf(), IRubyObject.NULL_ARRAY).isTrue()) {
- loop: while (true) { // Used for the 'redo' command
- try {
- if (processLineEnds) {
- getGlobalVariables().get("$_").callMethod(context, "chop!");
- }
-
- if (split) {
- getGlobalVariables().set("$F", getGlobalVariables().get("$_").callMethod(context, "split"));
- }
-
- if (script != null) {
- runScript(script);
- } else if (runner != null) {
- runYarv(runner);
- } else {
- runInterpreter(scriptNode);
- }
-
- if (printing) RubyKernel.print(context, getKernel(), new IRubyObject[] {getGlobalVariables().get("$_")});
- break loop;
- } catch (JumpException.RedoJump rj) {
- // do nothing, this iteration restarts
- } catch (JumpException.NextJump nj) {
- // recheck condition
- break loop;
- } catch (JumpException.BreakJump bj) {
- // end loop
- return (IRubyObject) bj.getValue();
- }
- }
- }
-
- return getNil();
- }
-
- /**
- * Run the specified script without any of the loop-processing wrapper
- * code.
- *
- * @param scriptNode The root node of the script to be executed
- * @param yarvCompile Whether to compile the script to YARV (Ruby 1.9)
- * bytecode before execution
- * @return The result of executing the script
- */
- public IRubyObject runNormally(Node scriptNode, boolean yarvCompile) {
- Script script = null;
- YARVCompiledRunner runner = null;
- boolean compile = getInstanceConfig().getCompileMode().shouldPrecompileCLI();
- boolean forceCompile = getInstanceConfig().getCompileMode().shouldPrecompileAll();
- if (yarvCompile) {
- runner = tryCompileYarv(scriptNode);
- } else if (compile) {
- script = tryCompile(scriptNode);
- if (forceCompile && script == null) {
- System.err.println("Error, could not compile; pass -J-Djruby.jit.logging.verbose=true for more details");
- return getNil();
- }
- }
-
- if (script != null) {
- if (config.isShowBytecode()) {
- return nilObject;
- } else {
- return runScript(script);
- }
- } else if (runner != null) {
- return runYarv(runner);
- } else {
- if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
- return runInterpreter(scriptNode);
- }
- }
-
- private Script tryCompile(Node node) {
- return tryCompile(node, new JRubyClassLoader(getJRubyClassLoader()));
- }
-
- private Script tryCompile(Node node, JRubyClassLoader classLoader) {
- Script script = null;
- try {
- String filename = node.getPosition().getFile();
- String classname = JavaNameMangler.mangledFilenameForStartupClasspath(filename);
- ASTInspector inspector = new ASTInspector();
- inspector.inspect(node);
- StandardASMCompiler asmCompiler = new StandardASMCompiler(classname, filename);
- ASTCompiler compiler = new ASTCompiler();
- if (config.isShowBytecode()) {
- compiler.compileRoot(node, asmCompiler, inspector, false, false);
- asmCompiler.dumpClass(System.out);
- } else {
- compiler.compileRoot(node, asmCompiler, inspector, true, false);
- }
- script = (Script)asmCompiler.loadClass(classLoader).newInstance();
- if (config.isJitLogging()) {
- System.err.println("compiled: " + node.getPosition().getFile());
- }
- } catch (NotCompilableException nce) {
- if (config.isJitLoggingVerbose()) {
- System.err.println("Error -- Not compileable: " + nce.getMessage());
- nce.printStackTrace();
- }
- } catch (ClassNotFoundException e) {
- if (config.isJitLoggingVerbose()) {
- System.err.println("Error -- Not compileable: " + e.getMessage());
- e.printStackTrace();
- }
- } catch (InstantiationException e) {
- if (config.isJitLoggingVerbose()) {
- System.err.println("Error -- Not compileable: " + e.getMessage());
- e.printStackTrace();
- }
- } catch (IllegalAccessException e) {
- if (config.isJitLoggingVerbose()) {
- System.err.println("Error -- Not compileable: " + e.getMessage());
- e.printStackTrace();
- }
- } catch (Throwable t) {
- if (config.isJitLoggingVerbose()) {
- System.err.println("could not compile: " + node.getPosition().getFile() + " because of: \"" + t.getMessage() + "\"");
- t.printStackTrace();
- }
- }
-
- return script;
- }
-
- private YARVCompiledRunner tryCompileYarv(Node node) {
- try {
- StandardYARVCompiler compiler = new StandardYARVCompiler(this);
- ASTCompiler.getYARVCompiler().compile(node, compiler);
- org.jruby.lexer.yacc.ISourcePosition p = node.getPosition();
- if(p == null && node instanceof org.jruby.ast.RootNode) {
- p = ((org.jruby.ast.RootNode)node).getBodyNode().getPosition();
- }
- return new YARVCompiledRunner(this,compiler.getInstructionSequence("<main>",p.getFile(),"toplevel"));
- } catch (NotCompilableException nce) {
- System.err.println("Error -- Not compileable: " + nce.getMessage());
- return null;
- } catch (JumpException.ReturnJump rj) {
- return null;
- }
- }
-
- private IRubyObject runScript(Script script) {
- ThreadContext context = getCurrentContext();
-
- try {
- return script.load(context, context.getFrameSelf(), IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
- } catch (JumpException.ReturnJump rj) {
- return (IRubyObject) rj.getValue();
- }
- }
-
- private IRubyObject runYarv(YARVCompiledRunner runner) {
- try {
- return runner.run();
- } catch (JumpException.ReturnJump rj) {
- return (IRubyObject) rj.getValue();
- }
- }
-
- private IRubyObject runInterpreter(Node scriptNode) {
- ThreadContext context = getCurrentContext();
-
- assert scriptNode != null : "scriptNode is not null";
-
- try {
- return scriptNode.interpret(this, context, getTopSelf(), Block.NULL_BLOCK);
- } catch (JumpException.ReturnJump rj) {
- return (IRubyObject) rj.getValue();
- }
- }
-
- public BeanManager getBeanManager() {
- return beanManager;
- }
-
- public JITCompiler getJITCompiler() {
- return jitCompiler;
- }
- /**
- * @deprecated use #newInstance()
- */
- public static Ruby getDefaultInstance() {
- return newInstance();
- }
-
- @Deprecated
- public static Ruby getCurrentInstance() {
- return null;
- }
-
- @Deprecated
- public static void setCurrentInstance(Ruby runtime) {
- }
-
- public int allocSymbolId() {
- return symbolLastId.incrementAndGet();
- }
- public int allocModuleId() {
- return moduleLastId.incrementAndGet();
- }
- /**
- * Retrieve the module with the given name from the Object namespace.
- *
- * @param name The name of the module
- * @return The module or null if not found
- */
- public RubyModule getModule(String name) {
- return (RubyModule) objectClass.getConstantAt(name);
- }
- /**
- * Retrieve the module with the given name from the Object namespace. The
- * module name must be an interned string, but this method will be faster
- * than the non-interned version.
- *
- * @param internedName The name of the module; <em>must</em> be an interned String
- * @return The module or null if not found
- */
- public RubyModule fastGetModule(String internedName) {
- return (RubyModule) objectClass.fastGetConstantAt(internedName);
- }
- /**
- * Retrieve the class with the given name from the Object namespace.
- *
- * @param name The name of the class
- * @return The class
- */
- public RubyClass getClass(String name) {
- return objectClass.getClass(name);
- }
- /**
- * Retrieve the class with the given name from the Object namespace. The
- * module name must be an interned string, but this method will be faster
- * than the non-interned version.
- *
- * @param internedName the name of the class; <em>must</em> be an interned String!
- * @return
- */
- public RubyClass fastGetClass(String internedName) {
- return objectClass.fastGetClass(internedName);
- }
- /**
- * Define a new class under the Object namespace. Roughly equivalent to
- * rb_define_class in MRI.
- *
- * @param name The name for the new class
- * @param superClass The super class for the new class
- * @param allocator An ObjectAllocator instance that can construct
- * instances of the new class.
- * @return The new class
- */
- public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator) {
- return defineClassUnder(name, superClass, allocator, objectClass);
- }
- /**
- * A variation of defineClass that allows passing in an array of subplementary
- * call sites for improving dynamic invocation performance.
- *
- * @param name The name for the new class
- * @param superClass The super class for the new class
- * @param allocator An ObjectAllocator instance that can construct
- * instances of the new class.
- * @return The new class
- */
- public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator, CallSite[] callSites) {
- return defineClassUnder(name, superClass, allocator, objectClass, callSites);
- }
- /**
- * Define a new class with the given name under the given module or class
- * namespace. Roughly equivalent to rb_define_class_under in MRI.
- *
- * If the name specified is already bound, its value will be returned if:
- * * It is a class
- * * No new superclass is being defined
- *
- * @param name The name for the new class
- * @param superClass The super class for the new class
- * @param allocator An ObjectAllocator instance that can construct
- * instances of the new class.
- * @param parent The namespace under which to define the new class
- * @return The new class
- */
- public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent) {
- return defineClassUnder(name, superClass, allocator, parent, null);
- }
- /**
- * A variation of defineClassUnder that allows passing in an array of
- * supplementary call sites to improve dynamic invocation.
- *
- * @param name The name for the new class
- * @param superClass The super class for the new class
- * @param allocator An ObjectAllocator instance that can construct
- * instances of the new class.
- * @param parent The namespace under which to define the new class
- * @param callSites The array of call sites to add
- * @return The new class
- */
- public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent, CallSite[] callSites) {
- IRubyObject classObj = parent.getConstantAt(name);
- if (classObj != null) {
- if (!(classObj instanceof RubyClass)) throw newTypeError(name + " is not a class");
- RubyClass klazz = (RubyClass)classObj;
- if (klazz.getSuperClass().getRealClass() != superClass) {
- throw newNameError(name + " is already defined", name);
- }
- // If we define a class in Ruby, but later want to allow it to be defined in Java,
- // the allocator needs to be updated
- if (klazz.getAllocator() != allocator) {
- klazz.setAllocator(allocator);
- }
- return klazz;
- }
-
- boolean parentIsObject = parent == objectClass;
- if (superClass == null) {
- String className = parentIsObject ? name : parent.getName() + "::" + name;
- warnings.warn(ID.NO_SUPER_CLASS, "no super class for `" + className + "', Object assumed", className);
-
- superClass = objectClass;
- }
- return RubyClass.newClass(this, superClass, name, allocator, parent, !parentIsObject, callSites);
- }
- /**
- * Define a new module under the Object namespace. Roughly equivalent to
- * rb_define_module in MRI.
- *
- * @param name The name of the new module
- * @returns The new module
- */
- public RubyModule defineModule(String name) {
- return defineModuleUnder(name, objectClass);
- }
- /**
- * Define a new module with the given name under the given module or
- * class namespace. Roughly equivalent to rb_define_module_under in MRI.
- *
- * @param name The name of the new module
- * @param parent The class or module namespace under which to define the
- * module
- * @returns The new module
- */
- public RubyModule defineModuleUnder(String name, RubyModule parent) {
- IRubyObject moduleObj = parent.getConstantAt(name);
-
- boolean parentIsObject = parent == objectClass;
- if (moduleObj != null ) {
- if (moduleObj.isModule()) return (RubyModule)moduleObj;
-
- if (parentIsObject) {
- throw newTypeError(moduleObj.getMetaClass().getName() + " is not a module");
- } else {
- throw newTypeError(parent.getName() + "::" + moduleObj.getMetaClass().getName() + " is not a module");
- }
- }
- return RubyModule.newModule(this, name, parent, !parentIsObject);
- }
- /**
- * From Object, retrieve the named module. If it doesn't exist a
- * new module is created.
- *
- * @param name The name of the module
- * @returns The existing or new module
- */
- public RubyModule getOrCreateModule(String name) {
- IRubyObject module = objectClass.getConstantAt(name);
- if (module == null) {
- module = defineModule(name);
- } else if (getSafeLevel() >= 4) {
- throw newSecurityError("Extending module prohibited.");
- } else if (!module.isModule()) {
- throw newTypeError(name + " is not a Module");
- }
- return (RubyModule) module;
- }
- /**
- * Retrieve the current safe level.
- *
- * @see org.jruby.Ruby#setSaveLevel
- */
- public int getSafeLevel() {
- return this.safeLevel;
- }
- /**
- * Set the current safe level:
- *
- * 0 - strings from streams/environment/ARGV are tainted (default)
- * 1 - no dangerous operation by tainted value
- * 2 - process/file operations prohibited
- * 3 - all generated objects are tainted
- * 4 - no global (non-tainted) variable modification/no direct output
- *
- * The safe level is set using $SAFE in Ruby code. It is not particularly
- * well supported in JRuby.
- */
- public void setSafeLevel(int safeLevel) {
- this.safeLevel = safeLevel;
- }
- public KCode getKCode() {
- return kcode;
- }
- public void setKCode(KCode kcode) {
- this.kcode = kcode;
- }
- public void secure(int level) {
- if (level <= safeLevel) {
- throw newSecurityError("Insecure operation '" + getCurrentContext().getFrameName() + "' at level " + safeLevel);
- }
- }
- // FIXME moved this here to get what's obviously a utility method out of IRubyObject.
- // perhaps security methods should find their own centralized home at some point.
- public void checkSafeString(IRubyObject object) {
- if (getSafeLevel() > 0 && object.isTaint()) {
- ThreadContext tc = getCurrentContext();
- if (tc.getFrameName() != null) {
- throw newSecurityError("Insecure operation - " + tc.getFrameName());
- }
- throw newSecurityError("Insecure operation: -r");
- }
- secure(4);
- if (!(object instanceof RubyString)) {
- throw newTypeError(
- "wrong argument type " + object.getMetaClass().getName() + " (expected String)");
- }
- }
- /** rb_define_global_const
- *
- */
- public void defineGlobalConstant(String name, IRubyObject value) {
- objectClass.defineConstant(name, value);
- }
- public boolean isClassDefined(String name) {
- return getModule(name) != null;
- }
-
- /**
- * A ThreadFactory for when we're using pooled threads; we want to create
- * the threads with daemon = true so they don't keep us from shutting down.
- */
- public static class DaemonThreadFactory implements ThreadFactory {
- public Thread newThread(Runnable runnable) {
- Thread thread = new Thread(runnable);
- thread.setDaemon(true);
-
- return thread;
- }
- }
- /**
- * This method is called immediately after constructing the Ruby instance.
- * The main thread is prepared for execution, all core classes and libraries
- * are initialized, and any libraries required on the command line are
- * loaded.
- */
- private void init() {
- // Get the main threadcontext (gets constructed for us)
- ThreadContext tc = getCurrentContext();
- safeLevel = config.getSafeLevel();
-
- // Construct key services
- loadService = config.createLoadService(this);
- posix = POSIXFactory.getPOSIX(new JRubyPOSIXHandler(this), RubyInstanceConfig.nativeEnabled);
- javaSupport = new JavaSupport(this);
-
- if (RubyInstanceConfig.POOLING_ENABLED) {
- Executors.newCachedThreadPool();
- executor = new ThreadPoolExecutor(
- RubyInstanceConfig.POOL_MIN,
- RubyInstanceConfig.POOL_MAX,
- RubyInstanceConfig.POOL_TTL,
- TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>(),
- new DaemonThreadFactory());
- }
-
- // initialize the root of the class hierarchy completely
- initRoot(tc);
- // Construct the top-level execution frame and scope for the main thread
- tc.prepareTopLevel(objectClass, topSelf);
- // Initialize all the core classes
- bootstrap();
-
- // Create global constants and variables
- RubyGlobal.createGlobals(tc, this);
- // Prepare LoadService and load path
- getLoadService().init(config.loadPaths());
- // initialize builtin libraries
- initBuiltins();
-
- // Require in all libraries specified on command line
- for (String scriptName : config.requiredLibraries()) {
- RubyKernel.require(getTopSelf(), newString(scriptName), Block.NULL_BLOCK);
- }
- }
- private void bootstrap() {
- initCore();
- initExceptions();
- }
- private void initRoot(ThreadContext context) {
- // Bootstrap the top of the hierarchy
- objectClass = RubyClass.createBootstrapClass(this, "Object", null, RubyObject.OBJECT_ALLOCATOR);
- moduleClass = RubyClass.createBootstrapClass(this, "Module", objectClass, RubyModule.MODULE_ALLOCATOR);
- classClass = RubyClass.createBootstrapClass(this, "Class", moduleClass, RubyClass.CLASS_ALLOCATOR);
- objectClass.setMetaClass(classClass);
- moduleClass.setMetaClass(classClass);
- classClass.setMetaClass(classClass);
- RubyClass metaClass;
- metaClass = objectClass.makeMetaClass(classClass);
- metaClass = moduleClass.makeMetaClass(metaClass);
- metaClass = classClass.makeMetaClass(metaClass);
- RubyObject.createObjectClass(this, objectClass);
- RubyModule.createModuleClass(this, moduleClass);
- RubyClass.createClassClass(this, classClass);
-
- // set constants now that they're initialized
- objectClass.setConstant("Object", objectClass);
- objectClass.setConstant("Class", classClass);
- objectClass.setConstant("Module", moduleClass);
- // Initialize Kernel and include into Object
- RubyKernel.createKernelModule(this);
- objectClass.includeModule(kernelModule);
-
- // Initialize the "dummy" class used as a marker
- dummyClass = new RubyClass(this);
- dummyClass.freeze(context);
- // Object is ready, create top self
- topSelf = TopSelfFactory.createTopSelf(this);
- }
- private void initCore() {
- // Pre-create all the core classes potentially referenced during startup
- RubyNil.createNilClass(this);
- RubyBoolean.createFalseClass(this);
- RubyBoolean.createTrueClass(this);
- nilObject = new RubyNil(this);
- falseObject = new RubyBoolean(this, false);
- trueObject = new RubyBoolean(this, true);
- RubyComparable.createComparable(this);
- RubyEnumerable.createEnumerableModule(this);
- RubyString.createStringClass(this);
- RubySymbol.createSymbolClass(this);
- if (profile.allowClass("ThreadGroup")) {
- RubyThreadGroup.createThreadGroupClass(this);
- }
- if (profile.allowClass("Thread")) {
- RubyThread.createThreadClass(this);
- }
- if (profile.allowClass("Exception")) {
- RubyException.createExceptionClass(this);
- }
- if (profile.allowModule("Precision")) {
- RubyPrecision.createPrecisionModule(this);
- }
- if (profile.allowClass("Numeric")) {
- RubyNumeric.createNumericClass(this);
- }
- if (profile.allowClass("Integer")) {
- RubyInteger.createIntegerClass(this);
- }
- if (profile.allowClass("Fixnum")) {
- RubyFixnum.createFixnumClass(this);
- }
- if (config.getCompatVersion() == CompatVersion.RUBY1_9) {
- if (profile.allowClass("Complex")) {
- RubyComplex.createComplexClass(this);
- }
- if (profile.allowClass("Rational")) {
- RubyRational.createRationalClass(this);
- }
- }
- if (profile.allowClass("Hash")) {
- RubyHash.createHashClass(this);
- }
- if (profile.allowClass("Array")) {
- RubyArray.createArrayClass(this);
- }
- if (profile.allowClass("Float")) {
- RubyFloat.createFloatClass(this);
- }
- if (profile.allowClass("Bignum")) {
- RubyBignum.createBignumClass(this);
- }
- ioClass = RubyIO.createIOClass(this);
- if (profile.allowClass("Struct")) {
- RubyStruct.createStructClass(this);
- }
- if (profile.allowClass("Tms")) {
- tmsStruct = RubyStruct.newInstance(structClass, new IRubyObject[]{newString("Tms"), newSymbol("utime"), newSymbol("stime"), newSymbol("cutime"), newSymbol("cstime")}, Block.NULL_BLOCK);
- }
- if (profile.allowClass("Binding")) {
- RubyBinding.createBindingClass(this);
- }
- // Math depends on all numeric types
- if (profile.allowModule("Math")) {
- RubyMath.createMathModule(this);
- }
- if (profile.allowClass("Regexp")) {
- RubyRegexp.createRegexpClass(this);
- }
- if (profile.allowClass("Range")) {
- RubyRange.createRangeClass(this);
- }
- if (profile.allowModule("ObjectSpace")) {
- RubyObjectSpace.createObjectSpaceModule(this);
- }
- if (profile.allowModule("GC")) {
- RubyGC.createGCModule(this);
- }
- if (profile.allowClass("Proc")) {
- RubyProc.createProcClass(this);
- }
- if (profile.allowClass("Method")) {
- RubyMethod.createMethodClass(this);
- }
- if (profile.allowClass("MatchData")) {
- RubyMatchData.createMatchDataClass(this);
- }
- if (profile.allowModule("Marshal")) {
- RubyMarshal.createMarshalModule(this);
- }
- if (profile.allowClass("Dir")) {
- RubyDir.createDirClass(this);
- }
- if (profile.allowModule("FileTest")) {
- RubyFileTest.createFileTestModule(this);
- }
- // depends on IO, FileTest
- if (profile.allowClass("File")) {
- RubyFile.createFileClass(this);
- }
- if (profile.allowClass("File::Stat")) {
- RubyFileStat.createFileStatClass(this);
- }
- if (profile.allowModule("Process")) {
- RubyProcess.createProcessModule(this);
- }
- if (profile.allowClass("Time")) {
- RubyTime.createTimeClass(this);
- }
- if (profile.allowClass("UnboundMethod")) {
- RubyUnboundMethod.defineUnboundMethodClass(this);
- }
- if (profile.allowClass("Data")) {
- defineClass("Data", objectClass, objectClass.getAllocator());
- }
- if (!isSecurityRestricted()) {
- // Signal uses sun.misc.* classes, this is not allowed
- // in the security-sensitive environments
- if (profile.allowModule("Signal")) {
- RubySignal.createSignal(this);
- }
- }
- if (profile.allowClass("Continuation")) {
- RubyContinuation.createContinuation(this);
- }
- }
- private void initExceptions() {
- standardError = defineClassIfAllowed("StandardError", exceptionClass);
- runtimeError = defineClassIfAllowed("RuntimeError", standardError);
- ioError = defineClassIfAllowed("IOError", standardError);
- scriptError = defineClassIfAllowed("ScriptError", exceptionClass);
- rangeError = defineClassIfAllowed("RangeError", standardError);
- signalException = defineClassIfAllowed("SignalException", exceptionClass);
-
- if (profile.allowClass("NameError")) {
- nameError = RubyNameError.createNameErrorClass(this, standardError);
- nameErrorMessage = RubyNameError.createNameErrorMessageClass(this, nameError);
- }
- if (profile.allowClass("NoMethodError")) {
- noMethodError = RubyNoMethodError.createNoMethodErrorClass(this, nameError);
- }
- if (profile.allowClass("SystemExit")) {
- systemExit = RubySystemExit.createSystemExitClass(this, exceptionClass);
- }
- if (profile.allowClass("LocalJumpError")) {
- localJumpError = RubyLocalJumpError.createLocalJumpErrorClass(this, standardError);
- }
- if (profile.allowClass("NativeException")) {
- nativeException = NativeException.createClass(this, runtimeError);
- }
- if (profile.allowClass("SystemCallError")) {
- systemCallError = RubySystemCallError.createSystemCallErrorClass(this, standardError);
- }
- fatal = defineClassIfAllowed("Fatal", exceptionClass);
- interrupt = defineClassIfAllowed("Interrupt", signalException);
- typeError = defineClassIfAllowed("TypeError", standardError);
- argumentError = defineClassIfAllowed("ArgumentError", standardError);
- indexError = defineClassIfAllowed("IndexError", standardError);
- syntaxError = defineClassIfAllowed("SyntaxError", scriptError);
- loadError = defineClassIfAllowed("LoadError", scriptError);
- notImplementedError = defineClassIfAllowed("NotImplementedError", scriptError);
- securityError = defineClassIfAllowed("SecurityError", standardError);
- noMemoryError = defineClassIfAllowed("NoMemoryError", exceptionClass);
- regexpError = defineClassIfAllowed("RegexpError", standardError);
- eofError = defineClassIfAllowed("EOFError", ioError);
- threadError = defineClassIfAllowed("ThreadError", standardError);
- concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
- systemStackError = defineClassIfAllowed("SystemStackError", standardError);
- zeroDivisionError = defineClassIfAllowed("ZeroDivisionError", standardError);
- floatDomainError = defineClassIfAllowed("FloatDomainError", rangeError);
- initErrno();
- }
-
- private RubyClass defineClassIfAllowed(String name, RubyClass superClass) {
- // TODO: should probably apply the null object pattern for a
- // non-allowed class, rather than null
- if (superClass != null && profile.allowClass(name)) {
- return defineClass(name, superClass, superClass.getAllocator());
- }
- return null;
- }
- private Map<Integer, RubyClass> errnos = new HashMap<Integer, RubyClass>();
- public RubyClass getErrno(int n) {
- return errnos.get(n);
- }
- /**
- * Create module Errno's Variables. We have this method since Errno does not have it's
- * own java class.
- */
- private void initErrno() {
- if (profile.allowModule("Errno")) {
- errnoModule = defineModule("Errno");
- Field[] fields = IErrno.class.getFields();
- for (int i = 0; i < fields.length; i++) {
- try {
- createSysErr(fields[i].getInt(IErrno.class), fields[i].getName());
- } catch (IllegalAccessException e) {
- throw new RuntimeException("Someone defined a non-public constant in IErrno.java", e);
- }
- }
- }
- }
- /**
- * Creates a system error.
- * @param i the error code (will probably use a java exception instead)
- * @param name of the error to define.
- **/
- private void createSysErr(int i, String name) {
- if(profile.allowClass(name)) {
- RubyClass errno = getErrno().defineClassUnder(name, systemCallError, systemCallError.getAllocator());
- errnos.put(i, errno);
- errno.defineConstant("Errno", newFixnum(i));
- }
- }
- private void initBuiltins() {
- addLazyBuiltin("java.rb", "java", "org.jruby.javasupport.Java");
- addLazyBuiltin("jruby.rb", "jruby", "org.jruby.libraries.JRubyLibrary");
-
- addLazyBuiltin("minijava.rb", "minijava", "org.jruby.java.MiniJava");
-
- addLazyBuiltin("jruby/ext.rb", "jruby/ext", "org.jruby.RubyJRuby$ExtLibrary");
- addLazyBuiltin("jruby/type.rb", "jruby/type", "org.jruby.RubyJRuby$TypeLibrary");
- addLazyBuiltin("iconv.so", "iconv", "org.jruby.libraries.IConvLibrary");
- addLazyBuiltin("nkf.so", "nkf", "org.jruby.libraries.NKFLibrary");
- addLazyBuiltin("stringio.so", "stringio", "org.jruby.libraries.StringIOLibrary");
- addLazyBuiltin("strscan.so", "strscan", "org.jruby.libraries.StringScannerLibrary");
- addLazyBuiltin("zlib.so", "zlib", "org.jruby.libraries.ZlibLibrary");
- addLazyBuiltin("yaml_internal.rb", "yaml_internal", "org.jruby.libraries.YamlLibrary");
- addLazyBuiltin("enumerator.so", "enumerator", "org.jruby.libraries.EnumeratorLibrary");
- addLazyBuiltin("generator_internal.rb", "generator_internal", "org.jruby.ext.Generator$Service");
- addLazyBuiltin("readline.so", "readline", "org.jruby.ext.Readline$Service");
- addLazyBuiltin("thread.so", "thread", "org.jruby.libraries.ThreadLibrary");
- addLazyBuiltin("digest.so", "digest", "org.jruby.libraries.DigestLibrary");
- addLazyBuiltin("digest.rb", "digest", "org.jruby.libraries.DigestLibrary");
- addLazyBuiltin("digest/md5.so", "digest/md5", "org.jruby.libraries.DigestLibrary$MD5");
- addLazyBuiltin("digest/rmd160.so", "digest/rmd160", "org.jruby.libraries.DigestLibrary$RMD160");
- addLazyBuiltin("digest/sha1.so", "digest/sha1", "org.jruby.libraries.DigestLibrary$SHA1");
- addLazyBuiltin("digest/sha2.so", "digest/sha2", "org.jruby.libraries.DigestLibrary$SHA2");
- addLazyBuiltin("bigdecimal.so", "bigdecimal", "org.jruby.libraries.BigDecimalLibrary");
- addLazyBuiltin("io/wait.so", "io/wait", "org.jruby.libraries.IOWaitLibrary");
- addLazyBuiltin("etc.so", "etc", "org.jruby.libraries.EtcLibrary");
- addLazyBuiltin("weakref.rb", "weakref", "org.jruby.ext.WeakRef$WeakRefLibrary");
- addLazyBuiltin("socket.so", "socket", "org.jruby.ext.socket.RubySocket$Service");
- addLazyBuiltin("rbconfig.rb", "rbconfig", "org.jruby.libraries.RbConfigLibrary");
- addLazyBuiltin("jruby/serialization.rb", "serialization", "org.jruby.libraries.JRubySerializationLibrary");
- addLazyBuiltin("ffi.so", "ffi", "org.jruby.ext.ffi.Factory$Service");
- if(RubyInstanceConfig.NATIVE_NET_PROTOCOL) {
- addLazyBuiltin("net/protocol.rb", "net/protocol", "org.jruby.libraries.NetProtocolBufferedIOLibrary");
- }
-
- if (config.getCompatVersion() == CompatVersion.RUBY1_9) {
- addLazyBuiltin("fiber.so", "fiber", "org.jruby.libraries.FiberLibrary");
- }
-
- addBuiltinIfAllowed("openssl.so", new Library() {
- public void load(Ruby runtime, boolean wrap) throws IOException {
- runtime.getLoadService().require("jruby/openssl/stub");
- }
- });
-
- String[] builtins = {"fcntl", "yaml", "yaml/syck", "jsignal" };
- for (String library : builtins) {
- addBuiltinIfAllowed(library + ".rb", new BuiltinScript(library));
- }
- getLoadService().require("builtin/core_ext/symbol");
-
- RubyKernel.autoload(topSelf, newSymbol("Java"), newString("java"));
- getLoadService().require("enumerator");
- }
- private void addLazyBuiltin(String name, String shortName, String className) {
- addBuiltinIfAllowed(name, new LateLoadingLibrary(shortName, className, getJRubyClassLoader()));
- }
- private void addBuiltinIfAllowed(String name, Library lib) {
- if(profile.allowBuiltin(name)) {
- loadService.addBuiltinLibrary(name,lib);
- }
- }
- Object getRespondToMethod() {
- return respondToMethod;
- }
- void setRespondToMethod(Object rtm) {
- this.respondToMethod = rtm;
- }
- public Object getObjectToYamlMethod() {
- return objectToYamlMethod;
- }
- void setObjectToYamlMethod(Object otym) {
- this.objectToYamlMethod = otym;
- }
- /**
- * Retrieve mappings of cached methods to where they have been cached. When a cached
- * method needs to be invalidated this map can be used to remove all places it has been
- * cached.
- *
- * @return the mappings of where cached methods have been stored
- */
- public CacheMap getCacheMap() {
- return cacheMap;
- }
- /** Getter for property rubyTopSelf.
- * @return Value of property rubyTopSelf.
- */
- public IRubyObject getTopSelf() {
- return topSelf;
- }
- public void setCurrentDirectory(String dir) {
- currentDirectory = dir;
- }
- public String getCurrentDirectory() {
- return currentDirectory;
- }
-
- public RubyModule getEtc() {
- return etcModule;
- }
-
- public void setEtc(RubyModule etcModule) {
- this.etcModule = etcModule;
- }
- public RubyClass getObject() {
- return objectClass;
- }
- public RubyClass getModule() {
- return moduleClass;
- }
- public RubyClass getClassClass() {
- return classClass;
- }
-
- public RubyModule getKernel() {
- return kernelModule;
- }
- void setKernel(RubyModule kernelModule) {
- this.kernelModule = kernelModule;
- }
-
- public RubyClass getDummy() {
- return dummyClass;
- }
- public RubyModule getComparable() {
- return comparableModule;
- }
- void setComparable(RubyModule comparableModule) {
- this.comparableModule = comparableModule;
- }
- public RubyClass getNumeric() {
- return numericClass;
- }
- void setNumeric(RubyClass numericClass) {
- this.numericClass = numericClass;
- }
- public RubyClass getFloat() {
- return floatClass;
- }
- void setFloat(RubyClass floatClass) {
- this.floatClass = floatClass;
- }
-
- public RubyClass getInteger() {
- return integerClass;
- }
- void setInteger(RubyClass integerClass) {
- this.integerClass = integerClass;
- }
-
- public RubyClass getFixnum() {
- return fixnumClass;
- }
- void setFixnum(RubyClass fixnumClass) {
- this.fixnumClass = fixnumClass;
- }
- public RubyClass getComplex() {
- return complexClass;
- }
- void setComplex(RubyClass complexClass) {
- this.complexClass = complexClass;
- }
- public RubyClass getRational() {
- return rationalClass;
- }
- void setRational(RubyClass rationalClass) {
- this.rationalClass = rationalClass;
- }
- public RubyModule getEnumerable() {
- return enumerableModule;
- }
- void setEnumerable(RubyModule enumerableModule) {
- this.enumerableModule = enumerableModule;
- }
- public RubyModule getEnumerator() {
- return enumeratorClass;
- }
- void setEnumerator(RubyClass enumeratorClass) {
- this.enumeratorClass = enumeratorClass;
- }
- public RubyClass getString() {
- return stringClass;
- }
- void setString(RubyClass stringClass) {
- this.stringClass = stringClass;
- }
- public RubyClass getSymbol() {
- return symbolClass;
- }
- void setSymbol(RubyClass symbolClass) {
- this.symbolClass = symbolClass;
- }
- public RubyClass getArray() {
- return arrayClass;
- }
- void setArray(RubyClass arrayClass) {
- this.arrayClass = arrayClass;
- }
- public RubyClass getHash() {
- return hashClass;
- }
- void setHash(RubyClass hashClass) {
- this.hashClass = hashClass;
- }
- public RubyClass getRange() {
- return rangeClass;
- }
- void setRange(RubyClass rangeClass) {
- this.rangeClass = rangeClass;
- }
- /** Returns the "true" instance from the instance pool.
- * @return The "true" instance.
- */
- public RubyBoolean getTrue() {
- return trueObject;
- }
- /** Returns the "false" instance from the instance pool.
- * @return The "false" instance.
- */
- public RubyBoolean getFalse() {
- return falseObject;
- }
- /** Returns the "nil" singleton instance.
- * @return "nil"
- */
- public IRubyObject getNil() {
- return nilObject;
- }
- public RubyClass getNilClass() {
- return nilClass;
- }
- void setNilClass(RubyClass nilClass) {
- this.nilClass = nilClass;
- }
- public RubyClass getTrueClass() {
- return trueClass;
- }
- void setTrueClass(RubyClass trueClass) {
- this.trueClass = trueClass;
- }
- public RubyClass getFalseClass() {
- return falseClass;
- }
- void setFalseClass(RubyClass falseClass) {
- this.falseClass = falseClass;
- }
- public RubyClass getProc() {
- return procClass;
- }
- void setProc(RubyClass procClass) {
- this.procClass = procClass;
- }
- public RubyClass getBinding() {
- return bindingClass;
- }
- void setBinding(RubyClass bindingClass) {
- this.bindingClass = bindingClass;
- }
- public RubyClass getMethod() {
- return methodClass;
- }
- void setMethod(RubyClass methodClass) {
- this.methodClass = methodClass;
- }
- public RubyClass getUnboundMethod() {
- return unboundMethodClass;
- }
- void setUnboundMethod(RubyClass unboundMethodClass) {
- this.unboundMethodClass = unboundMethodClass;
- }
- public RubyClass getMatchData() {
- return matchDataClass;
- }
- void setMatchData(RubyClass matchDataClass) {
- this.matchDataClass = matchDataClass;
- }
- public RubyClass getRegexp() {
- return regexpClass;
- }
- void setRegexp(RubyClass regexpClass) {
- this.regexpClass = regexpClass;
- }
- public RubyClass getTime() {
- return timeClass;
- }
- void setTime(RubyClass timeClass) {
- this.timeClass = timeClass;
- }
- public RubyModule getMath() {
- return mathModule;
- }
- void setMath(RubyModule mathModule) {
- this.mathModule = mathModule;
- }
- public RubyModule getMarshal() {
- return marshalModule;
- }
- void setMarshal(RubyModule marshalModule) {
- this.marshalModule = marshalModule;
- }
- public RubyClass getBignum() {
- return bignumClass;
- }
- void setBignum(RubyClass bignumClass) {
- this.bignumClass = bignumClass;
- }
- public RubyClass getDir() {
- return dirClass;
- }
- void setDir(RubyClass dirClass) {
- this.dirClass = dirClass;
- }
- public RubyClass getFile() {
- return fileClass;
- }
- void setFile(RubyClass fileClass) {
- this.fileClass = fileClass;
- }
- public RubyClass getFileStat() {
- return fileStatClass;
- }
- void setFileStat(RubyClass fileStatClass) {
- this.fileStatClass = fileStatClass;
- }
- public RubyModule getFileTest() {
- return fileTestModule;
- }
- void setFileTest(RubyModule fileTestModule) {
- this.fileTestModule = fileTestModule;
- }
-
- public RubyClass getIO() {
- return ioClass;
- }
- void setIO(RubyClass ioClass) {
- this.ioClass = ioClass;
- }
- public RubyClass getThread() {
- return threadClass;
- }
- void setThread(RubyClass threadClass) {
- this.threadClass = threadClass;
- }
- public RubyClass getThreadGroup() {
- return threadGroupClass;
- }
- void setThreadGroup(RubyClass threadGroupClass) {
- this.threadGroupClass = threadGroupClass;
- }
-
- public RubyThreadGroup getDefaultThreadGroup() {
- return defaultThreadGroup;
- }
- void setDefaultThreadGroup(RubyThreadGroup defaultThreadGroup) {
- this.defaultThreadGroup = defaultThreadGroup;
- }
- public RubyClass getContinuation() {
- return continuationClass;
- }
- void setContinuation(RubyClass continuationClass) {
- this.continuationClass = continuationClass;
- }
- public RubyClass getStructClass() {
- return structClass;
- }
- void setStructClass(RubyClass structClass) {
- this.structClass = structClass;
- }
- public IRubyObject getTmsStruct() {
- return tmsStruct;
- }
- void setTmsStruct(RubyClass tmsStruct) {
- this.tmsStruct = tmsStruct;
- }
-
- public IRubyObject getPasswdStruct() {
- return passwdStruct;
- }
- void setPasswdStruct(RubyClass passwdStruct) {
- this.passwdStruct = passwdStruct;
- }
- public IRubyObject getGroupStruct() {
- return groupStruct;
- }
- void setGroupStruct(RubyClass groupStruct) {
- this.groupStruct = groupStruct;
- }
- public RubyModule getGC() {
- return gcModule;
- }
- void setGC(RubyModule gcModule) {
- this.gcModule = gcModule;
- }
- public RubyModule getObjectSpaceModule() {
- return objectSpaceModule;
- }
- void setObjectSpaceModule(RubyModule objectSpaceModule) {
- this.objectSpaceModule = objectSpaceModule;
- }
- public RubyModule getProcess() {
- return processModule;
- }
- void setProcess(RubyModule processModule) {
- this.processModule = processModule;
- }
- public RubyClass getProcStatus() {
- return procStatusClass;
- }
- void setProcStatus(RubyClass procStatusClass) {
- this.procStatusClass = procStatusClass;
- }
-
- public RubyModule getProcUID() {
- return procUIDModule;
- }
- void setProcUID(RubyModule procUIDModule) {
- this.procUIDModule = procUIDModule;
- }
-
- public RubyModule getProcGID() {
- return procGIDModule;
- }
- void setProcGID(RubyModule procGIDModule) {
- this.procGIDModule = procGIDModule;
- }
-
- public RubyModule getProcSysModule() {
- return procSysModule;
- }
- void setProcSys(RubyModule procSysModule) {
- this.procSysModule = procSysModule;
- }
- public RubyModule getPrecision() {
- return precisionModule;
- }
- void setPrecision(RubyModule precisionModule) {
- this.precisionModule = precisionModule;
- }
- public RubyModule getErrno() {
- return errnoModule;
- }
- public RubyClass getException() {
- return exceptionClass;
- }
- void setException(RubyClass exceptionClass) {
- this.exceptionClass = exceptionClass;
- }
- public RubyClass getNameError() {
- return nameError;
- }
- public RubyClass getNameErrorMessage() {
- return nameErrorMessage;
- }
- public RubyClass getNoMethodError() {
- return noMethodError;
- }
- public RubyClass getSignalException() {
- return signalException;
- }
- public RubyClass getRangeError() {
- return rangeError;
- }
- public RubyClass getSystemExit() {
- return systemExit;
- }
- public RubyClass getLocalJumpError() {
- return localJumpError;
- }
- public RubyClass getNativeException() {
- return nativeException;
- }
- public RubyClass getSystemCallError() {
- return systemCallError;
- }
- public RubyClass getFatal() {
- return fatal;
- }
-
- public RubyClass getInterrupt() {
- return interrupt;
- }
-
- public RubyClass getTypeError() {
- return typeError;
- }
- public RubyClass getArgumentError() {
- return argumentError;
- }
- public RubyClass getIndexError() {
- return indexError;
- }
-
- public RubyClass getSyntaxError() {
- return syntaxError;
- }
- public RubyClass getStandardError() {
- return standardError;
- }
-
- public RubyClass getRuntimeError() {
- return runtimeError;
- }
-
- public RubyClass getIOError() {
- return ioError;
- }
- public RubyClass getLoadError() {
- return loadError;
- }
- public RubyClass getNotImplementedError() {
- return notImplementedError;
- }
- public RubyClass getSecurityError() {
- return securityError;
- }
- public RubyClass getNoMemoryError() {
- return noMemoryError;
- }
- public RubyClass getRegexpError() {
- return regexpError;
- }
- public RubyClass getEOFError() {
- return eofError;
- }
- public RubyClass getThreadError() {
- return threadError;
- }
- public RubyClass getConcurrencyError() {
- return concurrencyError;
- }
- public RubyClass getSystemStackError() {
- return systemStackError;
- }
- public RubyClass getZeroDivisionError() {
- return zeroDivisionError;
- }
- public RubyClass getFloatDomainError() {
- return floatDomainError;
- }
- private RubyHash charsetMap;
- public RubyHash getCharsetMap() {
- if (charsetMap == null) charsetMap = new RubyHash(this);
- return charsetMap;
- }
- /** Getter for property isVerbose.
- * @return Value of property isVerbose.
- */
- public IRubyObject getVerbose() {
- return verbose;
- }
- /** Setter for property isVerbose.
- * @param verbose New value of property isVerbose.
- */
- public void setVerbose(IRubyObject verbose) {
- this.verbose = verbose;
- }
- /** Getter for property isDebug.
- * @return Value of property isDebug.
- */
- public IRubyObject getDebug() {
- return debug;
- }
- /** Setter for property isDebug.
- * @param debug New value of property isDebug.
- */
- public void setDebug(IRubyObject debug) {
- this.debug = debug;
- }
- public JavaSupport getJavaSupport() {
- return javaSupport;
- }
-
- public static ClassLoader getClassLoader() {
- // we try to get the classloader that loaded JRuby, falling back on System
- ClassLoader loader = Ruby.class.getClassLoader();
- if (loader == null) {
- loader = ClassLoader.getSystemClassLoader();
- }
-
- return loader;
- }
- public synchronized JRubyClassLoader getJRubyClassLoader() {
- // FIXME: Get rid of laziness and handle restricted access elsewhere
- if (!Ruby.isSecurityRestricted() && jrubyClassLoader == null) {
- jrubyClassLoader = new JRubyClassLoader(config.getLoader());
- }
-
- return jrubyClassLoader;
- }
- /** Defines a global variable
- */
- public void defineVariable(final GlobalVariable variable) {
- globalVariables.define(variable.name(), new IAccessor() {
- public IRubyObject getValue() {
- return variable.get();
- }
- public IRubyObject setValue(IRubyObject newValue) {
- return variable.set(newValue);
- }
- });
- }
- /** defines a readonly global variable
- *
- */
- public void defineReadonlyVariable(String name, IRubyObject value) {
- globalVariables.defineReadonly(name, new ValueAccessor(value));
- }
-
- public Node parseFile(InputStream in, String file, DynamicScope scope) {
- return parser.parse(file, in, scope, new ParserConfiguration(0, false, false, true));
- }
- public Node parseInline(InputStream in, String file, DynamicScope scope) {
- return parser.parse(file, in, scope, new ParserConfiguration(0, false, true));
- }
- public Node parseEval(String content, String file, DynamicScope scope, int lineNumber) {
- byte[] bytes;
-
- try {
- bytes = content.getBytes(KCode.NONE.getKCode());
- } catch (UnsupportedEncodingException e) {
- bytes = content.getBytes();
- }
-
- return parser.parse(file, new ByteArrayInputStream(bytes), scope,
- new ParserConfiguration(lineNumber, false));
- }
- public Node parse(String content, String file, DynamicScope scope, int lineNumber,
- boolean extraPositionInformation) {
- byte[] bytes;
-
- try {
- bytes = content.getBytes(KCode.NONE.getKCode());
- } catch (UnsupportedEncodingException e) {
- bytes = content.getBytes();
- }
- return parser.parse(file, new ByteArrayInputStream(bytes), scope,
- new ParserConfiguration(lineNumber, extraPositionInformation, false));
- }
-
- public Node parseEval(ByteList content, String file, DynamicScope scope, int lineNumber) {
- return parser.parse(file, content, scope, new ParserConfiguration(lineNumber, false));
- }
- public Node parse(ByteList content, String file, DynamicScope scope, int lineNumber,
- boolean extraPositionInformation) {
- return parser.parse(file, content, scope,
- new ParserConfiguration(lineNumber, extraPositionInformation, false));
- }
- public ThreadService getThreadService() {
- return threadService;
- }
- public ThreadContext getCurrentContext() {
- return threadService.getCurrentContext();
- }
- /**
- * Returns the loadService.
- * @return ILoadService
- */
- public LoadService getLoadService() {
- return loadService;
- }
- public RubyWarnings getWarnings() {
- return warnings;
- }
- public PrintStream getErrorStream() {
- // FIXME: We can't guarantee this will always be a RubyIO...so the old code here is not safe
- /*java.io.OutputStream os = ((RubyIO) getGlobalVariables().get("$stderr")).getOutStream();
- if(null != os) {
- return new PrintStream(os);
- } else {
- return new PrintStream(new org.jruby.util.SwallowingOutputStream());
- }*/
- return new PrintStream(new IOOutputStream(getGlobalVariables().get("$stderr")));
- }
- public InputStream getInputStream() {
- return new IOInputStream(getGlobalVariables().get("$stdin"));
- }
- public PrintStream getOutputStream() {
- return new PrintStream(new IOOutputStream(getGlobalVariables().get("$stdout")));
- }
- public RubyModule getClassFromPath(String path) {
- RubyModule c = getObject();
- if (path.length() == 0 || path.charAt(0) == '#') {
- throw newTypeError("can't retrieve anonymous class " + path);
- }
- int pbeg = 0, p = 0;
- for(int l=path.length(); p<l; ) {
- while(p<l && path.charAt(p) != ':') {
- p++;
- }
- String str = path.substring(pbeg, p);
- if(p<l && path.charAt(p) == ':') {
- if(p+1 < l && path.charAt(p+1) != ':') {
- throw newTypeError("undefined class/module " + path.substring(pbeg,p));
- }
- p += 2;
- pbeg = p;
- }
- IRubyObject cc = c.getConstant(str);
- if(!(cc instanceof RubyModule)) {
- throw newTypeError("" + path + " does not refer to class/module");
- }
- c = (RubyModule)cc;
- }
- return c;
- }
- /** Prints an error with backtrace to the error stream.
- *
- * MRI: eval.c - error_print()
- *
- */
- public void printError(RubyException excp) {
- if (excp == null || excp.isNil()) {
- return;
- }
- ThreadContext context = getCurrentContext();
- IRubyObject backtrace = excp.callMethod(context, "backtrace");
- PrintStream errorStream = getErrorStream();
- if (backtrace.isNil() || !(backtrace instanceof RubyArray)) {
- if (context.getFile() != null) {
- errorStream.print(context.getFile() + ":" + context.getLine());
- } else {
- errorStream.print(context.getLine());
- }
- } else if (((RubyArray) backtrace).getLength() == 0) {
- printErrorPos(context, errorStream);
- } else {
- IRubyObject mesg = ((RubyArray) backtrace).first();
- if (mesg.isNil()) {
- printErrorPos(context, errorStream);
- } else {
- errorStream.print(mesg);
- }
- }
- RubyClass type = excp.getMetaClass();
- String info = excp.toString();
- if (type == getRuntimeError() && (info == null || info.length() == 0)) {
- errorStream.print(": unhandled exception\n");
- } else {
- String path = type.getName();
- if (info.length() == 0) {
- errorStream.print(": " + path + '\n');
- } else {
- if (path.startsWith("#")) {
- path = null;
- }
- String tail = null;
- if (info.indexOf("\n") != -1) {
- tail = info.substring(info.indexOf("\n") + 1);
- info = info.substring(0, info.indexOf("\n"));
- }
- errorStream.print(": " + info);
- if (path != null) {
- errorStream.print(" (" + path + ")\n");
- }
- if (tail != null) {
- errorStream.print(tail + '\n');
- }
- }
- }
- excp.printBacktrace(errorStream);
- }
- private void printErrorPos(ThreadContext context, PrintStream errorStream) {
- if (context.getFile() != null) {
- if (context.getFrameName() != null) {
- errorStream.print(context.getFile() + ":" + context.getLine());
- errorStream.print(":in '" + context.getFrameName() + '\'');
- } else if (context.getLine() != 0) {
- errorStream.print(context.getFile() + ":" + context.getLine());
- } else {
- errorStream.print(context.getFile());
- }
- }
- }
-
- public void loadFile(String scriptName, InputStream in, boolean wrap) {
- IRubyObject self = wrap ? TopSelfFactory.createTopSelf(this) : getTopSelf();
- ThreadContext context = getCurrentContext();
- String file = context.getFile();
-
- try {
- secure(4); /* should alter global state */
- context.setFile(scriptName);
- context.preNodeEval(objectClass, self, scriptName);
- parseFile(in, scriptName, null).interpret(this, context, self, Block.NULL_BLOCK);
- } catch (JumpException.ReturnJump rj) {
- return;
- } finally {
- context.postNodeEval();
- context.setFile(file);
- }
- }
-
- public void compileAndLoadFile(String filename, InputStream in, boolean wrap) {
- IRubyObject self = wrap ? TopSelfFactory.createTopSelf(this) : getTopSelf();
- ThreadContext context = getCurrentContext();
- String file = context.getFile();
-
- try {
- secure(4); /* should alter global state */
- context.setFile(filename);
- context.preNodeEval(objectClass, self, filename);
-
- Node scriptNode = parseFile(in, filename, null);
-
- Script script = tryCompile(scriptNode, new JRubyClassLoader(jrubyClassLoader));
- if (script == null) {
- System.err.println("Error, could not compile; pass -J-Djruby.jit.logging.verbose=true for more details");
- }
- runScript(script);
- } catch (JumpException.ReturnJump rj) {
- return;
- } finally {
- context.postNodeEval();
- context.setFile(file);
- }
- }
- public void loadScript(Script script) {
- IRubyObject self = getTopSelf();
- ThreadContext context = getCurrentContext();
- try {
- secure(4); /* should alter global state */
- context.preNodeEval(objectClass, self);
-
- script.load(context, self, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
- } catch (JumpException.ReturnJump rj) {
- return;
- } finally {
- context.postNodeEval();
- }
- }
- public class CallTraceFuncHook extends EventHook {
- private RubyProc traceFunc;
-
- public void setTraceFunc(RubyProc traceFunc) {
- this.traceFunc = traceFunc;
- }
-
- public void eventHandler(ThreadContext context, String eventName, String file, int line, String name, IRubyObject type) {
- if (!context.isWithinTrace()) {
- if (file == null) file = "(ruby)";
- if (type == null) type = getFalse();
-
- RubyBinding binding = RubyBinding.newBinding(Ruby.this);
- context.preTrace();
- try {
- traceFunc.call(context, new IRubyObject[] {
- newString(eventName), // event name
- newString(file), // filename
- newFixnum(line), // line numbers should be 1-based
- name != null ? newSymbol(name) : getNil(),
- binding,
- type
- });
- } finally {
- context.postTrace();
- }
- }
- }
- public boolean isInterestedInEvent(RubyEvent event) {
- return true;
- }
- };
-
- private final CallTraceFuncHook callTraceFuncHook = new CallTraceFuncHook();
-
- public void addEventHook(EventHook hook) {
- eventHooks.add(hook);
- hasEventHooks = true;
- }
-
- public void removeEventHook(EventHook hook) {
- eventHooks.remove(hook);
- hasEventHooks = !eventHooks.isEmpty();
- }
- public void setTraceFunction(RubyProc traceFunction) {
- removeEventHook(callTraceFuncHook);
-
- if (traceFunction == null) {
- return;
- }
-
- callTraceFuncHook.setTraceFunc(traceFunction);
- addEventHook(callTraceFuncHook);
- }
-
- public void callEventHooks(ThreadContext context, RubyEvent event, String file, int line, String name, IRubyObject type) {
- for (EventHook eventHook : eventHooks) {
- if (eventHook.isInterestedInEvent(event)) {
- eventHook.event(context, event, file, line, name, type);
- }
- }
- }
-
- public boolean hasEventHooks() {
- return hasEventHooks;
- }
-
- public GlobalVariables getGlobalVariables() {
- return globalVariables;
- }
- // For JSR 223 support: see http://scripting.java.net/
- public void setGlobalVariables(GlobalVariables globalVariables) {
- this.globalVariables = globalVariables;
- }
- public CallbackFactory callbackFactory(Class<?> type) {
- return CallbackFactory.createFactory(this, type);
- }
- /**
- * Push block onto exit stack. When runtime environment exits
- * these blocks will be evaluated.
- *
- * @return the element that was pushed onto stack
- */
- public IRubyObject pushExitBlock(RubyProc proc) {
- atExitBlocks.push(proc);
- return proc;
- }
- // use this for JRuby-internal finalizers
- public void addInternalFinalizer(Finalizable finalizer) {
- synchronized (internalFinalizersMutex) {
- if (internalFinalizers == null) {
- internalFinalizers = new WeakHashMap<Finalizable, Object>();
- }
- internalFinalizers.put(finalizer, null);
- }
- }
- // this method is for finalizers registered via ObjectSpace
- public void addFinalizer(Finalizable finalizer) {
- synchronized (finalizersMutex) {
- if (finalizers == null) {
- finalizers = new WeakHashMap<Finalizable, Object>();
- }
- finalizers.put(finalizer, null);
- }
- }
-
- public void removeInternalFinalizer(Finalizable finalizer) {
- synchronized (internalFinalizersMutex) {
- if (internalFinalizers != null) {
- internalFinalizers.remove(finalizer);
- }
- }
- }
- public void removeFinalizer(Finalizable finalizer) {
- synchronized (finalizersMutex) {
- if (finalizers != null) {
- finalizers.remove(finalizer);
- }
- }
- }
- /**
- * Make sure Kernel#at_exit procs get invoked on runtime shutdown.
- * This method needs to be explicitly called to work properly.
- * I thought about using finalize(), but that did not work and I
- * am not sure the runtime will be at a state to run procs by the
- * time Ruby is going away. This method can contain any other
- * things that need to be cleaned up at shutdown.
- */
- public void tearDown() {
- int status = 0;
- while (!atExitBlocks.empty()) {
- RubyProc proc = atExitBlocks.pop();
- try {
- proc.call(getCurrentContext(), IRubyObject.NULL_ARRAY);
- } catch (RaiseException rj) {
- RubyException raisedException = rj.getException();
- if (!getSystemExit().isInstance(raisedException)) {
- status = 1;
- printError(raisedException);
- } else {
- IRubyObject statusObj = raisedException.callMethod(
- getCurrentContext(), "status");
- if (statusObj != null && !statusObj.isNil()) {
- status = RubyNumeric.fix2int(statusObj);
- }
- }
- }
- }
- if (finalizers != null) {
- synchronized (finalizers) {
- for (Iterator<Finalizable> finalIter = new ArrayList<Finalizable>(finalizers.keySet()).iterator(); finalIter.hasNext();) {
- finalIter.next().finalize();
- finalIter.remove();
- }
- }
- }
- synchronized (internalFinalizersMutex) {
- if (internalFinalizers != null) {
- for (Iterator<Finalizable> finalIter = new ArrayList<Finalizable>(
- internalFinalizers.keySet()).iterator(); finalIter.hasNext();) {
- finalIter.next().finalize();
- finalIter.remove();
- }
- }
- }
- getThreadService().disposeCurrentThread();
- getBeanManager().unregisterCompiler();
- getBeanManager().unregisterConfig();
- getBeanManager().unregisterClassCache();
- getBeanManager().unregisterMethodCache();
- if (status != 0) {
- throw newSystemExit(status);
- }
- }
- // new factory methods ------------------------------------------------------------------------
- public RubyArray newEmptyArray() {
- return RubyArray.newEmptyArray(this);
- }
- public RubyArray newArray() {
- return RubyArray.newArray(this);
- }
- public RubyArray newArrayLight() {
- return RubyArray.newArrayLight(this);
- }
- public RubyArray newArray(IRubyObject object) {
- return RubyArray.newArray(this, object);
- }
- public RubyArray newArray(IRubyObject car, IRubyObject cdr) {
- return RubyArray.newArray(this, car, cdr);
- }
- public RubyArray newArray(IRubyObject[] objects) {
- return RubyArray.newArray(this, objects);
- }
-
- public RubyArray newArrayNoCopy(IRubyObject[] objects) {
- return RubyArray.newArrayNoCopy(this, objects);
- }
-
- public RubyArray newArrayNoCopyLight(IRubyObject[] objects) {
- return RubyArray.newArrayNoCopyLight(this, objects);
- }
-
- public RubyArray newArray(List<IRubyObject> list) {
- return RubyArray.newArray(this, list);
- }
- public RubyArray newArray(int size) {
- return RubyArray.newArray(this, size);
- }
- public RubyBoolean newBoolean(boolean value) {
- return RubyBoolean.newBoolean(this, value);
- }
- public RubyFileStat newFileStat(String filename, boolean lstat) {
- return RubyFileStat.newFileStat(this, filename, lstat);
- }
-
- public RubyFileStat newFileStat(FileDescriptor descriptor) {
- return RubyFileStat.newFileStat(this, descriptor);
- }
- public RubyFixnum newFixnum(long value) {
- return RubyFixnum.newFixnum(this, value);
- }
- public RubyFixnum newFixnum(int value) {
- return RubyFixnum.newFixnum(this, value);
- }
- public RubyFloat newFloat(double value) {
- return RubyFloat.newFloat(this, value);
- }
- public RubyNumeric newNumeric() {
- return RubyNumeric.newNumeric(this);
- }
- public RubyProc newProc(Block.Type type, Block block) {
- if (type != Block.Type.LAMBDA && block.getProcObject() != null) return block.getProcObject();
- RubyProc proc = RubyProc.newProc(this, type);
- proc.callInit(IRubyObject.NULL_ARRAY, block);
- return proc;
- }
- public RubyProc newBlockPassProc(Block.Type type, Block block) {
- if (type != Block.Type.LAMBDA && block.getProcObject() != null) return block.getProcObject();
- RubyProc proc = RubyProc.newProc(this, type);
- proc.initialize(getCurrentContext(), block);
- return proc;
- }
- public RubyBinding newBinding() {
- return RubyBinding.newBinding(this);
- }
- public RubyBinding newBinding(Binding binding) {
- return RubyBinding.newBinding(this, binding);
- }
- public RubyString newString() {
- return RubyString.newString(this, new ByteList());
- }
- public RubyString newString(String string) {
- return RubyString.newString(this, string);
- }
-
- public RubyString newString(ByteList byteList) {
- return RubyString.newString(this, byteList);
- }
- @Deprecated
- public RubyString newStringShared(ByteList byteList) {
- return RubyString.newStringShared(this, byteList);
- }
- public RubySymbol newSymbol(String name) {
- return symbolTable.getSymbol(name);
- }
- /**
- * Faster than {@link #newSymbol(String)} if you already have an interned
- * name String. Don't intern your string just to call this version - the
- * overhead of interning will more than wipe out any benefit from the faster
- * lookup.
- *
- * @param internedName the symbol name, <em>must</em> be interned! if in
- * doubt, call {@link #newSymbol(String)} instead.
- * @return the symbol for name
- */
- public RubySymbol fastNewSymbol(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- return symbolTable.fastGetSymbol(internedName);
- }
- public RubyTime newTime(long milliseconds) {
- return RubyTime.newTime(this, milliseconds);
- }
- public RaiseException newRuntimeError(String message) {
- return newRaiseException(getRuntimeError(), message);
- }
-
- public RaiseException newArgumentError(String message) {
- return newRaiseException(getArgumentError(), message);
- }
- public RaiseException newArgumentError(int got, int expected) {
- return newRaiseException(getArgumentError(), "wrong # of arguments(" + got + " for " + expected + ")");
- }
- public RaiseException newErrnoEBADFError() {
- return newRaiseException(getErrno().fastGetClass("EBADF"), "Bad file descriptor");
- }
- public RaiseException newErrnoENOPROTOOPTError() {
- return newRaiseException(getErrno().fastGetClass("ENOPROTOOPT"), "Protocol not available");
- }
- public RaiseException newErrnoEPIPEError() {
- return newRaiseException(getErrno().fastGetClass("EPIPE"), "Broken pipe");
- }
- public RaiseException newErrnoECONNREFUSEDError() {
- return newRaiseException(getErrno().fastGetClass("ECONNREFUSED"), "Connection refused");
- }
- public RaiseException newErrnoECONNRESETError() {
- return newRaiseException(getErrno().fastGetClass("ECONNRESET"), "Connection reset by peer");
- }
- public RaiseException newErrnoEADDRINUSEError() {
- return newRaiseException(getErrno().fastGetClass("EADDRINUSE"), "Address in use");
- }
- public RaiseException newErrnoEINVALError() {
- return newRaiseException(getErrno().fastGetClass("EINVAL"), "Invalid file");
- }
- public RaiseException newErrnoENOENTError() {
- return newRaiseException(getErrno().fastGetClass("ENOENT"), "File not found");
- }
- public RaiseException newErrnoEACCESError(String message) {
- return newRaiseException(getErrno().fastGetClass("EACCES"), message);
- }
- public RaiseException newErrnoEAGAINError(String message) {
- return newRaiseException(getErrno().fastGetClass("EAGAIN"), message);
- }
- public RaiseException newErrnoEISDirError() {
- return newRaiseException(getErrno().fastGetClass("EISDIR"), "Is a directory");
- }
- public RaiseException newErrnoESPIPEError() {
- return newRaiseException(getErrno().fastGetClass("ESPIPE"), "Illegal seek");
- }
- public RaiseException newErrnoEBADFError(String message) {
- return newRaiseException(getErrno().fastGetClass("EBADF"), message);
- }
- public RaiseException newErrnoEINVALError(String message) {
- return newRaiseException(getErrno().fastGetClass("EINVAL"), message);
- }
- public RaiseException newErrnoENOTDIRError(String message) {
- return newRaiseException(getErrno().fastGetClass("ENOTDIR"), message);
- }
- public RaiseException newErrnoENOTSOCKError(String message) {
- return newRaiseException(getErrno().fastGetClass("ENOTSOCK"), message);
- }
- public RaiseException newErrnoENOENTError(String message) {
- return newRaiseException(getErrno().fastGetClass("ENOENT"), message);
- }
- public RaiseException newErrnoESPIPEError(String message) {
- return newRaiseException(getErrno().fastGetClass("ESPIPE"), message);
- }
- public RaiseException newErrnoEEXISTError(String message) {
- return newRaiseException(getErrno().fastGetClass("EEXIST"), message);
- }
-
- public RaiseException newErrnoEDOMError(String message) {
- return newRaiseException(getErrno().fastGetClass("EDOM"), "Domain error - " + message);
- }
-
- public RaiseException newErrnoECHILDError() {
- return newRaiseException(getErrno().fastGetClass("ECHILD"), "No child processes");
- }
- public RaiseException newIndexError(String message) {
- return newRaiseException(getIndexError(), message);
- }
- public RaiseException newSecurityError(String message) {
- return newRaiseException(getSecurityError(), message);
- }
- public RaiseException newSystemCallError(String message) {
- return newRaiseException(getSystemCallError(), message);
- }
- public RaiseException newTypeError(String message) {
- return newRaiseException(getTypeError(), message);
- }
- public RaiseException newThreadError(String message) {
- return newRaiseException(getThreadError(), message);
- }
- public RaiseException newConcurrencyError(String message) {
- return newRaiseException(getConcurrencyError(), message);
- }
- public RaiseException newSyntaxError(String message) {
- return newRaiseException(getSyntaxError(), message);
- }
- public RaiseException newRegexpError(String message) {
- return newRaiseException(getRegexpError(), message);
- }
- public RaiseException newRangeError(String message) {
- return newRaiseException(getRangeError(), message);
- }
- public RaiseException newNotImplementedError(String message) {
- return newRaiseException(getNotImplementedError(), message);
- }
-
- public RaiseException newInvalidEncoding(String message) {
- return newRaiseException(fastGetClass("Iconv").fastGetClass("InvalidEncoding"), message);
- }
- public RaiseException newNoMethodError(String message, String name, IRubyObject args) {
- return new RaiseException(new RubyNoMethodError(this, getNoMethodError(), message, name, args), true);
- }
- public RaiseException newNameError(String message, String name) {
- return newNameError(message, name, null);
- }
- public RaiseException newNameError(String message, String name, Throwable origException) {
- return newNameError(message, name, origException, true);
- }
- public RaiseException newNameError(String message, String name, Throwable origException, boolean printWhenVerbose) {
- if (printWhenVerbose && origException != null && this.getVerbose().isTrue()) {
- origException.printStackTrace(getErrorStream());
- }
- return new RaiseException(new RubyNameError(
- this, getNameError(), message, name), true);
- }
- public RaiseException newLocalJumpError(String reason, IRubyObject exitValue, String message) {
- return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), message, reason, exitValue), true);
- }
- public RaiseException newRedoLocalJumpError() {
- return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), "unexpected redo", "redo", getNil()), true);
- }
- public RaiseException newLoadError(String message) {
- return newRaiseException(getLoadError(), message);
- }
- public RaiseException newFrozenError(String objectType) {
- // TODO: Should frozen error have its own distinct class? If not should more share?
- return newRaiseException(getTypeError(), "can't modify frozen " + objectType);
- }
- public RaiseException newSystemStackError(String message) {
- return newRaiseException(getSystemStackError(), message);
- }
- public RaiseException newSystemExit(int status) {
- return new RaiseException(RubySystemExit.newInstance(this, status));
- }
- public RaiseException newIOError(String message) {
- return newRaiseException(getIOError(), message);
- }
- public RaiseException newStandardError(String message) {
- return newRaiseException(getStandardError(), message);
- }
- public RaiseException newIOErrorFromException(IOException ioe) {
- // TODO: this is kinda gross
- if(ioe.getMessage() != null) {
- if (ioe.getMessage().equals("Broken pipe")) {
- throw newErrnoEPIPEError();
- } else if (ioe.getMessage().equals("Connection reset by peer")) {
- throw newErrnoECONNRESETError();
- }
- return newRaiseException(getIOError(), ioe.getMessage());
- } else {
- return newRaiseException(getIOError(), "IO Error");
- }
- }
- public RaiseException newTypeError(IRubyObject receivedObject, RubyClass expectedType) {
- return newRaiseException(getTypeError(), "wrong argument type " +
- receivedObject.getMetaClass().getRealClass() + " (expected " + expectedType + ")");
- }
- public RaiseException newEOFError() {
- return newRaiseException(getEOFError(), "End of file reached");
- }
- public RaiseException newEOFError(String message) {
- return newRaiseException(getEOFError(), message);
- }
- public RaiseException newZeroDivisionError() {
- return newRaiseException(getZeroDivisionError(), "divided by 0");
- }
- public RaiseException newFloatDomainError(String message){
- return newRaiseException(getFloatDomainError(), message);
- }
- /**
- * @param exceptionClass
- * @param message
- * @return
- */
- private RaiseException newRaiseException(RubyClass exceptionClass, String message) {
- RaiseException re = new RaiseException(this, exceptionClass, message, true);
- return re;
- }
- public RubySymbol.SymbolTable getSymbolTable() {
- return symbolTable;
- }
- public void setStackTraces(int stackTraces) {
- this.stackTraces = stackTraces;
- }
- public int getStackTraces() {
- return stackTraces;
- }
- public void setRandomSeed(long randomSeed) {
- this.randomSeed = randomSeed;
- }
- public long getRandomSeed() {
- return randomSeed;
- }
- public Random getRandom() {
- return random;
- }
- public ObjectSpace getObjectSpace() {
- return objectSpace;
- }
- public Map<Integer, WeakReference<ChannelDescriptor>> getDescriptors() {
- return descriptors;
- }
- public long incrementRandomSeedSequence() {
- return randomSeedSequence++;
- }
- public InputStream getIn() {
- return in;
- }
- public PrintStream getOut() {
- return out;
- }
- public PrintStream getErr() {
- return err;
- }
- public boolean isGlobalAbortOnExceptionEnabled() {
- return globalAbortOnExceptionEnabled;
- }
- public void setGlobalAbortOnExceptionEnabled(boolean enable) {
- globalAbortOnExceptionEnabled = enable;
- }
- public boolean isDoNotReverseLookupEnabled() {
- return doNotReverseLookupEnabled;
- }
- public void setDoNotReverseLookupEnabled(boolean b) {
- doNotReverseLookupEnabled = b;
- }
- private ThreadLocal<Map<Object, Object>> inspect = new ThreadLocal<Map<Object, Object>>();
- public void registerInspecting(Object obj) {
- Map<Object, Object> val = inspect.get();
- if (val == null) inspect.set(val = new IdentityHashMap<Object, Object>());
- val.put(obj, null);
- }
- public boolean isInspecting(Object obj) {
- Map<Object, Object> val = inspect.get();
- return val == null ? false : val.containsKey(obj);
- }
- public void unregisterInspecting(Object obj) {
- Map<Object, Object> val = inspect.get();
- if (val != null ) val.remove(obj);
- }
- public boolean isObjectSpaceEnabled() {
- return objectSpaceEnabled;
- }
- // The method is intentionally not public, since it typically should
- // not be used outside of the core.
- /* package-private */ void setObjectSpaceEnabled(boolean objectSpaceEnabled) {
- this.objectSpaceEnabled = objectSpaceEnabled;
- }
- public long getStartTime() {
- return startTime;
- }
- public Profile getProfile() {
- return profile;
- }
- public String getJRubyHome() {
- return config.getJRubyHome();
- }
- public void setJRubyHome(String home) {
- config.setJRubyHome(home);
- }
- public RubyInstanceConfig getInstanceConfig() {
- return config;
- }
- /** GET_VM_STATE_VERSION */
- public long getGlobalState() {
- synchronized(this) {
- return globalState;
- }
- }
- /** INC_VM_STATE_VERSION */
- public void incGlobalState() {
- synchronized(this) {
- globalState = (globalState+1) & 0x8fffffff;
- }
- }
- public static boolean isSecurityRestricted() {
- return securityRestricted;
- }
-
- public static void setSecurityRestricted(boolean restricted) {
- securityRestricted = restricted;
- }
-
- public POSIX getPosix() {
- return posix;
- }
-
- public void setRecordSeparatorVar(GlobalVariable recordSeparatorVar) {
- this.recordSeparatorVar = recordSeparatorVar;
- }
-
- public GlobalVariable getRecordSeparatorVar() {
- return recordSeparatorVar;
- }
-
- public Set<Script> getJittedMethods() {
- return jittedMethods;
- }
-
- public ExecutorService getExecutor() {
- return executor;
- }
- public Map<String, DateTimeZone> getLocalTimezoneCache() {
- return localTimeZoneCache;
- }
- private final CacheMap cacheMap;
- private final ThreadService threadService;
- private Hashtable<Object, Object> runtimeInformation;
-
- private POSIX posix;
- private int stackTraces = 0;
- private ObjectSpace objectSpace = new ObjectSpace();
- private final RubySymbol.SymbolTable symbolTable = new RubySymbol.SymbolTable(this);
- private Map<Integer, WeakReference<ChannelDescriptor>> descriptors = new ConcurrentHashMap<Integer, WeakReference<ChannelDescriptor>>();
- private long randomSeed = 0;
- private long randomSeedSequence = 0;
- private Random random = new Random();
- private List<EventHook> eventHooks = new Vector<EventHook>();
- private boolean hasEventHooks;
- private boolean globalAbortOnExceptionEnabled = false;
- private boolean doNotReverseLookupEnabled = false;
- private volatile boolean objectSpaceEnabled;
-
- private final Set<Script> jittedMethods = Collections.synchronizedSet(new WeakHashSet<Script>());
-
- private static ThreadLocal<Ruby> currentRuntime = new ThreadLocal<Ruby>();
-
- private long globalState = 1;
-
- private int safeLevel = -1;
- // Default objects
- private IRubyObject topSelf;
- private RubyNil nilObject;
- private RubyBoolean trueObject;
- private RubyBoolean falseObject;
- public final RubyFixnum[] fixnumCache = new RubyFixnum[256];
- private IRubyObject verbose;
- private IRubyObject debug;
-
- private RubyThreadGroup defaultThreadGroup;
- /**
- * All the core classes we keep hard references to. These are here largely
- * so that if someone redefines String or Array we won't start blowing up
- * creating strings and arrays internally. They also provide much faster
- * access than going through normal hash lookup on the Object class.
- */
- private RubyClass
- objectClass, moduleClass, classClass, nilClass, trueClass,
- falseClass, numericClass, floatClass, integerClass, fixnumClass,
- complexClass, rationalClass, enumeratorClass,
- arrayClass, hashClass, rangeClass, stringClass, symbolClass,
- procClass, bindingClass, methodClass, unboundMethodClass,
- matchDataClass, regexpClass, timeClass, bignumClass, dirClass,
- fileClass, fileStatClass, ioClass, threadClass, threadGroupClass,
- continuationClass, structClass, tmsStruct, passwdStruct,
- groupStruct, procStatusClass, exceptionClass, runtimeError, ioError,
- scriptError, nameError, nameErrorMessage, noMethodError, signalException,
- rangeError, dummyClass, systemExit, localJumpError, nativeException,
- systemCallError, fatal, interrupt, typeError, argumentError, indexError,
- syntaxError, standardError, loadError, notImplementedError, securityError, noMemoryError,
- regexpError, eofError, threadError, concurrencyError, systemStackError, zeroDivisionError, floatDomainError;
- /**
- * All the core modules we keep direct references to, for quick access and
- * to ensure they remain available.
- */
- private RubyModule
- kernelModule, comparableModule, enumerableModule, mathModule,
- marshalModule, etcModule, fileTestModule, gcModule,
- objectSpaceModule, processModule, procUIDModule, procGIDModule,
- procSysModule, precisionModule, errnoModule;
-
- // record separator var, to speed up io ops that use it
- private GlobalVariable recordSeparatorVar;
- // former java.lang.System concepts now internalized for MVM
- private String currentDirectory;
- private long startTime = System.currentTimeMillis();
- private RubyInstanceConfig config;
- private InputStream in;
- private PrintStream out;
- private PrintStream err;
- // Java support
- private JavaSupport javaSupport;
- private JRubyClassLoader jrubyClassLoader;
-
- // Management/monitoring
- private BeanManager beanManager;
-
- // Compilation
- private final JITCompiler jitCompiler;
- // Note: this field and the following static initializer
- // must be located be in this order!
- private volatile static boolean securityRestricted = false;
- static {
- if (SafePropertyAccessor.isSecurityProtected("jruby.reflection")) {
- // can't read non-standard properties
- securityRestricted = true;
- } else {
- SecurityManager sm = System.getSecurityManager();
- if (sm != null) {
- try {
- sm.checkCreateClassLoader();
- } catch (SecurityException se) {
- // can't create custom classloaders
- securityRestricted = true;
- }
- }
- }
- }
- private Parser parser = new Parser(this);
- private LoadService loadService;
- private GlobalVariables globalVariables = new GlobalVariables(this);
- private RubyWarnings warnings = new RubyWarnings(this);
- // Contains a list of all blocks (as Procs) that should be called when
- // the runtime environment exits.
- private Stack<RubyProc> atExitBlocks = new Stack<RubyProc>();
- private Profile profile;
- private KCode kcode = KCode.NONE;
- // Atomic integers for symbol and method IDs
- private AtomicInteger symbolLastId = new AtomicInteger(128);
- private AtomicInteger moduleLastId = new AtomicInteger(0);
- private Object respondToMethod;
- private Object objectToYamlMethod;
- private Map<String, DateTimeZone> localTimeZoneCache = new HashMap<String,DateTimeZone>();
- /**
- * A list of "external" finalizers (the ones, registered via ObjectSpace),
- * weakly referenced, to be executed on tearDown.
- */
- private Map<Finalizable, Object> finalizers;
-
- /**
- * A list of JRuby-internal finalizers, weakly referenced,
- * to be executed on tearDown.
- */
- private Map<Finalizable, Object> internalFinalizers;
- // mutex that controls modifications of user-defined finalizers
- private final Object finalizersMutex = new Object();
- // mutex that controls modifications of internal finalizers
- private final Object internalFinalizersMutex = new Object();
-
- // A thread pool to use for executing this runtime's Ruby threads
- private ExecutorService executor;
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jruby.anno.FrameField;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.ByteList;
- public class RubyArgsFile {
- private static final class ArgsFileData {
- private final Ruby runtime;
- public ArgsFileData(Ruby runtime) {
- this.runtime = runtime;
- }
- public IRubyObject currentFile;
- public int currentLineNumber;
- public boolean startedProcessing = false;
- public boolean finishedProcessing = false;
- public boolean nextArgsFile(ThreadContext context) {
- if (finishedProcessing) {
- return false;
- }
- RubyArray args = (RubyArray)runtime.getGlobalVariables().get("$*");
- if (args.getLength() == 0) {
- if (!startedProcessing) {
- currentFile = runtime.getGlobalVariables().get("$stdin");
- ((RubyString) runtime.getGlobalVariables().get("$FILENAME")).setValue(new ByteList(new byte[]{'-'}));
- currentLineNumber = 0;
- startedProcessing = true;
- return true;
- } else {
- finishedProcessing = true;
- return false;
- }
- }
- IRubyObject arg = args.shift();
- RubyString filename = (RubyString)((RubyObject)arg).to_s();
- ByteList filenameBytes = filename.getByteList();
- ((RubyString) runtime.getGlobalVariables().get("$FILENAME")).setValue(filenameBytes);
- if (filenameBytes.length() == 1 && filenameBytes.get(0) == '-') {
- currentFile = runtime.getGlobalVariables().get("$stdin");
- } else {
- currentFile = RubyFile.open(context, runtime.getFile(),
- new IRubyObject[] {filename.strDup(context.getRuntime())}, Block.NULL_BLOCK);
- }
-
- startedProcessing = true;
- return true;
- }
- public static ArgsFileData getDataFrom(IRubyObject recv) {
- ArgsFileData data = (ArgsFileData)recv.dataGetStruct();
- if(data == null) {
- data = new ArgsFileData(recv.getRuntime());
- recv.dataWrapStruct(data);
- }
- return data;
- }
- }
-
- public static void setCurrentLineNumber(IRubyObject recv, int newLineNumber) {
- ArgsFileData.getDataFrom(recv).currentLineNumber = newLineNumber;
- }
- public static void initArgsFile(Ruby runtime) {
- RubyObject argsFile = new RubyObject(runtime, runtime.getObject());
- runtime.getEnumerable().extend_object(argsFile);
-
- runtime.defineReadonlyVariable("$<", argsFile);
- runtime.defineGlobalConstant("ARGF", argsFile);
-
- RubyClass argfClass = argsFile.getMetaClass();
- argfClass.defineAnnotatedMethods(RubyArgsFile.class);
- runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"));
- }
- @JRubyMethod(name = {"fileno", "to_i"})
- public static IRubyObject fileno(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if (data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream");
- }
- return ((RubyIO) data.currentFile).fileno(context);
- }
- @JRubyMethod(name = "to_io")
- public static IRubyObject to_io(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if (data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream");
- }
- return data.currentFile;
- }
- public static IRubyObject internalGets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return context.getRuntime().getNil();
- }
-
- IRubyObject line = data.currentFile.callMethod(context, "gets", args);
-
- while (line instanceof RubyNil) {
- data.currentFile.callMethod(context, "close");
- if (!data.nextArgsFile(context)) {
- data.currentFile = null;
- return line;
- }
- line = data.currentFile.callMethod(context, "gets", args);
- }
-
- data.currentLineNumber++;
- context.getRuntime().getGlobalVariables().set("$.", context.getRuntime().newFixnum(data.currentLineNumber));
-
- return line;
- }
-
- // ARGF methods
- /** Read a line.
- *
- */
- @JRubyMethod(name = "gets", optional = 1, frame = true, writes = FrameField.LASTLINE)
- public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- IRubyObject result = internalGets(context, recv, args);
- if (!result.isNil()) {
- context.getCurrentFrame().setLastLine(result);
- }
- return result;
- }
-
- /** Read a line.
- *
- */
- @JRubyMethod(name = "readline", optional = 1, frame = true, writes = FrameField.LASTLINE)
- public static IRubyObject readline(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- IRubyObject line = gets(context, recv, args);
- if (line.isNil()) {
- throw context.getRuntime().newEOFError();
- }
-
- return line;
- }
- @JRubyMethod(name = "readlines", optional = 1, frame = true)
- public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- IRubyObject[] separatorArgument;
- if (args.length > 0) {
- if (!context.getRuntime().getNilClass().isInstance(args[0]) &&
- !context.getRuntime().getString().isInstance(args[0])) {
- throw context.getRuntime().newTypeError(args[0], context.getRuntime().getString());
- }
- separatorArgument = new IRubyObject[] { args[0] };
- } else {
- separatorArgument = IRubyObject.NULL_ARRAY;
- }
- RubyArray result = context.getRuntime().newArray();
- IRubyObject line;
- while (! (line = internalGets(context, recv, separatorArgument)).isNil()) {
- result.append(line);
- }
- return result;
- }
-
- @JRubyMethod(name = "each_byte", frame = true)
- public static IRubyObject each_byte(ThreadContext context, IRubyObject recv, Block block) {
- IRubyObject bt;
- while(!(bt = getc(context, recv)).isNil()) {
- block.yield(context, bt);
- }
- return recv;
- }
- /** Invoke a block for each line.
- *
- */
- @JRubyMethod(name = "each_line", alias = {"each"}, optional = 1, frame = true)
- public static IRubyObject each_line(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- IRubyObject nextLine = internalGets(context, recv, args);
-
- while (!nextLine.isNil()) {
- block.yield(context, nextLine);
- nextLine = internalGets(context, recv, args);
- }
-
- return recv;
- }
- @JRubyMethod(name = "file")
- public static IRubyObject file(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return context.getRuntime().getNil();
- }
- return data.currentFile;
- }
- @JRubyMethod(name = "skip")
- public static IRubyObject skip(IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- data.currentFile = null;
- return recv;
- }
- @JRubyMethod(name = "close")
- public static IRubyObject close(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return recv;
- }
- data.currentFile = null;
- data.currentLineNumber = 0;
- return recv;
- }
- @JRubyMethod(name = "closed?")
- public static IRubyObject closed_p(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return recv;
- }
- return ((RubyIO)data.currentFile).closed_p(context);
- }
- @JRubyMethod(name = "binmode")
- public static IRubyObject binmode(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream");
- }
-
- return ((RubyIO)data.currentFile).binmode();
- }
- @JRubyMethod(name = "lineno")
- public static IRubyObject lineno(ThreadContext context, IRubyObject recv) {
- return context.getRuntime().newFixnum(ArgsFileData.getDataFrom(recv).currentLineNumber);
- }
- @JRubyMethod(name = "tell", alias = {"pos"})
- public static IRubyObject tell(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream to tell");
- }
- return ((RubyIO)data.currentFile).pos(context);
- }
- @JRubyMethod(name = "rewind")
- public static IRubyObject rewind(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream to rewind");
- }
- return ((RubyIO)data.currentFile).rewind(context);
- }
- @JRubyMethod(name = {"eof", "eof?"})
- public static IRubyObject eof(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if (data.currentFile == null && !data.nextArgsFile(context)) {
- return context.getRuntime().getTrue();
- }
- return ((RubyIO) data.currentFile).eof_p(context);
- }
- @JRubyMethod(name = "pos=", required = 1)
- public static IRubyObject set_pos(ThreadContext context, IRubyObject recv, IRubyObject offset) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream to set position");
- }
- return ((RubyIO)data.currentFile).pos_set(context, offset);
- }
- @JRubyMethod(name = "seek", required = 1, optional = 1)
- public static IRubyObject seek(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- throw context.getRuntime().newArgumentError("no stream to seek");
- }
- return ((RubyIO)data.currentFile).seek(context, args);
- }
- @JRubyMethod(name = "lineno=", required = 1)
- public static IRubyObject set_lineno(ThreadContext context, IRubyObject recv, IRubyObject line) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- data.currentLineNumber = RubyNumeric.fix2int(line);
- return context.getRuntime().getNil();
- }
- @JRubyMethod(name = "readchar")
- public static IRubyObject readchar(ThreadContext context, IRubyObject recv) {
- IRubyObject c = getc(context, recv);
-
- if(c.isNil()) throw context.getRuntime().newEOFError();
- return c;
- }
- @JRubyMethod(name = "getc")
- public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- IRubyObject bt;
- while(true) {
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return context.getRuntime().getNil();
- }
- if(!(data.currentFile instanceof RubyFile)) {
- bt = data.currentFile.callMethod(context,"getc");
- } else {
- bt = ((RubyIO)data.currentFile).getc();
- }
- if(bt.isNil()) {
- data.currentFile = null;
- continue;
- }
- return bt;
- }
- }
- @JRubyMethod(name = "read", optional = 2)
- public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- ArgsFileData data = ArgsFileData.getDataFrom(recv);
- IRubyObject tmp, str, length;
- long len = 0;
- if(args.length > 0) {
- length = args[0];
- if(args.length > 1) {
- str = args[1];
- } else {
- str = runtime.getNil();
- }
- } else {
- length = str = runtime.getNil();
- }
- if(!length.isNil()) {
- len = RubyNumeric.num2long(length);
- }
- if(!str.isNil()) {
- str = str.convertToString();
- ((RubyString)str).modify();
- ((RubyString)str).getByteList().length(0);
- args[1] = runtime.getNil();
- }
- while(true) {
- if(data.currentFile == null && !data.nextArgsFile(context)) {
- return str;
- }
- if(!(data.currentFile instanceof RubyIO)) {
- tmp = data.currentFile.callMethod(context, "read", args);
- } else {
- tmp = ((RubyIO)data.currentFile).read(args);
- }
- if(str.isNil()) {
- str = tmp;
- } else if(!tmp.isNil()) {
- ((RubyString)str).append(tmp);
- }
- if(tmp.isNil() || length.isNil()) {
- data.currentFile = null;
- continue;
- } else if(args.length >= 1) {
- if(((RubyString)str).getByteList().length() < len) {
- len -= ((RubyString)str).getByteList().length();
- args[0] = runtime.newFixnum(len);
- continue;
- }
- }
- return str;
- }
- }
- @JRubyMethod(name = "filename", alias = {"path"})
- public static RubyString filename(ThreadContext context, IRubyObject recv) {
- return (RubyString) context.getRuntime().getGlobalVariables().get("$FILENAME");
- }
- @JRubyMethod(name = "to_s")
- public static IRubyObject to_s(IRubyObject recv) {
- return recv.getRuntime().newString("ARGF");
- }
- }
- /*
- **** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
- * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Ola Bini <Ola.Bini@ki.se>
- * Copyright (C) 2006 Daniel Steer <damian.steer@hp.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.lang.reflect.Array;
- import java.io.IOException;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Comparator;
- import java.util.Iterator;
- import java.util.List;
- import java.util.ListIterator;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyClass;
- import org.jruby.common.IRubyWarnings.ID;
- import org.jruby.javasupport.JavaUtil;
- import org.jruby.runtime.Arity;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.MethodIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.Visibility;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.marshal.MarshalStream;
- import org.jruby.runtime.marshal.UnmarshalStream;
- import org.jruby.util.ByteList;
- import org.jruby.util.Pack;
- /**
- * The implementation of the built-in class Array in Ruby.
- *
- * Concurrency: no synchronization is required among readers, but
- * all users must synchronize externally with writers.
- *
- */
- @JRubyClass(name="Array")
- public class RubyArray extends RubyObject implements List {
- public static RubyClass createArrayClass(Ruby runtime) {
- RubyClass arrayc = runtime.defineClass("Array", runtime.getObject(), ARRAY_ALLOCATOR);
- runtime.setArray(arrayc);
- arrayc.index = ClassIndex.ARRAY;
- arrayc.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyArray;
- }
- };
- arrayc.includeModule(runtime.getEnumerable());
- arrayc.defineAnnotatedMethods(RubyArray.class);
- return arrayc;
- }
- private static ObjectAllocator ARRAY_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyArray(runtime, klass);
- }
- };
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.ARRAY;
- }
- private final void concurrentModification() {
- throw getRuntime().newConcurrencyError("Detected invalid array contents due to unsynchronized modifications with concurrent users");
- }
- /** rb_ary_s_create
- *
- */
- @JRubyMethod(name = "[]", rest = true, frame = true, meta = true)
- public static IRubyObject create(IRubyObject klass, IRubyObject[] args, Block block) {
- RubyArray arr = (RubyArray) ((RubyClass) klass).allocate();
- arr.callInit(IRubyObject.NULL_ARRAY, block);
-
- if (args.length > 0) {
- arr.alloc(args.length);
- System.arraycopy(args, 0, arr.values, 0, args.length);
- arr.realLength = args.length;
- }
- return arr;
- }
- /** rb_ary_new2
- *
- */
- public static final RubyArray newArray(final Ruby runtime, final long len) {
- return new RubyArray(runtime, len);
- }
- public static final RubyArray newArrayLight(final Ruby runtime, final long len) {
- return new RubyArray(runtime, len, false);
- }
- /** rb_ary_new
- *
- */
- public static final RubyArray newArray(final Ruby runtime) {
- return new RubyArray(runtime, ARRAY_DEFAULT_SIZE);
- }
- /** rb_ary_new
- *
- */
- public static final RubyArray newArrayLight(final Ruby runtime) {
- /* Ruby arrays default to holding 16 elements, so we create an
- * ArrayList of the same size if we're not told otherwise
- */
- RubyArray arr = new RubyArray(runtime, false);
- arr.alloc(ARRAY_DEFAULT_SIZE);
- return arr;
- }
- public static RubyArray newArray(Ruby runtime, IRubyObject obj) {
- return new RubyArray(runtime, new IRubyObject[] { obj });
- }
- public static RubyArray newArrayLight(Ruby runtime, IRubyObject obj) {
- return new RubyArray(runtime, new IRubyObject[] { obj }, false);
- }
- /** rb_assoc_new
- *
- */
- public static RubyArray newArray(Ruby runtime, IRubyObject car, IRubyObject cdr) {
- return new RubyArray(runtime, new IRubyObject[] { car, cdr });
- }
-
- public static RubyArray newEmptyArray(Ruby runtime) {
- return new RubyArray(runtime, NULL_ARRAY);
- }
- /** rb_ary_new4, rb_ary_new3
- *
- */
- public static RubyArray newArray(Ruby runtime, IRubyObject[] args) {
- RubyArray arr = new RubyArray(runtime, args.length);
- System.arraycopy(args, 0, arr.values, 0, args.length);
- arr.realLength = args.length;
- return arr;
- }
-
- public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args) {
- return new RubyArray(runtime, args);
- }
-
- public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args, int begin) {
- return new RubyArray(runtime, args, begin);
- }
-
- public static RubyArray newArrayNoCopyLight(Ruby runtime, IRubyObject[] args) {
- RubyArray arr = new RubyArray(runtime, false);
- arr.values = args;
- arr.realLength = args.length;
- return arr;
- }
- public static RubyArray newArray(Ruby runtime, Collection collection) {
- RubyArray arr = new RubyArray(runtime, collection.size());
- collection.toArray(arr.values);
- arr.realLength = arr.values.length;
- return arr;
- }
- public static final int ARRAY_DEFAULT_SIZE = 16;
- // volatile to ensure that initial nil-fill is visible to other threads
- private volatile IRubyObject[] values;
- private static final int TMPLOCK_ARR_F = 1 << 9;
- private static final int TMPLOCK_OR_FROZEN_ARR_F = TMPLOCK_ARR_F | FROZEN_F;
- private volatile boolean isShared = false;
- private int begin = 0;
- private int realLength = 0;
- /*
- * plain internal array assignment
- */
- private RubyArray(Ruby runtime, IRubyObject[] vals) {
- super(runtime, runtime.getArray());
- values = vals;
- realLength = vals.length;
- }
- /*
- * plain internal array assignment
- */
- private RubyArray(Ruby runtime, IRubyObject[] vals, boolean objectSpace) {
- super(runtime, runtime.getArray(), objectSpace);
- values = vals;
- realLength = vals.length;
- }
- /*
- * plain internal array assignment
- */
- private RubyArray(Ruby runtime, IRubyObject[] vals, int begin) {
- super(runtime, runtime.getArray());
- this.values = vals;
- this.begin = begin;
- this.realLength = vals.length - begin;
- this.isShared = true;
- }
-
- /* rb_ary_new2
- * just allocates the internal array
- */
- private RubyArray(Ruby runtime, long length) {
- super(runtime, runtime.getArray());
- checkLength(length);
- alloc((int) length);
- }
- private RubyArray(Ruby runtime, long length, boolean objectspace) {
- super(runtime, runtime.getArray(), objectspace);
- checkLength(length);
- alloc((int)length);
- }
- /* rb_ary_new3, rb_ary_new4
- * allocates the internal array of size length and copies the 'length' elements
- */
- public RubyArray(Ruby runtime, long length, IRubyObject[] vals) {
- super(runtime, runtime.getArray());
- checkLength(length);
- int ilength = (int) length;
- alloc(ilength);
- if (ilength > 0 && vals.length > 0) System.arraycopy(vals, 0, values, 0, ilength);
- realLength = ilength;
- }
- /* NEWOBJ and OBJSETUP equivalent
- * fastest one, for shared arrays, optional objectspace
- */
- private RubyArray(Ruby runtime, boolean objectSpace) {
- super(runtime, runtime.getArray(), objectSpace);
- }
- private RubyArray(Ruby runtime) {
- super(runtime, runtime.getArray());
- alloc(ARRAY_DEFAULT_SIZE);
- }
- public RubyArray(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- alloc(ARRAY_DEFAULT_SIZE);
- }
-
- /* Array constructors taking the MetaClass to fulfil MRI Array subclass behaviour
- *
- */
- private RubyArray(Ruby runtime, RubyClass klass, int length) {
- super(runtime, klass);
- alloc(length);
- }
-
- private RubyArray(Ruby runtime, RubyClass klass, long length) {
- super(runtime, klass);
- checkLength(length);
- alloc((int)length);
- }
- private RubyArray(Ruby runtime, RubyClass klass, long length, boolean objectspace) {
- super(runtime, klass, objectspace);
- checkLength(length);
- alloc((int)length);
- }
- private RubyArray(Ruby runtime, RubyClass klass, boolean objectSpace) {
- super(runtime, klass, objectSpace);
- }
-
- private RubyArray(Ruby runtime, RubyClass klass, RubyArray original) {
- super(runtime, klass);
- realLength = original.realLength;
- alloc(realLength);
- try {
- System.arraycopy(original.values, original.begin, values, 0, realLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
-
- private final IRubyObject[] reserve(int length) {
- final IRubyObject[] arr = new IRubyObject[length];
- Arrays.fill(arr, getRuntime().getNil());
- return arr;
- }
- private final void alloc(int length) {
- final IRubyObject[] newValues = new IRubyObject[length];
- Arrays.fill(newValues, getRuntime().getNil());
- values = newValues;
- }
- private final void realloc(int newLength) {
- IRubyObject[] reallocated = new IRubyObject[newLength];
- Arrays.fill(reallocated, getRuntime().getNil());
- try {
- System.arraycopy(values, 0, reallocated, 0, newLength > realLength ? realLength : newLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- values = reallocated;
- }
- private final void checkLength(long length) {
- if (length < 0) {
- throw getRuntime().newArgumentError("negative array size (or size too big)");
- }
- if (length >= Integer.MAX_VALUE) {
- throw getRuntime().newArgumentError("array size too big");
- }
- }
- /** Getter for property list.
- * @return Value of property list.
- */
- public List getList() {
- return Arrays.asList(toJavaArray());
- }
- public int getLength() {
- return realLength;
- }
- public IRubyObject[] toJavaArray() {
- IRubyObject[] copy = reserve(realLength);
- try {
- System.arraycopy(values, begin, copy, 0, realLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- return copy;
- }
-
- public IRubyObject[] toJavaArrayUnsafe() {
- return !isShared ? values : toJavaArray();
- }
- public IRubyObject[] toJavaArrayMaybeUnsafe() {
- return (!isShared && begin == 0 && values.length == realLength) ? values : toJavaArray();
- }
- /** rb_ary_make_shared
- *
- */
- private final RubyArray makeShared(int beg, int len, RubyClass klass) {
- return makeShared(beg, len, klass, klass.getRuntime().isObjectSpaceEnabled());
- }
-
- /** rb_ary_make_shared
- *
- */
- private final RubyArray makeShared(int beg, int len, RubyClass klass, boolean objectSpace) {
- RubyArray sharedArray = new RubyArray(getRuntime(), klass, objectSpace);
- isShared = true;
- sharedArray.values = values;
- sharedArray.isShared = true;
- sharedArray.begin = beg;
- sharedArray.realLength = len;
- return sharedArray;
- }
- /** rb_ary_modify_check
- *
- */
- private final void modifyCheck() {
- if ((flags & TMPLOCK_OR_FROZEN_ARR_F) != 0) {
- if ((flags & FROZEN_F) != 0) throw getRuntime().newFrozenError("array");
- if ((flags & TMPLOCK_ARR_F) != 0) throw getRuntime().newTypeError("can't modify array during iteration");
- }
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
- throw getRuntime().newSecurityError("Insecure: can't modify array");
- }
- }
- /** rb_ary_modify
- *
- */
- private final void modify() {
- modifyCheck();
- if (isShared) {
- IRubyObject[] vals = reserve(realLength);
- isShared = false;
- try {
- System.arraycopy(values, begin, vals, 0, realLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- begin = 0;
- values = vals;
- }
- }
- /* ================
- * Instance Methods
- * ================
- */
- /** rb_ary_initialize
- *
- */
- @JRubyMethod(name = "initialize", required = 0, optional = 2, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
- int argc = args.length;
- Ruby runtime = getRuntime();
- if (argc == 0) {
- modifyCheck();
- realLength = 0;
- if (block.isGiven()) runtime.getWarnings().warn(ID.BLOCK_UNUSED, "given block not used");
- return this;
- }
- if (argc == 1 && !(args[0] instanceof RubyFixnum)) {
- IRubyObject val = args[0].checkArrayType();
- if (!val.isNil()) {
- replace(val);
- return this;
- }
- }
- long len = RubyNumeric.num2long(args[0]);
- if (len < 0) throw runtime.newArgumentError("negative array size");
- if (len >= Integer.MAX_VALUE) throw runtime.newArgumentError("array size too big");
- int ilen = (int) len;
- modify();
- if (ilen > values.length) values = reserve(ilen);
- if (block.isGiven()) {
- if (argc == 2) {
- runtime.getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
- }
- for (int i = 0; i < ilen; i++) {
- store(i, block.yield(context, new RubyFixnum(runtime, i)));
- realLength = i + 1;
- }
- } else {
- try {
- Arrays.fill(values, 0, ilen, (argc == 2) ? args[1] : runtime.getNil());
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength = ilen;
- }
- return this;
- }
- /** rb_ary_initialize_copy
- *
- */
- @JRubyMethod(name = {"initialize_copy"}, required = 1, visibility=Visibility.PRIVATE)
- @Override
- public IRubyObject initialize_copy(IRubyObject orig) {
- return this.replace(orig);
- }
-
- /** rb_ary_replace
- *
- */
- @JRubyMethod(name = {"replace"}, required = 1)
- public IRubyObject replace(IRubyObject orig) {
- modifyCheck();
- RubyArray origArr = orig.convertToArray();
- if (this == orig) return this;
- origArr.isShared = true;
- isShared = true;
- values = origArr.values;
- realLength = origArr.realLength;
- begin = origArr.begin;
- return this;
- }
- /** rb_ary_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- @Override
- public IRubyObject to_s() {
- if (realLength == 0) return RubyString.newEmptyString(getRuntime());
- return join(getRuntime().getCurrentContext(), getRuntime().getGlobalVariables().get("$,"));
- }
-
- public boolean includes(ThreadContext context, IRubyObject item) {
- int begin = this.begin;
-
- for (int i = begin; i < begin + realLength; i++) {
- final IRubyObject value;
- try {
- value = values[i];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- continue;
- }
- if (equalInternal(context, value, item)) return true;
- }
-
- return false;
- }
- /** rb_ary_hash
- *
- */
- @JRubyMethod(name = "hash")
- public RubyFixnum hash(ThreadContext context) {
- int h = realLength;
- Ruby runtime = getRuntime();
- int begin = this.begin;
- for (int i = begin; i < begin + realLength; i++) {
- h = (h << 1) | (h < 0 ? 1 : 0);
- final IRubyObject value;
- try {
- value = values[i];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- continue;
- }
- h ^= RubyNumeric.num2long(value.callMethod(context, MethodIndex.HASH, "hash"));
- }
- return runtime.newFixnum(h);
- }
- /** rb_ary_store
- *
- */
- public final IRubyObject store(long index, IRubyObject value) {
- if (index < 0) {
- index += realLength;
- if (index < 0) {
- throw getRuntime().newIndexError("index " + (index - realLength) + " out of array");
- }
- }
- modify();
- if (index >= realLength) {
- if (index >= values.length) {
- long newLength = values.length >> 1;
- if (newLength < ARRAY_DEFAULT_SIZE) newLength = ARRAY_DEFAULT_SIZE;
- newLength += index;
- if (index >= Integer.MAX_VALUE || newLength >= Integer.MAX_VALUE) {
- throw getRuntime().newArgumentError("index too big");
- }
- realloc((int) newLength);
- }
-
- realLength = (int) index + 1;
- }
- try {
- values[(int) index] = value;
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- return value;
- }
- /** rb_ary_elt
- *
- */
- private final IRubyObject elt(long offset) {
- if (offset < 0 || offset >= realLength) {
- return getRuntime().getNil();
- }
- try {
- return values[begin + (int)offset];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- }
- /** rb_ary_entry
- *
- */
- public final IRubyObject entry(long offset) {
- return (offset < 0 ) ? elt(offset + realLength) : elt(offset);
- }
- /** rb_ary_entry
- *
- */
- public final IRubyObject entry(int offset) {
- return (offset < 0 ) ? elt(offset + realLength) : elt(offset);
- }
- public final IRubyObject eltInternal(int offset) {
- return values[begin + offset];
- }
-
- public final IRubyObject eltInternalSet(int offset, IRubyObject item) {
- return values[begin + offset] = item;
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 1:
- return fetch(context, args[0], block);
- case 2:
- return fetch(context, args[0], args[1], block);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
- /** rb_ary_fetch
- *
- */
- @JRubyMethod(name = "fetch", frame = true)
- public IRubyObject fetch(ThreadContext context, IRubyObject arg0, Block block) {
- long index = RubyNumeric.num2long(arg0);
- if (index < 0) index += realLength;
- if (index < 0 || index >= realLength) {
- if (block.isGiven()) return block.yield(context, arg0);
- throw getRuntime().newIndexError("index " + index + " out of array");
- }
-
- try {
- return values[begin + (int) index];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- }
- /** rb_ary_fetch
- *
- */
- @JRubyMethod(name = "fetch", frame = true)
- public IRubyObject fetch(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- if (block.isGiven()) getRuntime().getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
- long index = RubyNumeric.num2long(arg0);
- if (index < 0) index += realLength;
- if (index < 0 || index >= realLength) {
- if (block.isGiven()) return block.yield(context, arg0);
- return arg1;
- }
-
- try {
- return values[begin + (int) index];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- }
- /** rb_ary_to_ary
- *
- */
- private static RubyArray aryToAry(IRubyObject obj) {
- if (obj instanceof RubyArray) return (RubyArray) obj;
- if (obj.respondsTo("to_ary")) return obj.convertToArray();
- RubyArray arr = new RubyArray(obj.getRuntime(), false); // possibly should not in object space
- arr.alloc(1);
- arr.values[0] = obj;
- arr.realLength = 1;
- return arr;
- }
- /** rb_ary_splice
- *
- */
- private final void splice(long beg, long len, IRubyObject rpl) {
- if (len < 0) throw getRuntime().newIndexError("negative length (" + len + ")");
- if (beg < 0) {
- beg += realLength;
- if (beg < 0) {
- beg -= realLength;
- throw getRuntime().newIndexError("index " + beg + " out of array");
- }
- }
-
- final RubyArray rplArr;
- final int rlen;
- if (rpl == null || rpl.isNil()) {
- rplArr = null;
- rlen = 0;
- } else {
- rplArr = aryToAry(rpl);
- rlen = rplArr.realLength;
- }
- modify();
- if (beg >= realLength) {
- len = beg + rlen;
- if (len >= values.length) {
- int tryNewLength = values.length + (values.length >> 1);
- realloc(len > tryNewLength ? (int)len : tryNewLength);
- }
- realLength = (int) len;
- } else {
- if (beg + len > realLength) len = realLength - beg;
- long alen = realLength + rlen - len;
- if (alen >= values.length) {
- int tryNewLength = values.length + (values.length >> 1);
- realloc(alen > tryNewLength ? (int)alen : tryNewLength);
- }
- if (len != rlen) {
- try {
- System.arraycopy(values, (int) (beg + len), values, (int) beg + rlen, realLength - (int) (beg + len));
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength = (int) alen;
- }
- }
- if (rlen > 0) {
- try {
- System.arraycopy(rplArr.values, rplArr.begin, values, (int) beg, rlen);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
- }
- /** rb_ary_splice
- *
- */
- private final void spliceOne(long beg, long len, IRubyObject rpl) {
- if (len < 0) throw getRuntime().newIndexError("negative length (" + len + ")");
- if (beg < 0) {
- beg += realLength;
- if (beg < 0) {
- beg -= realLength;
- throw getRuntime().newIndexError("index " + beg + " out of array");
- }
- }
- modify();
- if (beg >= realLength) {
- len = beg + 1;
- if (len >= values.length) {
- int tryNewLength = values.length + (values.length >> 1);
- realloc(len > tryNewLength ? (int)len : tryNewLength);
- }
- realLength = (int) len;
- } else {
- if (beg + len > realLength) len = realLength - beg;
- int alen = realLength + 1 - (int)len;
- if (alen >= values.length) {
- int tryNewLength = values.length + (values.length >> 1);
- realloc(alen > tryNewLength ? alen : tryNewLength);
- }
- if (len != 1) {
- try {
- System.arraycopy(values, (int) (beg + len), values, (int) beg + 1, realLength - (int) (beg + len));
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength = alen;
- }
- }
- try {
- values[(int)beg] = rpl;
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
- @JRubyMethod
- public IRubyObject insert() {
- throw getRuntime().newArgumentError(0, 1);
- }
- /** rb_ary_insert
- *
- */
- @JRubyMethod
- public IRubyObject insert(IRubyObject arg) {
- return this;
- }
- /** rb_ary_insert
- *
- */
- @JRubyMethod
- public IRubyObject insert(IRubyObject arg1, IRubyObject arg2) {
- long pos = RubyNumeric.num2long(arg1);
- if (pos == -1) pos = realLength;
- if (pos < 0) pos++;
-
- spliceOne(pos, 0, arg2); // rb_ary_new4
-
- return this;
- }
- /** rb_ary_insert
- *
- */
- @JRubyMethod(name = "insert", required = 1, rest = true)
- public IRubyObject insert(IRubyObject[] args) {
- if (args.length == 1) return this;
- long pos = RubyNumeric.num2long(args[0]);
- if (pos == -1) pos = realLength;
- if (pos < 0) pos++;
- RubyArray inserted = new RubyArray(getRuntime(), false);
- inserted.values = args;
- inserted.begin = 1;
- inserted.realLength = args.length - 1;
-
- splice(pos, 0, inserted); // rb_ary_new4
-
- return this;
- }
- /** rb_ary_dup
- *
- */
- public final RubyArray aryDup() {
- RubyArray dup = new RubyArray(getRuntime(), getMetaClass(), this);
- dup.flags |= flags & TAINTED_F; // from DUP_SETUP
- // rb_copy_generic_ivar from DUP_SETUP here ...unlikely..
- return dup;
- }
- /** rb_ary_transpose
- *
- */
- @JRubyMethod(name = "transpose")
- public RubyArray transpose() {
- RubyArray tmp, result = null;
- int alen = realLength;
- if (alen == 0) return aryDup();
-
- Ruby runtime = getRuntime();
- int elen = -1;
- int end = begin + alen;
- for (int i = begin; i < end; i++) {
- tmp = elt(i).convertToArray();
- if (elen < 0) {
- elen = tmp.realLength;
- result = new RubyArray(runtime, elen);
- for (int j = 0; j < elen; j++) {
- result.store(j, new RubyArray(runtime, alen));
- }
- } else if (elen != tmp.realLength) {
- throw runtime.newIndexError("element size differs (" + tmp.realLength
- + " should be " + elen + ")");
- }
- for (int j = 0; j < elen; j++) {
- ((RubyArray) result.elt(j)).store(i - begin, tmp.elt(j));
- }
- }
- return result;
- }
- /** rb_values_at (internal)
- *
- */
- private final IRubyObject values_at(long olen, IRubyObject[] args) {
- RubyArray result = new RubyArray(getRuntime(), args.length);
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof RubyFixnum) {
- result.append(entry(((RubyFixnum)args[i]).getLongValue()));
- continue;
- }
- long beglen[];
- if (!(args[i] instanceof RubyRange)) {
- } else if ((beglen = ((RubyRange) args[i]).begLen(olen, 0)) == null) {
- continue;
- } else {
- int beg = (int) beglen[0];
- int len = (int) beglen[1];
- int end = begin + len;
- for (int j = begin; j < end; j++) {
- result.append(entry(j + beg));
- }
- continue;
- }
- result.append(entry(RubyNumeric.num2long(args[i])));
- }
- return result;
- }
- /** rb_values_at
- *
- */
- @JRubyMethod(name = "values_at", rest = true)
- public IRubyObject values_at(IRubyObject[] args) {
- return values_at(realLength, args);
- }
- /** rb_ary_subseq
- *
- */
- public IRubyObject subseq(long beg, long len) {
- if (beg > realLength || beg < 0 || len < 0) return getRuntime().getNil();
- if (beg + len > realLength) {
- len = realLength - beg;
-
- if (len < 0) len = 0;
- }
-
- if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0);
- return makeShared(begin + (int) beg, (int) len, getMetaClass());
- }
- /** rb_ary_subseq
- *
- */
- public IRubyObject subseqLight(long beg, long len) {
- if (beg > realLength || beg < 0 || len < 0) return getRuntime().getNil();
- if (beg + len > realLength) {
- len = realLength - beg;
-
- if (len < 0) len = 0;
- }
-
- if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0, false);
- return makeShared(begin + (int) beg, (int) len, getMetaClass(), false);
- }
- /** rb_ary_length
- *
- */
- @JRubyMethod(name = "length", alias = "size")
- public RubyFixnum length() {
- return getRuntime().newFixnum(realLength);
- }
- /** rb_ary_push - specialized rb_ary_store
- *
- */
- @JRubyMethod(name = "<<", required = 1)
- public RubyArray append(IRubyObject item) {
- modify();
-
- if (realLength == values.length) {
- if (realLength == Integer.MAX_VALUE) throw getRuntime().newArgumentError("index too big");
-
- long newLength = values.length + (values.length >> 1);
- if ( newLength > Integer.MAX_VALUE ) {
- newLength = Integer.MAX_VALUE;
- }else if ( newLength < ARRAY_DEFAULT_SIZE ) {
- newLength = ARRAY_DEFAULT_SIZE;
- }
- realloc((int) newLength);
- }
-
- try {
- values[realLength++] = item;
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- return this;
- }
- /** rb_ary_push_m
- * FIXME: Whis is this named "push_m"?
- */
- @JRubyMethod(name = "push", rest = true)
- public RubyArray push_m(IRubyObject[] items) {
- for (int i = 0; i < items.length; i++) {
- append(items[i]);
- }
-
- return this;
- }
- /** rb_ary_pop
- *
- */
- @JRubyMethod(name = "pop")
- public IRubyObject pop() {
- modifyCheck();
-
- if (realLength == 0) return getRuntime().getNil();
- if (isShared) {
- try {
- return values[begin + --realLength];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- } else {
- int index = begin + --realLength;
- try {
- final IRubyObject obj = values[index];
- values[index] = getRuntime().getNil();
- return obj;
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- }
- }
- /** rb_ary_shift
- *
- */
- @JRubyMethod(name = "shift")
- public IRubyObject shift() {
- modify();
- if (realLength == 0) return getRuntime().getNil();
- final IRubyObject obj;
- try {
- obj = values[begin];
- values[begin] = getRuntime().getNil();
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return getRuntime().getNil();
- }
- isShared = true;
- begin++;
- realLength--;
- return obj;
- }
- /** rb_ary_unshift
- *
- */
- public RubyArray unshift(IRubyObject item) {
- modify();
- if (realLength == values.length) {
- int newLength = values.length >> 1;
- if (newLength < ARRAY_DEFAULT_SIZE) newLength = ARRAY_DEFAULT_SIZE;
- newLength += values.length;
- realloc(newLength);
- }
- try {
- System.arraycopy(values, 0, values, 1, realLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength++;
- values[0] = item;
- return this;
- }
- /** rb_ary_unshift_m
- *
- */
- @JRubyMethod(name = "unshift", rest = true)
- public RubyArray unshift_m(IRubyObject[] items) {
- long len = realLength;
- if (items.length == 0) return this;
- store(len + items.length - 1, getRuntime().getNil());
- try {
- // it's safe to use zeroes here since modified by store()
- System.arraycopy(values, 0, values, items.length, (int) len);
- System.arraycopy(items, 0, values, 0, items.length);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
-
- return this;
- }
- /** rb_ary_includes
- *
- */
- @JRubyMethod(name = "include?", required = 1)
- public RubyBoolean include_p(ThreadContext context, IRubyObject item) {
- return context.getRuntime().newBoolean(includes(context, item));
- }
- /** rb_ary_frozen_p
- *
- */
- @JRubyMethod(name = "frozen?")
- @Override
- public RubyBoolean frozen_p(ThreadContext context) {
- return context.getRuntime().newBoolean(isFrozen() || (flags & TMPLOCK_ARR_F) != 0);
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject aref(IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return aref(args[0]);
- case 2:
- return aref(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
- /** rb_ary_aref
- */
- @JRubyMethod(name = {"[]", "slice"})
- public IRubyObject aref(IRubyObject arg0) {
- if (arg0 instanceof RubyFixnum) return entry(((RubyFixnum)arg0).getLongValue());
- if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
-
- long[] beglen;
- if (!(arg0 instanceof RubyRange)) {
- } else if ((beglen = ((RubyRange) arg0).begLen(realLength, 0)) == null) {
- return getRuntime().getNil();
- } else {
- return subseq(beglen[0], beglen[1]);
- }
- return entry(RubyNumeric.num2long(arg0));
- }
- /** rb_ary_aref
- */
- @JRubyMethod(name = {"[]", "slice"})
- public IRubyObject aref(IRubyObject arg0, IRubyObject arg1) {
- if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
- long beg = RubyNumeric.num2long(arg0);
- if (beg < 0) beg += realLength;
- return subseq(beg, RubyNumeric.num2long(arg1));
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject aset(IRubyObject[] args) {
- switch (args.length) {
- case 2:
- return aset(args[0], args[1]);
- case 3:
- return aset(args[0], args[1], args[2]);
- default:
- throw getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 2)");
- }
- }
- /** rb_ary_aset
- *
- */
- @JRubyMethod(name = "[]=")
- public IRubyObject aset(IRubyObject arg0, IRubyObject arg1) {
- if (arg0 instanceof RubyFixnum) {
- store(((RubyFixnum)arg0).getLongValue(), arg1);
- return arg1;
- }
- if (arg0 instanceof RubyRange) {
- long[] beglen = ((RubyRange) arg0).begLen(realLength, 1);
- splice(beglen[0], beglen[1], arg1);
- return arg1;
- }
- if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
- store(RubyNumeric.num2long(arg0), arg1);
- return arg1;
- }
- /** rb_ary_aset
- *
- */
- @JRubyMethod(name = "[]=")
- public IRubyObject aset(IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
- if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
- if (arg1 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as subarray length");
- splice(RubyNumeric.num2long(arg0), RubyNumeric.num2long(arg1), arg2);
- return arg2;
- }
- /** rb_ary_at
- *
- */
- @JRubyMethod(name = "at", required = 1)
- public IRubyObject at(IRubyObject pos) {
- return entry(RubyNumeric.num2long(pos));
- }
- /** rb_ary_concat
- *
- */
- @JRubyMethod(name = "concat", required = 1)
- public RubyArray concat(IRubyObject obj) {
- RubyArray ary = obj.convertToArray();
-
- if (ary.realLength > 0) splice(realLength, 0, ary);
- return this;
- }
- /** inspect_ary
- *
- */
- private IRubyObject inspectAry(ThreadContext context) {
- ByteList buffer = new ByteList();
- buffer.append('[');
- boolean tainted = isTaint();
- for (int i = 0; i < realLength; i++) {
- if (i > 0) buffer.append(',').append(' ');
- RubyString str = inspect(context, values[begin + i]);
- if (str.isTaint()) tainted = true;
- buffer.append(str.getByteList());
- }
- buffer.append(']');
- RubyString str = getRuntime().newString(buffer);
- if (tainted) str.setTaint(true);
- return str;
- }
- /** rb_ary_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- @Override
- public IRubyObject inspect() {
- if (realLength == 0) return getRuntime().newString("[]");
- if (getRuntime().isInspecting(this)) return getRuntime().newString("[...]");
- try {
- getRuntime().registerInspecting(this);
- return inspectAry(getRuntime().getCurrentContext());
- } finally {
- getRuntime().unregisterInspecting(this);
- }
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject first(IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return first();
- case 1:
- return first(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
- /** rb_ary_first
- *
- */
- @JRubyMethod(name = "first")
- public IRubyObject first() {
- if (realLength == 0) return getRuntime().getNil();
- return values[begin];
- }
- /** rb_ary_first
- *
- */
- @JRubyMethod(name = "first")
- public IRubyObject first(IRubyObject arg0) {
- long n = RubyNumeric.num2long(arg0);
- if (n > realLength) {
- n = realLength;
- } else if (n < 0) {
- throw getRuntime().newArgumentError("negative array size (or size too big)");
- }
- return makeShared(begin, (int) n, getRuntime().getArray());
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject last(IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return last();
- case 1:
- return last(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
- /** rb_ary_last
- *
- */
- @JRubyMethod(name = "last")
- public IRubyObject last() {
- if (realLength == 0) return getRuntime().getNil();
- return values[begin + realLength - 1];
- }
- /** rb_ary_last
- *
- */
- @JRubyMethod(name = "last")
- public IRubyObject last(IRubyObject arg0) {
- long n = RubyNumeric.num2long(arg0);
- if (n > realLength) {
- n = realLength;
- } else if (n < 0) {
- throw getRuntime().newArgumentError("negative array size (or size too big)");
- }
- return makeShared(begin + realLength - (int) n, (int) n, getRuntime().getArray());
- }
- /** rb_ary_each
- *
- */
- @JRubyMethod(name = "each", frame = true)
- public IRubyObject each(ThreadContext context, Block block) {
- for (int i = 0; i < realLength; i++) {
- block.yield(context, values[begin + i]);
- }
- return this;
- }
- /** rb_ary_each_index
- *
- */
- @JRubyMethod(name = "each_index", frame = true)
- public IRubyObject each_index(ThreadContext context, Block block) {
- Ruby runtime = getRuntime();
- for (int i = 0; i < realLength; i++) {
- block.yield(context, runtime.newFixnum(i));
- }
- return this;
- }
- /** rb_ary_reverse_each
- *
- */
- @JRubyMethod(name = "reverse_each", frame = true)
- public IRubyObject reverse_each(ThreadContext context, Block block) {
- int len = realLength;
-
- while(len-- > 0) {
- block.yield(context, values[begin + len]);
-
- if (realLength < len) len = realLength;
- }
-
- return this;
- }
- private IRubyObject inspectJoin(ThreadContext context, RubyArray tmp, IRubyObject sep) {
- Ruby runtime = getRuntime();
- // If already inspecting, there is no need to register/unregister again.
- if (runtime.isInspecting(this)) {
- return tmp.join(context, sep);
- }
- try {
- runtime.registerInspecting(this);
- return tmp.join(context, sep);
- } finally {
- runtime.unregisterInspecting(this);
- }
- }
- /** rb_ary_join
- *
- */
- public RubyString join(ThreadContext context, IRubyObject sep) {
- final Ruby runtime = getRuntime();
- if (realLength == 0) return RubyString.newEmptyString(getRuntime());
- boolean taint = isTaint() || sep.isTaint();
- long len = 1;
- for (int i = begin; i < begin + realLength; i++) {
- IRubyObject value;
- try {
- value = values[i];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return runtime.newString("");
- }
- IRubyObject tmp = value.checkStringType();
- len += tmp.isNil() ? 10 : ((RubyString) tmp).getByteList().length();
- }
- RubyString strSep = null;
- if (!sep.isNil()) {
- sep = strSep = sep.convertToString();
- len += strSep.getByteList().length() * (realLength - 1);
- }
- ByteList buf = new ByteList((int)len);
- for (int i = begin; i < begin + realLength; i++) {
- IRubyObject tmp;
- try {
- tmp = values[i];
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- return runtime.newString("");
- }
- if (tmp instanceof RubyString) {
- // do nothing
- } else if (tmp instanceof RubyArray) {
- if (runtime.isInspecting(tmp)) {
- tmp = runtime.newString("[...]");
- } else {
- tmp = inspectJoin(context, (RubyArray)tmp, sep);
- }
- } else {
- tmp = RubyString.objAsString(context, tmp);
- }
- if (i > begin && !sep.isNil()) buf.append(strSep.getByteList());
- buf.append(tmp.asString().getByteList());
- if (tmp.isTaint()) taint = true;
- }
- RubyString result = runtime.newString(buf);
- if (taint) result.setTaint(true);
- return result;
- }
- /** rb_ary_join_m
- *
- */
- @JRubyMethod(name = "join", optional = 1)
- public RubyString join_m(ThreadContext context, IRubyObject[] args) {
- int argc = args.length;
- IRubyObject sep = (argc == 1) ? args[0] : getRuntime().getGlobalVariables().get("$,");
-
- return join(context, sep);
- }
- /** rb_ary_to_a
- *
- */
- @JRubyMethod(name = "to_a")
- @Override
- public RubyArray to_a() {
- if(getMetaClass() != getRuntime().getArray()) {
- RubyArray dup = new RubyArray(getRuntime(), getRuntime().isObjectSpaceEnabled());
- isShared = true;
- dup.isShared = true;
- dup.values = values;
- dup.realLength = realLength;
- dup.begin = begin;
-
- return dup;
- }
- return this;
- }
- @JRubyMethod(name = "to_ary")
- public IRubyObject to_ary() {
- return this;
- }
- @Override
- public RubyArray convertToArray() {
- return this;
- }
-
- @Override
- public IRubyObject checkArrayType(){
- return this;
- }
- /** rb_ary_equal
- *
- */
- @JRubyMethod(name = "==", required = 1)
- @Override
- public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
- if (this == obj) return getRuntime().getTrue();
- if (!(obj instanceof RubyArray)) {
- if (!obj.respondsTo("to_ary")) {
- return getRuntime().getFalse();
- } else {
- if (equalInternal(context, obj.callMethod(context, "to_ary"), this)) return getRuntime().getTrue();
- return getRuntime().getFalse();
- }
- }
- RubyArray ary = (RubyArray) obj;
- if (realLength != ary.realLength) return getRuntime().getFalse();
- Ruby runtime = getRuntime();
- for (long i = 0; i < realLength; i++) {
- if (!equalInternal(context, elt(i), ary.elt(i))) return runtime.getFalse();
- }
- return runtime.getTrue();
- }
- /** rb_ary_eql
- *
- */
- @JRubyMethod(name = "eql?", required = 1)
- public RubyBoolean eql_p(ThreadContext context, IRubyObject obj) {
- if (this == obj) return getRuntime().getTrue();
- if (!(obj instanceof RubyArray)) return getRuntime().getFalse();
- RubyArray ary = (RubyArray) obj;
- if (realLength != ary.realLength) return getRuntime().getFalse();
- Ruby runtime = getRuntime();
- for (int i = 0; i < realLength; i++) {
- if (!eqlInternal(context, elt(i), ary.elt(i))) return runtime.getFalse();
- }
- return runtime.getTrue();
- }
- /** rb_ary_compact_bang
- *
- */
- @JRubyMethod(name = "compact!")
- public IRubyObject compact_bang() {
- modify();
- int p = 0;
- int t = 0;
- int end = p + realLength;
- while (t < end) {
- if (values[t].isNil()) {
- t++;
- } else {
- values[p++] = values[t++];
- }
- }
- if (realLength == p) return getRuntime().getNil();
- realloc(p);
- realLength = p;
- return this;
- }
- /** rb_ary_compact
- *
- */
- @JRubyMethod(name = "compact")
- public IRubyObject compact() {
- RubyArray ary = aryDup();
- ary.compact_bang();
- return ary;
- }
- /** rb_ary_empty_p
- *
- */
- @JRubyMethod(name = "empty?")
- public IRubyObject empty_p() {
- return realLength == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
- }
- /** rb_ary_clear
- *
- */
- @JRubyMethod(name = "clear")
- public IRubyObject rb_clear() {
- modifyCheck();
- if(isShared) {
- alloc(ARRAY_DEFAULT_SIZE);
- isShared = true;
- } else if (values.length > ARRAY_DEFAULT_SIZE << 1){
- alloc(ARRAY_DEFAULT_SIZE << 1);
- } else {
- final int begin = this.begin;
- try {
- Arrays.fill(values, begin, begin + realLength, getRuntime().getNil());
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
- begin = 0;
- realLength = 0;
- return this;
- }
- /** rb_ary_fill
- *
- */
- @JRubyMethod(name = "fill", optional = 3, frame = true)
- public IRubyObject fill(ThreadContext context, IRubyObject[] args, Block block) {
- IRubyObject item = null;
- IRubyObject begObj = null;
- IRubyObject lenObj = null;
- int argc = args.length;
- if (block.isGiven()) {
- Arity.checkArgumentCount(getRuntime(), args, 0, 2);
- item = null;
- begObj = argc > 0 ? args[0] : null;
- lenObj = argc > 1 ? args[1] : null;
- argc++;
- } else {
- Arity.checkArgumentCount(getRuntime(), args, 1, 3);
- item = args[0];
- begObj = argc > 1 ? args[1] : null;
- lenObj = argc > 2 ? args[2] : null;
- }
- int beg = 0, end = 0, len = 0;
- switch (argc) {
- case 1:
- beg = 0;
- len = realLength;
- break;
- case 2:
- if (begObj instanceof RubyRange) {
- long[] beglen = ((RubyRange) begObj).begLen(realLength, 1);
- beg = (int) beglen[0];
- len = (int) beglen[1];
- break;
- }
- /* fall through */
- case 3:
- beg = begObj.isNil() ? 0 : RubyNumeric.num2int(begObj);
- if (beg < 0) {
- beg = realLength + beg;
- if (beg < 0) beg = 0;
- }
- len = (lenObj == null || lenObj.isNil()) ? realLength - beg : RubyNumeric.num2int(lenObj);
- // TODO: In MRI 1.9, an explicit check for negative length is
- // added here. IndexError is raised when length is negative.
- // See [ruby-core:12953] for more details.
- //
- // New note: This is actually under re-evaluation,
- // see [ruby-core:17483].
- break;
- }
- modify();
- // See [ruby-core:17483]
- if (len < 0) {
- return this;
- }
- if (len > Integer.MAX_VALUE - beg) {
- throw getRuntime().newArgumentError("argument too big");
- }
- end = beg + len;
- if (end > realLength) {
- if (end >= values.length) realloc(end);
- realLength = end;
- }
- if (block.isGiven()) {
- Ruby runtime = getRuntime();
- for (int i = beg; i < end; i++) {
- IRubyObject v = block.yield(context, runtime.newFixnum(i));
- if (i >= realLength) break;
- try {
- values[i] = v;
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
- } else {
- if (len > 0) {
- try {
- Arrays.fill(values, beg, beg + len, item);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- }
- }
- return this;
- }
- /** rb_ary_index
- *
- */
- @JRubyMethod(name = "index", required = 1)
- public IRubyObject index(ThreadContext context, IRubyObject obj) {
- Ruby runtime = getRuntime();
- for (int i = begin; i < begin + realLength; i++) {
- if (equalInternal(context, values[i], obj)) return runtime.newFixnum(i - begin);
- }
- return runtime.getNil();
- }
- /** rb_ary_rindex
- *
- */
- @JRubyMethod(name = "rindex", required = 1)
- public IRubyObject rindex(ThreadContext context, IRubyObject obj) {
- Ruby runtime = getRuntime();
- int i = realLength;
- while (i-- > 0) {
- if (i > realLength) {
- i = realLength;
- continue;
- }
- if (equalInternal(context, values[begin + i], obj)) return getRuntime().newFixnum(i);
- }
- return runtime.getNil();
- }
- /** rb_ary_indexes
- *
- */
- @JRubyMethod(name = {"indexes", "indices"}, required = 1, rest = true)
- public IRubyObject indexes(IRubyObject[] args) {
- getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Array#indexes is deprecated; use Array#values_at", "Array#indexes", "Array#values_at");
- RubyArray ary = new RubyArray(getRuntime(), args.length);
- IRubyObject[] arefArgs = new IRubyObject[1];
- for (int i = 0; i < args.length; i++) {
- arefArgs[0] = args[i];
- ary.append(aref(arefArgs));
- }
- return ary;
- }
- /** rb_ary_reverse_bang
- *
- */
- @JRubyMethod(name = "reverse!")
- public IRubyObject reverse_bang() {
- modify();
- final int realLength = this.realLength;
- final IRubyObject[] values = this.values;
- try {
- if (realLength > 1) {
- int p1 = 0;
- int p2 = p1 + realLength - 1;
- while (p1 < p2) {
- final IRubyObject tmp = values[p1];
- values[p1++] = values[p2];
- values[p2--] = tmp;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- return this;
- }
- /** rb_ary_reverse_m
- *
- */
- @JRubyMethod(name = "reverse")
- public IRubyObject reverse() {
- return aryDup().reverse_bang();
- }
- /** rb_ary_collect
- *
- */
- @JRubyMethod(name = {"collect", "map"}, frame = true)
- public RubyArray collect(ThreadContext context, Block block) {
- Ruby runtime = getRuntime();
-
- if (!block.isGiven()) return new RubyArray(getRuntime(), runtime.getArray(), this);
-
- RubyArray collect = new RubyArray(runtime, realLength);
-
- for (int i = begin; i < begin + realLength; i++) {
- collect.append(block.yield(context, values[i]));
- }
-
- return collect;
- }
- /** rb_ary_collect_bang
- *
- */
- @JRubyMethod(name = {"collect!", "map!"}, frame = true)
- public RubyArray collect_bang(ThreadContext context, Block block) {
- modify();
- for (int i = 0, len = realLength; i < len; i++) {
- store(i, block.yield(context, values[begin + i]));
- }
- return this;
- }
- /** rb_ary_select
- *
- */
- @JRubyMethod(name = "select", frame = true)
- public RubyArray select(ThreadContext context, Block block) {
- Ruby runtime = getRuntime();
- RubyArray result = new RubyArray(runtime, realLength);
- if (isShared) {
- for (int i = begin; i < begin + realLength; i++) {
- if (block.yield(context, values[i]).isTrue()) result.append(elt(i - begin));
- }
- } else {
- for (int i = 0; i < realLength; i++) {
- if (block.yield(context, values[i]).isTrue()) result.append(elt(i));
- }
- }
- return result;
- }
- /** rb_ary_delete
- *
- */
- @JRubyMethod(name = "delete", required = 1, frame = true)
- public IRubyObject delete(ThreadContext context, IRubyObject item, Block block) {
- int i2 = 0;
- Ruby runtime = getRuntime();
- for (int i1 = 0; i1 < realLength; i1++) {
- IRubyObject e = values[begin + i1];
- if (equalInternal(context, e, item)) continue;
- if (i1 != i2) store(i2, e);
- i2++;
- }
- if (realLength == i2) {
- if (block.isGiven()) return block.yield(context, item);
- return runtime.getNil();
- }
- modify();
- final int realLength = this.realLength;
- final int begin = this.begin;
- final IRubyObject[] values = this.values;
- if (realLength > i2) {
- try {
- Arrays.fill(values, begin + i2, begin + realLength, getRuntime().getNil());
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- this.realLength = i2;
- if (i2 << 1 < values.length && values.length > ARRAY_DEFAULT_SIZE) realloc(i2 << 1);
- }
- return item;
- }
- /** rb_ary_delete_at
- *
- */
- private final IRubyObject delete_at(int pos) {
- int len = realLength;
- if (pos >= len) return getRuntime().getNil();
- if (pos < 0) pos += len;
- if (pos < 0) return getRuntime().getNil();
- modify();
- IRubyObject obj = values[pos];
- try {
- System.arraycopy(values, pos + 1, values, pos, len - (pos + 1));
- values[realLength-1] = getRuntime().getNil();
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength--;
- return obj;
- }
- /** rb_ary_delete_at_m
- *
- */
- @JRubyMethod(name = "delete_at", required = 1)
- public IRubyObject delete_at(IRubyObject obj) {
- return delete_at((int) RubyNumeric.num2long(obj));
- }
- /** rb_ary_reject_bang
- *
- */
- @JRubyMethod(name = "reject", frame = true)
- public IRubyObject reject(ThreadContext context, Block block) {
- RubyArray ary = aryDup();
- ary.reject_bang(context, block);
- return ary;
- }
- /** rb_ary_reject_bang
- *
- */
- @JRubyMethod(name = "reject!", frame = true)
- public IRubyObject reject_bang(ThreadContext context, Block block) {
- int i2 = 0;
- modify();
- for (int i1 = 0; i1 < realLength; i1++) {
- IRubyObject v = values[i1];
- if (block.yield(context, v).isTrue()) continue;
- if (i1 != i2) store(i2, v);
- i2++;
- }
- if (realLength == i2) return getRuntime().getNil();
- if (i2 < realLength) {
- try {
- Arrays.fill(values, i2, realLength, getRuntime().getNil());
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- realLength = i2;
- }
- return this;
- }
- /** rb_ary_delete_if
- *
- */
- @JRubyMethod(name = "delete_if", frame = true)
- public IRubyObject delete_if(ThreadContext context, Block block) {
- reject_bang(context, block);
- return this;
- }
- /** rb_ary_zip
- *
- */
- @JRubyMethod(name = "zip", optional = 1, rest = true, frame = true)
- public IRubyObject zip(ThreadContext context, IRubyObject[] args, Block block) {
- for (int i = 0; i < args.length; i++) {
- args[i] = args[i].convertToArray();
- }
- Ruby runtime = getRuntime();
- if (block.isGiven()) {
- for (int i = 0; i < realLength; i++) {
- RubyArray tmp = new RubyArray(runtime, args.length + 1);
- tmp.append(elt(i));
- for (int j = 0; j < args.length; j++) {
- tmp.append(((RubyArray) args[j]).elt(i));
- }
- block.yield(context, tmp);
- }
- return runtime.getNil();
- }
-
- int len = realLength;
- RubyArray result = new RubyArray(runtime, len);
- for (int i = 0; i < len; i++) {
- RubyArray tmp = new RubyArray(runtime, args.length + 1);
- tmp.append(elt(i));
- for (int j = 0; j < args.length; j++) {
- tmp.append(((RubyArray) args[j]).elt(i));
- }
- result.append(tmp);
- }
- return result;
- }
- /** rb_ary_cmp
- *
- */
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject obj) {
- RubyArray ary2 = obj.convertToArray();
- int len = realLength;
- if (len > ary2.realLength) len = ary2.realLength;
- Ruby runtime = getRuntime();
- for (int i = 0; i < len; i++) {
- IRubyObject v = elt(i).callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", ary2.elt(i));
- if (!(v instanceof RubyFixnum) || ((RubyFixnum) v).getLongValue() != 0) return v;
- }
- len = realLength - ary2.realLength;
- if (len == 0) return RubyFixnum.zero(runtime);
- if (len > 0) return RubyFixnum.one(runtime);
- return RubyFixnum.minus_one(runtime);
- }
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject slice_bang(IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return slice_bang(args[0]);
- case 2:
- return slice_bang(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
- /** rb_ary_slice_bang
- *
- */
- @JRubyMethod(name = "slice!")
- public IRubyObject slice_bang(IRubyObject arg0) {
- if (arg0 instanceof RubyRange) {
- long[] beglen = ((RubyRange) arg0).begLen(realLength, 1);
- long pos = beglen[0];
- long len = beglen[1];
- if (pos < 0) pos = realLength + pos;
- arg0 = subseq(pos, len);
- splice(pos, len, null);
- return arg0;
- }
- return delete_at((int) RubyNumeric.num2long(arg0));
- }
- /** rb_ary_slice_bang
- *
- */
- @JRubyMethod(name = "slice!")
- public IRubyObject slice_bang(IRubyObject arg0, IRubyObject arg1) {
- long pos = RubyNumeric.num2long(arg0);
- long len = RubyNumeric.num2long(arg1);
- if (pos < 0) pos = realLength + pos;
- arg1 = subseq(pos, len);
- splice(pos, len, null);
- return arg1;
- }
- /** rb_ary_assoc
- *
- */
- @JRubyMethod(name = "assoc", required = 1)
- public IRubyObject assoc(ThreadContext context, IRubyObject key) {
- Ruby runtime = getRuntime();
- for (int i = begin; i < begin + realLength; i++) {
- IRubyObject v = values[i];
- if (v instanceof RubyArray) {
- RubyArray arr = (RubyArray)v;
- if (arr.realLength > 0 && equalInternal(context, arr.values[arr.begin], key)) return arr;
- }
- }
- return runtime.getNil();
- }
- /** rb_ary_rassoc
- *
- */
- @JRubyMethod(name = "rassoc", required = 1)
- public IRubyObject rassoc(ThreadContext context, IRubyObject value) {
- Ruby runtime = getRuntime();
- for (int i = begin; i < begin + realLength; i++) {
- IRubyObject v = values[i];
- if (v instanceof RubyArray) {
- RubyArray arr = (RubyArray)v;
- if (arr.realLength > 1 && equalInternal(context, arr.values[arr.begin + 1], value)) return arr;
- }
- }
- return runtime.getNil();
- }
- /** flatten
- *
- */
- private final int flatten(ThreadContext context, int index, RubyArray ary2, RubyArray memo) {
- int i = index;
- int n;
- int lim = index + ary2.realLength;
- IRubyObject id = ary2.id();
- if (memo.includes(context, id)) throw getRuntime().newArgumentError("tried to flatten recursive array");
- memo.append(id);
- splice(index, 1, ary2);
- while (i < lim) {
- IRubyObject tmp = elt(i).checkArrayType();
- if (!tmp.isNil()) {
- n = flatten(context, i, (RubyArray) tmp, memo);
- i += n;
- lim += n;
- }
- i++;
- }
- memo.pop();
- return lim - index - 1; /* returns number of increased items */
- }
- /** rb_ary_flatten_bang
- *
- */
- @JRubyMethod(name = "flatten!")
- public IRubyObject flatten_bang(ThreadContext context) {
- int i = 0;
- RubyArray memo = null;
- while (i < realLength) {
- IRubyObject ary2 = values[begin + i];
- IRubyObject tmp = ary2.checkArrayType();
- if (!tmp.isNil()) {
- if (memo == null) {
- memo = new RubyArray(getRuntime(), false);
- memo.values = reserve(ARRAY_DEFAULT_SIZE);
- }
- i += flatten(context, i, (RubyArray) tmp, memo);
- }
- i++;
- }
- if (memo == null) return getRuntime().getNil();
- return this;
- }
- /** rb_ary_flatten
- *
- */
- @JRubyMethod(name = "flatten")
- public IRubyObject flatten(ThreadContext context) {
- RubyArray ary = aryDup();
- ary.flatten_bang(context);
- return ary;
- }
- /** rb_ary_nitems
- *
- */
- @JRubyMethod(name = "nitems")
- public IRubyObject nitems() {
- int n = 0;
- for (int i = begin; i < begin + realLength; i++) {
- if (!values[i].isNil()) n++;
- }
-
- return getRuntime().newFixnum(n);
- }
- /** rb_ary_plus
- *
- */
- @JRubyMethod(name = "+", required = 1)
- public IRubyObject op_plus(IRubyObject obj) {
- RubyArray y = obj.convertToArray();
- int len = realLength + y.realLength;
- RubyArray z = new RubyArray(getRuntime(), len);
- try {
- System.arraycopy(values, begin, z.values, 0, realLength);
- System.arraycopy(y.values, y.begin, z.values, realLength, y.realLength);
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- z.realLength = len;
- return z;
- }
- /** rb_ary_times
- *
- */
- @JRubyMethod(name = "*", required = 1)
- public IRubyObject op_times(ThreadContext context, IRubyObject times) {
- IRubyObject tmp = times.checkStringType();
- if (!tmp.isNil()) return join(context, tmp);
- long len = RubyNumeric.num2long(times);
- if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0);
- if (len < 0) throw getRuntime().newArgumentError("negative argument");
- if (Long.MAX_VALUE / len < realLength) {
- throw getRuntime().newArgumentError("argument too big");
- }
- len *= realLength;
- RubyArray ary2 = new RubyArray(getRuntime(), getMetaClass(), len);
- ary2.realLength = (int) len;
- try {
- for (int i = 0; i < len; i += realLength) {
- System.arraycopy(values, begin, ary2.values, i, realLength);
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- concurrentModification();
- }
- ary2.infectBy(this);
- return ary2;
- }
- /** ary_make_hash
- *
- */
- private final RubyHash makeHash(RubyArray ary2) {
- RubyHash hash = new RubyHash(getRuntime(), false);
- int begin = this.begin;
- for (int i = begin; i < begin + realLength; i++) {
- hash.fastASet(values[i], NEVER);
- }
- if (ary2 != null) {
- begin = ary2.begin;
- for (int i = begin; i < begin + ary2.realLength; i++) {
- hash.fastASet(ary2.values[i], NEVER);
- }
- }
- return hash;
- }
- /** rb_ary_uniq_bang
- *
- */
- @JRubyMethod(name = "uniq!")
- public IRubyObject uniq_bang() {
- RubyHash hash = makeHash(null);
- if (realLength == hash.size()) return getRuntime().getNil();
- int j = 0;
- for (int i = 0; i < realLength; i++) {
- IRubyObject v = elt(i);
- if (hash.fastDelete(v)) store(j++, v);
- }
- realLength = j;
- return this;
- }
- /** rb_ary_uniq
- *
- */
- @JRubyMethod(name = "uniq")
- public IRubyObject uniq() {
- RubyArray ary = aryDup();
- ary.uniq_bang();
- return ary;
- }
- /** rb_ary_diff
- *
- */
- @JRubyMethod(name = "-", required = 1)
- public IRubyObject op_diff(IRubyObject other) {
- RubyHash hash = other.convertToArray().makeHash(null);
- RubyArray ary3 = new RubyArray(getRuntime());
- int begin = this.begin;
- for (int i = begin; i < begin + realLength; i++) {
- if (hash.fastARef(values[i]) != null) continue;
- ary3.append(elt(i - begin));
- }
- return ary3;
- }
- /** rb_ary_and
- *
- */
- @JRubyMethod(name = "&", required = 1)
- public IRubyObject op_and(IRubyObject other) {
- RubyArray ary2 = other.convertToArray();
- RubyHash hash = ary2.makeHash(null);
- RubyArray ary3 = new RubyArray(getRuntime(),
- realLength < ary2.realLength ? realLength : ary2.realLength);
- for (int i = 0; i < realLength; i++) {
- IRubyObject v = elt(i);
- if (hash.fastDelete(v)) ary3.append(v);
- }
- return ary3;
- }
- /** rb_ary_or
- *
- */
- @JRubyMethod(name = "|", required = 1)
- public IRubyObject op_or(IRubyObject other) {
- RubyArray ary2 = other.convertToArray();
- RubyHash set = makeHash(ary2);
- RubyArray ary3 = new RubyArray(getRuntime(), realLength + ary2.realLength);
- for (int i = 0; i < realLength; i++) {
- IRubyObject v = elt(i);
- if (set.fastDelete(v)) ary3.append(v);
- }
- for (int i = 0; i < ary2.realLength; i++) {
- IRubyObject v = ary2.elt(i);
- if (set.fastDelete(v)) ary3.append(v);
- }
- return ary3;
- }
- /** rb_ary_sort
- *
- */
- @JRubyMethod(name = "sort", frame = true)
- public RubyArray sort(Block block) {
- RubyArray ary = aryDup();
- ary.sort_bang(block);
- return ary;
- }
- /** rb_ary_sort_bang
- *
- */
- @JRubyMethod(name = "sort!", frame = true)
- public RubyArray sort_bang(Block block) {
- modify();
- if (realLength > 1) {
- flags |= TMPLOCK_ARR_F;
- try {
- if (block.isGiven()) {
- Arrays.sort(values, 0, realLength, new BlockComparator(block));
- } else {
- Arrays.sort(values, 0, realLength, new DefaultComparator());
- }
- } finally {
- flags &= ~TMPLOCK_ARR_F;
- }
- }
- return this;
- }
- final class BlockComparator implements Comparator {
- private Block block;
- public BlockComparator(Block block) {
- this.block = block;
- }
- public int compare(Object o1, Object o2) {
- ThreadContext context = getRuntime().getCurrentContext();
- IRubyObject obj1 = (IRubyObject) o1;
- IRubyObject obj2 = (IRubyObject) o2;
- IRubyObject ret = block.yield(context, getRuntime().newArray(obj1, obj2), null, null, true);
- int n = RubyComparable.cmpint(context, ret, obj1, obj2);
- //TODO: ary_sort_check should be done here
- return n;
- }
- }
- static final class DefaultComparator implements Comparator {
- public int compare(Object o1, Object o2) {
- if (o1 instanceof RubyFixnum && o2 instanceof RubyFixnum) {
- return compareFixnums(o1, o2);
- }
- if (o1 instanceof RubyString && o2 instanceof RubyString) {
- return ((RubyString) o1).op_cmp((RubyString) o2);
- }
- //TODO: ary_sort_check should be done here
- return compareOthers((IRubyObject)o1, (IRubyObject)o2);
- }
- private int compareFixnums(Object o1, Object o2) {
- long a = ((RubyFixnum) o1).getLongValue();
- long b = ((RubyFixnum) o2).getLongValue();
- if (a > b) {
- return 1;
- }
- if (a < b) {
- return -1;
- }
- return 0;
- }
- private int compareOthers(IRubyObject o1, IRubyObject o2) {
- ThreadContext context = o1.getRuntime().getCurrentContext();
- IRubyObject ret = o1.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", o2);
- int n = RubyComparable.cmpint(context, ret, o1, o2);
- //TODO: ary_sort_check should be done here
- return n;
- }
- }
- public static void marshalTo(RubyArray array, MarshalStream output) throws IOException {
- output.registerLinkTarget(array);
- output.writeInt(array.getList().size());
- for (Iterator iter = array.getList().iterator(); iter.hasNext();) {
- output.dumpObject((IRubyObject) iter.next());
- }
- }
- public static RubyArray unmarshalFrom(UnmarshalStream input) throws IOException {
- RubyArray result = input.getRuntime().newArray();
- input.registerLinkTarget(result);
- int size = input.unmarshalInt();
- for (int i = 0; i < size; i++) {
- result.append(input.unmarshalObject());
- }
- return result;
- }
- /**
- * @see org.jruby.util.Pack#pack
- */
- @JRubyMethod(name = "pack", required = 1)
- public RubyString pack(ThreadContext context, IRubyObject obj) {
- RubyString iFmt = RubyString.objAsString(context, obj);
- return Pack.pack(getRuntime(), this, iFmt.getByteList());
- }
- @Override
- public Class getJavaClass() {
- return List.class;
- }
- // Satisfy java.util.List interface (for Java integration)
- public int size() {
- return realLength;
- }
- public boolean isEmpty() {
- return realLength == 0;
- }
- public boolean contains(Object element) {
- return indexOf(element) != -1;
- }
- public Object[] toArray() {
- Object[] array = new Object[realLength];
- for (int i = begin; i < realLength; i++) {
- array[i - begin] = JavaUtil.convertRubyToJava(values[i]);
- }
- return array;
- }
- public Object[] toArray(final Object[] arg) {
- Object[] array = arg;
- if (array.length < realLength) {
- Class type = array.getClass().getComponentType();
- array = (Object[]) Array.newInstance(type, realLength);
- }
- int length = realLength - begin;
- for (int i = 0; i < length; i++) {
- array[i] = JavaUtil.convertRubyToJava(values[i + begin]);
- }
- return array;
- }
- public boolean add(Object element) {
- append(JavaUtil.convertJavaToRuby(getRuntime(), element));
- return true;
- }
- public boolean remove(Object element) {
- IRubyObject deleted = delete(getRuntime().getCurrentContext(), JavaUtil.convertJavaToRuby(getRuntime(), element), Block.NULL_BLOCK);
- return deleted.isNil() ? false : true; // TODO: is this correct ?
- }
- public boolean containsAll(Collection c) {
- for (Iterator iter = c.iterator(); iter.hasNext();) {
- if (indexOf(iter.next()) == -1) {
- return false;
- }
- }
- return true;
- }
- public boolean addAll(Collection c) {
- for (Iterator iter = c.iterator(); iter.hasNext();) {
- add(iter.next());
- }
- return !c.isEmpty();
- }
- public boolean addAll(int index, Collection c) {
- Iterator iter = c.iterator();
- for (int i = index; iter.hasNext(); i++) {
- add(i, iter.next());
- }
- return !c.isEmpty();
- }
- public boolean removeAll(Collection c) {
- boolean listChanged = false;
- for (Iterator iter = c.iterator(); iter.hasNext();) {
- if (remove(iter.next())) {
- listChanged = true;
- }
- }
- return listChanged;
- }
- public boolean retainAll(Collection c) {
- boolean listChanged = false;
- for (Iterator iter = iterator(); iter.hasNext();) {
- Object element = iter.next();
- if (!c.contains(element)) {
- remove(element);
- listChanged = true;
- }
- }
- return listChanged;
- }
- public Object get(int index) {
- return JavaUtil.convertRubyToJava((IRubyObject) elt(index), Object.class);
- }
- public Object set(int index, Object element) {
- return store(index, JavaUtil.convertJavaToRuby(getRuntime(), element));
- }
- // TODO: make more efficient by not creating IRubyArray[]
- public void add(int index, Object element) {
- insert(new IRubyObject[]{RubyFixnum.newFixnum(getRuntime(), index), JavaUtil.convertJavaToRuby(getRuntime(), element)});
- }
- public Object remove(int index) {
- return JavaUtil.convertRubyToJava(delete_at(index), Object.class);
- }
- public int indexOf(Object element) {
- int begin = this.begin;
- if (element != null) {
- IRubyObject convertedElement = JavaUtil.convertJavaToRuby(getRuntime(), element);
- for (int i = begin; i < begin + realLength; i++) {
- if (convertedElement.equals(values[i])) {
- return i;
- }
- }
- }
- return -1;
- }
- public int lastIndexOf(Object element) {
- int begin = this.begin;
- if (element != null) {
- IRubyObject convertedElement = JavaUtil.convertJavaToRuby(getRuntime(), element);
- for (int i = begin + realLength - 1; i >= begin; i--) {
- if (convertedElement.equals(values[i])) {
- return i;
- }
- }
- }
- return -1;
- }
- public class RubyArrayConversionIterator implements Iterator {
- protected int index = 0;
- protected int last = -1;
- public boolean hasNext() {
- return index < realLength;
- }
- public Object next() {
- IRubyObject element = elt(index);
- last = index++;
- return JavaUtil.convertRubyToJava(element, Object.class);
- }
- public void remove() {
- if (last == -1) throw new IllegalStateException();
- delete_at(last);
- if (last < index) index--;
- last = -1;
-
- }
- }
- public Iterator iterator() {
- return new RubyArrayConversionIterator();
- }
- final class RubyArrayConversionListIterator extends RubyArrayConversionIterator implements ListIterator {
- public RubyArrayConversionListIterator() {
- }
- public RubyArrayConversionListIterator(int index) {
- this.index = index;
- }
- public boolean hasPrevious() {
- return index >= 0;
- }
- public Object previous() {
- return JavaUtil.convertRubyToJava((IRubyObject) elt(last = --index), Object.class);
- }
- public int nextIndex() {
- return index;
- }
- public int previousIndex() {
- return index - 1;
- }
- public void set(Object obj) {
- if (last == -1) throw new IllegalStateException();
- store(last, JavaUtil.convertJavaToRuby(getRuntime(), obj));
- }
- public void add(Object obj) {
- insert(new IRubyObject[] { RubyFixnum.newFixnum(getRuntime(), index++), JavaUtil.convertJavaToRuby(getRuntime(), obj) });
- last = -1;
- }
- }
- public ListIterator listIterator() {
- return new RubyArrayConversionListIterator();
- }
- public ListIterator listIterator(int index) {
- return new RubyArrayConversionListIterator(index);
- }
- // TODO: list.subList(from, to).clear() is supposed to clear the sublist from the list.
- // How can we support this operation?
- public List subList(int fromIndex, int toIndex) {
- if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex) {
- throw new IndexOutOfBoundsException();
- }
-
- IRubyObject subList = subseq(fromIndex, toIndex - fromIndex + 1);
- return subList.isNil() ? null : (List) subList;
- }
- public void clear() {
- rb_clear();
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import java.math.MathContext;
- import java.math.RoundingMode;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyConstant;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Arity;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.CallbackFactory;
- import org.jruby.runtime.MethodIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.Visibility;
- import org.jruby.runtime.builtin.IRubyObject;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- @JRubyClass(name="BigDecimal", parent="Numeric")
- public class RubyBigDecimal extends RubyNumeric {
- private static final ObjectAllocator BIGDECIMAL_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyBigDecimal(runtime, klass);
- }
- };
- @JRubyConstant
- public final static int ROUND_DOWN = BigDecimal.ROUND_DOWN;
- @JRubyConstant
- public final static int ROUND_CEILING = BigDecimal.ROUND_CEILING;
- @JRubyConstant
- public final static int ROUND_UP = BigDecimal.ROUND_UP;
- @JRubyConstant
- public final static int ROUND_HALF_DOWN = BigDecimal.ROUND_HALF_DOWN;
- @JRubyConstant
- public final static int ROUND_HALF_EVEN = BigDecimal.ROUND_HALF_EVEN;
- @JRubyConstant
- public final static int ROUND_HALF_UP = BigDecimal.ROUND_HALF_UP;
- @JRubyConstant
- public final static int ROUND_FLOOR = BigDecimal.ROUND_FLOOR;
- @JRubyConstant
- public final static int SIGN_POSITIVE_INFINITE=3;
- @JRubyConstant
- public final static int EXCEPTION_OVERFLOW=1;
- @JRubyConstant
- public final static int SIGN_POSITIVE_ZERO=1;
- @JRubyConstant
- public final static int EXCEPTION_ALL=255;
- @JRubyConstant
- public final static int SIGN_NEGATIVE_FINITE=-2;
- @JRubyConstant
- public final static int EXCEPTION_UNDERFLOW=4;
- @JRubyConstant
- public final static int SIGN_NaN=0;
- @JRubyConstant
- public final static int BASE=10000;
- @JRubyConstant
- public final static int ROUND_MODE=256;
- @JRubyConstant
- public final static int SIGN_POSITIVE_FINITE=2;
- @JRubyConstant
- public final static int EXCEPTION_INFINITY=1;
- @JRubyConstant
- public final static int SIGN_NEGATIVE_INFINITE=-3;
- @JRubyConstant
- public final static int EXCEPTION_ZERODIVIDE=1;
- @JRubyConstant
- public final static int SIGN_NEGATIVE_ZERO=-1;
- @JRubyConstant
- public final static int EXCEPTION_NaN=2;
-
- // Static constants
- private static final BigDecimal TWO = new BigDecimal(2);
- private static final double SQRT_10 = 3.162277660168379332;
-
- public static RubyClass createBigDecimal(Ruby runtime) {
- RubyClass result = runtime.defineClass("BigDecimal",runtime.getNumeric(), BIGDECIMAL_ALLOCATOR);
- CallbackFactory callbackFactory = runtime.callbackFactory(RubyBigDecimal.class);
- runtime.getKernel().defineAnnotatedMethods(BigDecimalKernelMethods.class);
- result.setInternalModuleVariable("vpPrecLimit", RubyFixnum.zero(runtime));
- result.setInternalModuleVariable("vpExceptionMode", RubyFixnum.zero(runtime));
- result.setInternalModuleVariable("vpRoundingMode", runtime.newFixnum(ROUND_HALF_UP));
-
- result.defineAnnotatedMethods(RubyBigDecimal.class);
- result.defineAnnotatedConstants(RubyBigDecimal.class);
- return result;
- }
- private boolean isNaN = false;
- private int infinitySign = 0;
- private int zeroSign = 0;
- private BigDecimal value;
- public BigDecimal getValue() {
- return value;
- }
- public RubyBigDecimal(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- }
- public RubyBigDecimal(Ruby runtime, BigDecimal value) {
- super(runtime, runtime.fastGetClass("BigDecimal"));
- this.value = value;
- }
-
- public static class BigDecimalKernelMethods {
- @JRubyMethod(name = "BigDecimal", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject newBigDecimal(IRubyObject recv, IRubyObject[] args) {
- return RubyBigDecimal.newBigDecimal(recv, args, Block.NULL_BLOCK);
- }
- }
- public static RubyBigDecimal newBigDecimal(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- return newInstance(recv.getRuntime().fastGetClass("BigDecimal"), args);
- }
- @JRubyMethod(name = "ver", meta = true)
- public static IRubyObject ver(IRubyObject recv) {
- return recv.getRuntime().newString("1.0.1");
- }
- @JRubyMethod(name = "_dump", optional = 1, frame = true)
- public IRubyObject dump(IRubyObject[] args, Block unusedBlock) {
- RubyString precision = RubyString.newUnicodeString(args[0].getRuntime(), "0:");
- RubyString str = this.asString();
- return precision.append(str);
- }
-
- @JRubyMethod(name = "_load", required = 1, frame = true, meta = true)
- public static RubyBigDecimal load(IRubyObject recv, IRubyObject from, Block block) {
- RubyBigDecimal rubyBigDecimal = (RubyBigDecimal) (((RubyClass)recv).allocate());
- String precisionAndValue = from.convertToString().asJavaString();
- String value = precisionAndValue.substring(precisionAndValue.indexOf(":")+1);
- rubyBigDecimal.value = new BigDecimal(value);
- return rubyBigDecimal;
- }
- @JRubyMethod(name = "double_fig", meta = true)
- public static IRubyObject double_fig(IRubyObject recv) {
- return recv.getRuntime().newFixnum(20);
- }
-
- @JRubyMethod(name = "limit", optional = 1, meta = true)
- public static IRubyObject limit(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- RubyModule c = (RubyModule)recv;
- IRubyObject nCur = c.searchInternalModuleVariable("vpPrecLimit");
- if (args.length > 0) {
- IRubyObject arg = args[0];
- if (!arg.isNil()) {
- if (!(arg instanceof RubyFixnum)) {
- throw runtime.newTypeError(arg, runtime.getFixnum());
- }
- if (0 > ((RubyFixnum)arg).getLongValue()) {
- throw runtime.newArgumentError("argument must be positive");
- }
- c.setInternalModuleVariable("vpPrecLimit", arg);
- }
- }
- return nCur;
- }
- @JRubyMethod(name = "mode", required = 1, optional = 1, meta = true)
- public static IRubyObject mode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- // FIXME: I doubt any of the constants referenced in this method
- // are ever redefined -- should compare to the known values, rather
- // than do an expensive constant lookup.
- Ruby runtime = recv.getRuntime();
- RubyClass clazz = runtime.fastGetClass("BigDecimal");
- RubyModule c = (RubyModule)recv;
-
- args = Arity.scanArgs(runtime, args, 1, 1);
-
- IRubyObject mode = args[0];
- IRubyObject value = args[1];
-
- if (!(mode instanceof RubyFixnum)) {
- throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
- }
-
- long longMode = ((RubyFixnum)mode).getLongValue();
- long EXCEPTION_ALL = ((RubyFixnum)clazz.fastGetConstant("EXCEPTION_ALL")).getLongValue();
- if ((longMode & EXCEPTION_ALL) != 0) {
- if (value.isNil()) {
- return c.searchInternalModuleVariable("vpExceptionMode");
- }
- if (!(value.isNil()) && !(value instanceof RubyBoolean)) {
- throw runtime.newTypeError("second argument must be true or false");
- }
- RubyFixnum currentExceptionMode = (RubyFixnum)c.searchInternalModuleVariable("vpExceptionMode");
- RubyFixnum newExceptionMode = new RubyFixnum(runtime, currentExceptionMode.getLongValue());
-
- RubyFixnum EXCEPTION_INFINITY = (RubyFixnum)clazz.fastGetConstant("EXCEPTION_INFINITY");
- if ((longMode & EXCEPTION_INFINITY.getLongValue()) != 0) {
- newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context, "|", EXCEPTION_INFINITY)
- : (RubyFixnum)currentExceptionMode.callCoerced(context, "&", new RubyFixnum(runtime, ~(EXCEPTION_INFINITY).getLongValue()));
- }
-
- RubyFixnum EXCEPTION_NaN = (RubyFixnum)clazz.fastGetConstant("EXCEPTION_NaN");
- if ((longMode & EXCEPTION_NaN.getLongValue()) != 0) {
- newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context, "|", EXCEPTION_NaN)
- : (RubyFixnum)currentExceptionMode.callCoerced(context, "&", new RubyFixnum(runtime, ~(EXCEPTION_NaN).getLongValue()));
- }
- c.setInternalModuleVariable("vpExceptionMode", newExceptionMode);
- return newExceptionMode;
- }
-
- long ROUND_MODE = ((RubyFixnum)clazz.fastGetConstant("ROUND_MODE")).getLongValue();
- if (longMode == ROUND_MODE) {
- if (value.isNil()) {
- return c.searchInternalModuleVariable("vpRoundingMode");
- }
- if (!(value instanceof RubyFixnum)) {
- throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
- }
-
- RubyFixnum roundingMode = (RubyFixnum)value;
- if (roundingMode == clazz.fastGetConstant("ROUND_UP") ||
- roundingMode == clazz.fastGetConstant("ROUND_DOWN") ||
- roundingMode == clazz.fastGetConstant("ROUND_FLOOR") ||
- roundingMode == clazz.fastGetConstant("ROUND_CEILING") ||
- roundingMode == clazz.fastGetConstant("ROUND_HALF_UP") ||
- roundingMode == clazz.fastGetConstant("ROUND_HALF_DOWN") ||
- roundingMode == clazz.fastGetConstant("ROUND_HALF_EVEN")) {
- c.setInternalModuleVariable("vpRoundingMode", roundingMode);
- } else {
- throw runtime.newTypeError("invalid rounding mode");
- }
- return c.searchInternalModuleVariable("vpRoundingMode");
- }
- throw runtime.newTypeError("first argument for BigDecimal#mode invalid");
- }
- private RoundingMode getRoundingMode(Ruby runtime) {
- RubyFixnum roundingMode = (RubyFixnum)runtime.fastGetClass("BigDecimal")
- .searchInternalModuleVariable("vpRoundingMode");
- return RoundingMode.valueOf((int)roundingMode.getLongValue());
- }
- private RubyBigDecimal getVpValue(IRubyObject v, boolean must) {
- if(v instanceof RubyBigDecimal) {
- return (RubyBigDecimal)v;
- } else if(v instanceof RubyFixnum || v instanceof RubyBignum) {
- String s = v.toString();
- return newInstance(getRuntime().fastGetClass("BigDecimal"),new IRubyObject[]{getRuntime().newString(s)});
- }
- if(must) {
- String err;
- if (isImmediate()) {
- ThreadContext context = getRuntime().getCurrentContext();
- err = inspect(context, this).toString();
- } else {
- err = getMetaClass().getBaseName();
- }
- throw getRuntime().newTypeError(err + " can't be coerced into BigDecimal");
- }
- return null;
- }
- private final static Pattern INFINITY_PATTERN = Pattern.compile("^([+-])?Infinity$");
- private final static Pattern NUMBER_PATTERN
- = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*");
-
- @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
- public static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) {
- BigDecimal decimal;
- if (args.length == 0) {
- decimal = new BigDecimal(0);
- } else {
- String strValue = args[0].convertToString().toString();
- strValue = strValue.trim();
- if ("NaN".equals(strValue)) {
- return newNaN(recv.getRuntime());
- }
- Matcher m = INFINITY_PATTERN.matcher(strValue);
- if (m.matches()) {
- int sign = 1;
- String signGroup = m.group(1);
- if ("-".equals(signGroup)) {
- sign = -1;
- }
- return newInfinity(recv.getRuntime(), sign);
- }
- // Clean-up string representation so that it could be understood
- // by Java's BigDecimal. Not terribly efficient for now.
- // 1. MRI allows d and D as exponent separators
- strValue = strValue.replaceFirst("[dD]", "E");
- // 2. MRI allows underscores anywhere
- strValue = strValue.replaceAll("_", "");
- // 3. MRI ignores the trailing junk
- strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1");
- try {
- decimal = new BigDecimal(strValue);
- } catch(NumberFormatException e) {
- decimal = new BigDecimal(0);
- }
- if (decimal.signum() == 0) {
- // MRI behavior: -0 and +0 are two different things
- if (strValue.matches("^\\s*-.*")) {
- return newZero(recv.getRuntime(), -1);
- } else {
- return newZero(recv.getRuntime(), 1);
- }
- }
- }
- return new RubyBigDecimal(recv.getRuntime(), decimal);
- }
- private static RubyBigDecimal newZero(Ruby runtime, int sign) {
- RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
- if (sign < 0) {
- rbd.zeroSign = -1;
- } else {
- rbd.zeroSign = 1;
- }
- return rbd;
- }
- private static RubyBigDecimal newNaN(Ruby runtime) {
- RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
- rbd.isNaN = true;
- return rbd;
- }
-
- private static RubyBigDecimal newInfinity(Ruby runtime, int sign) {
- RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
- if (sign < 0) {
- rbd.infinitySign = -1;
- } else {
- rbd.infinitySign = 1;
- }
- return rbd;
- }
- private RubyBigDecimal setResult() {
- return setResult(0);
- }
- private RubyBigDecimal setResult(int scale) {
- int prec = RubyFixnum.fix2int(getRuntime().fastGetClass("BigDecimal").searchInternalModuleVariable("vpPrecLimit"));
- int prec2 = Math.max(scale,prec);
- if(prec2 > 0 && this.value.scale() > (prec2-getExponent())) {
- this.value = this.value.setScale(prec2-getExponent(),BigDecimal.ROUND_HALF_UP);
- }
- return this;
- }
-
- @JRubyMethod(name = "hash")
- public RubyFixnum hash() {
- return getRuntime().newFixnum(value.hashCode());
- }
- @JRubyMethod(name = {"%", "modulo"}, required = 1)
- public IRubyObject op_mod(ThreadContext context, IRubyObject arg) {
- // TODO: full-precision remainder is 1000x slower than MRI!
- Ruby runtime = context.getRuntime();
- if (isInfinity() || isNaN()) {
- return newNaN(runtime);
- }
- RubyBigDecimal val = getVpValue(arg, false);
- if (val == null) {
- return callCoerced(context, "%", arg, true);
- }
- if (val.isInfinity() || val.isNaN() || val.isZero()) {
- return newNaN(runtime);
- }
- // Java and MRI definitions of modulo are different.
- BigDecimal modulo = value.remainder(val.value);
- if (modulo.signum() * val.value.signum() < 0) {
- modulo = modulo.add(val.value);
- }
- return new RubyBigDecimal(runtime, modulo).setResult();
- }
- @JRubyMethod(name = "remainder", required = 1)
- public IRubyObject remainder(ThreadContext context, IRubyObject arg) {
- // TODO: full-precision remainder is 1000x slower than MRI!
- Ruby runtime = context.getRuntime();
- if (isInfinity() || isNaN()) {
- return newNaN(runtime);
- }
- RubyBigDecimal val = getVpValue(arg,false);
- if (val == null) {
- return callCoerced(context, "remainder", arg, true);
- }
- if (val.isInfinity() || val.isNaN() || val.isZero()) {
- return newNaN(runtime);
- }
- // Java and MRI definitions of remainder are the same.
- return new RubyBigDecimal(runtime, value.remainder(val.value)).setResult();
- }
- @JRubyMethod(name = "*", required = 1)
- public IRubyObject op_mul(ThreadContext context, IRubyObject arg) {
- return mult2(context, arg, RubyFixnum.zero(context.getRuntime()));
- }
- @JRubyMethod(name = "mult", required = 2)
- public IRubyObject mult2(ThreadContext context, IRubyObject b, IRubyObject n) {
- Ruby runtime = context.getRuntime();
- RubyBigDecimal val = getVpValue(b,false);
- if(val == null) {
- // TODO: what about n arg?
- return callCoerced(context, "*", b);
- }
- int digits = RubyNumeric.fix2int(n);
- if (isNaN() || val.isNaN()) {
- return newNaN(runtime);
- }
- if ((isInfinity() && val.isZero()) || (isZero() && val.isInfinity())) {
- return newNaN(runtime);
- }
- if (isZero() || val.isZero()) {
- int sign1 = isZero()? zeroSign : value.signum();
- int sign2 = val.isZero() ? val.zeroSign : val.value.signum();
- return newZero(runtime, sign1 * sign2);
- }
- if (isInfinity() || val.isInfinity()) {
- int sign1 = isInfinity() ? infinitySign : value.signum();
- int sign2 = val.isInfinity() ? val.infinitySign : val.value.signum();
- return newInfinity(runtime, sign1 * sign2);
- }
- BigDecimal res = value.multiply(val.value);
- if (res.precision() > digits) {
- // TODO: rounding mode should not be hard-coded. See #mode.
- res = res.round(new MathContext(digits, RoundingMode.HALF_UP));
- }
- return new RubyBigDecimal(runtime, res).setResult();
- }
-
- @JRubyMethod(name = {"**", "power"}, required = 1)
- public IRubyObject op_pow(IRubyObject arg) {
- if (!(arg instanceof RubyFixnum)) {
- throw getRuntime().newTypeError("wrong argument type " + arg.getMetaClass() + " (expected Fixnum)");
- }
- if (isNaN() || isInfinity()) {
- return newNaN(getRuntime());
- }
- int times = RubyNumeric.fix2int(arg.convertToInteger());
- if (times < 0) {
- if (isZero()) {
- return newInfinity(getRuntime(), value.signum());
- }
- // Note: MRI has a very non-trivial way of calculating the precision,
- // so we use very simple approximation here:
- int precision = (-times + 4) * (getAllDigits().length() + 4);
- return new RubyBigDecimal(getRuntime(),
- value.pow(times, new MathContext(precision, RoundingMode.HALF_UP)));
- } else {
- return new RubyBigDecimal(getRuntime(), value.pow(times));
- }
- }
- @JRubyMethod(name = "+", required = 1, frame=true)
- public IRubyObject op_plus(ThreadContext context, IRubyObject b) {
- return addInternal(context, b, "add", RubyFixnum.zero(context.getRuntime()));
- }
- @JRubyMethod(name = "add", required = 2, frame=true)
- public IRubyObject add2(ThreadContext context, IRubyObject b, IRubyObject digits) {
- return addInternal(context, b, "add", digits);
- }
- private IRubyObject addInternal(ThreadContext context, IRubyObject b, String op, IRubyObject digits) {
- Ruby runtime = context.getRuntime();
- int prec = getPositiveInt(context, digits);
- RubyBigDecimal val = getVpValue(b, false);
- if (val == null) {
- // TODO:
- // MRI behavior: Call "+" or "add", depending on the call.
- // But this leads to exceptions when Floats are added. See:
- // http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17374
- // return callCoerced(context, op, b, true); -- this is MRI behavior.
- // We'll use ours for now, thus providing an ability to add Floats.
- return callCoerced(context, "+", b, true);
- }
- IRubyObject res = handleAddSpecialValues(val);
- if (res != null) {
- return res;
- }
- RoundingMode roundMode = getRoundingMode(runtime);
- return new RubyBigDecimal(runtime, value.add(
- val.value, new MathContext(prec, roundMode))); // TODO: why this: .setResult();
- }
- private int getPositiveInt(ThreadContext context, IRubyObject arg) {
- Ruby runtime = context.getRuntime();
- if (arg instanceof RubyFixnum) {
- int value = RubyNumeric.fix2int(arg);
- if (value < 0) {
- throw runtime.newArgumentError("argument must be positive");
- }
- return value;
- } else {
- throw runtime.newTypeError(arg, runtime.getFixnum());
- }
- }
- private IRubyObject handleAddSpecialValues(RubyBigDecimal val) {
- if (isNaN() || val.isNaN) {
- return newNaN(getRuntime());
- }
- // TODO: don't calculate the same value 3 times
- if (infinitySign * val.infinitySign > 0) {
- return isInfinity() ? this : val;
- }
- if (infinitySign * val.infinitySign < 0) {
- return newNaN(getRuntime());
- }
- if (infinitySign * val.infinitySign == 0) {
- int sign = infinitySign + val.infinitySign;
- if (sign != 0) {
- return newInfinity(getRuntime(), sign);
- }
- }
- return null;
- }
- @JRubyMethod(name = "+@")
- public IRubyObject op_uplus() {
- return this;
- }
-
- @JRubyMethod(name = "-", required = 1)
- public IRubyObject op_minus(ThreadContext context, IRubyObject arg) {
- RubyBigDecimal val = getVpValue(arg, false);
- if(val == null) {
- return callCoerced(context, "-", arg);
- }
- RubyBigDecimal res = handleMinusSpecialValues(val);
- if (res != null) {
- return res;
- }
- return new RubyBigDecimal(getRuntime(),value.subtract(val.value)).setResult();
- }
- @JRubyMethod(name = "sub", required = 2)
- public IRubyObject sub2(ThreadContext context, IRubyObject b, IRubyObject n) {
- RubyBigDecimal val = getVpValue(b, false);
- if(val == null) {
- return callCoerced(context, "-", b);
- }
- RubyBigDecimal res = handleMinusSpecialValues(val);
- if (res != null) {
- return res;
- }
- return new RubyBigDecimal(getRuntime(),value.subtract(val.value)).setResult();
- }
- private RubyBigDecimal handleMinusSpecialValues(RubyBigDecimal val) {
- if (isNaN() || val.isNaN()) {
- return newNaN(getRuntime());
- }
-
- // TODO: 3 times calculate the same value below
- if (infinitySign * val.infinitySign > 0) {
- return newNaN(getRuntime());
- }
- if (infinitySign * val.infinitySign < 0) {
- return this;
- }
- if (infinitySign * val.infinitySign == 0) {
- if (isInfinity()) {
- return this;
- }
- if (val.isInfinity()) {
- return newInfinity(getRuntime(), val.infinitySign * -1);
- }
- int sign = infinitySign + val.infinitySign;
- if (sign != 0) {
- return newInfinity(getRuntime(), sign);
- }
- }
- return null;
- }
- @JRubyMethod(name = "-@")
- public IRubyObject op_uminus() {
- Ruby runtime = getRuntime();
- if (isNaN()) {
- return newNaN(runtime);
- }
- if (isInfinity()) {
- return newInfinity(runtime, -infinitySign);
- }
- if (isZero()) {
- return newZero(runtime, -zeroSign);
- }
- return new RubyBigDecimal(getRuntime(), value.negate());
- }
- @JRubyMethod(name = {"/", "quo"})
- public IRubyObject op_quo(ThreadContext context, IRubyObject other) {
- // regular division with some default precision
- // TODO: proper algorithm to set the precision
- return op_div(context, other, getRuntime().newFixnum(200));
- }
- @JRubyMethod(name = "div")
- public IRubyObject op_div(ThreadContext context, IRubyObject other) {
- // integer division
- RubyBigDecimal val = getVpValue(other, false);
- if (val == null) {
- return callCoerced(context, "div", other);
- }
- if (isNaN() || val.isZero() || val.isNaN()) {
- return newNaN(getRuntime());
- }
- if (isInfinity() || val.isInfinity()) {
- return newNaN(getRuntime());
- }
- return new RubyBigDecimal(getRuntime(),
- this.value.divideToIntegralValue(val.value)).setResult();
- }
- @JRubyMethod(name = "div")
- public IRubyObject op_div(ThreadContext context, IRubyObject other, IRubyObject digits) {
- // TODO: take BigDecimal.mode into account.
- int scale = RubyNumeric.fix2int(digits);
- RubyBigDecimal val = getVpValue(other, false);
- if (val == null) {
- return callCoerced(context, "/", other);
- }
- if (isNaN() || (isZero() && val.isZero()) || val.isNaN()) {
- return newNaN(getRuntime());
- }
- if (val.isZero()) {
- int sign1 = isInfinity() ? infinitySign : value.signum();
- return newInfinity(getRuntime(), sign1 * val.zeroSign);
- }
- if (isInfinity() && !val.isInfinity()) {
- return newInfinity(getRuntime(), infinitySign * val.value.signum());
- }
- if (!isInfinity() && val.isInfinity()) {
- return newZero(getRuntime(), value.signum() * val.infinitySign);
- }
- if (isInfinity() && val.isInfinity()) {
- return newNaN(getRuntime());
- }
- if (scale == 0) {
- // MRI behavior: "If digits is 0, the result is the same as the / operator."
- return op_quo(context, other);
- } else {
- // TODO: better algorithm to set precision needed
- int prec = Math.max(200, scale);
- return new RubyBigDecimal(getRuntime(),
- value.divide(val.value, new MathContext(prec, RoundingMode.HALF_UP))).setResult(scale);
- }
- }
- private IRubyObject cmp(ThreadContext context, IRubyObject r, char op) {
- int e = 0;
- RubyBigDecimal rb = getVpValue(r,false);
- if(rb == null) {
- IRubyObject ee = callCoerced(context, "<=>",r);
- if(ee.isNil()) {
- return getRuntime().getNil();
- }
- e = RubyNumeric.fix2int(ee);
- } else {
- if (isNaN() | rb.isNaN()) {
- return getRuntime().getNil();
- }
- if (infinitySign != 0 || rb.infinitySign != 0) {
- e = infinitySign - rb.infinitySign;
- } else {
- e = value.compareTo(rb.value);
- }
- }
- switch(op) {
- case '*': return getRuntime().newFixnum(e);
- case '=': return (e==0)?getRuntime().getTrue():getRuntime().getFalse();
- case '!': return (e!=0)?getRuntime().getTrue():getRuntime().getFalse();
- case 'G': return (e>=0)?getRuntime().getTrue():getRuntime().getFalse();
- case '>': return (e> 0)?getRuntime().getTrue():getRuntime().getFalse();
- case 'L': return (e<=0)?getRuntime().getTrue():getRuntime().getFalse();
- case '<': return (e< 0)?getRuntime().getTrue():getRuntime().getFalse();
- }
- return getRuntime().getNil();
- }
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'*');
- }
- @JRubyMethod(name = {"eql?", "==", "==="}, required = 1)
- public IRubyObject eql_p(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'=');
- }
- @JRubyMethod(name = "<", required = 1)
- public IRubyObject op_lt(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'<');
- }
- @JRubyMethod(name = "<=", required = 1)
- public IRubyObject op_le(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'L');
- }
- @JRubyMethod(name = ">", required = 1)
- public IRubyObject op_gt(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'>');
- }
- @JRubyMethod(name = ">=", required = 1)
- public IRubyObject op_ge(ThreadContext context, IRubyObject arg) {
- return cmp(context, arg,'G');
- }
- @JRubyMethod(name = "abs")
- public IRubyObject abs() {
- Ruby runtime = getRuntime();
- if (isNaN) {
- return newNaN(runtime);
- }
- if (isInfinity()) {
- return newInfinity(runtime, 1);
- }
- return new RubyBigDecimal(getRuntime(), value.abs()).setResult();
- }
- @JRubyMethod(name = "ceil", optional = 1)
- public IRubyObject ceil(IRubyObject[] args) {
- if (isNaN) {
- return newNaN(getRuntime());
- }
- if (isInfinity()) {
- return newInfinity(getRuntime(), infinitySign);
- }
- int n = 0;
- if (args.length > 0) {
- n = RubyNumeric.fix2int(args[0]);
- }
-
- if (value.scale() > n) { // rounding neccessary
- return new RubyBigDecimal(getRuntime(),
- value.setScale(n, RoundingMode.CEILING));
- } else {
- return this;
- }
- }
- @JRubyMethod(name = "coerce", required = 1)
- public IRubyObject coerce(IRubyObject other) {
- IRubyObject obj;
- if(other instanceof RubyFloat) {
- obj = getRuntime().newArray(other,to_f());
- } else {
- obj = getRuntime().newArray(getVpValue(other, true),this);
- }
- return obj;
- }
- public double getDoubleValue() { return value.doubleValue(); }
- public long getLongValue() { return value.longValue(); }
- public RubyNumeric multiplyWith(ThreadContext context, RubyInteger value) {
- return (RubyNumeric)op_mul(context, value);
- }
- public RubyNumeric multiplyWith(ThreadContext context, RubyFloat value) {
- return (RubyNumeric)op_mul(context, value);
- }
- public RubyNumeric multiplyWith(ThreadContext context, RubyBignum value) {
- return (RubyNumeric)op_mul(context, value);
- }
- @JRubyMethod(name = "divmod", required = 1)
- public IRubyObject divmod(ThreadContext context, IRubyObject other) {
- // TODO: full-precision divmod is 1000x slower than MRI!
- Ruby runtime = context.getRuntime();
- if (isInfinity() || isNaN()) {
- return RubyArray.newArray(runtime, newNaN(runtime), newNaN(runtime));
- }
- RubyBigDecimal val = getVpValue(other, false);
- if (val == null) {
- return callCoerced(context, "divmod", other, true);
- }
- if (val.isInfinity() || val.isNaN() || val.isZero()) {
- return RubyArray.newArray(runtime, newNaN(runtime), newNaN(runtime));
- }
- // Java and MRI definitions of divmod are different.
- BigDecimal[] divmod = value.divideAndRemainder(val.value);
- BigDecimal div = divmod[0];
- BigDecimal mod = divmod[1];
- if (mod.signum() * val.value.signum() < 0) {
- div = div.subtract(BigDecimal.ONE);
- mod = mod.add(val.value);
- }
- return RubyArray.newArray(runtime,
- new RubyBigDecimal(runtime, div),
- new RubyBigDecimal(runtime, mod));
- }
- @JRubyMethod(name = "exponent")
- public IRubyObject exponent() {
- return getRuntime().newFixnum(getExponent());
- }
- @JRubyMethod(name = "finite?")
- public IRubyObject finite_p() {
- if (isNaN()) {
- return getRuntime().getFalse();
- }
- return getRuntime().newBoolean(!isInfinity());
- }
- @JRubyMethod(name = "floor", optional = 1)
- public IRubyObject floor(IRubyObject[]args) {
- if (isNaN) {
- return newNaN(getRuntime());
- }
- if (isInfinity()) {
- return newInfinity(getRuntime(), infinitySign);
- }
- int n = 0;
- if (args.length > 0) {
- n = RubyNumeric.fix2int(args[0]);
- }
- if (value.scale() > n) { // rounding neccessary
- return new RubyBigDecimal(getRuntime(),
- value.setScale(n, RoundingMode.FLOOR));
- } else {
- return this;
- }
- }
-
- @JRubyMethod(name = "frac")
- public IRubyObject frac() {
- if (isNaN) {
- return newNaN(getRuntime());
- }
- if (isInfinity()) {
- return newInfinity(getRuntime(), infinitySign);
- }
- if (value.scale() > 0 && value.precision() < value.scale()) {
- return new RubyBigDecimal(getRuntime(), value);
- }
- BigDecimal val = value.subtract(((RubyBigDecimal)fix()).value);
- return new RubyBigDecimal(getRuntime(), val);
- }
- @JRubyMethod(name = "infinite?")
- public IRubyObject infinite_p() {
- if (infinitySign == 0) {
- return getRuntime().getNil();
- }
- return getRuntime().newFixnum(infinitySign);
- }
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect(ThreadContext context) {
- StringBuilder val = new StringBuilder("#<BigDecimal:").append(Integer.toHexString(System.identityHashCode(this))).append(",");
- val.append("'").append(this.callMethod(context, MethodIndex.TO_S, "to_s")).append("'").append(",");
- val.append(getSignificantDigits().length()).append("(");
- int len = getAllDigits().length();
- int pow = len / 4;
- val.append((pow + 1) * 4).append(")").append(">");
- return getRuntime().newString(val.toString());
- }
- @JRubyMethod(name = "nan?")
- public IRubyObject nan_p() {
- return getRuntime().newBoolean(isNaN);
- }
- @JRubyMethod(name = "nonzero?")
- public IRubyObject nonzero_p() {
- return isZero() ? getRuntime().getNil() : this;
- }
-
- @JRubyMethod(name = "precs")
- public IRubyObject precs() {
- final Ruby runtime = getRuntime();
- final IRubyObject[] array = new IRubyObject[2];
- array[0] = runtime.newFixnum(getSignificantDigits().length());
- int len = getAllDigits().length();
- int pow = len / 4;
- array[1] = runtime.newFixnum((pow + 1) * 4);
- return RubyArray.newArray(runtime, array);
- }
- @JRubyMethod(name = "round", optional = 2)
- public IRubyObject round(IRubyObject[] args) {
- int scale = args.length > 0 ? num2int(args[0]) : 0;
- int mode = (args.length > 1) ? javaRoundingModeFromRubyRoundingMode(args[1]) : BigDecimal.ROUND_HALF_UP;
- // JRUBY-914: Java 1.4 BigDecimal does not allow a negative scale, so we have to simulate it
- if (scale < 0) {
- // shift the decimal point just to the right of the digit to be rounded to (divide by 10**(abs(scale)))
- // -1 -> 10's digit, -2 -> 100's digit, etc.
- BigDecimal normalized = value.movePointRight(scale);
- // ...round to that digit
- BigDecimal rounded = normalized.setScale(0,mode);
- // ...and shift the result back to the left (multiply by 10**(abs(scale)))
- return new RubyBigDecimal(getRuntime(), rounded.movePointLeft(scale));
- } else {
- return new RubyBigDecimal(getRuntime(), value.setScale(scale, mode));
- }
- }
- //this relies on the Ruby rounding enumerations == Java ones, which they (currently) all are
- private int javaRoundingModeFromRubyRoundingMode(IRubyObject arg) {
- return num2int(arg);
- }
-
- @JRubyMethod(name = "sign")
- public IRubyObject sign() {
- if (isNaN()) {
- return getMetaClass().fastGetConstant("SIGN_NaN");
- }
- if (isInfinity()) {
- if (infinitySign < 0) {
- return getMetaClass().fastGetConstant("SIGN_NEGATIVE_INFINITE");
- } else {
- return getMetaClass().fastGetConstant("SIGN_POSITIVE_INFINITE");
- }
- }
- if (isZero()) {
- if (zeroSign < 0) {
- return getMetaClass().fastGetConstant("SIGN_NEGATIVE_ZERO");
- } else {
- return getMetaClass().fastGetConstant("SIGN_POSITIVE_ZERO");
- }
- }
-
- if (value.signum() < 0) {
- return getMetaClass().fastGetConstant("SIGN_NEGATIVE_FINITE");
- } else {
- return getMetaClass().fastGetConstant("SIGN_POSITIVE_FINITE");
- }
- }
- @JRubyMethod(name = "split")
- public RubyArray split() {
- final Ruby runtime = getRuntime();
- final IRubyObject[] array = new IRubyObject[4];
- // sign
- final RubyFixnum sign;
- if (isNaN) {
- sign = RubyFixnum.zero(runtime);
- } else if (isInfinity()) {
- sign = runtime.newFixnum(infinitySign);
- } else if (isZero()){
- sign = runtime.newFixnum(zeroSign);
- } else {
- sign = runtime.newFixnum(value.signum());
- }
- array[0] = sign;
- // significant digits and exponent
- final RubyString digits;
- final RubyFixnum exp;
- if (isNaN()) {
- digits = runtime.newString("NaN");
- exp = RubyFixnum.zero(runtime);
- } else if (isInfinity()) {
- digits = runtime.newString("Infinity");
- exp = RubyFixnum.zero(runtime);
- } else if (isZero()){
- digits = runtime.newString("0");
- exp = RubyFixnum.zero(runtime);
- } else {
- // normalize the value
- digits = runtime.newString(getSignificantDigits());
- exp = runtime.newFixnum(getExponent());
- }
- array[1] = digits;
- array[3] = exp;
- // base
- array[2] = runtime.newFixnum(10);
- return RubyArray.newArray(runtime, array);
- }
- // it doesn't handle special cases
- private String getSignificantDigits() {
- // TODO: no need to calculate every time.
- BigDecimal val = value.abs().stripTrailingZeros();
- return val.unscaledValue().toString();
- }
- private String getAllDigits() {
- // TODO: no need to calculate every time.
- BigDecimal val = value.abs();
- return val.unscaledValue().toString();
- }
- // it doesn't handle special cases
- private int getExponent() {
- // TODO: no need to calculate every time.
- if (isZero()) {
- return 0;
- }
- BigDecimal val = value.abs().stripTrailingZeros();
- return val.precision() - val.scale();
- }
- @JRubyMethod(name = "sqrt", required = 1)
- public IRubyObject sqrt(IRubyObject arg) {
- Ruby runtime = getRuntime();
- if (isNaN()) {
- throw runtime.newFloatDomainError("(VpSqrt) SQRT(NaN value)");
- }
- if ((isInfinity() && infinitySign < 0) || value.signum() < 0) {
- throw runtime.newFloatDomainError("(VpSqrt) SQRT(negative value)");
- }
- if (isInfinity() && infinitySign > 0) {
- return newInfinity(runtime, 1);
- }
- // NOTE: MRI's sqrt precision is limited by 100,
- // but we allow values more than 100.
- int n = RubyNumeric.fix2int(arg);
- if (n < 0) {
- throw runtime.newArgumentError("argument must be positive");
- }
- n += 4; // just in case, add a bit of extra precision
- return new RubyBigDecimal(getRuntime(),
- bigSqrt(this.value, new MathContext(n, RoundingMode.HALF_UP))).setResult();
- }
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f() {
- if (isNaN()) {
- return RubyFloat.newFloat(getRuntime(), Double.NaN);
- }
- if (isInfinity()) {
- return RubyFloat.newFloat(getRuntime(),
- infinitySign < 0 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
- }
- if (isZero()) {
- return RubyFloat.newFloat(getRuntime(),
- zeroSign < 0 ? -0.0 : 0.0);
- }
- return RubyFloat.newFloat(getRuntime(), value.doubleValue());
- }
- @JRubyMethod(name = {"to_i", "to_int"})
- public IRubyObject to_int() {
- if (isNaN() || infinitySign != 0) {
- return getRuntime().getNil();
- }
- try {
- return RubyNumeric.int2fix(getRuntime(), value.longValueExact());
- } catch (ArithmeticException ae) {
- return RubyBignum.bignorm(getRuntime(), value.toBigInteger());
- }
- }
- private String removeTrailingZeroes(String in) {
- while(in.length() > 0 && in.charAt(in.length()-1)=='0') {
- in = in.substring(0,in.length()-1);
- }
- return in;
- }
-
- public static boolean formatHasLeadingPlus(String format) {
- return format.startsWith("+");
- }
- public static boolean formatHasLeadingSpace(String format) {
- return format.startsWith(" ");
- }
- public static boolean formatHasFloatingPointNotation(String format) {
- return format.endsWith("F");
- }
- public static int formatFractionalDigitGroups(String format) {
- int groups = 0;
- Pattern p = Pattern.compile("(\\+| )?(\\d+)(E|F)?");
- Matcher m = p.matcher(format);
- if (m.matches()) {
- groups = Integer.parseInt(m.group(2));
- }
- return groups;
- }
-
- private boolean hasArg(IRubyObject[] args) {
- return args.length != 0 && !args[0].isNil();
- }
- private String format(IRubyObject[] args) {
- return args[0].toString();
- }
- private String firstArgument(IRubyObject[] args) {
- if (hasArg(args)) {
- return format(args);
- }
- return null;
- }
- private boolean posSpace(String arg) {
- if (null != arg) {
- return formatHasLeadingSpace(arg);
- }
- return false;
- }
- private boolean posSign(String arg) {
- if (null != arg) {
- return formatHasLeadingPlus(arg) || posSpace(arg);
- }
- return false;
- }
- private boolean asEngineering(String arg) {
- if (null != arg) {
- return !formatHasFloatingPointNotation(arg);
- }
- return true;
- }
- private int groups(String arg) {
- if (null != arg) {
- return formatFractionalDigitGroups(arg);
- }
- return 0;
- }
- private boolean isZero() {
- return !isNaN() && !isInfinity() && (value.signum() == 0);
- }
- private boolean isNaN() {
- return isNaN;
- }
- private boolean isInfinity() {
- return infinitySign != 0;
- }
- private String unscaledValue() {
- return value.abs().unscaledValue().toString();
- }
- private IRubyObject engineeringValue(String arg) {
- int exponent = getExponent();
- int signum = value.signum();
- StringBuilder build = new StringBuilder();
- build.append(signum == -1 ? "-" : (signum == 1 ? (posSign(arg) ? (posSpace(arg) ? " " : "+") : "") : ""));
- build.append("0.");
- if (0 == groups(arg)) {
- String s = removeTrailingZeroes(unscaledValue());
- if ("".equals(s)) {
- build.append("0");
- } else {
- build.append(s);
- }
- } else {
- int index = 0;
- String sep = "";
- while (index < unscaledValue().length()) {
- int next = index + groups(arg);
- if (next > unscaledValue().length()) {
- next = unscaledValue().length();
- }
- build.append(sep).append(unscaledValue().substring(index, next));
- sep = " ";
- index += groups(arg);
- }
- }
- build.append("E").append(exponent);
- return getRuntime().newString(build.toString());
- }
- private IRubyObject floatingPointValue(String arg) {
- String values[] = value.abs().stripTrailingZeros().toPlainString().split("\\.");
- String whole = "0";
- if (values.length > 0) {
- whole = values[0];
- }
- String after = "0";
- if (values.length > 1) {
- after = values[1];
- }
- int signum = value.signum();
- StringBuilder build = new StringBuilder();
- build.append(signum == -1 ? "-" : (signum == 1 ? (posSign(arg) ? (posSpace(arg) ? " " : "+") : "") : ""));
- if (groups(arg) == 0) {
- build.append(whole);
- if (null != after) {
- build.append(".").append(after);
- }
- } else {
- int index = 0;
- String sep = "";
- while (index < whole.length()) {
- int next = index + groups(arg);
- if (next > whole.length()) {
- next = whole.length();
- }
- build.append(sep).append(whole.substring(index, next));
- sep = " ";
- index += groups(arg);
- }
- if (null != after) {
- build.append(".");
- index = 0;
- sep = "";
- while (index < after.length()) {
- int next = index + groups(arg);
- if (next > after.length()) {
- next = after.length();
- }
- build.append(sep).append(after.substring(index, next));
- sep = " ";
- index += groups(arg);
- }
- }
- }
- return getRuntime().newString(build.toString());
- }
-
- @JRubyMethod(name = "to_s", optional = 1)
- public IRubyObject to_s(IRubyObject[] args) {
- String arg = firstArgument(args);
- if (isNaN()) {
- return getRuntime().newString("NaN");
- }
- if (infinitySign != 0) {
- if (infinitySign == -1) {
- return getRuntime().newString("-Infinity");
- } else {
- return getRuntime().newString("Infinity");
- }
- }
- if (isZero()) {
- String zero = "0.0";
- if (zeroSign < 0) {
- zero = "-" + zero;
- }
- return getRuntime().newString(zero);
- }
- if(asEngineering(arg)) {
- return engineeringValue(arg);
- } else {
- return floatingPointValue(arg);
- }
- }
- // Note: #fix has only no-arg form, but truncate allows optional parameter.
- @JRubyMethod
- public IRubyObject fix() {
- return truncate(RubyFixnum.zero(getRuntime()));
- }
- @JRubyMethod
- public IRubyObject truncate() {
- return truncate(RubyFixnum.zero(getRuntime()));
- }
- @JRubyMethod
- public IRubyObject truncate(IRubyObject arg) {
- if (isNaN) {
- return newNaN(getRuntime());
- }
- if (isInfinity()) {
- return newInfinity(getRuntime(), infinitySign);
- }
- int n = RubyNumeric.fix2int(arg);
-
- int precision = value.precision() - value.scale() + n;
-
- if (precision > 0) {
- return new RubyBigDecimal(getRuntime(),
- value.round(new MathContext(precision, RoundingMode.DOWN)));
- } else {
- // TODO: proper sign
- return new RubyBigDecimal(getRuntime(), BigDecimal.ZERO);
- }
- }
- @JRubyMethod(name = "zero?")
- public IRubyObject zero_p() {
- return getRuntime().newBoolean(isZero());
- }
- /**
- * Returns the correctly rounded square root of a positive
- * BigDecimal. This method performs the fast <i>Square Root by
- * Coupled Newton Iteration</i> algorithm by Timm Ahrendt, from
- * the book "Pi, unleashed" by Jรถrg Arndt in a neat loop.
- * <p>
- * The code is based on Frans Lelieveld's code , used here with
- * permission.
- *
- * @param squarD The number to get the root from.
- * @param rootMC Precision and rounding mode.
- * @return the root of the argument number
- * @throws ArithmeticException
- * if the argument number is negative
- * @throws IllegalArgumentException
- * if rootMC has precision 0
- */
- public static BigDecimal bigSqrt(BigDecimal squarD, MathContext rootMC) {
- // General number and precision checking
- int sign = squarD.signum();
- if (sign == -1) {
- throw new ArithmeticException("Square root of a negative number: " + squarD);
- } else if(sign == 0) {
- return squarD.round(rootMC);
- }
- int prec = rootMC.getPrecision(); // the requested precision
- if (prec == 0) {
- throw new IllegalArgumentException("Most roots won't have infinite precision = 0");
- }
- // Initial precision is that of double numbers 2^63/2 ~ 4E18
- int BITS = 62; // 63-1 an even number of number bits
- int nInit = 16; // precision seems 16 to 18 digits
- MathContext nMC = new MathContext(18, RoundingMode.HALF_DOWN);
- // Iteration variables, for the square root x and the reciprocal v
- BigDecimal x = null, e = null; // initial x: x0 ~ sqrt()
- BigDecimal v = null, g = null; // initial v: v0 = 1/(2*x)
- // Estimate the square root with the foremost 62 bits of squarD
- BigInteger bi = squarD.unscaledValue(); // bi and scale are a tandem
- int biLen = bi.bitLength();
- int shift = Math.max(0, biLen - BITS + (biLen%2 == 0 ? 0 : 1)); // even shift..
- bi = bi.shiftRight(shift); // ..floors to 62 or 63 bit BigInteger
- double root = Math.sqrt(bi.doubleValue());
- BigDecimal halfBack = new BigDecimal(BigInteger.ONE.shiftLeft(shift/2));
- int scale = squarD.scale();
- if (scale % 2 == 1) {
- root *= SQRT_10; // 5 -> 2, -5 -> -3 need half a scale more..
- }
- scale = (int) Math.floor(scale/2.); // ..where 100 -> 10 shifts the scale
- // Initial x - use double root - multiply by halfBack to unshift - set new scale
- x = new BigDecimal(root, nMC);
- x = x.multiply(halfBack, nMC); // x0 ~ sqrt()
- if (scale != 0) {
- x = x.movePointLeft(scale);
- }
- if (prec < nInit) { // for prec 15 root x0 must surely be OK
- return x.round(rootMC); // return small prec roots without iterations
- }
- // Initial v - the reciprocal
- v = BigDecimal.ONE.divide(TWO.multiply(x), nMC); // v0 = 1/(2*x)
- // Collect iteration precisions beforehand
- List<Integer> nPrecs = new ArrayList<Integer>();
- assert nInit > 3 : "Never ending loop!"; // assume nInit = 16 <= prec
- // Let m be the exact digits precision in an earlier! loop
- for (int m = prec + 1; m > nInit; m = m/2 + (m > 100 ? 1 : 2)) {
- nPrecs.add(m);
- }
- // The loop of "Square Root by Coupled Newton Iteration"
- for (int i = nPrecs.size() - 1; i > -1; i--) {
- // Increase precision - next iteration supplies n exact digits
- nMC = new MathContext(nPrecs.get(i), (i%2 == 1) ? RoundingMode.HALF_UP :
- RoundingMode.HALF_DOWN);
- // Next x // e = d - x^2
- e = squarD.subtract(x.multiply(x, nMC), nMC);
- if (i != 0) {
- x = x.add(e.multiply(v, nMC)); // x += e*v ~ sqrt()
- } else {
- x = x.add(e.multiply(v, rootMC), rootMC); // root x is ready!
- break;
- }
- // Next v // g = 1 - 2*x*v
- g = BigDecimal.ONE.subtract(TWO.multiply(x).multiply(v, nMC));
- v = v.add(g.multiply(v, nMC)); // v += g*v ~ 1/2/sqrt()
- }
- return x; // return sqrt(squarD) with precision of rootMC
- }
- }// RubyBigdecimal
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.IOException;
- import java.math.BigDecimal;
- import java.math.BigInteger;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.common.IRubyWarnings.ID;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.marshal.MarshalStream;
- import org.jruby.runtime.marshal.UnmarshalStream;
- /**
- *
- * @author jpetersen
- */
- @JRubyClass(name="Bignum", parent="Integer")
- public class RubyBignum extends RubyInteger {
- public static RubyClass createBignumClass(Ruby runtime) {
- RubyClass bignum = runtime.defineClass("Bignum", runtime.getInteger(),
- ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setBignum(bignum);
- bignum.index = ClassIndex.BIGNUM;
-
- bignum.defineAnnotatedMethods(RubyBignum.class);
- return bignum;
- }
- private static final int BIT_SIZE = 64;
- private static final long MAX = (1L << (BIT_SIZE - 1)) - 1;
- private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX);
- private static final BigInteger LONG_MIN = BigInteger.valueOf(-MAX - 1);
- private final BigInteger value;
- public RubyBignum(Ruby runtime, BigInteger value) {
- super(runtime, runtime.getBignum());
- this.value = value;
- }
-
- public int getNativeTypeIndex() {
- return ClassIndex.BIGNUM;
- }
-
- public Class<?> getJavaClass() {
- return BigInteger.class;
- }
- public static RubyBignum newBignum(Ruby runtime, long value) {
- return newBignum(runtime, BigInteger.valueOf(value));
- }
- public static RubyBignum newBignum(Ruby runtime, double value) {
- return newBignum(runtime, new BigDecimal(value).toBigInteger());
- }
- public static RubyBignum newBignum(Ruby runtime, BigInteger value) {
- return new RubyBignum(runtime, value);
- }
- public static RubyBignum newBignum(Ruby runtime, String value) {
- return new RubyBignum(runtime, new BigInteger(value));
- }
- public double getDoubleValue() {
- return big2dbl(this);
- }
- public long getLongValue() {
- return big2long(this);
- }
- /** Getter for property value.
- * @return Value of property value.
- */
- public BigInteger getValue() {
- return value;
- }
- /* ================
- * Utility Methods
- * ================
- */
- /* If the value will fit in a Fixnum, return one of those. */
- /** rb_big_norm
- *
- */
- public static RubyInteger bignorm(Ruby runtime, BigInteger bi) {
- if (bi.compareTo(LONG_MIN) < 0 || bi.compareTo(LONG_MAX) > 0) {
- return newBignum(runtime, bi);
- }
- return runtime.newFixnum(bi.longValue());
- }
- /** rb_big2long
- *
- */
- public static long big2long(RubyBignum value) {
- BigInteger big = value.getValue();
- if (big.compareTo(LONG_MIN) < 0 || big.compareTo(LONG_MAX) > 0) {
- throw value.getRuntime().newRangeError("bignum too big to convert into `long'");
- }
- return big.longValue();
- }
- /** rb_big2dbl
- *
- */
- public static double big2dbl(RubyBignum value) {
- BigInteger big = value.getValue();
- double dbl = convertToDouble(big);
- if (dbl == Double.NEGATIVE_INFINITY || dbl == Double.POSITIVE_INFINITY) {
- value.getRuntime().getWarnings().warn(ID.BIGNUM_FROM_FLOAT_RANGE, "Bignum out of Float range");
- }
- return dbl;
- }
-
- private IRubyObject checkShiftDown(RubyBignum other) {
- if (other.value.signum() == 0) return RubyFixnum.zero(getRuntime());
- if (value.compareTo(LONG_MIN) < 0 || value.compareTo(LONG_MAX) > 0) {
- return other.value.signum() >= 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.minus_one(getRuntime());
- }
- return getRuntime().getNil();
- }
- /**
- * BigInteger#doubleValue is _really_ slow currently.
- * This is faster, and mostly correct (?)
- */
- static double convertToDouble(BigInteger bigint) {
- byte[] arr = bigint.toByteArray();
- double res = 0;
- double acc = 1;
- for (int i = arr.length - 1; i > 0 ; i--)
- {
- res += (double) (arr[i] & 0xff) * acc;
- acc *= 256;
- }
- res += (double) arr[0] * acc; // final byte sign is significant
- return res;
- }
-
- /** rb_int2big
- *
- */
- public static BigInteger fix2big(RubyFixnum arg) {
- return BigInteger.valueOf(arg.getLongValue());
- }
- /* ================
- * Instance Methods
- * ================
- */
- /** rb_big_to_s
- *
- */
- @JRubyMethod(name = "to_s", optional = 1)
- public IRubyObject to_s(IRubyObject[] args) {
- int base = args.length == 0 ? 10 : num2int(args[0]);
- if (base < 2 || base > 36) {
- throw getRuntime().newArgumentError("illegal radix " + base);
- }
- return getRuntime().newString(getValue().toString(base));
- }
- /** rb_big_coerce
- *
- */
- @JRubyMethod(name = "coerce", required = 1)
- public IRubyObject coerce(IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return getRuntime().newArray(newBignum(getRuntime(), ((RubyFixnum) other).getLongValue()), this);
- } else if (other instanceof RubyBignum) {
- return getRuntime().newArray(newBignum(getRuntime(), ((RubyBignum) other).getValue()), this);
- }
- throw getRuntime().newTypeError("Can't coerce " + other.getMetaClass().getName() + " to Bignum");
- }
- /** rb_big_uminus
- *
- */
- @JRubyMethod(name = "-@")
- public IRubyObject op_uminus() {
- return bignorm(getRuntime(), value.negate());
- }
- /** rb_big_plus
- *
- */
- @JRubyMethod(name = "+", required = 1)
- public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return addFixnum((RubyFixnum)other);
- } else if (other instanceof RubyBignum) {
- return addBignum((RubyBignum)other);
- } else if (other instanceof RubyFloat) {
- return addFloat((RubyFloat)other);
- }
- return addOther(context, other);
- }
-
- private IRubyObject addFixnum(RubyFixnum other) {
- return bignorm(getRuntime(), value.add(fix2big(other)));
- }
-
- private IRubyObject addBignum(RubyBignum other) {
- return bignorm(getRuntime(), value.add(other.value));
- }
-
- private IRubyObject addFloat(RubyFloat other) {
- return RubyFloat.newFloat(getRuntime(), big2dbl(this) + other.getDoubleValue());
- }
-
- private IRubyObject addOther(ThreadContext context, IRubyObject other) {
- return coerceBin(context, "+", other);
- }
- /** rb_big_minus
- *
- */
- @JRubyMethod(name = "-", required = 1)
- public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return subtractFixnum((RubyFixnum)other);
- } else if (other instanceof RubyBignum) {
- return subtractBignum((RubyBignum)other);
- } else if (other instanceof RubyFloat) {
- return subtractFloat((RubyFloat)other);
- }
- return subtractOther(context, other);
- }
-
- private IRubyObject subtractFixnum(RubyFixnum other) {
- return bignorm(getRuntime(), value.subtract(fix2big(((RubyFixnum) other))));
- }
-
- private IRubyObject subtractBignum(RubyBignum other) {
- return bignorm(getRuntime(), value.subtract(((RubyBignum) other).value));
- }
-
- private IRubyObject subtractFloat(RubyFloat other) {
- return RubyFloat.newFloat(getRuntime(), big2dbl(this) - ((RubyFloat) other).getDoubleValue());
- }
-
- private IRubyObject subtractOther(ThreadContext context, IRubyObject other) {
- return coerceBin(context, "-", other);
- }
- /** rb_big_mul
- *
- */
- @JRubyMethod(name = "*", required = 1)
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return bignorm(getRuntime(), value.multiply(fix2big(((RubyFixnum) other))));
- }
- if (other instanceof RubyBignum) {
- return bignorm(getRuntime(), value.multiply(((RubyBignum) other).value));
- } else if (other instanceof RubyFloat) {
- return RubyFloat.newFloat(getRuntime(), big2dbl(this) * ((RubyFloat) other).getDoubleValue());
- }
- return coerceBin(context, "*", other);
- }
- /**
- * rb_big_divide. Shared part for both "/" and "div" operations.
- */
- private IRubyObject op_divide(ThreadContext context, IRubyObject other, String op) {
- assert ("/".equals(op) || "div".equals(op));
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big((RubyFixnum) other);
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else if (other instanceof RubyFloat) {
- double div = big2dbl(this) / ((RubyFloat) other).getDoubleValue();
- if ("/".equals(op)) {
- return RubyFloat.newFloat(getRuntime(),
- big2dbl(this) / ((RubyFloat) other).getDoubleValue());
- } else {
- return RubyNumeric.dbl2num(getRuntime(), div);
- }
- } else {
- return coerceBin(context, op, other);
- }
- if (otherValue.equals(BigInteger.ZERO)) {
- throw getRuntime().newZeroDivisionError();
- }
- BigInteger[] results = value.divideAndRemainder(otherValue);
- if ((value.signum() * otherValue.signum()) == -1 && results[1].signum() != 0) {
- return bignorm(getRuntime(), results[0].subtract(BigInteger.ONE));
- }
- return bignorm(getRuntime(), results[0]);
- }
- /** rb_big_div
- *
- */
- @JRubyMethod(name = {"/"}, required = 1)
- public IRubyObject op_div(ThreadContext context, IRubyObject other) {
- return op_divide(context, other, "/");
- }
- /** rb_big_idiv
- *
- */
- @JRubyMethod(name = {"div"}, required = 1)
- public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
- return op_divide(context, other, "div");
- }
- /** rb_big_divmod
- *
- */
- @JRubyMethod(name = "divmod", required = 1)
- public IRubyObject divmod(ThreadContext context, IRubyObject other) {
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big((RubyFixnum) other);
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else {
- return coerceBin(context, "divmod", other);
- }
- if (otherValue.equals(BigInteger.ZERO)) {
- throw getRuntime().newZeroDivisionError();
- }
- BigInteger[] results = value.divideAndRemainder(otherValue);
- if ((value.signum() * otherValue.signum()) == -1 && results[1].signum() != 0) {
- results[0] = results[0].subtract(BigInteger.ONE);
- results[1] = otherValue.add(results[1]);
- }
- final Ruby runtime = getRuntime();
- return RubyArray.newArray(getRuntime(), bignorm(runtime, results[0]), bignorm(runtime, results[1]));
- }
- /** rb_big_modulo
- *
- */
- @JRubyMethod(name = {"%", "modulo"}, required = 1)
- public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big((RubyFixnum) other);
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else {
- return coerceBin(context, "%", other);
- }
- if (otherValue.equals(BigInteger.ZERO)) {
- throw getRuntime().newZeroDivisionError();
- }
- BigInteger result = value.mod(otherValue.abs());
- if (otherValue.signum() == -1 && result.signum() != 0) {
- result = otherValue.add(result);
- }
- return bignorm(getRuntime(), result);
- }
- /** rb_big_remainder
- *
- */
- @JRubyMethod(name = "remainder", required = 1)
- public IRubyObject remainder(ThreadContext context, IRubyObject other) {
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big(((RubyFixnum) other));
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else {
- return coerceBin(context, "remainder", other);
- }
- if (otherValue.equals(BigInteger.ZERO)) {
- throw getRuntime().newZeroDivisionError();
- }
- return bignorm(getRuntime(), value.remainder(otherValue));
- }
- /** rb_big_quo
- *
- */
- @JRubyMethod(name = "quo", required = 1)
- public IRubyObject quo(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyNumeric) {
- return RubyFloat.newFloat(getRuntime(), big2dbl(this) / ((RubyNumeric) other).getDoubleValue());
- } else {
- return coerceBin(context, "quo", other);
- }
- }
- /** rb_big_pow
- *
- */
- @JRubyMethod(name = {"**", "power"}, required = 1)
- public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
- double d;
- if (other instanceof RubyFixnum) {
- RubyFixnum fix = (RubyFixnum) other;
- long fixValue = fix.getLongValue();
- // MRI issuses warning here on (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024)
- if (((value.bitLength() + 7) / 8) * 4 * Math.abs(fixValue) > 1024 * 1024) {
- getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", fixValue);
- }
- if (fixValue >= 0) {
- return bignorm(getRuntime(), value.pow((int) fixValue)); // num2int is also implemented
- } else {
- return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), (double)fixValue));
- }
- } else if (other instanceof RubyBignum) {
- d = ((RubyBignum) other).getDoubleValue();
- getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", d);
- } else if (other instanceof RubyFloat) {
- d = ((RubyFloat) other).getDoubleValue();
- } else {
- return coerceBin(context, "**", other);
- }
- return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), d));
- }
- /** rb_big_pow
- *
- */
- @JRubyMethod(name = {"**", "power"}, required = 1, compat = CompatVersion.RUBY1_9)
- public IRubyObject op_pow_19(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- if (other == RubyFixnum.zero(runtime)) return RubyFixnum.one(runtime);
- double d;
- if (other instanceof RubyFixnum) {
- RubyFixnum fix = (RubyFixnum) other;
- long fixValue = fix.getLongValue();
-
- if (fixValue < 0) {
- return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
- }
- // MRI issuses warning here on (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024)
- if (((value.bitLength() + 7) / 8) * 4 * Math.abs(fixValue) > 1024 * 1024) {
- getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", fixValue);
- }
- if (fixValue >= 0) {
- return bignorm(runtime, value.pow((int) fixValue)); // num2int is also implemented
- } else {
- return RubyFloat.newFloat(runtime, Math.pow(big2dbl(this), (double)fixValue));
- }
- } else if (other instanceof RubyBignum) {
- if (other.callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue()) {
- return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
- }
- d = ((RubyBignum) other).getDoubleValue();
- getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", d);
- } else if (other instanceof RubyFloat) {
- d = ((RubyFloat) other).getDoubleValue();
- } else {
- return coerceBin(context, "**", other);
- }
- return RubyNumeric.dbl2num(runtime, Math.pow(big2dbl(this), d));
- }
-
- /** rb_big_and
- *
- */
- @JRubyMethod(name = "&", required = 1)
- public IRubyObject op_and(ThreadContext context, IRubyObject other) {
- other = other.convertToInteger();
- if (other instanceof RubyBignum) {
- return bignorm(getRuntime(), value.and(((RubyBignum) other).value));
- } else if(other instanceof RubyFixnum) {
- return bignorm(getRuntime(), value.and(fix2big((RubyFixnum)other)));
- }
- return coerceBin(context, "&", other);
- }
- /** rb_big_or
- *
- */
- @JRubyMethod(name = "|", required = 1)
- public IRubyObject op_or(ThreadContext context, IRubyObject other) {
- other = other.convertToInteger();
- if (other instanceof RubyBignum) {
- return bignorm(getRuntime(), value.or(((RubyBignum) other).value));
- }
- if (other instanceof RubyFixnum) { // no bignorm here needed
- return bignorm(getRuntime(), value.or(fix2big((RubyFixnum)other)));
- }
- return coerceBin(context, "|", other);
- }
- /** rb_big_xor
- *
- */
- @JRubyMethod(name = "^", required = 1)
- public IRubyObject op_xor(ThreadContext context, IRubyObject other) {
- other = other.convertToInteger();
- if (other instanceof RubyBignum) {
- return bignorm(getRuntime(), value.xor(((RubyBignum) other).value));
- }
- if (other instanceof RubyFixnum) {
- return bignorm(getRuntime(), value.xor(BigInteger.valueOf(((RubyFixnum) other).getLongValue())));
- }
- return coerceBin(context, "^", other);
- }
- /** rb_big_neg
- *
- */
- @JRubyMethod(name = "~")
- public IRubyObject op_neg() {
- return RubyBignum.newBignum(getRuntime(), value.not());
- }
- /** rb_big_lshift
- *
- */
- @JRubyMethod(name = "<<", required = 1)
- public IRubyObject op_lshift(IRubyObject other) {
- long shift;
- boolean neg = false;
- for (;;) {
- if (other instanceof RubyFixnum) {
- shift = ((RubyFixnum)other).getLongValue();
- if (shift < 0) {
- neg = true;
- shift = -shift;
- }
- break;
- } else if (other instanceof RubyBignum) {
- RubyBignum otherBignum = (RubyBignum)other;
- if (otherBignum.value.signum() < 0) {
- IRubyObject tmp = otherBignum.checkShiftDown(this);
- if (!tmp.isNil()) return tmp;
- neg = true;
- }
- shift = big2long(otherBignum);
- break;
- }
- other = other.convertToInteger();
- }
- return bignorm(getRuntime(), neg ? value.shiftRight((int)shift) : value.shiftLeft((int)shift));
- }
- /** rb_big_rshift
- *
- */
- @JRubyMethod(name = ">>", required = 1)
- public IRubyObject op_rshift(IRubyObject other) {
- long shift;
- boolean neg = false;
- for (;;) {
- if (other instanceof RubyFixnum) {
- shift = ((RubyFixnum)other).getLongValue();
- if (shift < 0) {
- neg = true;
- shift = -shift;
- }
- break;
- } else if (other instanceof RubyBignum) {
- RubyBignum otherBignum = (RubyBignum)other;
- if (otherBignum.value.signum() >= 0) {
- IRubyObject tmp = otherBignum.checkShiftDown(this);
- if (!tmp.isNil()) return tmp;
- } else {
- neg = true;
- }
- shift = big2long(otherBignum);
- break;
- }
- other = other.convertToInteger();
- }
- return bignorm(getRuntime(), neg ? value.shiftLeft((int)shift) : value.shiftRight((int)shift));
- }
- /** rb_big_aref
- *
- */
- @JRubyMethod(name = "[]", required = 1)
- public RubyFixnum op_aref(IRubyObject other) {
- if (other instanceof RubyBignum) {
- if (((RubyBignum) other).value.signum() >= 0 || value.signum() == -1) {
- return RubyFixnum.zero(getRuntime());
- }
- return RubyFixnum.one(getRuntime());
- }
- long position = num2long(other);
- if (position < 0 || position > Integer.MAX_VALUE) {
- return RubyFixnum.zero(getRuntime());
- }
-
- return value.testBit((int)position) ? RubyFixnum.one(getRuntime()) : RubyFixnum.zero(getRuntime());
- }
- /** rb_big_cmp
- *
- */
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big((RubyFixnum) other);
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else if (other instanceof RubyFloat) {
- return dbl_cmp(getRuntime(), big2dbl(this), ((RubyFloat) other).getDoubleValue());
- } else {
- return coerceCmp(context, "<=>", other);
- }
- // wow, the only time we can use the java protocol ;)
- return RubyFixnum.newFixnum(getRuntime(), value.compareTo(otherValue));
- }
- /** rb_big_eq
- *
- */
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(IRubyObject other) {
- final BigInteger otherValue;
- if (other instanceof RubyFixnum) {
- otherValue = fix2big((RubyFixnum) other);
- } else if (other instanceof RubyBignum) {
- otherValue = ((RubyBignum) other).value;
- } else if (other instanceof RubyFloat) {
- double a = ((RubyFloat) other).getDoubleValue();
- if (Double.isNaN(a)) {
- return getRuntime().getFalse();
- }
- return RubyBoolean.newBoolean(getRuntime(), a == big2dbl(this));
- } else {
- return other.op_eqq(getRuntime().getCurrentContext(), this);
- }
- return RubyBoolean.newBoolean(getRuntime(), value.compareTo(otherValue) == 0);
- }
- /** rb_big_eql
- *
- */
- @JRubyMethod(name = {"eql?", "==="}, required = 1)
- public IRubyObject eql_p(IRubyObject other) {
- if (other instanceof RubyBignum) {
- return value.compareTo(((RubyBignum)other).value) == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
- }
- return getRuntime().getFalse();
- }
- /** rb_big_hash
- *
- */
- @JRubyMethod(name = "hash")
- public RubyFixnum hash() {
- return getRuntime().newFixnum(value.hashCode());
- }
- /** rb_big_to_f
- *
- */
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f() {
- return RubyFloat.newFloat(getRuntime(), getDoubleValue());
- }
- /** rb_big_abs
- *
- */
- @JRubyMethod(name = "abs")
- public IRubyObject abs() {
- return RubyBignum.newBignum(getRuntime(), value.abs());
- }
- /** rb_big_size
- *
- */
- @JRubyMethod(name = "size")
- public IRubyObject size() {
- return getRuntime().newFixnum((value.bitLength() + 7) / 8);
- }
- public static void marshalTo(RubyBignum bignum, MarshalStream output) throws IOException {
- output.registerLinkTarget(bignum);
- output.write(bignum.value.signum() >= 0 ? '+' : '-');
-
- BigInteger absValue = bignum.value.abs();
-
- byte[] digits = absValue.toByteArray();
-
- boolean oddLengthNonzeroStart = (digits.length % 2 != 0 && digits[0] != 0);
- int shortLength = digits.length / 2;
- if (oddLengthNonzeroStart) {
- shortLength++;
- }
- output.writeInt(shortLength);
-
- for (int i = 1; i <= shortLength * 2 && i <= digits.length; i++) {
- output.write(digits[digits.length - i]);
- }
-
- if (oddLengthNonzeroStart) {
- // Pad with a 0
- output.write(0);
- }
- }
- public static RubyNumeric unmarshalFrom(UnmarshalStream input) throws IOException {
- boolean positive = input.readUnsignedByte() == '+';
- int shortLength = input.unmarshalInt();
- // BigInteger required a sign byte in incoming array
- byte[] digits = new byte[shortLength * 2 + 1];
- for (int i = digits.length - 1; i >= 1; i--) {
- digits[i] = input.readSignedByte();
- }
- BigInteger value = new BigInteger(digits);
- if (!positive) {
- value = value.negate();
- }
- RubyNumeric result = bignorm(input.getRuntime(), value);
- input.registerLinkTarget(result);
- return result;
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Binding;
- import org.jruby.runtime.Frame;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.Visibility;
- import org.jruby.runtime.builtin.IRubyObject;
- /**
- * @author jpetersen
- */
- @JRubyClass(name="Binding")
- public class RubyBinding extends RubyObject {
- private Binding binding;
- public RubyBinding(Ruby runtime, RubyClass rubyClass, Binding binding) {
- super(runtime, rubyClass);
-
- this.binding = binding;
- }
- private RubyBinding(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
- }
-
- private static ObjectAllocator BINDING_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyBinding instance = new RubyBinding(runtime, klass);
-
- return instance;
- }
- };
-
- public static RubyClass createBindingClass(Ruby runtime) {
- RubyClass bindingClass = runtime.defineClass("Binding", runtime.getObject(), BINDING_ALLOCATOR);
- runtime.setBinding(bindingClass);
-
- bindingClass.defineAnnotatedMethods(RubyBinding.class);
-
- return bindingClass;
- }
- public Binding getBinding() {
- return binding;
- }
- // Proc class
-
- public static RubyBinding newBinding(Ruby runtime, Binding binding) {
- return new RubyBinding(runtime, runtime.getBinding(), binding);
- }
- public static RubyBinding newBinding(Ruby runtime) {
- ThreadContext context = runtime.getCurrentContext();
-
- // FIXME: We should be cloning, not reusing: frame, scope, dynvars, and potentially iter/block info
- Frame frame = context.getCurrentFrame();
- Binding binding = new Binding(frame, context.getBindingRubyClass(), context.getCurrentScope());
-
- return new RubyBinding(runtime, runtime.getBinding(), binding);
- }
- /**
- * Create a binding appropriate for a bare "eval", by using the previous (caller's) frame and current
- * scope.
- */
- public static RubyBinding newBindingForEval(ThreadContext context) {
- // This requires some explaining. We use Frame values when executing blocks to fill in
- // various values in ThreadContext and EvalState.eval like rubyClass, cref, and self.
- // Largely, for an eval that is using the logical binding at a place where the eval is
- // called we mostly want to use the current frames value for this. Most importantly,
- // we need that self (JRUBY-858) at this point. We also need to make sure that returns
- // jump to the right place (which happens to be the previous frame). Lastly, we do not
- // want the current frames klazz since that will be the klazz represented of self. We
- // want the class right before the eval (well we could use cref class for this too I think).
- // Once we end up having Frames created earlier I think the logic of stuff like this will
- // be better since we won't be worried about setting Frame to setup other variables/stacks
- // but just making sure Frame itself is correct...
-
- Frame previousFrame = context.getPreviousFrame();
- Frame currentFrame = context.getCurrentFrame();
- currentFrame.setKlazz(previousFrame.getKlazz());
-
- // Set jump target to whatever the previousTarget thinks is good.
- // currentFrame.setJumpTarget(previousFrame.getJumpTarget() != null ? previousFrame.getJumpTarget() : previousFrame);
-
- Binding binding = new Binding(previousFrame, context.getBindingRubyClass(), context.getCurrentScope());
- Ruby runtime = context.getRuntime();
-
- return new RubyBinding(runtime, runtime.getBinding(), binding);
- }
-
- @JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context) {
- // FIXME: We should be cloning, not reusing: frame, scope, dynvars, and potentially iter/block info
- Frame frame = context.getCurrentFrame();
- binding = new Binding(frame, context.getBindingRubyClass(), context.getCurrentScope());
-
- return this;
- }
-
- @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
- @Override
- public IRubyObject initialize_copy(IRubyObject other) {
- RubyBinding otherBinding = (RubyBinding)other;
-
- binding = otherBinding.binding;
-
- return this;
- }
- }
- /*
- ***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.marshal.MarshalStream;
- /**
- *
- * @author jpetersen
- */
- @JRubyClass(name={"TrueClass", "FalseClass"})
- public class RubyBoolean extends RubyObject {
-
- public RubyBoolean(Ruby runtime, boolean value) {
- super(runtime, (value ? runtime.getTrueClass() : runtime.getFalseClass()), // Don't initialize with class
- false); // Don't put in object space
- if (!value) flags = FALSE_F;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return (flags & FALSE_F) == 0 ? ClassIndex.TRUE : ClassIndex.FALSE;
- }
-
- @Override
- public boolean isImmediate() {
- return true;
- }
- @Override
- public RubyClass getSingletonClass() {
- return metaClass;
- }
- @Override
- public Class<?> getJavaClass() {
- return boolean.class;
- }
- public static RubyClass createFalseClass(Ruby runtime) {
- RubyClass falseClass = runtime.defineClass("FalseClass", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setFalseClass(falseClass);
- falseClass.index = ClassIndex.FALSE;
-
- falseClass.defineAnnotatedMethods(False.class);
-
- falseClass.getMetaClass().undefineMethod("new");
-
- return falseClass;
- }
-
- public static RubyClass createTrueClass(Ruby runtime) {
- RubyClass trueClass = runtime.defineClass("TrueClass", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setTrueClass(trueClass);
- trueClass.index = ClassIndex.TRUE;
-
- trueClass.defineAnnotatedMethods(True.class);
-
- trueClass.getMetaClass().undefineMethod("new");
-
- return trueClass;
- }
-
- public static RubyBoolean newBoolean(Ruby runtime, boolean value) {
- return value ? runtime.getTrue() : runtime.getFalse();
- }
-
- public static class False {
- @JRubyMethod(name = "&")
- public static IRubyObject false_and(IRubyObject f, IRubyObject oth) {
- return f;
- }
- @JRubyMethod(name = "|")
- public static IRubyObject false_or(IRubyObject f, IRubyObject oth) {
- return oth.isTrue() ? f.getRuntime().getTrue() : f;
- }
- @JRubyMethod(name = "^")
- public static IRubyObject false_xor(IRubyObject f, IRubyObject oth) {
- return oth.isTrue() ? f.getRuntime().getTrue() : f;
- }
- @JRubyMethod(name = "to_s")
- public static IRubyObject false_to_s(IRubyObject f) {
- return f.getRuntime().newString("false");
- }
- }
-
- public static class True {
- @JRubyMethod(name = "&")
- public static IRubyObject true_and(IRubyObject t, IRubyObject oth) {
- return oth.isTrue() ? t : t.getRuntime().getFalse();
- }
- @JRubyMethod(name = "|")
- public static IRubyObject true_or(IRubyObject t, IRubyObject oth) {
- return t;
- }
- @JRubyMethod(name = "^")
- public static IRubyObject true_xor(IRubyObject t, IRubyObject oth) {
- return oth.isTrue() ? t.getRuntime().getFalse() : t;
- }
- @JRubyMethod(name = "to_s")
- public static IRubyObject true_to_s(IRubyObject t) {
- return t.getRuntime().newString("true");
- }
- }
-
- @Override
- public RubyFixnum id() {
- if ((flags & FALSE_F) == 0) {
- return RubyFixnum.newFixnum(getRuntime(), 2);
- } else {
- return RubyFixnum.zero(getRuntime());
- }
- }
- @Override
- public IRubyObject taint(ThreadContext context) {
- return this;
- }
- @Override
- public IRubyObject freeze(ThreadContext context) {
- return this;
- }
-
- public void marshalTo(MarshalStream output) throws java.io.IOException {
- output.write(isTrue() ? 'T' : 'F');
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Set;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyClass;
- import org.jruby.internal.runtime.methods.DynamicMethod;
- import org.jruby.internal.runtime.methods.JavaMethod;
- import org.jruby.javasupport.util.RuntimeHelpers;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.CallSite;
- import org.jruby.runtime.CallSite.InlineCachingCallSite;
- import org.jruby.runtime.CallType;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ObjectMarshal;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.Visibility;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.marshal.MarshalStream;
- import org.jruby.runtime.marshal.UnmarshalStream;
- import org.jruby.util.collections.WeakHashSet;
- /**
- *
- * @author jpetersen
- */
- @JRubyClass(name="Class", parent="Module")
- public class RubyClass extends RubyModule {
- public static final int CS_IDX_INITIALIZE = 0;
- public static final String[] CS_NAMES = {
- "initialize"
- };
- private final CallSite[] baseCallSites = new CallSite[CS_NAMES.length];
- {
- for(int i = 0; i < CS_NAMES.length; i++) {
- baseCallSites[i] = new InlineCachingCallSite(CS_NAMES[i], CallType.FUNCTIONAL);
- }
- }
-
- private CallSite[] extraCallSites;
-
- public static void createClassClass(Ruby runtime, RubyClass classClass) {
- classClass.index = ClassIndex.CLASS;
- classClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyClass;
- }
- };
-
- classClass.undefineMethod("module_function");
- classClass.undefineMethod("append_features");
- classClass.undefineMethod("extend_object");
-
- classClass.defineAnnotatedMethods(RubyClass.class);
-
- classClass.addMethod("new", new SpecificArityNew(classClass, Visibility.PUBLIC));
-
- // This is a non-standard method; have we decided to start extending Ruby?
- //classClass.defineFastMethod("subclasses", callbackFactory.getFastOptMethod("subclasses"));
-
- // FIXME: for some reason this dispatcher causes a VerifyError...
- //classClass.dispatcher = callbackFactory.createDispatcher(classClass);
- }
-
- public static final ObjectAllocator CLASS_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyClass clazz = new RubyClass(runtime);
- clazz.allocator = ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR; // Class.allocate object is not allocatable before it is initialized
- return clazz;
- }
- };
- public ObjectAllocator getAllocator() {
- return allocator;
- }
- public void setAllocator(ObjectAllocator allocator) {
- this.allocator = allocator;
- }
- @JRubyMethod(name = "allocate")
- public IRubyObject allocate() {
- if (superClass == null) throw runtime.newTypeError("can't instantiate uninitialized class");
- IRubyObject obj = allocator.allocate(runtime, this);
- if (obj.getMetaClass().getRealClass() != getRealClass()) throw runtime.newTypeError("wrong instance allocation");
- return obj;
- }
- public CallSite[] getBaseCallSites() {
- return baseCallSites;
- }
-
- public CallSite[] getExtraCallSites() {
- return extraCallSites;
- }
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.CLASS;
- }
-
- @Override
- public boolean isModule() {
- return false;
- }
- @Override
- public boolean isClass() {
- return true;
- }
- @Override
- public boolean isSingleton() {
- return false;
- }
- /** boot_defclass
- * Create an initial Object meta class before Module and Kernel dependencies have
- * squirreled themselves together.
- *
- * @param runtime we need it
- * @return a half-baked meta class for object
- */
- public static RubyClass createBootstrapClass(Ruby runtime, String name, RubyClass superClass, ObjectAllocator allocator) {
- RubyClass obj;
- if (superClass == null ) { // boot the Object class
- obj = new RubyClass(runtime);
- obj.marshal = DEFAULT_OBJECT_MARSHAL;
- } else { // boot the Module and Class classes
- obj = new RubyClass(runtime, superClass);
- }
- obj.setAllocator(allocator);
- obj.setBaseName(name);
- return obj;
- }
- private final Ruby runtime;
- private ObjectAllocator allocator; // the default allocator
- protected ObjectMarshal marshal;
- private Set<RubyClass> subclasses;
- /** separate path for MetaClass and IncludedModuleWrapper construction
- * (rb_class_boot version for MetaClasses)
- * no marshal, allocator initialization and addSubclass(this) here!
- */
- protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
- super(runtime, runtime.getClassClass(), objectSpace);
- this.runtime = runtime;
- this.superClass = superClass; // this is the only case it might be null here (in MetaClass construction)
- }
-
- /** used by CLASS_ALLOCATOR (any Class' class will be a Class!)
- * also used to bootstrap Object class
- */
- protected RubyClass(Ruby runtime) {
- super(runtime, runtime.getClassClass());
- this.runtime = runtime;
- index = ClassIndex.CLASS;
- }
-
- /** rb_class_boot (for plain Classes)
- * also used to bootstrap Module and Class classes
- */
- protected RubyClass(Ruby runtime, RubyClass superClazz) {
- this(runtime);
- superClass = superClazz;
- marshal = superClazz.marshal; // use parent's marshal
- superClazz.addSubclass(this);
-
- infectBy(superClass);
- }
-
- /**
- * A constructor which allows passing in an array of supplementary call sites.
- */
- protected RubyClass(Ruby runtime, RubyClass superClazz, CallSite[] extraCallSites) {
- this(runtime);
- this.superClass = superClazz;
- this.marshal = superClazz.marshal; // use parent's marshal
- superClazz.addSubclass(this);
-
- this.extraCallSites = extraCallSites;
-
- infectBy(superClass);
- }
- /**
- * Construct a new class with the given name scoped under Object (global)
- * and with Object as its immediate superclass.
- * Corresponds to rb_class_new in MRI.
- */
- public static RubyClass newClass(Ruby runtime, RubyClass superClass) {
- if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
- if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
- return new RubyClass(runtime, superClass);
- }
- /**
- * A variation on newClass that allow passing in an array of supplementary
- * call sites to improve dynamic invocation.
- */
- public static RubyClass newClass(Ruby runtime, RubyClass superClass, CallSite[] extraCallSites) {
- if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
- if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
- return new RubyClass(runtime, superClass, extraCallSites);
- }
- /**
- * Construct a new class with the given name, allocator, parent class,
- * and containing class. If setParent is true, the class's parent will be
- * explicitly set to the provided parent (rather than the new class just
- * being assigned to a constant in that parent).
- * Corresponds to rb_class_new/rb_define_class_id/rb_name_class/rb_set_class_path
- * in MRI.
- */
- public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent) {
- RubyClass clazz = newClass(runtime, superClass);
- clazz.setBaseName(name);
- clazz.setAllocator(allocator);
- clazz.makeMetaClass(superClass.getMetaClass());
- if (setParent) clazz.setParent(parent);
- parent.setConstant(name, clazz);
- clazz.inherit(superClass);
- return clazz;
- }
- /**
- * A variation on newClass that allows passing in an array of supplementary
- * call sites to improve dynamic invocation performance.
- */
- public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent, CallSite[] extraCallSites) {
- RubyClass clazz = newClass(runtime, superClass, extraCallSites);
- clazz.setBaseName(name);
- clazz.setAllocator(allocator);
- clazz.makeMetaClass(superClass.getMetaClass());
- if (setParent) clazz.setParent(parent);
- parent.setConstant(name, clazz);
- clazz.inherit(superClass);
- return clazz;
- }
- /** rb_make_metaclass
- *
- */
- @Override
- public RubyClass makeMetaClass(RubyClass superClass) {
- if (isSingleton()) { // could be pulled down to RubyClass in future
- MetaClass klass = new MetaClass(getRuntime(), superClass); // rb_class_boot
- setMetaClass(klass);
- klass.setAttached(this);
- klass.setMetaClass(klass);
- klass.setSuperClass(getSuperClass().getRealClass().getMetaClass());
-
- return klass;
- } else {
- return super.makeMetaClass(superClass);
- }
- }
-
- @Deprecated
- public IRubyObject invoke(ThreadContext context, IRubyObject self, int methodIndex, String name, IRubyObject[] args, CallType callType, Block block) {
- return invoke(context, self, name, args, callType, block);
- }
-
- public boolean notVisibleAndNotMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
- return !method.isCallableFrom(caller, callType) && !name.equals("method_missing");
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- CallType callType, Block block) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, caller, callType, block);
- }
- return method.call(context, self, this, name, block);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name, Block block) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, block);
- }
- return method.call(context, self, this, name, block);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject[] args, CallType callType, Block block) {
- assert args != null;
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, args, caller, callType, block);
- }
- return method.call(context, self, this, name, args, block);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject[] args, Block block) {
- assert args != null;
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, block);
- }
- return method.call(context, self, this, name, args, block);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg, CallType callType, Block block) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, caller, callType, block);
- }
- return method.call(context, self, this, name, arg, block);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg, Block block) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, block);
- }
- return method.call(context, self, this, name, arg, block);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, CallType callType, Block block) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, caller, callType, block);
- }
- return method.call(context, self, this, name, arg0, arg1, block);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, Block block) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, block);
- }
- return method.call(context, self, this, name, arg0, arg1, block);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType, Block block) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, caller, callType, block);
- }
- return method.call(context, self, this, name, arg0, arg1, arg2, block);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, block);
- }
- return method.call(context, self, this, name, arg0, arg1, arg2, block);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- CallType callType) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, caller, callType, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject[] args, CallType callType) {
- assert args != null;
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, args, caller, callType, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, args);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject[] args) {
- assert args != null;
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, args);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg, CallType callType) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, caller, callType, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, CallType callType) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, caller, callType, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg0, arg1);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg0, arg1);
- }
-
- public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType) {
- DynamicMethod method = searchMethod(name);
- IRubyObject caller = context.getFrameSelf();
- if (shouldCallMethodMissing(method, name, caller, callType)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, caller, callType, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg0, arg1, arg2);
- }
-
- public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
- IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
- DynamicMethod method = searchMethod(name);
- if (shouldCallMethodMissing(method)) {
- return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, this, name, arg0, arg1, arg2);
- }
-
- private boolean shouldCallMethodMissing(DynamicMethod method) {
- return method.isUndefined();
- }
- private boolean shouldCallMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
- return method.isUndefined() || notVisibleAndNotMethodMissing(method, name, caller, callType);
- }
-
- public IRubyObject invokeInherited(ThreadContext context, IRubyObject self, IRubyObject subclass) {
- DynamicMethod method = getMetaClass().searchMethod("inherited");
- if (method.isUndefined()) {
- return RuntimeHelpers.callMethodMissing(context, self, method, "inherited", subclass, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
- }
- return method.call(context, self, getMetaClass(), "inherited", subclass, Block.NULL_BLOCK);
- }
- /** rb_class_new_instance
- *
- */
- public IRubyObject newInstance(ThreadContext context, IRubyObject[] args, Block block) {
- IRubyObject obj = allocate();
- baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
- return obj;
- }
-
- // TODO: replace this with a smarter generated invoker that can handle 0-N args
- public static class SpecificArityNew extends JavaMethod {
- public SpecificArityNew(RubyModule implClass, Visibility visibility) {
- super(implClass, visibility);
- }
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
- RubyClass cls = (RubyClass)self;
- IRubyObject obj = cls.allocate();
- cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
- return obj;
- }
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
- RubyClass cls = (RubyClass)self;
- IRubyObject obj = cls.allocate();
- cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, block);
- return obj;
- }
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
- RubyClass cls = (RubyClass)self;
- IRubyObject obj = cls.allocate();
- cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, block);
- return obj;
- }
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
- RubyClass cls = (RubyClass)self;
- IRubyObject obj = cls.allocate();
- cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, block);
- return obj;
- }
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- RubyClass cls = (RubyClass)self;
- IRubyObject obj = cls.allocate();
- cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, arg2, block);
- return obj;
- }
- }
- /** rb_class_initialize
- *
- */
- @JRubyMethod(name = "initialize", optional = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- if (superClass != null) {
- throw getRuntime().newTypeError("already initialized class");
- }
-
- IRubyObject superObject;
- if (args.length == 0) {
- superObject = getRuntime().getObject();
- } else {
- superObject = args[0];
- checkInheritable(superObject);
- }
-
- RubyClass superClazz = (RubyClass) superObject;
- superClass = superClazz;
- allocator = superClazz.allocator;
- makeMetaClass(superClazz.getMetaClass());
-
- marshal = superClazz.marshal;
-
- superClazz.addSubclass(this);
-
- super.initialize(block);
-
- inherit(superClazz);
- return this;
- }
- /** rb_class_init_copy
- *
- */
- @JRubyMethod(name = "initialize_copy", required = 1)
- @Override
- public IRubyObject initialize_copy(IRubyObject original){
- if (superClass != null) throw runtime.newTypeError("already initialized class");
- if (original instanceof MetaClass) throw getRuntime().newTypeError("can't copy singleton class");
-
- super.initialize_copy(original);
- allocator = ((RubyClass)original).allocator;
- return this;
- }
-
- // TODO: Someday, enable.
- // @JRubyMethod(name = "subclasses", optional = 1)
- public IRubyObject subclasses(ThreadContext context, IRubyObject[] args) {
- boolean recursive = false;
- if (args.length == 1) {
- if (args[0] instanceof RubyBoolean) {
- recursive = args[0].isTrue();
- } else {
- context.getRuntime().newTypeError(args[0], context.getRuntime().fastGetClass("Boolean"));
- }
- }
-
- return RubyArray.newArray(context.getRuntime(), subclasses(recursive)).freeze(context);
- }
-
- public Collection subclasses(boolean includeDescendants) {
- if (subclasses != null) {
- Collection<RubyClass> mine = new ArrayList<RubyClass>(subclasses);
- if (includeDescendants) {
- for (RubyClass i: subclasses) {
- mine.addAll(i.subclasses(includeDescendants));
- }
- }
- return mine;
- } else {
- return Collections.EMPTY_LIST;
- }
- }
-
- public synchronized void addSubclass(RubyClass subclass) {
- if (subclasses == null) subclasses = new WeakHashSet<RubyClass>();
- subclasses.add(subclass);
- }
-
- public Ruby getClassRuntime() {
- return runtime;
- }
- public RubyClass getRealClass() {
- return this;
- }
- @JRubyMethod(name = "inherited", required = 1)
- public IRubyObject inherited(ThreadContext context, IRubyObject arg) {
- return context.getRuntime().getNil();
- }
- /** rb_class_inherited (reversed semantics!)
- *
- */
- public void inherit(RubyClass superClazz) {
- if (superClazz == null) superClazz = getRuntime().getObject();
-
- superClazz.invokeInherited(getRuntime().getCurrentContext(), superClazz, this);
- }
- /** Return the real super class of this class.
- *
- * rb_class_superclass
- *
- */
- @JRubyMethod(name = "superclass")
- public IRubyObject superclass(ThreadContext context) {
- RubyClass superClazz = superClass;
- if (superClazz == null) throw context.getRuntime().newTypeError("uninitialized class");
-
- if (isSingleton()) superClazz = metaClass;
- while (superClazz != null && superClazz.isIncluded()) superClazz = superClazz.superClass;
- return superClazz != null ? superClazz : context.getRuntime().getNil();
- }
- /** rb_check_inheritable
- *
- */
- public static void checkInheritable(IRubyObject superClass) {
- if (!(superClass instanceof RubyClass)) {
- throw superClass.getRuntime().newTypeError("superclass must be a Class (" + superClass.getMetaClass() + " given)");
- }
- if (((RubyClass)superClass).isSingleton()) {
- throw superClass.getRuntime().newTypeError("can't make subclass of virtual class");
- }
- }
- public final ObjectMarshal getMarshal() {
- return marshal;
- }
-
- public final void setMarshal(ObjectMarshal marshal) {
- this.marshal = marshal;
- }
-
- public final void marshal(Object obj, MarshalStream marshalStream) throws IOException {
- getMarshal().marshalTo(getRuntime(), obj, this, marshalStream);
- }
-
- public final Object unmarshal(UnmarshalStream unmarshalStream) throws IOException {
- return getMarshal().unmarshalFrom(getRuntime(), this, unmarshalStream);
- }
-
- public static void marshalTo(RubyClass clazz, MarshalStream output) throws java.io.IOException {
- output.registerLinkTarget(clazz);
- output.writeString(MarshalStream.getPathFromClass(clazz));
- }
- public static RubyClass unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- String name = RubyString.byteListToString(input.unmarshalString());
- RubyClass result = UnmarshalStream.getClassFromPath(input.getRuntime(), name);
- input.registerLinkTarget(result);
- return result;
- }
- protected static final ObjectMarshal DEFAULT_OBJECT_MARSHAL = new ObjectMarshal() {
- public void marshalTo(Ruby runtime, Object obj, RubyClass type,
- MarshalStream marshalStream) throws IOException {
- IRubyObject object = (IRubyObject)obj;
-
- marshalStream.registerLinkTarget(object);
- marshalStream.dumpVariables(object.getVariableList());
- }
- public Object unmarshalFrom(Ruby runtime, RubyClass type,
- UnmarshalStream unmarshalStream) throws IOException {
- IRubyObject result = type.allocate();
-
- unmarshalStream.registerLinkTarget(result);
- unmarshalStream.defaultVariablesUnmarshal(result);
- return result;
- }
- };
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.File;
- import java.net.MalformedURLException;
- import java.net.URL;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- public class RubyClassPathVariable extends RubyObject {
- public static void createClassPathVariable(Ruby runtime) {
- RubyClassPathVariable self = new RubyClassPathVariable(runtime);
- runtime.getEnumerable().extend_object(self);
- runtime.defineReadonlyVariable("$CLASSPATH", self);
-
- self.getMetaClass().defineAnnotatedMethods(RubyClassPathVariable.class);
- }
- private RubyClassPathVariable(Ruby runtime) {
- super(runtime, runtime.getObject());
- }
- @JRubyMethod(name = {"append", "<<"}, required = 1)
- public IRubyObject append(IRubyObject obj) throws Exception {
- String ss = obj.convertToString().toString();
- URL url = getURL(ss);
- getRuntime().getJRubyClassLoader().addURL(url);
- return this;
- }
-
- private URL getURL(String target) throws MalformedURLException {
- if(target.indexOf("://") == -1) {
- return new File(target).toURI().toURL();
- } else {
- return new URL(target);
- }
- }
- @JRubyMethod(name = {"size", "length"})
- public IRubyObject size() {
- return getRuntime().newFixnum(getRuntime().getJRubyClassLoader().getURLs().length);
- }
- @JRubyMethod(name = "each", frame = true)
- public IRubyObject each(Block block) {
- URL[] urls = getRuntime().getJRubyClassLoader().getURLs();
- ThreadContext ctx = getRuntime().getCurrentContext();
- for(int i=0,j=urls.length;i<j;i++) {
- block.yield(ctx, getRuntime().newString(urls[i].toString()));
- }
- return getRuntime().getNil();
- }
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- return callMethod(getRuntime().getCurrentContext(), "to_a").callMethod(getRuntime().getCurrentContext(), "to_s");
- }
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- return callMethod(getRuntime().getCurrentContext(), "to_a").callMethod(getRuntime().getCurrentContext(), "inspect");
- }
- }// RubyClassPathVariable
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyModule;
- import org.jruby.exceptions.RaiseException;
- import org.jruby.runtime.MethodIndex;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- /** Implementation of the Comparable module.
- *
- */
- @JRubyModule(name="Comparable")
- public class RubyComparable {
- public static RubyModule createComparable(Ruby runtime) {
- RubyModule comparableModule = runtime.defineModule("Comparable");
- runtime.setComparable(comparableModule);
-
- comparableModule.defineAnnotatedMethods(RubyComparable.class);
- return comparableModule;
- }
- /* ================
- * Utility Methods
- * ================
- */
- /** rb_cmpint
- *
- */
- public static int cmpint(ThreadContext context, IRubyObject val, IRubyObject a, IRubyObject b) {
- if (val.isNil()) cmperr(a, b);
- if (val instanceof RubyFixnum) return RubyNumeric.fix2int((RubyFixnum) val);
- if (val instanceof RubyBignum) return ((RubyBignum) val).getValue().signum() == -1 ? 1 : -1;
- RubyFixnum zero = RubyFixnum.zero(context.getRuntime());
-
- if (val.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue()) return 1;
- if (val.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue()) return -1;
- return 0;
- }
- /** rb_cmperr
- *
- */
- public static IRubyObject cmperr(IRubyObject recv, IRubyObject other) {
- IRubyObject target;
- if (other.isImmediate() || !(other.isNil() || other.isTrue() || other == recv.getRuntime().getFalse())) {
- target = other.inspect();
- } else {
- target = other.getType();
- }
- throw recv.getRuntime().newArgumentError("comparison of " + recv.getType() + " with " + target + " failed");
- }
- /* ================
- * Module Methods
- * ================
- */
- /** cmp_equal (cmp_eq inlined here)
- *
- */
- @JRubyMethod(name = "==", required = 1)
- public static IRubyObject op_equal(ThreadContext context, IRubyObject recv, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- if (recv == other) return runtime.getTrue();
- try {
- IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
-
- return RubyBoolean.newBoolean(runtime, cmpint(context, result, recv, other) == 0);
- } catch (RaiseException e) {
- if (e.getException().kind_of_p(context, runtime.getStandardError()).isTrue()) {
- return runtime.getNil();
- } else {
- throw e;
- }
- }
- }
- /** cmp_gt
- *
- */
- // <=> may return nil in many circumstances, e.g. 3 <=> NaN
- @JRubyMethod(name = ">", required = 1)
- public static RubyBoolean op_gt(ThreadContext context, IRubyObject recv, IRubyObject other) {
- IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
-
- if (result.isNil()) cmperr(recv, other);
- return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) > 0);
- }
- /** cmp_ge
- *
- */
- @JRubyMethod(name = ">=", required = 1)
- public static RubyBoolean op_ge(ThreadContext context, IRubyObject recv, IRubyObject other) {
- IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
-
- if (result.isNil()) cmperr(recv, other);
- return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) >= 0);
- }
- /** cmp_lt
- *
- */
- @JRubyMethod(name = "<", required = 1)
- public static RubyBoolean op_lt(ThreadContext context, IRubyObject recv, IRubyObject other) {
- IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
- if (result.isNil()) cmperr(recv, other);
- return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) < 0);
- }
- /** cmp_le
- *
- */
- @JRubyMethod(name = "<=", required = 1)
- public static RubyBoolean op_le(ThreadContext context, IRubyObject recv, IRubyObject other) {
- IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
- if (result.isNil()) cmperr(recv, other);
- return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) <= 0);
- }
- /** cmp_between
- *
- */
- @JRubyMethod(name = "between?", required = 2)
- public static RubyBoolean between_p(ThreadContext context, IRubyObject recv, IRubyObject first, IRubyObject second) {
- return context.getRuntime().newBoolean(op_lt(context, recv, first).isFalse() && op_gt(context, recv, second).isFalse());
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import static org.jruby.util.Numeric.f_abs;
- import static org.jruby.util.Numeric.f_abs2;
- import static org.jruby.util.Numeric.f_add;
- import static org.jruby.util.Numeric.f_arg;
- import static org.jruby.util.Numeric.f_conjugate;
- import static org.jruby.util.Numeric.f_denominator;
- import static org.jruby.util.Numeric.f_div;
- import static org.jruby.util.Numeric.f_divmod;
- import static org.jruby.util.Numeric.f_equal_p;
- import static org.jruby.util.Numeric.f_exact_p;
- import static org.jruby.util.Numeric.f_expt;
- import static org.jruby.util.Numeric.f_gt_p;
- import static org.jruby.util.Numeric.f_inspect;
- import static org.jruby.util.Numeric.f_lcm;
- import static org.jruby.util.Numeric.f_mul;
- import static org.jruby.util.Numeric.f_negate;
- import static org.jruby.util.Numeric.f_negative_p;
- import static org.jruby.util.Numeric.f_numerator;
- import static org.jruby.util.Numeric.f_one_p;
- import static org.jruby.util.Numeric.f_polar;
- import static org.jruby.util.Numeric.f_quo;
- import static org.jruby.util.Numeric.f_scalar_p;
- import static org.jruby.util.Numeric.f_sub;
- import static org.jruby.util.Numeric.f_to_f;
- import static org.jruby.util.Numeric.f_to_i;
- import static org.jruby.util.Numeric.f_to_r;
- import static org.jruby.util.Numeric.f_to_s;
- import static org.jruby.util.Numeric.f_xor;
- import static org.jruby.util.Numeric.f_zero_p;
- import org.jruby.anno.JRubyClass;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.runtime.Arity;
- import org.jruby.runtime.ClassIndex;
- import org.jruby.runtime.Frame;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.Visibility;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.ByteList;
- import org.jruby.util.Numeric;
- /**
- * 1.9 complex.c as of revision: 18876
- */
- @JRubyClass(name = "Complex", parent = "Numeric")
- public class RubyComplex extends RubyNumeric {
- public static RubyClass createComplexClass(Ruby runtime) {
- RubyClass complexc = runtime.defineClass("Complex", runtime.getNumeric(), COMPLEX_ALLOCATOR); // because one can Complex.send(:allocate)
- runtime.setComplex(complexc);
- complexc.index = ClassIndex.COMPLEX;
- complexc.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyComplex;
- }
- };
- ThreadContext context = runtime.getCurrentContext();
- complexc.callMethod(context, "private_class_method", runtime.newSymbol("allocate"));
- complexc.defineAnnotatedMethods(RubyComplex.class);
- String[]undefined = {"<", "<=", "<=>", ">", ">=", "between?", "divmod",
- "floor", "ceil", "modulo", "round", "step", "truncate"};
- for (String undef : undefined) {
- complexc.undefineMethod(undef);
- }
- complexc.defineConstant("I", RubyComplex.newComplexConvert(context, RubyFixnum.zero(runtime), RubyFixnum.one(runtime)));
- return complexc;
- }
- private static ObjectAllocator COMPLEX_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyComplex(runtime, klass, RubyFixnum.zero(runtime), RubyFixnum.zero(runtime));
- }
- };
- /** internal
- *
- */
- private RubyComplex(Ruby runtime, IRubyObject clazz, IRubyObject real, IRubyObject image) {
- super(runtime, (RubyClass)clazz);
- this.real = real;
- this.image = image;
- }
- /** rb_complex_raw
- *
- */
- static RubyComplex newComplexRaw(Ruby runtime, IRubyObject x, RubyObject y) {
- return new RubyComplex(runtime, runtime.getComplex(), x, y);
- }
- /** rb_complex_raw1
- *
- */
- static RubyComplex newComplexRaw(Ruby runtime, IRubyObject x) {
- return new RubyComplex(runtime, runtime.getComplex(), x, RubyFixnum.zero(runtime));
- }
- /** rb_complex_new1
- *
- */
- public static IRubyObject newComplexCanonicalize(ThreadContext context, IRubyObject x) {
- return newComplexCanonicalize(context, x, RubyFixnum.zero(context.getRuntime()));
- }
-
- /** rb_complex_new
- *
- */
- public static IRubyObject newComplexCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
- return canonicalizeInternal(context, context.getRuntime().getComplex(), x, y);
- }
- /** rb_complex_polar
- *
- */
- static IRubyObject newComplexPolar(ThreadContext context, IRubyObject x, IRubyObject y) {
- return polar(context, context.getRuntime().getComplex(), x, y);
- }
- /** f_complex_new1
- *
- */
- static IRubyObject newComplex(ThreadContext context, IRubyObject clazz, IRubyObject x) {
- return newComplex(context, clazz, x, RubyFixnum.zero(context.getRuntime()));
- }
- /** f_complex_new2
- *
- */
- static IRubyObject newComplex(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert !(x instanceof RubyComplex);
- return canonicalizeInternal(context, clazz, x, y);
- }
-
- /** f_complex_new_bang2
- *
- */
- static RubyComplex newComplexBang(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert x instanceof RubyComplex && y instanceof RubyComplex;
- return new RubyComplex(context.getRuntime(), clazz, x, y);
- }
- /** f_complex_new_bang1
- *
- */
- public static RubyComplex newComplexBang(ThreadContext context, IRubyObject clazz, IRubyObject x) {
- assert x instanceof RubyComplex;
- return newComplexBang(context, clazz, x, RubyFixnum.zero(context.getRuntime()));
- }
- private IRubyObject real;
- private IRubyObject image;
-
- IRubyObject getImage() {
- return image;
- }
- IRubyObject getReal() {
- return real;
- }
- /** m_cos
- *
- */
- private static IRubyObject m_cos(ThreadContext context, IRubyObject x) {
- if (f_scalar_p(context, x).isTrue()) return RubyMath.cos(x, x);
- RubyComplex complex = (RubyComplex)x;
- return newComplex(context, context.getRuntime().getComplex(),
- f_mul(context, RubyMath.cos(x, complex.real), RubyMath.cosh(x, complex.image)),
- f_mul(context, f_negate(context, RubyMath.sin(x, complex.real)), RubyMath.sinh(x, complex.image)));
- }
- /** m_sin
- *
- */
- private static IRubyObject m_sin(ThreadContext context, IRubyObject x) {
- if (f_scalar_p(context, x).isTrue()) return RubyMath.sin(x, x);
- RubyComplex complex = (RubyComplex)x;
- return newComplex(context, context.getRuntime().getComplex(),
- f_mul(context, RubyMath.sin(x, complex.real), RubyMath.cosh(x, complex.image)),
- f_mul(context, RubyMath.cos(x, complex.real), RubyMath.sinh(x, complex.image)));
- }
-
- /** m_sqrt
- *
- */
- private static IRubyObject m_sqrt(ThreadContext context, IRubyObject x) {
- if (f_scalar_p(context, x).isTrue()) {
- if (!f_negative_p(context, x)) return RubyMath.sqrt(x, x);
- return newComplex(context, context.getRuntime().getComplex(),
- RubyFixnum.zero(context.getRuntime()),
- RubyMath.sqrt(x, f_negate(context, x)));
- } else {
- RubyComplex complex = (RubyComplex)x;
- if (f_negative_p(context, complex.image)) {
- return f_conjugate(context, m_sqrt(context, f_conjugate(context, x)));
- } else {
- IRubyObject a = f_abs(context, x);
- IRubyObject two = RubyFixnum.two(context.getRuntime());
- return newComplex(context, context.getRuntime().getComplex(),
- RubyMath.sqrt(x, f_div(context, f_add(context, a, complex.real), two)),
- RubyMath.sqrt(x, f_div(context, f_sub(context, a, complex.real), two)));
- }
- }
- }
- /** nucomp_s_new_bang
- *
- */
- @Deprecated
- public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject[]args) {
- switch (args.length) {
- case 1: return newInstanceBang(context, recv, args[0]);
- case 2: return newInstanceBang(context, recv, args[0], args[1]);
- }
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
- return null;
- }
-
- @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject real) {
- if (!(real instanceof RubyNumeric)) real = f_to_i(context, real);
- return new RubyComplex(context.getRuntime(), recv, real, RubyFixnum.zero(context.getRuntime()));
- }
- @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject real, IRubyObject image) {
- if (!(real instanceof RubyNumeric)) real = f_to_i(context, real);
- if (!(image instanceof RubyNumeric)) image = f_to_i(context, image);
- return new RubyComplex(context.getRuntime(), recv, real, image);
- }
- /** nucomp_real_check (might go to bimorphic)
- *
- */
- private static void realCheck(ThreadContext context, IRubyObject num) {
- switch (num.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- case ClassIndex.RATIONAL:
- break;
- default:
- if (!(num instanceof RubyNumeric ) || !f_scalar_p(context, num).isTrue()) {
- throw context.getRuntime().newArgumentError("not a real");
- }
- }
- }
- /** nucomp_s_canonicalize_internal
- *
- */
- private static final boolean CL_CANNON = true;
- private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObject clazz, IRubyObject real, IRubyObject image) {
- if (f_zero_p(context, image) &&
- ((RubyModule)clazz).fastHasConstant("Unify") &&
- (!CL_CANNON ||
- (!(real instanceof RubyFloat) &&
- !(image instanceof RubyFloat)))) {
- return real;
- } else if (f_scalar_p(context, real).isTrue() &&
- f_scalar_p(context, image).isTrue()) {
- return new RubyComplex(context.getRuntime(), clazz, real, image);
- } else if (f_scalar_p(context, real).isTrue()) {
- RubyComplex complex = (RubyComplex)image;
- return new RubyComplex(context.getRuntime(), clazz,
- f_sub(context, real, complex.image),
- f_add(context, RubyFixnum.zero(context.getRuntime()), complex.real));
- } else if (f_scalar_p(context, image).isTrue()) {
- RubyComplex complex = (RubyComplex)real;
- return new RubyComplex(context.getRuntime(), clazz,
- complex.real,
- f_add(context, complex.image, image));
- } else {
- RubyComplex complex1 = (RubyComplex)real;
- RubyComplex complex2 = (RubyComplex)image;
- return new RubyComplex(context.getRuntime(), clazz,
- f_sub(context, complex1.real, complex2.image),
- f_add(context, complex1.image, complex2.real));
- }
- }
-
- /** nucomp_s_new
- *
- */
- @Deprecated
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[]args) {
- switch (args.length) {
- case 1: return newInstance(context, recv, args[0]);
- case 2: return newInstance(context, recv, args[0], args[1]);
- }
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
- return null;
- }
- @JRubyMethod(name = {"new", "rect", "rectangular"}, meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject real) {
- realCheck(context, real);
- return canonicalizeInternal(context, recv, real, RubyFixnum.zero(context.getRuntime()));
- }
- @JRubyMethod(name = {"new", "rect", "rectangular"}, meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject real, IRubyObject image) {
- realCheck(context, real);
- realCheck(context, image);
- return canonicalizeInternal(context, recv, real, image);
- }
- /** f_complex_polar
- *
- */
- private static IRubyObject f_complex_polar(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert !(x instanceof RubyComplex) && !(y instanceof RubyComplex);
- return canonicalizeInternal(context, clazz,
- f_mul(context, x, m_cos(context, y)),
- f_mul(context, x, m_sin(context, y)));
- }
- /** nucomp_s_polar
- *
- */
- @JRubyMethod(name = "polar", meta = true)
- public static IRubyObject polar(ThreadContext context, IRubyObject clazz, IRubyObject abs, IRubyObject arg) {
- return f_complex_polar(context, clazz, abs, arg);
- }
- /** rb_Complex1
- *
- */
- public static IRubyObject newComplexConvert(ThreadContext context, IRubyObject x) {
- return newComplexConvert(context, x, RubyFixnum.zero(context.getRuntime()));
- }
- /** rb_Complex/rb_Complex2
- *
- */
- public static IRubyObject newComplexConvert(ThreadContext context, IRubyObject x, IRubyObject y) {
- return convert(context, context.getRuntime().getComplex(), x, y);
- }
- @Deprecated
- public static IRubyObject convert(ThreadContext context, IRubyObject clazz, IRubyObject[]args) {
- switch (args.length) {
- case 0: return convert(context, clazz);
- case 1: return convert(context, clazz, args[0]);
- case 2: return convert(context, clazz, args[0], args[1]);
- }
- Arity.raiseArgumentError(context.getRuntime(), args.length, 0, 2);
- return null;
- }
- /** nucomp_s_convert
- *
- */
- @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject convert(ThreadContext context, IRubyObject recv) {
- IRubyObject nil = context.getRuntime().getNil();
- return convertCommon(context, recv, nil, nil);
- }
-
- /** nucomp_s_convert
- *
- */
- @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1) {
- return convertCommon(context, recv, a1, context.getRuntime().getNil());
- }
- /** nucomp_s_convert
- *
- */
- @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
- return convertCommon(context, recv, a1, a2);
- }
-
- private static IRubyObject convertCommon(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
- Frame frame = context.getCurrentFrame();
- IRubyObject backref = frame.getBackRef();
- if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
- if (a1 instanceof RubyString) a1 = str_to_c_strict(context, a1);
- if (a2 instanceof RubyString) a2 = str_to_c_strict(context, a2);
-
- frame.setBackRef(backref);
- if (a1 instanceof RubyComplex) {
- RubyComplex a1Complex = (RubyComplex)a1;
- if (!(a1Complex.image instanceof RubyFloat) && f_zero_p(context, a1Complex.image)) {
- a1 = a1Complex.real;
- }
- }
-
- if (a2 instanceof RubyComplex) {
- RubyComplex a2Complex = (RubyComplex)a2;
- if (!(a2Complex.image instanceof RubyFloat) && f_zero_p(context, a2Complex.image)) {
- a2 = a2Complex.real;
- }
- }
-
- if (a1 instanceof RubyComplex) {
- if (a2.isNil() || f_zero_p(context, a2)) return a1;
- }
- return a2.isNil() ? newInstance(context, recv, a1) : newInstance(context, recv, a1, a2);
- }
-
- /** nucomp_real
- *
- */
- @JRubyMethod(name = "real")
- public IRubyObject real() {
- return real;
- }
-
- /** nucomp_image
- *
- */
- @JRubyMethod(name = {"image", "imag"})
- public IRubyObject image() {
- return image;
- }
-
- /** nucomp_add
- *
- */
- @JRubyMethod(name = "+")
- public IRubyObject op_add(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyComplex) {
- RubyComplex otherComplex = (RubyComplex)other;
- return newComplex(context, getMetaClass(),
- f_add(context, real, otherComplex.real),
- f_add(context, image, otherComplex.image));
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- return newComplex(context, getMetaClass(), f_add(context, real, other), image);
- }
- return coerceBin(context, "+", other);
- }
- /** nucomp_sub
- *
- */
- @JRubyMethod(name = "-")
- public IRubyObject op_sub(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyComplex) {
- RubyComplex otherComplex = (RubyComplex)other;
- return newComplex(context, getMetaClass(),
- f_sub(context, real, otherComplex.real),
- f_sub(context, image, otherComplex.image));
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- return newComplex(context, getMetaClass(), f_sub(context, real, other), image);
- }
- return coerceBin(context, "-", other);
- }
- /** nucomp_mul
- *
- */
- @JRubyMethod(name = "*")
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyComplex) {
- RubyComplex otherComplex = (RubyComplex)other;
- IRubyObject realp = f_sub(context,
- f_mul(context, real, otherComplex.real),
- f_mul(context, image, otherComplex.image));
- IRubyObject imagep = f_add(context,
- f_mul(context, real, otherComplex.image),
- f_mul(context, image, otherComplex.real));
-
- return newComplex(context, getMetaClass(), realp, imagep);
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- return newComplex(context, getMetaClass(),
- f_mul(context, real, other),
- f_mul(context, image, other));
- }
- return coerceBin(context, "*", other);
- }
-
- /** nucomp_div
- *
- */
- @JRubyMethod(name = "/")
- public IRubyObject op_div(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyComplex) {
- RubyComplex otherComplex = (RubyComplex)other;
- if (real instanceof RubyFloat || image instanceof RubyFloat ||
- otherComplex.real instanceof RubyFloat || otherComplex.image instanceof RubyFloat) {
- IRubyObject magn = RubyMath.hypot(this, otherComplex.real, otherComplex.image);
- IRubyObject tmp = newComplexBang(context, getMetaClass(),
- f_quo(context, otherComplex.real, magn),
- f_quo(context, otherComplex.image, magn));
- return f_quo(context, f_mul(context, this, f_conjugate(context, tmp)), magn);
- }
- return f_quo(context, f_mul(context, this, f_conjugate(context, other)), f_abs2(context, other));
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- return newComplex(context, getMetaClass(),
- f_quo(context, real, other),
- f_quo(context, image, other));
- }
- return coerceBin(context, "/", other);
- }
- /** nucomp_fdiv / nucomp_quo
- *
- */
- @JRubyMethod(name = {"fdiv", "quo"})
- public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
- IRubyObject complex = newComplex(context, getMetaClass(),
- f_to_f(context, real),
- f_to_f(context, image));
- return f_div(context, complex, other);
- }
- /** nucomp_expt
- *
- */
- @JRubyMethod(name = "**")
- public IRubyObject op_expt(ThreadContext context, IRubyObject other) {
- if (f_zero_p(context, other)) {
- return newComplexBang(context, getMetaClass(), RubyFixnum.one(context.getRuntime()));
- } else if (other instanceof RubyRational && f_one_p(context, f_denominator(context, other))) {
- other = f_numerator(context, other);
- }
- if (other instanceof RubyComplex) {
- RubyArray a = f_polar(context, this).convertToArray();
- IRubyObject r = a.eltInternal(0);
- IRubyObject theta = a.eltInternal(1);
- RubyComplex otherComplex = (RubyComplex)other;
- IRubyObject nr = RubyMath.exp(this, f_sub(context,
- f_mul(context, otherComplex.real, RubyMath.log(this, r)),
- f_mul(context, otherComplex.image, theta)));
- IRubyObject ntheta = f_add(context,
- f_mul(context, theta, otherComplex.real),
- f_mul(context, otherComplex.image, RubyMath.log(this, r)));
- return polar(context, getMetaClass(), nr, ntheta);
- } else if (other instanceof RubyInteger) {
- IRubyObject one = RubyFixnum.one(context.getRuntime());
- if (f_gt_p(context, other, RubyFixnum.zero(context.getRuntime())).isTrue()) {
- IRubyObject x = this;
- IRubyObject z = x;
- IRubyObject n = f_sub(context, other, one);
-
- IRubyObject two = RubyFixnum.two(context.getRuntime());
-
- while (!f_zero_p(context, n)) {
-
- RubyArray a = f_divmod(context, n, two).convertToArray();
- while (f_zero_p(context, a.eltInternal(1))) {
- RubyComplex xComplex = (RubyComplex)x;
- x = newComplex(context, getMetaClass(),
- f_sub(context, f_mul(context, xComplex.real, xComplex.real),
- f_mul(context, xComplex.image, xComplex.image)),
- f_mul(context, f_mul(context, two, xComplex.real), xComplex.image));
-
- n = a.eltInternal(0);
- a = f_divmod(context, n, two).convertToArray();
- }
- z = f_mul(context, z, x);
- n = f_sub(context, n, one);
- }
- return z;
- }
- return f_expt(context, f_div(context, f_to_r(context, one), this), f_negate(context, other));
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- RubyArray a = f_polar(context, this).convertToArray();
- IRubyObject r = a.eltInternal(0);
- IRubyObject theta = a.eltInternal(1);
- return f_complex_polar(context, getMetaClass(), f_expt(context, r, other), f_mul(context, theta, other));
- }
- return coerceBin(context, "**", other);
- }
- /** nucomp_equal_p
- *
- */
- @JRubyMethod(name = "==")
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyComplex) {
- RubyComplex otherComplex = (RubyComplex)other;
- if (f_equal_p(context, real, otherComplex.real) && f_equal_p(context, image, otherComplex.image)) return context.getRuntime().getTrue();
- return context.getRuntime().getFalse();
- } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- if (f_equal_p(context, real, other) && f_zero_p(context, image)) return context.getRuntime().getTrue();
- return context.getRuntime().getFalse();
- }
- return f_equal_p(context, other, this) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
- /** nucomp_coerce
- *
- */
- @JRubyMethod(name = "coerce")
- public IRubyObject coerce(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
- return context.getRuntime().newArray(newComplexBang(context, getMetaClass(), other), this);
- }
- throw context.getRuntime().newTypeError(other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
- }
- /** nucomp_abs
- *
- */
- @JRubyMethod(name = {"abs", "magnitude"})
- public IRubyObject abs(ThreadContext context) {
- return RubyMath.hypot(this, real, image);
- }
- /** nucomp_abs2
- *
- */
- @JRubyMethod(name = "abs2")
- public IRubyObject abs2(ThreadContext context) {
- return f_add(context,
- f_mul(context, real, real),
- f_mul(context, image, image));
- }
- /** nucomp_arg
- *
- */
- @JRubyMethod(name = {"arg", "angle"})
- public IRubyObject arg(ThreadContext context) {
- return RubyMath.atan2(this, image, real);
- }
- /** nucomp_polar
- *
- */
- @JRubyMethod(name = "polar")
- public IRubyObject polar(ThreadContext context) {
- return context.getRuntime().newArray(f_abs(context, this), f_arg(context, this));
- }
- /** nucomp_conjugate
- *
- */
- @JRubyMethod(name = {"conjugate", "conj", "~"})
- public IRubyObject conjugate(ThreadContext context) {
- return newComplex(context, getMetaClass(), real, f_negate(context, image));
- }
- /** nucomp_real_p
- *
- */
- //@JRubyMethod(name = "real?")
- public IRubyObject real_p(ThreadContext context) {
- return context.getRuntime().getFalse();
- }
- /** nucomp_complex_p
- *
- */
- // @JRubyMethod(name = "complex?")
- public IRubyObject complex_p(ThreadContext context) {
- return context.getRuntime().getTrue();
- }
- /** nucomp_exact_p
- *
- */
- // @JRubyMethod(name = "exact?")
- public IRubyObject exact_p(ThreadContext context) {
- return (f_exact_p(context, real).isTrue() && f_exact_p(context, image).isTrue()) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
- /** nucomp_exact_p
- *
- */
- // @JRubyMethod(name = "inexact?")
- public IRubyObject inexact_p(ThreadContext context) {
- return exact_p(context).isTrue() ? context.getRuntime().getFalse() : context.getRuntime().getTrue();
- }
- /** nucomp_denominator
- *
- */
- @JRubyMethod(name = "denominator")
- public IRubyObject demoninator(ThreadContext context) {
- return f_lcm(context, f_denominator(context, real), f_denominator(context, image));
- }
- /** nucomp_numerator
- *
- */
- @JRubyMethod(name = "numerator")
- public IRubyObject numerator(ThreadContext context) {
- IRubyObject cd = callMethod(context, "denominator");
- return newComplex(context, getMetaClass(),
- f_mul(context,
- f_numerator(context, real),
- f_div(context, cd, f_denominator(context, real))),
- f_mul(context,
- f_numerator(context, image),
- f_div(context, cd, f_denominator(context, image))));
- }
-
- /** nucomp_hash
- *
- */
- @JRubyMethod(name = "hash")
- public IRubyObject hash(ThreadContext context) {
- return f_xor(context, real, image);
- }
- /** f_signbit
- *
- */
- private static boolean signbit(ThreadContext context, IRubyObject x) {
- if (x instanceof RubyFloat) {
- return Double.doubleToLongBits(((RubyFloat)x).getDoubleValue()) < 0;
- }
- return f_negative_p(context, x);
- }
- /** f_tpositive_p
- *
- */
- private static boolean tpositive_p(ThreadContext context, IRubyObject x) {
- return !signbit(context, x);
- }
- /** nucomp_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s(ThreadContext context) {
- boolean impos = tpositive_p(context, image);
- RubyString str = f_to_s(context, real).convertToString();
- str.cat(impos ? (byte)'+' : (byte)'-');
- str.cat(f_to_s(context, f_abs(context, image)).convertToString().getByteList());
- str.cat((byte)'i');
- return str;
- }
-
- /** nucomp_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect(ThreadContext context) {
- boolean impos = tpositive_p(context, image);
- RubyString str = context.getRuntime().newString();
- str.cat((byte)'(');
- str.cat(f_inspect(context, real).convertToString().getByteList());
- str.cat(impos ? (byte)'+' : (byte)'-');
- str.cat(f_inspect(context, f_abs(context, image)).convertToString().getByteList());
- str.cat((byte)'i');
- str.cat((byte)')');
- return str;
- }
- /** nucomp_marshal_dump
- *
- */
- @JRubyMethod(name = "marshal_dump")
- public IRubyObject marshal_dump(ThreadContext context) {
- return context.getRuntime().newArray(real, image);
- }
- /** nucomp_marshal_load
- *
- */
- @JRubyMethod(name = "marshal_load")
- public IRubyObject marshal_load(ThreadContext context, IRubyObject arg) {
- RubyArray a = arg.convertToArray();
- real = a.size() > 0 ? a.eltInternal(0) : context.getRuntime().getNil();
- image = a.size() > 1 ? a.eltInternal(1) : context.getRuntime().getNil();
- return this;
- }
- /** nucomp_scalar_p
- *
- */
- @JRubyMethod(name = "scalar?")
- public IRubyObject scalar_p(ThreadContext context) {
- return context.getRuntime().getFalse();
- }
- /** nucomp_to_i
- *
- */
- @JRubyMethod(name = "to_i")
- public IRubyObject to_i(ThreadContext context) {
- if (image instanceof RubyFloat || !f_zero_p(context, image)) {
- throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Integer");
- }
- return f_to_i(context, real);
- }
- /** nucomp_to_f
- *
- */
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f(ThreadContext context) {
- if (image instanceof RubyFloat || !f_zero_p(context, image)) {
- throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Float");
- }
- return f_to_f(context, real);
- }
- /** nucomp_to_f
- *
- */
- @JRubyMethod(name = "to_r")
- public IRubyObject to_r(ThreadContext context) {
- if (image instanceof RubyFloat || !f_zero_p(context, image)) {
- throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Rational");
- }
- return f_to_r(context, real);
- }
-
- static RubyArray str_to_c_internal(ThreadContext context, IRubyObject recv) {
- RubyString s = recv.callMethod(context, "strip").convertToString();
- ByteList bytes = s.getByteList();
- Ruby runtime = context.getRuntime();
- if (bytes.realSize == 0) return runtime.newArray(runtime.getNil(), recv);
- IRubyObject sr, si, re;
- sr = si = re = runtime.getNil();
- boolean po = false;
- IRubyObject m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat0).callMethod(context, "match", s);
- if (!m.isNil()) {
- sr = m.callMethod(context, "[]", RubyFixnum.one(runtime));
- si = m.callMethod(context, "[]", RubyFixnum.two(runtime));
- re = m.callMethod(context, "post_match");
- po = true;
- }
- if (m.isNil()) {
- m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat1).callMethod(context, "match", s);
- if (!m.isNil()) {
- sr = runtime.getNil();
- si = m.callMethod(context, "[]", RubyFixnum.one(runtime));
- if (si.isNil()) si = runtime.newString();
- IRubyObject t = m.callMethod(context, "[]", RubyFixnum.two(runtime));
- if (t.isNil()) t = runtime.newString(new ByteList(new byte[]{'1'}));
- si.convertToString().cat(t.convertToString().getByteList());
- re = m.callMethod(context, "post_match");
- po = false;
- }
- }
- if (m.isNil()) {
- m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat2).callMethod(context, "match", s);
- if (m.isNil()) return runtime.newArray(runtime.getNil(), recv);
- sr = m.callMethod(context, "[]", RubyFixnum.one(runtime));
- if (m.callMethod(context, "[]", RubyFixnum.two(runtime)).isNil()) {
- si = runtime.getNil();
- } else {
- si = m.callMethod(context, "[]", RubyFixnum.three(runtime));
- IRubyObject t = m.callMethod(context, "[]", RubyFixnum.four(runtime));
- if (t.isNil()) t = runtime.newString(new ByteList(new byte[]{'1'}));
- si.convertToString().cat(t.convertToString().getByteList());
- }
- re = m.callMethod(context, "post_match");
- po = false;
- }
- IRubyObject r = RubyFixnum.zero(runtime);
- IRubyObject i = r;
- if (!sr.isNil()) {
- if (sr.callMethod(context, "include?", runtime.newString(new ByteList(new byte[]{'/'}))).isTrue()) {
- r = f_to_r(context, sr);
- } else if (f_gt_p(context, sr.callMethod(context, "count", runtime.newString(".eE")), RubyFixnum.zero(runtime)).isTrue()) {
- r = f_to_f(context, sr);
- } else {
- r = f_to_i(context, sr);
- }
- }
- if (!si.isNil()) {
- if (si.callMethod(context, "include?", runtime.newString(new ByteList(new byte[]{'/'}))).isTrue()) {
- i = f_to_r(context, si);
- } else if (f_gt_p(context, si.callMethod(context, "count", runtime.newString(".eE")), RubyFixnum.zero(runtime)).isTrue()) {
- i = f_to_f(context, si);
- } else {
- i = f_to_i(context, si);
- }
- }
- return runtime.newArray(po ? newComplexPolar(context, r, i) : newComplexCanonicalize(context, r, i), re);
- }
-
- private static IRubyObject str_to_c_strict(ThreadContext context, IRubyObject recv) {
- RubyArray a = str_to_c_internal(context, recv);
- if (a.eltInternal(0).isNil() || a.eltInternal(1).convertToString().getByteList().length() > 0) {
- IRubyObject s = recv.callMethod(context, "inspect");
- throw context.getRuntime().newArgumentError("invalid value for Complex: " + s.convertToString());
- }
- return a.eltInternal(0);
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2007 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyClass;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.builtin.IRubyObject;
- /**
- * Placeholder until/if we can support this
- *
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- @JRubyClass(name="Continuation")
- public class RubyContinuation {
- public static void createContinuation(Ruby runtime) {
- RubyClass cContinuation = runtime.defineClass("Continuation",runtime.getObject(),runtime.getObject().getAllocator());
- cContinuation.defineAnnotatedMethods(RubyContinuation.class);
- runtime.setContinuation(cContinuation);
- }
- @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
- public static IRubyObject call(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- throw recv.getRuntime().newNotImplementedError("Continuations are not implemented in JRuby and will not work");
- }
- }// RubyContinuation
- /*
- ***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006, 2007 Ola Bini <ola@ologix.com>
- * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
- * Copyright (C) 2008 Vladimir Sizikov <vsizikov@gmail.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.security.Provider;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyModule;
- import org.jruby.anno.JRubyClass;
- import org.jruby.runtime.Arity;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.runtime.callback.Callback;
- import org.jruby.util.ByteList;
- /**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
- @JRubyModule(name="Digest")
- public class RubyDigest {
- private static Provider provider = null;
- public static void createDigest(Ruby runtime) {
- try {
- provider = (Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
- } catch(Exception e) {
- // provider is not available
- }
- RubyModule mDigest = runtime.defineModule("Digest");
- RubyClass cDigestBase = mDigest.defineClassUnder("Base",runtime.getObject(), Base.BASE_ALLOCATOR);
- cDigestBase.defineAnnotatedMethods(Base.class);
- }
- private static MessageDigest createMessageDigest(Ruby runtime, String providerName) throws NoSuchAlgorithmException {
- if(provider != null) {
- try {
- return MessageDigest.getInstance(providerName, provider);
- } catch(NoSuchAlgorithmException e) {
- // bouncy castle doesn't support algorithm
- }
- }
- // fall back to system JCA providers
- return MessageDigest.getInstance(providerName);
- }
- @JRubyClass(name="Digest::MD5", parent="Digest::Base")
- public static class MD5 {}
- @JRubyClass(name="Digest::RMD160", parent="Digest::Base")
- public static class RMD160 {}
- @JRubyClass(name="Digest::SHA1", parent="Digest::Base")
- public static class SHA1 {}
- @JRubyClass(name="Digest::SHA256", parent="Digest::Base")
- public static class SHA256 {}
- @JRubyClass(name="Digest::SHA384", parent="Digest::Base")
- public static class SHA384 {}
- @JRubyClass(name="Digest::SHA512", parent="Digest::Base")
- public static class SHA512 {}
- public static void createDigestMD5(Ruby runtime) {
- runtime.getLoadService().require("digest.so");
- RubyModule mDigest = runtime.fastGetModule("Digest");
- RubyClass cDigestBase = mDigest.fastGetClass("Base");
- RubyClass cDigest_MD5 = mDigest.defineClassUnder("MD5",cDigestBase,cDigestBase.getAllocator());
- cDigest_MD5.defineFastMethod("block_length", new Callback() {
- public Arity getArity() {
- return Arity.NO_ARGUMENTS;
- }
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- return RubyFixnum.newFixnum(recv.getRuntime(), 64);
- }
- });
- cDigest_MD5.setInternalModuleVariable("metadata",runtime.newString("MD5"));
- }
- public static void createDigestRMD160(Ruby runtime) {
- runtime.getLoadService().require("digest.so");
- if(provider == null) {
- throw runtime.newLoadError("RMD160 not supported without BouncyCastle");
- }
- RubyModule mDigest = runtime.fastGetModule("Digest");
- RubyClass cDigestBase = mDigest.fastGetClass("Base");
- RubyClass cDigest_RMD160 = mDigest.defineClassUnder("RMD160",cDigestBase,cDigestBase.getAllocator());
- cDigest_RMD160.setInternalModuleVariable("metadata",runtime.newString("RIPEMD160"));
- }
- public static void createDigestSHA1(Ruby runtime) {
- runtime.getLoadService().require("digest.so");
- RubyModule mDigest = runtime.fastGetModule("Digest");
- RubyClass cDigestBase = mDigest.fastGetClass("Base");
- RubyClass cDigest_SHA1 = mDigest.defineClassUnder("SHA1",cDigestBase,cDigestBase.getAllocator());
- cDigest_SHA1.setInternalModuleVariable("metadata",runtime.newString("SHA1"));
- }
- public static void createDigestSHA2(Ruby runtime) {
- runtime.getLoadService().require("digest.so");
- try {
- createMessageDigest(runtime, "SHA-256");
- } catch(NoSuchAlgorithmException e) {
- throw runtime.newLoadError("SHA2 not supported");
- }
- RubyModule mDigest = runtime.fastGetModule("Digest");
- RubyClass cDigestBase = mDigest.fastGetClass("Base");
- RubyClass cDigest_SHA2_256 = mDigest.defineClassUnder("SHA256",cDigestBase,cDigestBase.getAllocator());
- cDigest_SHA2_256.setInternalModuleVariable("metadata",runtime.newString("SHA-256"));
- RubyClass cDigest_SHA2_384 = mDigest.defineClassUnder("SHA384",cDigestBase,cDigestBase.getAllocator());
- cDigest_SHA2_384.setInternalModuleVariable("metadata",runtime.newString("SHA-384"));
- RubyClass cDigest_SHA2_512 = mDigest.defineClassUnder("SHA512",cDigestBase,cDigestBase.getAllocator());
- cDigest_SHA2_512.setInternalModuleVariable("metadata",runtime.newString("SHA-512"));
- }
- @JRubyClass(name="Digest::Base")
- public static class Base extends RubyObject {
- protected static final ObjectAllocator BASE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new Base(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "digest", required = 1, meta = true)
- public static IRubyObject s_digest(IRubyObject recv, IRubyObject str) {
- Ruby runtime = recv.getRuntime();
- String name = ((RubyClass)recv).searchInternalModuleVariable("metadata").toString();
- try {
- MessageDigest md = createMessageDigest(runtime, name);
- return RubyString.newString(runtime, md.digest(str.convertToString().getBytes()));
- } catch(NoSuchAlgorithmException e) {
- throw recv.getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
- }
- }
-
- @JRubyMethod(name = "hexdigest", required = 1, meta = true)
- public static IRubyObject s_hexdigest(IRubyObject recv, IRubyObject str) {
- Ruby runtime = recv.getRuntime();
- String name = ((RubyClass)recv).searchInternalModuleVariable("metadata").toString();
- try {
- MessageDigest md = createMessageDigest(runtime, name);
- return RubyString.newString(runtime, ByteList.plain(toHex(md.digest(str.convertToString().getBytes()))));
- } catch(NoSuchAlgorithmException e) {
- throw recv.getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
- }
- }
- private MessageDigest algo;
- private StringBuffer data;
- public Base(Ruby runtime, RubyClass type) {
- super(runtime,type);
- data = new StringBuffer();
- if(type == runtime.fastGetModule("Digest").fastGetClass("Base")) {
- throw runtime.newNotImplementedError("Digest::Base is an abstract class");
- }
- if(!type.hasInternalModuleVariable("metadata")) {
- throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
- }
- try {
- setAlgorithm(type.searchInternalModuleVariable("metadata"));
- } catch(NoSuchAlgorithmException e) {
- throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
- }
- }
-
- @JRubyMethod(name = "initialize", optional = 1, frame = true)
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- if(args.length > 0 && !args[0].isNil()) {
- update(args[0]);
- }
- return this;
- }
- @JRubyMethod(name = "initialize_copy", required = 1)
- public IRubyObject initialize_copy(IRubyObject obj) {
- if(this == obj) {
- return this;
- }
- ((RubyObject)obj).checkFrozen();
- data = new StringBuffer(((Base)obj).data.toString());
- String name = ((Base)obj).algo.getAlgorithm();
- try {
- algo = createMessageDigest(getRuntime(), name);
- } catch(NoSuchAlgorithmException e) {
- throw getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
- }
- return this;
- }
- @JRubyMethod(name = {"update", "<<"}, required = 1)
- public IRubyObject update(IRubyObject obj) {
- data.append(obj);
- return this;
- }
- @JRubyMethod(name = "digest", optional = 1)
- public IRubyObject digest(IRubyObject[] args) {
- if (args.length == 1) {
- reset();
- data.append(args[0]);
- }
- IRubyObject digest = getDigest();
- if (args.length == 1) {
- reset();
- }
- return digest;
- }
-
- private IRubyObject getDigest() {
- algo.reset();
- return RubyString.newString(getRuntime(), algo.digest(ByteList.plain(data)));
- }
-
- @JRubyMethod(name = "digest!")
- public IRubyObject digest_bang() {
- algo.reset();
- byte[] digest = algo.digest(ByteList.plain(data));
- reset();
- return RubyString.newString(getRuntime(), digest);
- }
- @JRubyMethod(name = {"hexdigest"}, optional = 1)
- public IRubyObject hexdigest(IRubyObject[] args) {
- algo.reset();
- if (args.length == 1) {
- reset();
- data.append(args[0]);
- }
- byte[] digest = ByteList.plain(toHex(algo.digest(ByteList.plain(data))));
- if (args.length == 1) {
- reset();
- }
- return RubyString.newString(getRuntime(), digest);
- }
-
- @JRubyMethod(name = {"to_s"})
- public IRubyObject to_s() {
- algo.reset();
- return RubyString.newString(getRuntime(), ByteList.plain(toHex(algo.digest(ByteList.plain(data)))));
- }
- @JRubyMethod(name = {"hexdigest!"})
- public IRubyObject hexdigest_bang() {
- algo.reset();
- byte[] digest = ByteList.plain(toHex(algo.digest(ByteList.plain(data))));
- reset();
- return RubyString.newString(getRuntime(), digest);
- }
-
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- algo.reset();
- return RubyString.newString(getRuntime(), ByteList.plain("#<" + getMetaClass().getRealClass().getName() + ": " + toHex(algo.digest(ByteList.plain(data))) + ">"));
- }
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(IRubyObject oth) {
- boolean ret = this == oth;
- if(!ret) {
- if (oth instanceof Base) {
- Base b = (Base)oth;
- ret = this.algo.getAlgorithm().equals(b.algo.getAlgorithm()) &&
- this.getDigest().equals(b.getDigest());
- } else {
- IRubyObject str = oth.convertToString();
- ret = this.to_s().equals(str);
- }
- }
- return ret ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = {"length", "size", "digest_length"})
- public IRubyObject length() {
- return RubyFixnum.newFixnum(getRuntime(), algo.getDigestLength());
- }
- @JRubyMethod(name = {"block_length"})
- public IRubyObject block_length() {
- throw getRuntime().newRuntimeError(
- this.getMetaClass() + " doesn't implement block_length()");
- }
- @JRubyMethod(name = {"reset"})
- public IRubyObject reset() {
- algo.reset();
- data = new StringBuffer();
- return getRuntime().getNil();
- }
- private void setAlgorithm(IRubyObject algo) throws NoSuchAlgorithmException {
- this.algo = createMessageDigest(getRuntime(), algo.toString());
- }
- private static String toHex(byte[] val) {
- StringBuilder out = new StringBuilder();
- for(int i=0,j=val.length;i<j;i++) {
- String ve = Integer.toString((((int)((char)val[i])) & 0xFF),16);
- if(ve.length() == 1) {
- ve = "0" + ve;
- }
- out.append(ve);
- }
- return out.toString();
- }
- }
- }// RubyDigest
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.util.ArrayList;
- import java.util.List;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyClass;
- import org.jruby.ext.posix.util.Platform;
- import org.jruby.javasupport.JavaUtil;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.ObjectAllocator;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.Dir;
- import org.jruby.util.JRubyFile;
- import org.jruby.util.ByteList;
- /**
- * .The Ruby built-in class Dir.
- *
- * @author jvoegele
- */
- @JRubyClass(name="Dir", include="Enumerable")
- public class RubyDir extends RubyObject {
- // What we passed to the constructor for method 'path'
- private RubyString path;
- protected JRubyFile dir;
- private String[] snapshot; // snapshot of contents of directory
- private int pos; // current position in directory
- private boolean isOpen = true;
- public RubyDir(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private static final ObjectAllocator DIR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyDir(runtime, klass);
- }
- };
- public static RubyClass createDirClass(Ruby runtime) {
- RubyClass dirClass = runtime.defineClass("Dir", runtime.getObject(), DIR_ALLOCATOR);
- runtime.setDir(dirClass);
- dirClass.includeModule(runtime.getEnumerable());
-
- dirClass.defineAnnotatedMethods(RubyDir.class);
- return dirClass;
- }
-
- private final void checkDir() {
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: operation on untainted Dir");
-
- testFrozen("");
-
- if (!isOpen) throw getRuntime().newIOError("closed directory");
- }
- /**
- * Creates a new <code>Dir</code>. This method takes a snapshot of the
- * contents of the directory at creation time, so changes to the contents
- * of the directory will not be reflected during the lifetime of the
- * <code>Dir</code> object returned, so a new <code>Dir</code> instance
- * must be created to reflect changes to the underlying file system.
- */
- @JRubyMethod(name = "initialize", required = 1, frame = true)
- public IRubyObject initialize(IRubyObject _newPath, Block unusedBlock) {
- RubyString newPath = _newPath.convertToString();
- getRuntime().checkSafeString(newPath);
- String adjustedPath = RubyFile.adjustRootPathOnWindows(getRuntime(), newPath.toString(), null);
- checkDirIsTwoSlashesOnWindows(getRuntime(), adjustedPath);
- dir = JRubyFile.create(getRuntime().getCurrentDirectory(), adjustedPath);
- if (!dir.isDirectory()) {
- dir = null;
- throw getRuntime().newErrnoENOENTError(newPath.toString() + " is not a directory");
- }
- path = newPath;
- List<String> snapshotList = new ArrayList<String>();
- snapshotList.add(".");
- snapshotList.add("..");
- snapshotList.addAll(getContents(dir));
- snapshot = (String[]) snapshotList.toArray(new String[snapshotList.size()]);
- pos = 0;
- return this;
- }
- // ----- Ruby Class Methods ----------------------------------------------------
-
- private static List<ByteList> dirGlobs(String cwd, IRubyObject[] args, int flags) {
- List<ByteList> dirs = new ArrayList<ByteList>();
-
- for (int i = 0; i < args.length; i++) {
- ByteList globPattern = args[i].convertToString().getByteList();
- dirs.addAll(Dir.push_glob(cwd, globPattern, flags));
- }
-
- return dirs;
- }
-
- private static IRubyObject asRubyStringList(Ruby runtime, List<ByteList> dirs) {
- List<RubyString> allFiles = new ArrayList<RubyString>();
- for (ByteList dir: dirs) {
- allFiles.add(RubyString.newString(runtime, dir));
- }
- IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
- allFiles.toArray(tempFileList);
-
- return runtime.newArrayNoCopy(tempFileList);
- }
-
- private static String getCWD(Ruby runtime) {
- try {
- return new org.jruby.util.NormalizedFile(runtime.getCurrentDirectory()).getCanonicalPath();
- } catch(Exception e) {
- return runtime.getCurrentDirectory();
- }
- }
- @JRubyMethod(name = "[]", required = 1, rest=true, meta = true)
- public static IRubyObject aref(IRubyObject recv, IRubyObject[] args) {
- List<ByteList> dirs;
- if (args.length == 1) {
- ByteList globPattern = args[0].convertToString().getByteList();
- dirs = Dir.push_glob(getCWD(recv.getRuntime()), globPattern, 0);
- } else {
- dirs = dirGlobs(getCWD(recv.getRuntime()), args, 0);
- }
- return asRubyStringList(recv.getRuntime(), dirs);
- }
-
- /**
- * Returns an array of filenames matching the specified wildcard pattern
- * <code>pat</code>. If a block is given, the array is iterated internally
- * with each filename is passed to the block in turn. In this case, Nil is
- * returned.
- */
- @JRubyMethod(name = "glob", required = 1, optional = 1, frame = true, meta = true)
- public static IRubyObject glob(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = recv.getRuntime();
- int flags = args.length == 2 ? RubyNumeric.num2int(args[1]) : 0;
- List<ByteList> dirs;
- IRubyObject tmp = args[0].checkArrayType();
- if (tmp.isNil()) {
- ByteList globPattern = args[0].convertToString().getByteList();
- dirs = Dir.push_glob(recv.getRuntime().getCurrentDirectory(), globPattern, flags);
- } else {
- dirs = dirGlobs(getCWD(runtime), ((RubyArray) tmp).toJavaArray(), flags);
- }
-
- if (block.isGiven()) {
- for (int i = 0; i < dirs.size(); i++) {
- block.yield(context, RubyString.newString(runtime, dirs.get(i)));
- }
-
- return recv.getRuntime().getNil();
- }
- return asRubyStringList(recv.getRuntime(), dirs);
- }
- /**
- * @return all entries for this Dir
- */
- @JRubyMethod(name = "entries")
- public RubyArray entries() {
- return getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(getRuntime(), snapshot));
- }
-
- /**
- * Returns an array containing all of the filenames in the given directory.
- */
- @JRubyMethod(name = "entries", required = 1, meta = true)
- public static RubyArray entries(IRubyObject recv, IRubyObject path) {
- Ruby runtime = recv.getRuntime();
- String adjustedPath = RubyFile.adjustRootPathOnWindows(
- runtime, path.convertToString().toString(), null);
- checkDirIsTwoSlashesOnWindows(runtime, adjustedPath);
- final JRubyFile directory = JRubyFile.create(
- recv.getRuntime().getCurrentDirectory(), adjustedPath);
- if (!directory.isDirectory()) {
- throw recv.getRuntime().newErrnoENOENTError("No such directory");
- }
- List<String> fileList = getContents(directory);
- fileList.add(0, ".");
- fileList.add(1, "..");
- Object[] files = fileList.toArray();
- return recv.getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(recv.getRuntime(), files));
- }
-
- // MRI behavior: just plain '//' or '\\\\' are considered illegal on Windows.
- private static void checkDirIsTwoSlashesOnWindows(Ruby runtime, String path) {
- if (Platform.IS_WINDOWS && ("//".equals(path) || "\\\\".equals(path))) {
- throw runtime.newErrnoEINVALError("Invalid argument - " + path);
- }
- }
- /** Changes the current directory to <code>path</code> */
- @JRubyMethod(name = "chdir", optional = 1, frame = true, meta = true)
- public static IRubyObject chdir(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyString path = args.length == 1 ?
- (RubyString) args[0].convertToString() : getHomeDirectoryPath(context);
- String adjustedPath = RubyFile.adjustRootPathOnWindows(
- recv.getRuntime(), path.toString(), null);
- checkDirIsTwoSlashesOnWindows(recv.getRuntime(), adjustedPath);
- JRubyFile dir = getDir(recv.getRuntime(), adjustedPath, true);
- String realPath = null;
- String oldCwd = recv.getRuntime().getCurrentDirectory();
-
- // We get canonical path to try and flatten the path out.
- // a dir '/subdir/..' should return as '/'
- // cnutter: Do we want to flatten path out?
- try {
- realPath = dir.getCanonicalPath();
- } catch (IOException e) {
- realPath = dir.getAbsolutePath();
- }
-
- IRubyObject result = null;
- if (block.isGiven()) {
- // FIXME: Don't allow multiple threads to do this at once
- recv.getRuntime().setCurrentDirectory(realPath);
- try {
- result = block.yield(context, path);
- } finally {
- dir = getDir(recv.getRuntime(), oldCwd, true);
- recv.getRuntime().setCurrentDirectory(oldCwd);
- }
- } else {
- recv.getRuntime().setCurrentDirectory(realPath);
- result = recv.getRuntime().newFixnum(0);
- }
-
- return result;
- }
- /**
- * Changes the root directory (only allowed by super user). Not available
- * on all platforms.
- */
- @JRubyMethod(name = "chroot", required = 1, meta = true)
- public static IRubyObject chroot(IRubyObject recv, IRubyObject path) {
- throw recv.getRuntime().newNotImplementedError("chroot not implemented: chroot is non-portable and is not supported.");
- }
- /**
- * Deletes the directory specified by <code>path</code>. The directory must
- * be empty.
- */
- @JRubyMethod(name = {"rmdir", "unlink", "delete"}, required = 1, meta = true)
- public static IRubyObject rmdir(IRubyObject recv, IRubyObject path) {
- JRubyFile directory = getDir(recv.getRuntime(), path.convertToString().toString(), true);
-
- if (!directory.delete()) {
- throw recv.getRuntime().newSystemCallError("No such directory");
- }
-
- return recv.getRuntime().newFixnum(0);
- }
- /**
- * Executes the block once for each file in the directory specified by
- * <code>path</code>.
- */
- @JRubyMethod(name = "foreach", required = 1, frame = true, meta = true)
- public static IRubyObject foreach(ThreadContext context, IRubyObject recv, IRubyObject _path, Block block) {
- RubyString path = _path.convertToString();
- recv.getRuntime().checkSafeString(path);
- RubyClass dirClass = recv.getRuntime().getDir();
- RubyDir dir = (RubyDir) dirClass.newInstance(context, new IRubyObject[] { path }, block);
-
- dir.each(context, block);
- return recv.getRuntime().getNil();
- }
- /** Returns the current directory. */
- @JRubyMethod(name = {"getwd", "pwd"}, meta = true)
- public static RubyString getwd(IRubyObject recv) {
- Ruby ruby = recv.getRuntime();
-
- return RubyString.newUnicodeString(ruby, ruby.getCurrentDirectory());
- }
- /**
- * Creates the directory specified by <code>path</code>. Note that the
- * <code>mode</code> parameter is provided only to support existing Ruby
- * code, and is ignored.
- */
- @JRubyMethod(name = "mkdir", required = 1, optional = 1, meta = true)
- public static IRubyObject mkdir(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- runtime.checkSafeString(args[0]);
- String path = args[0].toString();
- File newDir = getDir(runtime, path, false);
- if (File.separatorChar == '\\') {
- newDir = new File(newDir.getPath());
- }
-
- int mode = args.length == 2 ? ((int) args[1].convertToInteger().getLongValue()) : 0777;
- if (runtime.getPosix().mkdir(newDir.getAbsolutePath(), mode) < 0) {
- // FIXME: This is a system error based on errno
- throw recv.getRuntime().newSystemCallError("mkdir failed");
- }
-
- return RubyFixnum.zero(recv.getRuntime());
- }
- /**
- * Returns a new directory object for <code>path</code>. If a block is
- * provided, a new directory object is passed to the block, which closes the
- * directory object before terminating.
- */
- @JRubyMethod(name = "open", required = 1, frame = true, meta = true)
- public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject path, Block block) {
- RubyDir directory =
- (RubyDir) recv.getRuntime().getDir().newInstance(context,
- new IRubyObject[] { path }, Block.NULL_BLOCK);
- if (!block.isGiven()) return directory;
-
- try {
- return block.yield(context, directory);
- } finally {
- directory.close();
- }
- }
- // ----- Ruby Instance Methods -------------------------------------------------
- /**
- * Closes the directory stream.
- */
- @JRubyMethod(name = "close")
- public IRubyObject close() {
- // Make sure any read()s after close fail.
- checkDir();
-
- isOpen = false;
- return getRuntime().getNil();
- }
- /**
- * Executes the block once for each entry in the directory.
- */
- @JRubyMethod(name = "each", frame = true)
- public IRubyObject each(ThreadContext context, Block block) {
- checkDir();
-
- String[] contents = snapshot;
- for (int i=0; i<contents.length; i++) {
- block.yield(context, getRuntime().newString(contents[i]));
- }
- return this;
- }
- /**
- * Returns the current position in the directory.
- */
- @JRubyMethod(name = {"tell", "pos"})
- public RubyInteger tell() {
- checkDir();
- return getRuntime().newFixnum(pos);
- }
- /**
- * Moves to a position <code>d</code>. <code>pos</code> must be a value
- * returned by <code>tell</code> or 0.
- */
- @JRubyMethod(name = "seek", required = 1)
- public IRubyObject seek(IRubyObject newPos) {
- checkDir();
-
- set_pos(newPos);
- return this;
- }
-
- @JRubyMethod(name = "pos=", required = 1)
- public IRubyObject set_pos(IRubyObject newPos) {
- this.pos = RubyNumeric.fix2int(newPos);
- return newPos;
- }
- @JRubyMethod(name = "path")
- public IRubyObject path(ThreadContext context) {
- checkDir();
-
- return path.strDup(context.getRuntime());
- }
- /** Returns the next entry from this directory. */
- @JRubyMethod(name = "read")
- public IRubyObject read() {
- checkDir();
- if (pos >= snapshot.length) {
- return getRuntime().getNil();
- }
- RubyString result = getRuntime().newString(snapshot[pos]);
- pos++;
- return result;
- }
- /** Moves position in this directory to the first entry. */
- @JRubyMethod(name = "rewind")
- public IRubyObject rewind() {
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: can't close");
- checkDir();
- pos = 0;
- return this;
- }
- // ----- Helper Methods --------------------------------------------------------
- /** Returns a Java <code>File</code> object for the specified path. If
- * <code>path</code> is not a directory, throws <code>IOError</code>.
- *
- * @param path path for which to return the <code>File</code> object.
- * @param mustExist is true the directory must exist. If false it must not.
- * @throws IOError if <code>path</code> is not a directory.
- */
- protected static JRubyFile getDir(final Ruby runtime, final String path, final boolean mustExist) {
- JRubyFile result = JRubyFile.create(runtime.getCurrentDirectory(),path);
- if (mustExist && !result.exists()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + path);
- }
- boolean isDirectory = result.isDirectory();
-
- if (mustExist && !isDirectory) {
- throw runtime.newErrnoENOTDIRError(path + " is not a directory");
- } else if (!mustExist && isDirectory) {
- throw runtime.newErrnoEEXISTError("File exists - " + path);
- }
- return result;
- }
- /**
- * Returns the contents of the specified <code>directory</code> as an
- * <code>ArrayList</code> containing the names of the files as Java Strings.
- */
- protected static List<String> getContents(File directory) {
- String[] contents = directory.list();
- List<String> result = new ArrayList<String>();
- // If an IO exception occurs (something odd, but possible)
- // A directory may return null.
- if (contents != null) {
- for (int i=0; i<contents.length; i++) {
- result.add(contents[i]);
- }
- }
- return result;
- }
- /**
- * Returns the contents of the specified <code>directory</code> as an
- * <code>ArrayList</code> containing the names of the files as Ruby Strings.
- */
- protected static List<RubyString> getContents(File directory, Ruby runtime) {
- List<RubyString> result = new ArrayList<RubyString>();
- String[] contents = directory.list();
-
- for (int i = 0; i < contents.length; i++) {
- result.add(runtime.newString(contents[i]));
- }
- return result;
- }
-
- /**
- * Returns the home directory of the specified <code>user</code> on the
- * system. If the home directory of the specified user cannot be found,
- * an <code>ArgumentError it thrown</code>.
- */
- public static IRubyObject getHomeDirectoryPath(ThreadContext context, String user) {
- /*
- * TODO: This version is better than the hackish previous one. Windows
- * behavior needs to be defined though. I suppose this version
- * could be improved more too.
- * TODO: /etc/passwd is also inadequate for MacOSX since it does not
- * use /etc/passwd for regular user accounts
- */
- String passwd = null;
- try {
- FileInputStream stream = new FileInputStream("/etc/passwd");
- int totalBytes = stream.available();
- byte[] bytes = new byte[totalBytes];
- stream.read(bytes);
- stream.close();
- passwd = new String(bytes);
- } catch (IOException e) {
- return context.getRuntime().getNil();
- }
- String[] rows = passwd.split("\n");
- int rowCount = rows.length;
- for (int i = 0; i < rowCount; i++) {
- String[] fields = rows[i].split(":");
- if (fields[0].equals(user)) {
- return context.getRuntime().newString(fields[5]);
- }
- }
- throw context.getRuntime().newArgumentError("user " + user + " doesn't exist");
- }
- public static RubyString getHomeDirectoryPath(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- RubyHash systemHash = (RubyHash) runtime.getObject().fastGetConstant("ENV_JAVA");
- RubyHash envHash = (RubyHash) runtime.getObject().fastGetConstant("ENV");
- IRubyObject home = envHash.op_aref(context, runtime.newString("HOME"));
- if (home == null || home.isNil()) {
- home = systemHash.op_aref(context, runtime.newString("user.home"));
- }
- if (home == null || home.isNil()) {
- home = envHash.op_aref(context, runtime.newString("LOGDIR"));
- }
- if (home == null || home.isNil()) {
- throw runtime.newArgumentError("user.home/LOGDIR not set");
- }
- return (RubyString) home;
- }
- }
- /***** BEGIN LICENSE BLOCK *****
- * Version: CPL 1.0/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Common Public
- * License Version 1.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.eclipse.org/legal/cpl-v10.html
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * Copyright (C) 2006 Ola Bini <ola@ologix.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either of the GNU General Public License Version 2 or later (the "GPL"),
- * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the CPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the CPL, the GPL or the LGPL.
- ***** END LICENSE BLOCK *****/
- package org.jruby;
- import java.util.Comparator;
- import java.util.Arrays;
- import java.util.concurrent.atomic.AtomicInteger;
- import org.jruby.anno.JRubyMethod;
- import org.jruby.anno.JRubyModule;
- import org.jruby.exceptions.JumpException;
- import org.jruby.javasupport.util.RuntimeHelpers;
- import org.jruby.runtime.Arity;
- import org.jruby.runtime.Block;
- import org.jruby.runtime.CallBlock;
- import org.jruby.runtime.BlockCallback;
- import org.jruby.runtime.MethodIndex;
- import org.jruby.runtime.ThreadContext;
- import org.jruby.runtime.builtin.IRubyObject;
- import org.jruby.util.TypeConverter;
- /**
- * The implementation of Ruby's Enumerable module.
- */
- @JRubyModule(name="Enumerable")
- public class RubyEnumerable {
- public static RubyModule createEnumerableModule(Ruby runtime) {
- RubyModule enumModule = runtime.defineModule("Enumerable");
- runtime.setEnumerable(enumModule);
-
- enumModule.defineAnnotatedMethods(RubyEnumerable.class);
- return enumModule;
- }
- public static IRubyObject callEach(Ruby runtime, ThreadContext context, IRubyObject self,
- BlockCallback callback) {
- return RuntimeHelpers.invoke(context, self, "each", CallBlock.newCallClosure(self, runtime.getEnumerable(),
- Arity.noArguments(), callback, context));
- }
- private static class ExitIteration extends RuntimeException {
- public Throwable fillInStackTrace() {
- return this;
- }
- }
- @JRubyMethod(name = "first")
- public static IRubyObject first_0(ThreadContext context, IRubyObject self) {
- Ruby runtime = self.getRuntime();
- final ThreadContext localContext = context;
-
- final IRubyObject[] holder = new IRubyObject[]{runtime.getNil()};
- try {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw ctx.getRuntime().newThreadError("Enumerable#first cannot be parallelized");
- }
- holder[0] = largs[0];
- throw new ExitIteration();
- }
- });
- } catch(ExitIteration ei) {}
- return holder[0];
- }
- @JRubyMethod(name = "first")
- public static IRubyObject first_1(ThreadContext context, IRubyObject self, final IRubyObject num) {
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray();
- final ThreadContext localContext = context;
- if(RubyNumeric.fix2int(num) < 0) {
- throw runtime.newArgumentError("negative index");
- }
- try {
- callEach(runtime, context, self, new BlockCallback() {
- private int iter = RubyNumeric.fix2int(num);
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#first cannot be parallelized");
- }
- if(iter-- == 0) {
- throw new ExitIteration();
- }
- result.append(largs[0]);
- return runtime.getNil();
- }
- });
- } catch(ExitIteration ei) {}
- return result;
- }
- @JRubyMethod(name = {"to_a", "entries"})
- public static IRubyObject to_a(ThreadContext context, IRubyObject self) {
- Ruby runtime = self.getRuntime();
- RubyArray result = runtime.newArray();
- callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
- return result;
- }
- @JRubyMethod(name = "sort", frame = true)
- public static IRubyObject sort(ThreadContext context, IRubyObject self, final Block block) {
- Ruby runtime = self.getRuntime();
- RubyArray result = runtime.newArray();
- callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
- result.sort_bang(block);
-
- return result;
- }
- @JRubyMethod(name = "sort_by", frame = true)
- public static IRubyObject sort_by(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final ThreadContext localContext = context; // MUST NOT be used across threads
- if (self instanceof RubyArray) {
- RubyArray selfArray = (RubyArray) self;
- final IRubyObject[][] valuesAndCriteria = new IRubyObject[selfArray.size()][2];
- callEach(runtime, context, self, new BlockCallback() {
- AtomicInteger i = new AtomicInteger(0);
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- IRubyObject[] myVandC = valuesAndCriteria[i.getAndIncrement()];
- myVandC[0] = largs[0];
- myVandC[1] = block.yield(ctx, largs[0]);
- return runtime.getNil();
- }
- });
-
- Arrays.sort(valuesAndCriteria, new Comparator<IRubyObject[]>() {
- public int compare(IRubyObject[] o1, IRubyObject[] o2) {
- return RubyFixnum.fix2int(o1[1].callMethod(localContext, MethodIndex.OP_SPACESHIP, "<=>", o2[1]));
- }
- });
-
- IRubyObject dstArray[] = new IRubyObject[selfArray.size()];
- for (int i = 0; i < dstArray.length; i++) {
- dstArray[i] = valuesAndCriteria[i][0];
- }
- return runtime.newArrayNoCopy(dstArray);
- } else {
- final RubyArray result = runtime.newArray();
- callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
-
- final IRubyObject[][] valuesAndCriteria = new IRubyObject[result.size()][2];
- for (int i = 0; i < valuesAndCriteria.length; i++) {
- IRubyObject val = result.eltInternal(i);
- valuesAndCriteria[i][0] = val;
- valuesAndCriteria[i][1] = block.yield(context, val);
- }
-
- Arrays.sort(valuesAndCriteria, new Comparator<IRubyObject[]>() {
- public int compare(IRubyObject[] o1, IRubyObject[] o2) {
- return RubyFixnum.fix2int(o1[1].callMethod(localContext, MethodIndex.OP_SPACESHIP, "<=>", o2[1]));
- }
- });
-
- for (int i = 0; i < valuesAndCriteria.length; i++) {
- result.eltInternalSet(i, valuesAndCriteria[i][0]);
- }
- return result;
- }
- }
- @JRubyMethod(name = "grep", required = 1, frame = true)
- public static IRubyObject grep(ThreadContext context, IRubyObject self, final IRubyObject pattern, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray();
- if (block.isGiven()) {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- ctx.setRubyFrameDelta(ctx.getRubyFrameDelta()+2);
- if (pattern.callMethod(ctx, MethodIndex.OP_EQQ, "===", largs[0]).isTrue()) {
- IRubyObject value = block.yield(ctx, largs[0]);
- synchronized (result) {
- result.append(value);
- }
- }
- ctx.setRubyFrameDelta(ctx.getRubyFrameDelta()-2);
- return runtime.getNil();
- }
- });
- } else {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (pattern.callMethod(ctx, MethodIndex.OP_EQQ, "===", largs[0]).isTrue()) {
- synchronized (result) {
- result.append(largs[0]);
- }
- }
- return runtime.getNil();
- }
- });
- }
-
- return result;
- }
- @JRubyMethod(name = {"detect", "find"}, optional = 1, frame = true)
- public static IRubyObject detect(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
- final Ruby runtime = self.getRuntime();
- final IRubyObject result[] = new IRubyObject[] { null };
- final ThreadContext localContext = context;
- IRubyObject ifnone = null;
- if (args.length == 1) ifnone = args[0];
- try {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#detect/find cannot be parallelized");
- }
- if (block.yield(ctx, largs[0]).isTrue()) {
- result[0] = largs[0];
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- } catch (JumpException.SpecialJump sj) {
- return result[0];
- }
- return ifnone != null ? ifnone.callMethod(context, "call") : runtime.getNil();
- }
- @JRubyMethod(name = {"select", "find_all"}, frame = true)
- public static IRubyObject select(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray();
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (block.yield(ctx, largs[0]).isTrue()) {
- synchronized (result) {
- result.append(largs[0]);
- }
- }
- return runtime.getNil();
- }
- });
- return result;
- }
- @JRubyMethod(name = "reject", frame = true)
- public static IRubyObject reject(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray();
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (!block.yield(ctx, largs[0]).isTrue()) {
- synchronized (result) {
- result.append(largs[0]);
- }
- }
- return runtime.getNil();
- }
- });
- return result;
- }
- @JRubyMethod(name = {"collect", "map"}, frame = true)
- public static IRubyObject collect(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray();
- if (block.isGiven()) {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- IRubyObject value = block.yield(ctx, largs[0]);
- synchronized (result) {
- result.append(value);
- }
- return runtime.getNil();
- }
- });
- } else {
- callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
- }
- return result;
- }
- @JRubyMethod(name = "inject", optional = 1, frame = true)
- public static IRubyObject inject(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
- final Ruby runtime = self.getRuntime();
- final IRubyObject result[] = new IRubyObject[] { null };
- final ThreadContext localContext = context;
- if (args.length == 1) result[0] = args[0];
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#inject cannot be parallelized");
- }
- result[0] = result[0] == null ?
- largs[0] : block.yield(ctx, runtime.newArray(result[0], largs[0]), null, null, true);
- return runtime.getNil();
- }
- });
- return result[0] == null ? runtime.getNil() : result[0];
- }
- @JRubyMethod(name = "partition", frame = true)
- public static IRubyObject partition(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyArray arr_true = runtime.newArray();
- final RubyArray arr_false = runtime.newArray();
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (block.yield(ctx, largs[0]).isTrue()) {
- synchronized (arr_true) {
- arr_true.append(largs[0]);
- }
- } else {
- synchronized (arr_false) {
- arr_false.append(largs[0]);
- }
- }
- return runtime.getNil();
- }
- });
- return runtime.newArray(arr_true, arr_false);
- }
- private static class EachWithIndex implements BlockCallback {
- private int index = 0;
- private final Block block;
- private final Ruby runtime;
- public EachWithIndex(ThreadContext ctx, Block block) {
- this.block = block;
- this.runtime = ctx.getRuntime();
- }
- public IRubyObject call(ThreadContext context, IRubyObject[] iargs, Block block) {
- this.block.call(context, new IRubyObject[] { runtime.newArray(iargs[0], runtime.newFixnum(index++)) });
- return runtime.getNil();
- }
- }
- @JRubyMethod(name = "each_with_index", frame = true)
- public static IRubyObject each_with_index(ThreadContext context, IRubyObject self, Block block) {
- RuntimeHelpers.invoke(context, self, "each", CallBlock.newCallClosure(self, self.getRuntime().getEnumerable(),
- Arity.noArguments(), new EachWithIndex(context, block), context));
-
- return self;
- }
- @JRubyMethod(name = {"include?", "member?"}, required = 1, frame = true)
- public static IRubyObject include_p(ThreadContext context, IRubyObject self, final IRubyObject arg) {
- final Ruby runtime = context.getRuntime();
- final ThreadContext localContext = context;
- try {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#include?/member? cannot be parallelized");
- }
- if (RubyObject.equalInternal(ctx, largs[0], arg)) {
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- } catch (JumpException.SpecialJump sj) {
- return runtime.getTrue();
- }
-
- return runtime.getFalse();
- }
- @JRubyMethod(name = "max", frame = true)
- public static IRubyObject max(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final IRubyObject result[] = new IRubyObject[] { null };
- final ThreadContext localContext = context;
- if (block.isGiven()) {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#max{} cannot be parallelized");
- }
- if (result[0] == null || RubyComparable.cmpint(ctx, block.yield(ctx,
- runtime.newArray(largs[0], result[0])), largs[0], result[0]) > 0) {
- result[0] = largs[0];
- }
- return runtime.getNil();
- }
- });
- } else {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- synchronized (result) {
- if (result[0] == null || RubyComparable.cmpint(ctx, largs[0].callMethod(ctx,
- MethodIndex.OP_SPACESHIP, "<=>", result[0]), largs[0], result[0]) > 0) {
- result[0] = largs[0];