summaryrefslogtreecommitdiff
path: root/test/scanners/java/jruby.in.java
diff options
context:
space:
mode:
Diffstat (limited to 'test/scanners/java/jruby.in.java')
-rw-r--r--test/scanners/java/jruby.in.java51193
1 files changed, 0 insertions, 51193 deletions
diff --git a/test/scanners/java/jruby.in.java b/test/scanners/java/jruby.in.java
deleted file mode 100644
index 538abfa..0000000
--- a/test/scanners/java/jruby.in.java
+++ /dev/null
@@ -1,51193 +0,0 @@
-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];
- }
- }
- return runtime.getNil();
- }
- });
- }
-
- return result[0] == null ? runtime.getNil() : result[0];
- }
-
- @JRubyMethod(name = "min", frame = true)
- public static IRubyObject min(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#min{} 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];
- }
- }
- return runtime.getNil();
- }
- });
- }
-
- return result[0] == null ? runtime.getNil() : result[0];
- }
-
- @JRubyMethod(name = "all?", frame = true)
- public static IRubyObject all_p(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final ThreadContext localContext = context;
-
- try {
- 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#all? cannot be parallelized");
- }
- if (!block.yield(ctx, largs[0]).isTrue()) {
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- } else {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#all? cannot be parallelized");
- }
- if (!largs[0].isTrue()) {
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- }
- } catch (JumpException.SpecialJump sj) {
- return runtime.getFalse();
- }
-
- return runtime.getTrue();
- }
-
- @JRubyMethod(name = "any?", frame = true)
- public static IRubyObject any_p(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final ThreadContext localContext = context;
-
- try {
- 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#any? cannot be parallelized");
- }
- if (block.yield(ctx, largs[0]).isTrue()) {
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- } else {
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (localContext != ctx) {
- throw runtime.newThreadError("Enumerable#any? cannot be parallelized");
- }
- if (largs[0].isTrue()) {
- throw JumpException.SPECIAL_JUMP;
- }
- return runtime.getNil();
- }
- });
- }
- } catch (JumpException.SpecialJump sj) {
- return runtime.getTrue();
- }
-
- return runtime.getFalse();
- }
-
- @JRubyMethod(name = "zip", rest = true, frame = true)
- public static IRubyObject zip(ThreadContext context, IRubyObject self, final IRubyObject[] args, final Block block) {
- final Ruby runtime = self.getRuntime();
-
- for (int i = 0; i < args.length; i++) {
- args[i] = TypeConverter.convertToType(args[i], runtime.getArray(), MethodIndex.TO_A, "to_a");
- }
-
- final int aLen = args.length + 1;
-
- if (block.isGiven()) {
- callEach(runtime, context, self, new BlockCallback() {
- AtomicInteger ix = new AtomicInteger(0);
-
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- RubyArray array = runtime.newArray(aLen);
- int myIx = ix.getAndIncrement();
- array.append(largs[0]);
- for (int i = 0, j = args.length; i < j; i++) {
- array.append(((RubyArray) args[i]).entry(myIx));
- }
- block.yield(ctx, array);
- return runtime.getNil();
- }
- });
- return runtime.getNil();
- } else {
- final RubyArray zip = runtime.newArray();
- callEach(runtime, context, self, new BlockCallback() {
- AtomicInteger ix = new AtomicInteger(0);
-
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- RubyArray array = runtime.newArray(aLen);
- array.append(largs[0]);
- int myIx = ix.getAndIncrement();
- for (int i = 0, j = args.length; i < j; i++) {
- array.append(((RubyArray) args[i]).entry(myIx));
- }
- synchronized (zip) {
- zip.append(array);
- }
- return runtime.getNil();
- }
- });
- return zip;
- }
- }
-
- @JRubyMethod(name = "group_by", frame = true)
- public static IRubyObject group_by(ThreadContext context, IRubyObject self, final Block block) {
- final Ruby runtime = self.getRuntime();
- final RubyHash result = new RubyHash(runtime);
-
- callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- IRubyObject key = block.yield(ctx, largs[0]);
- synchronized (result) {
- IRubyObject curr = result.fastARef(key);
-
- if (curr == null) {
- curr = runtime.newArray();
- result.fastASet(key, curr);
- }
- curr.callMethod(ctx, MethodIndex.OP_LSHIFT, "<<", largs[0]);
- }
- return runtime.getNil();
- }
- });
-
- return result;
- }
-
- public static final class AppendBlockCallback implements BlockCallback {
- private Ruby runtime;
- private RubyArray result;
-
- public AppendBlockCallback(Ruby runtime, RubyArray result) {
- this.runtime = runtime;
- this.result = result;
- }
-
- public IRubyObject call(ThreadContext context, IRubyObject[] largs, Block blk) {
- result.append(largs[0]);
-
- return runtime.getNil();
- }
- }
-}
-/***** 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 Michael Studman <me@michaelstudman.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.JRubyModule;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.BlockCallback;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- * Implementation of Ruby's Enumerator module.
- */
-@JRubyModule(name="Enumerable::Enumerator", include="Enumerable")
-public class RubyEnumerator extends RubyObject {
- /** target for each operation */
- private IRubyObject object;
-
- /** method to invoke for each operation */
- private IRubyObject method;
-
- /** args to each method */
- private IRubyObject[] methodArgs;
-
- private static ObjectAllocator ENUMERATOR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyEnumerator(runtime, klass);
- }
- };
-
- public static void defineEnumerator(Ruby runtime) {
- RubyModule kernel = runtime.getKernel();
- kernel.defineAnnotatedMethod(RubyEnumerator.class, "obj_to_enum");
-
- RubyModule enm = runtime.getClassFromPath("Enumerable");
- enm.defineAnnotatedMethod(RubyEnumerator.class, "each_with_index");
- enm.defineAnnotatedMethod(RubyEnumerator.class, "each_slice");
- enm.defineAnnotatedMethod(RubyEnumerator.class, "enum_slice");
- enm.defineAnnotatedMethod(RubyEnumerator.class, "each_cons");
- enm.defineAnnotatedMethod(RubyEnumerator.class, "enum_cons");
-
- RubyClass enmr = enm.defineClassUnder("Enumerator", runtime.getObject(), ENUMERATOR_ALLOCATOR);
-
- enmr.includeModule(enm);
-
- enmr.defineAnnotatedMethod(RubyEnumerator.class, "initialize");
- enmr.defineAnnotatedMethod(RubyEnumerator.class, "each");
-
- runtime.setEnumerator(enmr);
- }
-
- @JRubyMethod(name = {"to_enum", "enum_for"}, optional = 1, rest = true, frame = true)
- public static IRubyObject obj_to_enum(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
- IRubyObject[] newArgs = new IRubyObject[args.length + 1];
- newArgs[0] = self;
- System.arraycopy(args, 0, newArgs, 1, args.length);
-
- return self.getRuntime().getEnumerator().callMethod(context, "new", newArgs);
- }
-
- private RubyEnumerator(Ruby runtime, RubyClass type) {
- super(runtime, type);
- object = method = runtime.getNil();
- }
-
- @JRubyMethod(name = "initialize", required = 1, rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args) {
- object = args[0];
- method = args.length > 1 ? args[1] : getRuntime().fastNewSymbol("each");
- if (args.length > 2) {
- methodArgs = new IRubyObject[Math.max(0, args.length - 2)];
- System.arraycopy(args, 2, methodArgs, 0, args.length - 2);
- } else {
- methodArgs = new IRubyObject[0];
- }
- return this;
- }
-
- /**
- * Send current block and supplied args to method on target. According to MRI
- * Block may not be given and "each" should just ignore it and call on through to
- * underlying method.
- */
- @JRubyMethod(name = "each", frame = true)
- public IRubyObject each(ThreadContext context, Block block) {
- return object.callMethod(context, method.asJavaString(), methodArgs, block);
- }
-
- @JRubyMethod(name = "enum_with_index")
- public static IRubyObject each_with_index(ThreadContext context, IRubyObject self) {
- IRubyObject enumerator = self.getRuntime().getEnumerator();
- return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_with_index"));
- }
-
- @JRubyMethod(name = "each_slice", required = 1, frame = true)
- public static IRubyObject each_slice(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
- final int size = (int)RubyNumeric.num2long(arg);
-
- if (size <= 0) throw self.getRuntime().newArgumentError("invalid slice size");
-
- final Ruby runtime = self.getRuntime();
- final RubyArray result[] = new RubyArray[]{runtime.newArray(size)};
-
- RubyEnumerable.callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- result[0].append(largs[0]);
- if (result[0].size() == size) {
- block.yield(ctx, result[0]);
- result[0] = runtime.newArray(size);
- }
- return runtime.getNil();
- }
- });
-
- if (result[0].size() > 0) block.yield(context, result[0]);
- return self.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "each_cons", required = 1, frame = true)
- public static IRubyObject each_cons(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
- final int size = (int)RubyNumeric.num2long(arg);
-
- if (size <= 0) throw self.getRuntime().newArgumentError("invalid size");
-
- final Ruby runtime = self.getRuntime();
- final RubyArray result = runtime.newArray(size);
-
- RubyEnumerable.callEach(runtime, context, self, new BlockCallback() {
- public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
- if (result.size() == size) result.shift();
- result.append(largs[0]);
- if (result.size() == size) block.yield(ctx, result.aryDup());
- return runtime.getNil();
- }
- });
-
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "enum_slice", required = 1)
- public static IRubyObject enum_slice(ThreadContext context, IRubyObject self, IRubyObject arg) {
- IRubyObject enumerator = self.getRuntime().getEnumerator();
- return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_slice"), arg);
- }
-
- @JRubyMethod(name = "enum_cons", required = 1)
- public static IRubyObject enum_cons(ThreadContext context, IRubyObject self, IRubyObject arg) {
- IRubyObject enumerator = self.getRuntime().getEnumerator();
- return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_cons"), arg);
- }
-}
-package org.jruby;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.ext.posix.Passwd;
-import org.jruby.ext.posix.Group;
-import org.jruby.ext.posix.POSIX;
-import org.jruby.ext.posix.util.Platform;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyModule(name="Etc")
-public class RubyEtc {
- public static RubyModule createEtcModule(Ruby runtime) {
- RubyModule etcModule = runtime.defineModule("Etc");
-
- runtime.setEtc(etcModule);
-
- etcModule.defineAnnotatedMethods(RubyEtc.class);
-
- definePasswdStruct(runtime);
- defineGroupStruct(runtime);
-
- return etcModule;
- }
-
- private static void definePasswdStruct(Ruby runtime) {
- IRubyObject[] args = new IRubyObject[] {
- runtime.newString("Passwd"),
- runtime.newSymbol("name"),
- runtime.newSymbol("passwd"),
- runtime.newSymbol("uid"),
- runtime.newSymbol("gid"),
- runtime.newSymbol("gecos"),
- runtime.newSymbol("dir"),
- runtime.newSymbol("shell"),
- runtime.newSymbol("change"),
- runtime.newSymbol("uclass"),
- runtime.newSymbol("expire")
- };
-
- runtime.setPasswdStruct(RubyStruct.newInstance(runtime.getStructClass(), args, Block.NULL_BLOCK));
- }
-
- private static void defineGroupStruct(Ruby runtime) {
- IRubyObject[] args = new IRubyObject[] {
- runtime.newString("Group"),
- runtime.newSymbol("name"),
- runtime.newSymbol("passwd"),
- runtime.newSymbol("gid"),
- runtime.newSymbol("mem")
- };
-
- runtime.setGroupStruct(RubyStruct.newInstance(runtime.getStructClass(), args, Block.NULL_BLOCK));
- }
-
- private static IRubyObject setupPasswd(Ruby runtime, Passwd passwd) {
- IRubyObject[] args = new IRubyObject[] {
- runtime.newString(passwd.getLoginName()),
- runtime.newString(passwd.getPassword()),
- runtime.newFixnum(passwd.getUID()),
- runtime.newFixnum(passwd.getGID()),
- runtime.newString(passwd.getGECOS()),
- runtime.newString(passwd.getHome()),
- runtime.newString(passwd.getShell()),
- runtime.newFixnum(passwd.getPasswdChangeTime()),
- runtime.newString(passwd.getAccessClass()),
- runtime.newFixnum(passwd.getExpire())
-
- };
-
- return RubyStruct.newStruct(runtime.getPasswdStruct(), args, Block.NULL_BLOCK);
- }
-
-
- private static IRubyObject setupGroup(Ruby runtime, Group group) {
- IRubyObject[] args = new IRubyObject[] {
- runtime.newString(group.getName()),
- runtime.newString(group.getPassword()),
- runtime.newFixnum(group.getGID()),
- intoStringArray(runtime, group.getMembers())
- };
-
- return RubyStruct.newStruct(runtime.getGroupStruct(), args, Block.NULL_BLOCK);
- }
-
- private static IRubyObject intoStringArray(Ruby runtime, String[] members) {
- IRubyObject[] arr = new IRubyObject[members.length];
- for(int i = 0; i<arr.length; i++) {
- arr[i] = runtime.newString(members[i]);
- }
- return runtime.newArrayNoCopy(arr);
- }
-
-
- @JRubyMethod(name = "getpwuid", optional=1, module = true)
- public static IRubyObject getpwuid(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- POSIX posix = runtime.getPosix();
- int uid = args.length == 0 ? posix.getuid() : RubyNumeric.fix2int(args[0]);
- Passwd pwd = posix.getpwuid(uid);
- if(pwd == null) {
- if (Platform.IS_WINDOWS) { // MRI behavior
- return recv.getRuntime().getNil();
- }
- throw runtime.newArgumentError("can't find user for " + uid);
- }
- return setupPasswd(runtime, pwd);
- }
-
- @JRubyMethod(name = "getpwnam", required=1, module = true)
- public static IRubyObject getpwnam(IRubyObject recv, IRubyObject name) {
- String nam = name.convertToString().toString();
- Passwd pwd = recv.getRuntime().getPosix().getpwnam(nam);
- if(pwd == null) {
- if (Platform.IS_WINDOWS) { // MRI behavior
- return recv.getRuntime().getNil();
- }
- throw recv.getRuntime().newArgumentError("can't find user for " + nam);
- }
- return setupPasswd(recv.getRuntime(), pwd);
- }
-
- @JRubyMethod(name = "passwd", module = true, frame=true)
- public static IRubyObject passwd(IRubyObject recv, Block block) {
- Ruby runtime = recv.getRuntime();
- POSIX posix = runtime.getPosix();
- if(block.isGiven()) {
- ThreadContext context = runtime.getCurrentContext();
- posix.setpwent();
- Passwd pw;
- while((pw = posix.getpwent()) != null) {
- block.yield(context, setupPasswd(runtime, pw));
- }
- posix.endpwent();
- }
-
- Passwd pw = posix.getpwent();
- if (pw != null) {
- return setupPasswd(runtime, pw);
- } else {
- return runtime.getNil();
- }
- }
-
- @JRubyMethod(name = "getlogin", module = true)
- public static IRubyObject getlogin(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
-
- String login = runtime.getPosix().getlogin();
-
- if (login != null) {
- return runtime.newString(login);
- } else {
- return runtime.getNil();
- }
- }
-
- @JRubyMethod(name = "endpwent", module = true)
- public static IRubyObject endpwent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- runtime.getPosix().endpwent();
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "setpwent", module = true)
- public static IRubyObject setpwent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- runtime.getPosix().setpwent();
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "getpwent", module = true)
- public static IRubyObject getpwent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- Passwd passwd = runtime.getPosix().getpwent();
- if (passwd != null) {
- return setupPasswd(recv.getRuntime(), passwd);
- } else {
- return runtime.getNil();
- }
- }
-
- @JRubyMethod(name = "getgrnam", required=1, module = true)
- public static IRubyObject getgrnam(IRubyObject recv, IRubyObject name) {
- String nam = name.convertToString().toString();
- Group grp = recv.getRuntime().getPosix().getgrnam(nam);
- if(grp == null) {
- if (Platform.IS_WINDOWS) { // MRI behavior
- return recv.getRuntime().getNil();
- }
- throw recv.getRuntime().newArgumentError("can't find group for " + nam);
- }
- return setupGroup(recv.getRuntime(), grp);
- }
-
- @JRubyMethod(name = "getgrgid", optional=1, module = true)
- public static IRubyObject getgrgid(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- POSIX posix = runtime.getPosix();
- int gid = args.length == 0 ? posix.getgid() : RubyNumeric.fix2int(args[0]);
- Group gr = posix.getgrgid(gid);
- if(gr == null) {
- if (Platform.IS_WINDOWS) { // MRI behavior
- return runtime.getNil();
- }
- throw runtime.newArgumentError("can't find group for " + gid);
- }
- return setupGroup(runtime, gr);
- }
-
- @JRubyMethod(name = "endgrent", module = true)
- public static IRubyObject endgrent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- runtime.getPosix().endgrent();
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "setgrent", module = true)
- public static IRubyObject setgrent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- runtime.getPosix().setgrent();
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "group", module = true, frame=true)
- public static IRubyObject group(IRubyObject recv, Block block) {
- Ruby runtime = recv.getRuntime();
- POSIX posix = runtime.getPosix();
- if(block.isGiven()) {
- ThreadContext context = runtime.getCurrentContext();
- posix.setgrent();
- Group gr;
- while((gr = posix.getgrent()) != null) {
- block.yield(context, setupGroup(runtime, gr));
- }
- posix.endgrent();
- }
-
- Group gr = posix.getgrent();
- if (gr != null) {
- return setupGroup(runtime, gr);
- } else {
- return runtime.getNil();
- }
- }
-
- @JRubyMethod(name = "getgrent", module = true)
- public static IRubyObject getgrent(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- Group gr = runtime.getPosix().getgrent();
- if (gr != null) {
- return setupGroup(recv.getRuntime(), gr);
- } else {
- return runtime.getNil();
- }
- }
-}
-/***** 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-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
- * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 David Corbin <dcorbin@users.sf.net>
- * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.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.IOException;
-import java.io.PrintStream;
-import java.util.List;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-
-import org.jruby.runtime.Block;
-import org.jruby.runtime.Frame;
-import org.jruby.runtime.MethodIndex;
-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.builtin.Variable;
-import org.jruby.runtime.component.VariableEntry;
-import org.jruby.runtime.marshal.MarshalStream;
-import org.jruby.runtime.marshal.UnmarshalStream;
-import org.jruby.util.SafePropertyAccessor;
-
-/**
- *
- * @author jpetersen
- */
-@JRubyClass(name="Exception")
-public class RubyException extends RubyObject {
- private StackTraceElement[] backtraceFrames;
- private StackTraceElement[] javaStackTrace;
- private IRubyObject backtrace;
- public IRubyObject message;
- public static final int TRACE_HEAD = 8;
- public static final int TRACE_TAIL = 4;
- public static final int TRACE_MAX = TRACE_HEAD + TRACE_TAIL + 6;
-
- protected RubyException(Ruby runtime, RubyClass rubyClass) {
- this(runtime, rubyClass, null);
- }
-
- public RubyException(Ruby runtime, RubyClass rubyClass, String message) {
- super(runtime, rubyClass);
-
- this.message = message == null ? runtime.getNil() : runtime.newString(message);
- }
-
- private static ObjectAllocator EXCEPTION_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyException instance = new RubyException(runtime, klass);
-
- // for future compatibility as constructors move toward not accepting metaclass?
- instance.setMetaClass(klass);
-
- return instance;
- }
- };
-
- private static final ObjectMarshal EXCEPTION_MARSHAL = new ObjectMarshal() {
- public void marshalTo(Ruby runtime, Object obj, RubyClass type,
- MarshalStream marshalStream) throws IOException {
- RubyException exc = (RubyException)obj;
-
- marshalStream.registerLinkTarget(exc);
- List<Variable<IRubyObject>> attrs = exc.getVariableList();
- attrs.add(new VariableEntry<IRubyObject>(
- "mesg", exc.message == null ? runtime.getNil() : exc.message));
- attrs.add(new VariableEntry<IRubyObject>("bt", exc.getBacktrace()));
- marshalStream.dumpVariables(attrs);
- }
-
- public Object unmarshalFrom(Ruby runtime, RubyClass type,
- UnmarshalStream unmarshalStream) throws IOException {
- RubyException exc = (RubyException)type.allocate();
-
- unmarshalStream.registerLinkTarget(exc);
- unmarshalStream.defaultVariablesUnmarshal(exc);
-
- exc.message = exc.removeInternalVariable("mesg");
- exc.set_backtrace(exc.removeInternalVariable("bt"));
-
- return exc;
- }
- };
-
- public static RubyClass createExceptionClass(Ruby runtime) {
- RubyClass exceptionClass = runtime.defineClass("Exception", runtime.getObject(), EXCEPTION_ALLOCATOR);
- runtime.setException(exceptionClass);
-
- exceptionClass.setMarshal(EXCEPTION_MARSHAL);
- exceptionClass.defineAnnotatedMethods(RubyException.class);
-
- return exceptionClass;
- }
-
- public static RubyException newException(Ruby runtime, RubyClass excptnClass, String msg) {
- return new RubyException(runtime, excptnClass, msg);
- }
-
- public void setBacktraceFrames(StackTraceElement[] backtraceFrames) {
- this.backtraceFrames = backtraceFrames;
- if (TRACE_TYPE == RAW ||
- TRACE_TYPE == RAW_FILTERED ||
- TRACE_TYPE == RUBY_COMPILED ||
- TRACE_TYPE == RUBY_HYBRID) {
- javaStackTrace = Thread.currentThread().getStackTrace();
- }
- }
-
- public static final int RAW = 0;
- public static final int RAW_FILTERED = 1;
- public static final int RUBY_FRAMED = 2;
- public static final int RUBY_COMPILED = 3;
- public static final int RUBY_HYBRID = 4;
-
- public static final int TRACE_TYPE;
-
- static {
- String style = SafePropertyAccessor.getProperty("jruby.backtrace.style", "ruby_framed").toLowerCase();
-
- if (style.equals("raw")) TRACE_TYPE = RAW;
- else if (style.equals("raw_filtered")) TRACE_TYPE = RAW_FILTERED;
- else if (style.equals("ruby_framed")) TRACE_TYPE = RUBY_FRAMED;
- else if (style.equals("ruby_compiled")) TRACE_TYPE = RUBY_COMPILED;
- else if (style.equals("ruby_hybrid")) TRACE_TYPE = RUBY_HYBRID;
- else TRACE_TYPE = RUBY_FRAMED;
- }
-
- public IRubyObject getBacktrace() {
- if (backtrace == null) {
- initBacktrace();
- }
- return backtrace;
- }
-
- public void initBacktrace() {
- switch (TRACE_TYPE) {
- case RAW:
- backtrace = ThreadContext.createRawBacktrace(getRuntime(), javaStackTrace, false);
- break;
- case RAW_FILTERED:
- backtrace = ThreadContext.createRawBacktrace(getRuntime(), javaStackTrace, true);
- break;
- case RUBY_FRAMED:
- backtrace = backtraceFrames == null ? getRuntime().getNil() : ThreadContext.createBacktraceFromFrames(getRuntime(), backtraceFrames);
- break;
- case RUBY_COMPILED:
- backtrace = ThreadContext.createRubyCompiledBacktrace(getRuntime(), javaStackTrace);
- break;
- case RUBY_HYBRID:
- backtrace = ThreadContext.createRubyHybridBacktrace(getRuntime(), backtraceFrames, javaStackTrace, getRuntime().getDebug().isTrue());
- break;
- }
- }
-
- @JRubyMethod(optional = 2, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- if (args.length == 1) message = args[0];
- return this;
- }
-
- @JRubyMethod
- public IRubyObject backtrace() {
- return getBacktrace();
- }
-
- @JRubyMethod(required = 1)
- public IRubyObject set_backtrace(IRubyObject obj) {
- if (obj.isNil()) {
- backtrace = null;
- } else if (!isArrayOfStrings(obj)) {
- throw getRuntime().newTypeError("backtrace must be Array of String");
- } else {
- backtrace = (RubyArray) obj;
- }
- return backtrace();
- }
-
- @JRubyMethod(name = "exception", optional = 1, rest = true, meta = true)
- public static IRubyObject exception(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- return ((RubyClass) recv).newInstance(context, args, block);
- }
-
- @JRubyMethod(optional = 1)
- public RubyException exception(IRubyObject[] args) {
- switch (args.length) {
- case 0 :
- return this;
- case 1 :
- if(args[0] == this) {
- return this;
- }
- RubyException ret = (RubyException)rbClone();
- ret.initialize(args, Block.NULL_BLOCK); // This looks wrong, but it's the way MRI does it.
- return ret;
- default :
- throw getRuntime().newArgumentError("Wrong argument count");
- }
- }
-
- @JRubyMethod
- public IRubyObject to_s() {
- if (message.isNil()) return getRuntime().newString(getMetaClass().getName());
- message.setTaint(isTaint());
- return message;
- }
-
- @JRubyMethod(name = {"to_str", "message"})
- public IRubyObject to_str(ThreadContext context) {
- return callMethod(context, MethodIndex.TO_S, "to_s");
- }
-
- /** inspects an object and return a kind of debug information
- *
- *@return A RubyString containing the debug information.
- */
- @JRubyMethod
- public IRubyObject inspect(ThreadContext context) {
- RubyModule rubyClass = getMetaClass();
- RubyString exception = RubyString.objAsString(context, this);
-
- if (exception.getByteList().realSize == 0) return getRuntime().newString(rubyClass.getName());
- StringBuilder sb = new StringBuilder("#<");
- sb.append(rubyClass.getName()).append(": ").append(exception.getByteList()).append(">");
- return getRuntime().newString(sb.toString());
- }
-
- public void printBacktrace(PrintStream errorStream) {
- IRubyObject backtrace = callMethod(getRuntime().getCurrentContext(), "backtrace");
- boolean debug = getRuntime().getDebug().isTrue();
- if (!backtrace.isNil() && backtrace instanceof RubyArray) {
- IRubyObject[] elements = backtrace.convertToArray().toJavaArray();
-
- for (int i = 1; i < elements.length; i++) {
- IRubyObject stackTraceLine = elements[i];
- if (stackTraceLine instanceof RubyString) {
- printStackTraceLine(errorStream, stackTraceLine);
- }
-
- if (!debug && i == RubyException.TRACE_HEAD && elements.length > RubyException.TRACE_MAX) {
- int hiddenLevels = elements.length - RubyException.TRACE_HEAD - RubyException.TRACE_TAIL;
- errorStream.print("\t ... " + hiddenLevels + " levels...\n");
- i = elements.length - RubyException.TRACE_TAIL;
- }
- }
- }
- }
-
- private void printStackTraceLine(PrintStream errorStream, IRubyObject stackTraceLine) {
- errorStream.print("\tfrom " + stackTraceLine + '\n');
- }
-
- private boolean isArrayOfStrings(IRubyObject backtrace) {
- if (!(backtrace instanceof RubyArray)) return false;
-
- IRubyObject[] elements = ((RubyArray) backtrace).toJavaArray();
-
- for (int i = 0 ; i < elements.length ; i++) {
- if (!(elements[i] instanceof RubyString)) return false;
- }
-
- return true;
- }
-}
-/*
- ***** 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-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2003 Joey Gibson <joey@joeygibson.com>
- * Copyright (C) 2004-2007 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004-2007 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@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 org.jruby.util.io.OpenFile;
-import org.jruby.util.io.ChannelDescriptor;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.ext.posix.util.Platform;
-import org.jruby.runtime.Block;
-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.util.ByteList;
-import org.jruby.util.io.DirectoryAsFileException;
-import org.jruby.util.io.Stream;
-import org.jruby.util.io.ChannelStream;
-import org.jruby.util.io.ModeFlags;
-import org.jruby.util.JRubyFile;
-import org.jruby.util.TypeConverter;
-import org.jruby.util.io.BadDescriptorException;
-import org.jruby.util.io.FileExistsException;
-import org.jruby.util.io.InvalidValueException;
-import org.jruby.util.io.PipeException;
-
-/**
- * Ruby File class equivalent in java.
- **/
-@JRubyClass(name="File", parent="IO", include="FileTest")
-public class RubyFile extends RubyIO {
- private static final long serialVersionUID = 1L;
-
- public static final int LOCK_SH = 1;
- public static final int LOCK_EX = 2;
- public static final int LOCK_NB = 4;
- public static final int LOCK_UN = 8;
-
- private static final int FNM_NOESCAPE = 1;
- private static final int FNM_PATHNAME = 2;
- private static final int FNM_DOTMATCH = 4;
- private static final int FNM_CASEFOLD = 8;
- private static final int FNM_SYSCASE;
-
- static {
- if (Platform.IS_WINDOWS) {
- FNM_SYSCASE = FNM_CASEFOLD;
- } else {
- FNM_SYSCASE = 0;
- }
- }
-
- private static boolean startsWithDriveLetterOnWindows(String path) {
- return (path != null)
- && Platform.IS_WINDOWS &&
- ((path.length()>1 && path.charAt(0) == '/') ?
- (path.length() > 2
- && isWindowsDriveLetter(path.charAt(1))
- && path.charAt(2) == ':') :
- (path.length() > 1
- && isWindowsDriveLetter(path.charAt(0))
- && path.charAt(1) == ':'));
- }
- // adjusts paths started with '/' or '\\', on windows.
- static String adjustRootPathOnWindows(Ruby runtime, String path, String dir) {
- if (path == null) return path;
- if (Platform.IS_WINDOWS) {
- // MRI behavior on Windows: it treats '/' as a root of
- // a current drive (but only if SINGLE slash is present!):
- // E.g., if current work directory is
- // 'D:/home/directory', then '/' means 'D:/'.
- //
- // Basically, '/path' is treated as a *RELATIVE* path,
- // relative to the current drive. '//path' is treated
- // as absolute one.
- if ((path.startsWith("/") && !(path.length()>2 && path.charAt(2) == ':')) || path.startsWith("\\")) {
- if (path.length() > 1 && (path.charAt(1) == '/' || path.charAt(1) == '\\')) {
- return path;
- }
-
- // First try to use drive letter from supplied dir value,
- // then try current work dir.
- if (!startsWithDriveLetterOnWindows(dir)) {
- dir = runtime.getCurrentDirectory();
- }
- if (dir.length() >= 2) {
- path = dir.substring(0, 2) + path;
- }
- } else if (startsWithDriveLetterOnWindows(path) && path.length() == 2) {
- // compensate for missing slash after drive letter on windows
- path += "/";
- }
- }
- return path;
- }
-
- protected String path;
- private FileLock currentLock;
-
- public RubyFile(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- // XXX This constructor is a hack to implement the __END__ syntax.
- // Converting a reader back into an InputStream doesn't generally work.
- public RubyFile(Ruby runtime, String path, final Reader reader) {
- this(runtime, path, new InputStream() {
- public int read() throws IOException {
- return reader.read();
- }
- });
- }
-
- public RubyFile(Ruby runtime, String path, InputStream in) {
- super(runtime, runtime.getFile());
- this.path = path;
- try {
- this.openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(in), getNewFileno(), new FileDescriptor())));
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- }
- this.openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- private static ObjectAllocator FILE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyFile instance = new RubyFile(runtime, klass);
-
- instance.setMetaClass(klass);
-
- return instance;
- }
- };
-
- @JRubyModule(name="File::Constants")
- public static class Constants {}
-
- public static RubyClass createFileClass(Ruby runtime) {
- RubyClass fileClass = runtime.defineClass("File", runtime.getIO(), FILE_ALLOCATOR);
- runtime.setFile(fileClass);
- RubyString separator = runtime.newString("/");
- ThreadContext context = runtime.getCurrentContext();
-
- fileClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyFile;
- }
- };
-
- separator.freeze(context);
- fileClass.defineConstant("SEPARATOR", separator);
- fileClass.defineConstant("Separator", separator);
-
- if (File.separatorChar == '\\') {
- RubyString altSeparator = runtime.newString("\\");
- altSeparator.freeze(context);
- fileClass.defineConstant("ALT_SEPARATOR", altSeparator);
- } else {
- fileClass.defineConstant("ALT_SEPARATOR", runtime.getNil());
- }
-
- RubyString pathSeparator = runtime.newString(File.pathSeparator);
- pathSeparator.freeze(context);
- fileClass.defineConstant("PATH_SEPARATOR", pathSeparator);
-
- // TODO: why are we duplicating the constants here, and then in
- // File::Constants below? File::Constants is included in IO.
-
- // TODO: These were missing, so we're not handling them elsewhere?
- // FIXME: The old value, 32786, didn't match what IOModes expected, so I reference
- // the constant here. THIS MAY NOT BE THE CORRECT VALUE.
- fileClass.fastSetConstant("BINARY", runtime.newFixnum(ModeFlags.BINARY));
- fileClass.fastSetConstant("FNM_NOESCAPE", runtime.newFixnum(FNM_NOESCAPE));
- fileClass.fastSetConstant("FNM_CASEFOLD", runtime.newFixnum(FNM_CASEFOLD));
- fileClass.fastSetConstant("FNM_SYSCASE", runtime.newFixnum(FNM_SYSCASE));
- fileClass.fastSetConstant("FNM_DOTMATCH", runtime.newFixnum(FNM_DOTMATCH));
- fileClass.fastSetConstant("FNM_PATHNAME", runtime.newFixnum(FNM_PATHNAME));
-
- // Create constants for open flags
- fileClass.fastSetConstant("RDONLY", runtime.newFixnum(ModeFlags.RDONLY));
- fileClass.fastSetConstant("WRONLY", runtime.newFixnum(ModeFlags.WRONLY));
- fileClass.fastSetConstant("RDWR", runtime.newFixnum(ModeFlags.RDWR));
- fileClass.fastSetConstant("CREAT", runtime.newFixnum(ModeFlags.CREAT));
- fileClass.fastSetConstant("EXCL", runtime.newFixnum(ModeFlags.EXCL));
- fileClass.fastSetConstant("NOCTTY", runtime.newFixnum(ModeFlags.NOCTTY));
- fileClass.fastSetConstant("TRUNC", runtime.newFixnum(ModeFlags.TRUNC));
- fileClass.fastSetConstant("APPEND", runtime.newFixnum(ModeFlags.APPEND));
- fileClass.fastSetConstant("NONBLOCK", runtime.newFixnum(ModeFlags.NONBLOCK));
-
- // Create constants for flock
- fileClass.fastSetConstant("LOCK_SH", runtime.newFixnum(RubyFile.LOCK_SH));
- fileClass.fastSetConstant("LOCK_EX", runtime.newFixnum(RubyFile.LOCK_EX));
- fileClass.fastSetConstant("LOCK_NB", runtime.newFixnum(RubyFile.LOCK_NB));
- fileClass.fastSetConstant("LOCK_UN", runtime.newFixnum(RubyFile.LOCK_UN));
-
- // Create Constants class
- RubyModule constants = fileClass.defineModuleUnder("Constants");
-
- // TODO: These were missing, so we're not handling them elsewhere?
- constants.fastSetConstant("BINARY", runtime.newFixnum(ModeFlags.BINARY));
- constants.fastSetConstant("SYNC", runtime.newFixnum(0x1000));
- constants.fastSetConstant("FNM_NOESCAPE", runtime.newFixnum(FNM_NOESCAPE));
- constants.fastSetConstant("FNM_CASEFOLD", runtime.newFixnum(FNM_CASEFOLD));
- constants.fastSetConstant("FNM_SYSCASE", runtime.newFixnum(FNM_SYSCASE));
- constants.fastSetConstant("FNM_DOTMATCH", runtime.newFixnum(FNM_DOTMATCH));
- constants.fastSetConstant("FNM_PATHNAME", runtime.newFixnum(FNM_PATHNAME));
-
- // Create constants for open flags
- constants.fastSetConstant("RDONLY", runtime.newFixnum(ModeFlags.RDONLY));
- constants.fastSetConstant("WRONLY", runtime.newFixnum(ModeFlags.WRONLY));
- constants.fastSetConstant("RDWR", runtime.newFixnum(ModeFlags.RDWR));
- constants.fastSetConstant("CREAT", runtime.newFixnum(ModeFlags.CREAT));
- constants.fastSetConstant("EXCL", runtime.newFixnum(ModeFlags.EXCL));
- constants.fastSetConstant("NOCTTY", runtime.newFixnum(ModeFlags.NOCTTY));
- constants.fastSetConstant("TRUNC", runtime.newFixnum(ModeFlags.TRUNC));
- constants.fastSetConstant("APPEND", runtime.newFixnum(ModeFlags.APPEND));
- constants.fastSetConstant("NONBLOCK", runtime.newFixnum(ModeFlags.NONBLOCK));
-
- // Create constants for flock
- constants.fastSetConstant("LOCK_SH", runtime.newFixnum(RubyFile.LOCK_SH));
- constants.fastSetConstant("LOCK_EX", runtime.newFixnum(RubyFile.LOCK_EX));
- constants.fastSetConstant("LOCK_NB", runtime.newFixnum(RubyFile.LOCK_NB));
- constants.fastSetConstant("LOCK_UN", runtime.newFixnum(RubyFile.LOCK_UN));
-
- // File::Constants module is included in IO.
- runtime.getIO().includeModule(constants);
-
- runtime.getFileTest().extend_object(fileClass);
-
- fileClass.defineAnnotatedMethods(RubyFile.class);
-
- return fileClass;
- }
-
- @JRubyMethod
- @Override
- public IRubyObject close() {
- // Make sure any existing lock is released before we try and close the file
- if (currentLock != null) {
- try {
- currentLock.release();
- } catch (IOException e) {
- throw getRuntime().newIOError(e.getMessage());
- }
- }
- return super.close();
- }
-
- @JRubyMethod(required = 1)
- public IRubyObject flock(ThreadContext context, IRubyObject lockingConstant) {
- // TODO: port exact behavior from MRI, and move most locking logic into ChannelDescriptor
- // TODO: for all LOCK_NB cases, return false if they would block
- ChannelDescriptor descriptor = openFile.getMainStream().getDescriptor();
-
- // null channel always succeeds for all locking operations
- if (descriptor.isNull()) return RubyFixnum.zero(context.getRuntime());
-
- FileChannel fileChannel = (FileChannel)descriptor.getChannel();
- int lockMode = RubyNumeric.num2int(lockingConstant);
-
- // Exclusive locks in Java require the channel to be writable, otherwise
- // an exception is thrown (terminating JRuby execution).
- // But flock behavior of MRI is that it allows
- // exclusive locks even on non-writable file. So we convert exclusive
- // lock to shared lock if the channel is not writable, to better match
- // the MRI behavior.
- if (!openFile.isWritable() && (lockMode & LOCK_EX) > 0) {
- lockMode = (lockMode ^ LOCK_EX) | LOCK_SH;
- }
-
- try {
- switch (lockMode) {
- case LOCK_UN:
- case LOCK_UN | LOCK_NB:
- if (currentLock != null) {
- currentLock.release();
- currentLock = null;
-
- return RubyFixnum.zero(context.getRuntime());
- }
- break;
- case LOCK_EX:
- if (currentLock != null) {
- currentLock.release();
- currentLock = null;
- }
- currentLock = fileChannel.lock();
- if (currentLock != null) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- break;
- case LOCK_EX | LOCK_NB:
- if (currentLock != null) {
- currentLock.release();
- currentLock = null;
- }
- currentLock = fileChannel.tryLock();
- if (currentLock != null) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- break;
- case LOCK_SH:
- if (currentLock != null) {
- currentLock.release();
- currentLock = null;
- }
-
- currentLock = fileChannel.lock(0L, Long.MAX_VALUE, true);
- if (currentLock != null) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- break;
- case LOCK_SH | LOCK_NB:
- if (currentLock != null) {
- currentLock.release();
- currentLock = null;
- }
-
- currentLock = fileChannel.tryLock(0L, Long.MAX_VALUE, true);
- if (currentLock != null) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- break;
- default:
- }
- } catch (IOException ioe) {
- if (context.getRuntime().getDebug().isTrue()) {
- ioe.printStackTrace(System.err);
- }
- // Return false here
- } catch (java.nio.channels.OverlappingFileLockException ioe) {
- if (context.getRuntime().getDebug().isTrue()) {
- ioe.printStackTrace(System.err);
- }
- // Return false here
- }
-
- return context.getRuntime().getFalse();
- }
-
- @JRubyMethod(required = 1, optional = 2, frame = true, visibility = Visibility.PRIVATE)
- @Override
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- if (openFile == null) {
- throw getRuntime().newRuntimeError("reinitializing File");
- }
-
- if (args.length > 0 && args.length < 3) {
- IRubyObject fd = TypeConverter.convertToTypeWithCheck(args[0], getRuntime().getFixnum(), MethodIndex.TO_INT, "to_int");
- if (!fd.isNil()) {
- args[0] = fd;
- return super.initialize(args, block);
- }
- }
-
- return openFile(args);
- }
-
- private IRubyObject openFile(IRubyObject args[]) {
- IRubyObject filename = args[0].convertToString();
- getRuntime().checkSafeString(filename);
-
- path = filename.convertToString().getUnicodeValue();
-
- String modeString;
- ModeFlags modes;
- int perm;
-
- try {
- if ((args.length > 1 && args[1] instanceof RubyFixnum) || (args.length > 2 && !args[2].isNil())) {
- if (args[1] instanceof RubyFixnum) {
- modes = new ModeFlags(RubyNumeric.num2int(args[1]));
- } else {
- modeString = args[1].convertToString().toString();
- modes = getIOModes(getRuntime(), modeString);
- }
- if (args.length > 2 && !args[2].isNil()) {
- perm = RubyNumeric.num2int(args[2]);
- } else {
- perm = 438; // 0666
- }
-
- sysopenInternal(path, modes, perm);
- } else {
- modeString = "r";
- if (args.length > 1) {
- if (!args[1].isNil()) {
- modeString = args[1].convertToString().toString();
- }
- }
- openInternal(path, modeString);
- }
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } finally {}
-
- return this;
- }
-
- private void sysopenInternal(String path, ModeFlags modes, int perm) throws InvalidValueException {
- openFile = new OpenFile();
-
- openFile.setPath(path);
- openFile.setMode(modes.getOpenFileFlags());
-
- ChannelDescriptor descriptor = sysopen(path, modes, perm);
- openFile.setMainStream(fdopen(descriptor, modes));
-
- registerDescriptor(descriptor);
- }
-
- private void openInternal(String path, String modeString) throws InvalidValueException {
- openFile = new OpenFile();
-
- openFile.setMode(getIOModes(getRuntime(), modeString).getOpenFileFlags());
- openFile.setPath(path);
- openFile.setMainStream(fopen(path, modeString));
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- private ChannelDescriptor sysopen(String path, ModeFlags modes, int perm) throws InvalidValueException {
- try {
- ChannelDescriptor descriptor = ChannelDescriptor.open(
- getRuntime().getCurrentDirectory(),
- path,
- modes,
- perm,
- getRuntime().getPosix());
-
- // TODO: check if too many open files, GC and try again
-
- return descriptor;
- } catch (FileNotFoundException fnfe) {
- throw getRuntime().newErrnoENOENTError();
- } catch (DirectoryAsFileException dafe) {
- throw getRuntime().newErrnoEISDirError();
- } catch (FileExistsException fee) {
- throw getRuntime().newErrnoEEXISTError("file exists: " + path);
- } catch (IOException ioe) {
- throw getRuntime().newIOErrorFromException(ioe);
- }
- }
-
- private Stream fopen(String path, String modeString) {
- try {
- Stream stream = ChannelStream.fopen(
- getRuntime(),
- path,
- getIOModes(getRuntime(), modeString));
-
- if (stream == null) {
- // TODO
- // if (errno == EMFILE || errno == ENFILE) {
- // rb_gc();
- // file = fopen(fname, mode);
- // }
- // if (!file) {
- // rb_sys_fail(fname);
- // }
- }
-
- // Do we need to be in SETVBUF mode for buffering to make sense? This comes up elsewhere.
- // #ifdef USE_SETVBUF
- // if (setvbuf(file, NULL, _IOFBF, 0) != 0)
- // rb_warn("setvbuf() can't be honoured for %s", fname);
- // #endif
- // #ifdef __human68k__
- // fmode(file, _IOTEXT);
- // #endif
- return stream;
- } catch (BadDescriptorException e) {
- throw getRuntime().newErrnoEBADFError();
- } catch (FileNotFoundException ex) {
- // FNFException can be thrown in both cases, when the file
- // is not found, or when permission is denied.
- if (Ruby.isSecurityRestricted() || new File(path).exists()) {
- throw getRuntime().newErrnoEACCESError(
- "Permission denied - " + path);
- }
- throw getRuntime().newErrnoENOENTError(
- "File not found - " + path);
- } catch (DirectoryAsFileException ex) {
- throw getRuntime().newErrnoEISDirError();
- } catch (FileExistsException ex) {
- throw getRuntime().newErrnoEEXISTError(path);
- } catch (IOException ex) {
- throw getRuntime().newIOErrorFromException(ex);
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } catch (PipeException ex) {
- throw getRuntime().newErrnoEPIPEError();
- }
- }
-
- @JRubyMethod(required = 1)
- public IRubyObject chmod(ThreadContext context, IRubyObject arg) {
- int mode = (int) arg.convertToInteger().getLongValue();
-
- if (!new File(path).exists()) {
- throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
- }
-
- return context.getRuntime().newFixnum(context.getRuntime().getPosix().chmod(path, mode));
- }
-
- @JRubyMethod(required = 2)
- public IRubyObject chown(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
- int owner = -1;
- if (!arg1.isNil()) {
- owner = RubyNumeric.num2int(arg1);
- }
-
- int group = -1;
- if (!arg2.isNil()) {
- group = RubyNumeric.num2int(arg2);
- }
-
- if (!new File(path).exists()) {
- throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
- }
-
- return context.getRuntime().newFixnum(context.getRuntime().getPosix().chown(path, owner, group));
- }
-
- @JRubyMethod
- public IRubyObject atime(ThreadContext context) {
- return context.getRuntime().newFileStat(path, false).atime();
- }
-
- @JRubyMethod
- public IRubyObject ctime(ThreadContext context) {
- return context.getRuntime().newFileStat(path, false).ctime();
- }
-
- @JRubyMethod(required = 1)
- public IRubyObject lchmod(ThreadContext context, IRubyObject arg) {
- int mode = (int) arg.convertToInteger().getLongValue();
-
- if (!new File(path).exists()) {
- throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
- }
-
- return context.getRuntime().newFixnum(context.getRuntime().getPosix().lchmod(path, mode));
- }
-
- // TODO: this method is not present in MRI!
- @JRubyMethod(required = 2)
- public IRubyObject lchown(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
- int owner = -1;
- if (!arg1.isNil()) {
- owner = RubyNumeric.num2int(arg1);
- }
-
- int group = -1;
- if (!arg2.isNil()) {
- group = RubyNumeric.num2int(arg2);
- }
-
- if (!new File(path).exists()) {
- throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
- }
-
- return context.getRuntime().newFixnum(context.getRuntime().getPosix().lchown(path, owner, group));
- }
-
- @JRubyMethod
- public IRubyObject lstat(ThreadContext context) {
- return context.getRuntime().newFileStat(path, true);
- }
-
- @JRubyMethod
- public IRubyObject mtime(ThreadContext context) {
- return getLastModified(context.getRuntime(), path);
- }
-
- @JRubyMethod
- public RubyString path(ThreadContext context) {
- return context.getRuntime().newString(path);
- }
-
- @JRubyMethod
- @Override
- public IRubyObject stat(ThreadContext context) {
- openFile.checkClosed(context.getRuntime());
- return context.getRuntime().newFileStat(path, false);
- }
-
- @JRubyMethod(required = 1)
- public IRubyObject truncate(ThreadContext context, IRubyObject arg) {
- RubyInteger newLength = arg.convertToInteger();
- if (newLength.getLongValue() < 0) {
- throw context.getRuntime().newErrnoEINVALError("invalid argument: " + path);
- }
- try {
- openFile.checkWritable(context.getRuntime());
- openFile.getMainStream().ftruncate(newLength.getLongValue());
- } catch (BadDescriptorException e) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (InvalidValueException ex) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (IOException e) {
- // Should we do anything?
- }
-
- return RubyFixnum.zero(context.getRuntime());
- }
-
- @Override
- public String toString() {
- return "RubyFile(" + path + ", " + openFile.getMode() + ", " + openFile.getMainStream().getDescriptor().getFileno() + ")";
- }
-
- // TODO: This is also defined in the MetaClass too...Consolidate somewhere.
- private static ModeFlags getModes(Ruby runtime, IRubyObject object) throws InvalidValueException {
- if (object instanceof RubyString) {
- return getIOModes(runtime, ((RubyString) object).toString());
- } else if (object instanceof RubyFixnum) {
- return new ModeFlags(((RubyFixnum) object).getLongValue());
- }
-
- throw runtime.newTypeError("Invalid type for modes");
- }
-
- @JRubyMethod
- @Override
- public IRubyObject inspect() {
- StringBuilder val = new StringBuilder();
- val.append("#<File:").append(path);
- if(!openFile.isOpen()) {
- val.append(" (closed)");
- }
- val.append(">");
- return getRuntime().newString(val.toString());
- }
-
- /* File class methods */
-
- @JRubyMethod(required = 1, optional = 1, meta = true)
- public static IRubyObject basename(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- String name = RubyString.stringValue(args[0]).toString();
-
- // MRI-compatible basename handling for windows drive letter paths
- if (Platform.IS_WINDOWS) {
- if (name.length() > 1 && name.charAt(1) == ':' && Character.isLetter(name.charAt(0))) {
- switch (name.length()) {
- case 2:
- return RubyString.newEmptyString(context.getRuntime()).infectBy(args[0]);
- case 3:
- return context.getRuntime().newString(name.substring(2)).infectBy(args[0]);
- default:
- switch (name.charAt(2)) {
- case '/':
- case '\\':
- break;
- default:
- // strip c: away from relative-pathed name
- name = name.substring(2);
- break;
- }
- break;
- }
- }
- }
-
- while (name.length() > 1 && name.charAt(name.length() - 1) == '/') {
- name = name.substring(0, name.length() - 1);
- }
-
- // Paths which end in "/" or "\\" must be stripped off.
- int slashCount = 0;
- int length = name.length();
- for (int i = length - 1; i >= 0; i--) {
- char c = name.charAt(i);
- if (c != '/' && c != '\\') {
- break;
- }
- slashCount++;
- }
- if (slashCount > 0 && length > 1) {
- name = name.substring(0, name.length() - slashCount);
- }
-
- int index = name.lastIndexOf('/');
- if (index == -1) {
- // XXX actually only on windows...
- index = name.lastIndexOf('\\');
- }
-
- if (!name.equals("/") && index != -1) {
- name = name.substring(index + 1);
- }
-
- if (args.length == 2) {
- String ext = RubyString.stringValue(args[1]).toString();
- if (".*".equals(ext)) {
- index = name.lastIndexOf('.');
- if (index > 0) { // -1 no match; 0 it is dot file not extension
- name = name.substring(0, index);
- }
- } else if (name.endsWith(ext)) {
- name = name.substring(0, name.length() - ext.length());
- }
- }
- return context.getRuntime().newString(name).infectBy(args[0]);
- }
-
- @JRubyMethod(required = 2, rest = true, meta = true)
- public static IRubyObject chmod(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- int count = 0;
- RubyInteger mode = args[0].convertToInteger();
- for (int i = 1; i < args.length; i++) {
- IRubyObject filename = args[i];
-
- if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
- }
-
- boolean result = 0 == runtime.getPosix().chmod(filename.toString(), (int)mode.getLongValue());
- if (result) {
- count++;
- }
- }
-
- return runtime.newFixnum(count);
- }
-
- @JRubyMethod(required = 3, rest = true, meta = true)
- public static IRubyObject chown(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- int count = 0;
- int owner = -1;
- if (!args[0].isNil()) {
- owner = RubyNumeric.num2int(args[0]);
- }
-
- int group = -1;
- if (!args[1].isNil()) {
- group = RubyNumeric.num2int(args[1]);
- }
- for (int i = 2; i < args.length; i++) {
- IRubyObject filename = args[i];
-
- if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
- }
-
- boolean result = 0 == runtime.getPosix().chown(filename.toString(), owner, group);
- if (result) {
- count++;
- }
- }
-
- return runtime.newFixnum(count);
- }
-
- @JRubyMethod(required = 1, meta = true)
- public static IRubyObject dirname(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- RubyString filename = RubyString.stringValue(arg);
- String jfilename = filename.toString();
- String name = jfilename.replace('\\', '/');
- int minPathLength = 1;
- boolean trimmedSlashes = false;
-
- boolean startsWithDriveLetterOnWindows = startsWithDriveLetterOnWindows(name);
-
- if (startsWithDriveLetterOnWindows) {
- minPathLength = 3;
- }
-
- while (name.length() > minPathLength && name.charAt(name.length() - 1) == '/') {
- trimmedSlashes = true;
- name = name.substring(0, name.length() - 1);
- }
-
- String result;
- if (startsWithDriveLetterOnWindows && name.length() == 2) {
- if (trimmedSlashes) {
- // C:\ is returned unchanged
- result = jfilename.substring(0, 3);
- } else {
- result = jfilename.substring(0, 2) + '.';
- }
- } else {
- //TODO deal with UNC names
- int index = name.lastIndexOf('/');
- if (index == -1) {
- if (startsWithDriveLetterOnWindows) {
- return context.getRuntime().newString(jfilename.substring(0, 2) + ".");
- } else {
- return context.getRuntime().newString(".");
- }
- }
- if (index == 0) return context.getRuntime().newString("/");
-
- if (startsWithDriveLetterOnWindows && index == 2) {
- // Include additional path separator
- // (so that dirname of "C:\file.txt" is "C:\", not "C:")
- index++;
- }
-
- result = jfilename.substring(0, index);
- }
-
- char endChar;
- // trim trailing slashes
- while (result.length() > minPathLength) {
- endChar = result.charAt(result.length() - 1);
- if (endChar == '/' || endChar == '\\') {
- result = result.substring(0, result.length() - 1);
- } else {
- break;
- }
- }
-
- return context.getRuntime().newString(result).infectBy(filename);
- }
-
- private static boolean isWindowsDriveLetter(char c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
- }
-
-
- /**
- * Returns the extension name of the file. An empty string is returned if
- * the filename (not the entire path) starts or ends with a dot.
- * @param recv
- * @param arg Path to get extension name of
- * @return Extension, including the dot, or an empty string
- */
- @JRubyMethod(required = 1, meta = true)
- public static IRubyObject extname(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- IRubyObject baseFilename = basename(context, recv, new IRubyObject[]{arg});
- String filename = RubyString.stringValue(baseFilename).toString();
- String result = "";
-
- int dotIndex = filename.lastIndexOf(".");
- if (dotIndex > 0 && dotIndex != (filename.length() - 1)) {
- // Dot is not at beginning and not at end of filename.
- result = filename.substring(dotIndex);
- }
-
- return context.getRuntime().newString(result);
- }
-
- /**
- * Converts a pathname to an absolute pathname. Relative paths are
- * referenced from the current working directory of the process unless
- * a second argument is given, in which case it will be used as the
- * starting point. If the second argument is also relative, it will
- * first be converted to an absolute pathname.
- * @param recv
- * @param args
- * @return Resulting absolute path as a String
- */
- @JRubyMethod(required = 1, optional = 1, meta = true)
- public static IRubyObject expand_path(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- String relativePath = RubyString.stringValue(args[0]).toString();
-
- boolean isAbsoluteWithFilePrefix = relativePath.startsWith("file:");
-
- String cwd = null;
-
- // Handle ~user paths
- relativePath = expandUserPath(context, relativePath);
-
- // If there's a second argument, it's the path to which the first
- // argument is relative.
- if (args.length == 2 && !args[1].isNil()) {
-
- String cwdArg = RubyString.stringValue(args[1]).toString();
-
- // Handle ~user paths.
- cwd = expandUserPath(context, cwdArg);
-
- cwd = adjustRootPathOnWindows(runtime, cwd, null);
-
- boolean startsWithSlashNotOnWindows = (cwd != null)
- && !Platform.IS_WINDOWS && cwd.length() > 0
- && cwd.charAt(0) == '/';
-
- // TODO: better detection when path is absolute or not.
- // If the path isn't absolute, then prepend the current working
- // directory to the path.
- if (!startsWithSlashNotOnWindows && !startsWithDriveLetterOnWindows(cwd)) {
- cwd = new File(runtime.getCurrentDirectory(), cwd).getAbsolutePath();
- }
- } else {
- // If there's no second argument, simply use the working directory
- // of the runtime.
- cwd = runtime.getCurrentDirectory();
- }
-
- // Something wrong we don't know the cwd...
- // TODO: Is this behavior really desirable? /mov
- if (cwd == null) return runtime.getNil();
-
- /* The counting of slashes that follows is simply a way to adhere to
- * Ruby's UNC (or something) compatibility. When Ruby's expand_path is
- * called with "//foo//bar" it will return "//foo/bar". JRuby uses
- * java.io.File, and hence returns "/foo/bar". In order to retain
- * java.io.File in the lower layers and provide full Ruby
- * compatibility, the number of extra slashes must be counted and
- * prepended to the result.
- */
-
- // TODO: special handling on windows for some corner cases
-// if (IS_WINDOWS) {
-// if (relativePath.startsWith("//")) {
-// if (relativePath.length() > 2 && relativePath.charAt(2) != '/') {
-// int nextSlash = relativePath.indexOf('/', 3);
-// if (nextSlash != -1) {
-// return runtime.newString(
-// relativePath.substring(0, nextSlash)
-// + canonicalize(relativePath.substring(nextSlash)));
-// } else {
-// return runtime.newString(relativePath);
-// }
-// }
-// }
-// }
-
- // Find out which string to check.
- String padSlashes = "";
- if (!Platform.IS_WINDOWS) {
- if (relativePath.length() > 0 && relativePath.charAt(0) == '/') {
- padSlashes = countSlashes(relativePath);
- } else if (cwd.length() > 0 && cwd.charAt(0) == '/') {
- padSlashes = countSlashes(cwd);
- }
- }
-
- JRubyFile path;
-
- if (relativePath.length() == 0) {
- path = JRubyFile.create(relativePath, cwd);
- } else {
- relativePath = adjustRootPathOnWindows(runtime, relativePath, cwd);
- path = JRubyFile.create(cwd, relativePath);
- }
-
- String tempResult = padSlashes + canonicalize(path.getAbsolutePath());
-
- if(isAbsoluteWithFilePrefix) {
- tempResult = tempResult.substring(tempResult.indexOf("file:"));
- }
-
- return runtime.newString(tempResult);
- }
-
- /**
- * This method checks a path, and if it starts with ~, then it expands
- * the path to the absolute path of the user's home directory. If the
- * string does not begin with ~, then the string is simply returned.
- * unaltered.
- * @param recv
- * @param path Path to check
- * @return Expanded path
- */
- public static String expandUserPath(ThreadContext context, String path) {
-
- int pathLength = path.length();
-
- if (pathLength >= 1 && path.charAt(0) == '~') {
- // Enebo : Should ~frogger\\foo work (it doesnt in linux ruby)?
- int userEnd = path.indexOf('/');
-
- if (userEnd == -1) {
- if (pathLength == 1) {
- // Single '~' as whole path to expand
- path = RubyDir.getHomeDirectoryPath(context).toString();
- } else {
- // No directory delimeter. Rest of string is username
- userEnd = pathLength;
- }
- }
-
- if (userEnd == 1) {
- // '~/...' as path to expand
- path = RubyDir.getHomeDirectoryPath(context).toString() +
- path.substring(1);
- } else if (userEnd > 1){
- // '~user/...' as path to expand
- String user = path.substring(1, userEnd);
- IRubyObject dir = RubyDir.getHomeDirectoryPath(context, user);
-
- if (dir.isNil()) {
- throw context.getRuntime().newArgumentError("user " + user + " does not exist");
- }
-
- path = "" + dir + (pathLength == userEnd ? "" : path.substring(userEnd));
- }
- }
- return path;
- }
-
- /**
- * Returns a string consisting of <code>n-1</code> slashes, where
- * <code>n</code> is the number of slashes at the beginning of the input
- * string.
- * @param stringToCheck
- * @return
- */
- private static String countSlashes( String stringToCheck ) {
-
- // Count number of extra slashes in the beginning of the string.
- int slashCount = 0;
- for (int i = 0; i < stringToCheck.length(); i++) {
- if (stringToCheck.charAt(i) == '/') {
- slashCount++;
- } else {
- break;
- }
- }
-
- // If there are N slashes, then we want N-1.
- if (slashCount > 0) {
- slashCount--;
- }
-
- // Prepare a string with the same number of redundant slashes so that
- // we easily can prepend it to the result.
- byte[] slashes = new byte[slashCount];
- for (int i = 0; i < slashCount; i++) {
- slashes[i] = '/';
- }
- return new String(slashes);
-
- }
-
- private static String canonicalize(String path) {
- return canonicalize(null, path);
- }
-
- private static String canonicalize(String canonicalPath, String remaining) {
- if (remaining == null) {
- if ("".equals(canonicalPath)) {
- return "/";
- } else {
- // compensate for missing slash after drive letter on windows
- if (startsWithDriveLetterOnWindows(canonicalPath)
- && canonicalPath.length() == 2) {
- canonicalPath += "/";
- }
- }
- return canonicalPath;
- }
-
- String child;
- int slash = remaining.indexOf('/');
- if (slash == -1) {
- child = remaining;
- remaining = null;
- } else {
- child = remaining.substring(0, slash);
- remaining = remaining.substring(slash + 1);
- }
-
- if (child.equals(".")) {
- // skip it
- if (canonicalPath != null && canonicalPath.length() == 0 ) canonicalPath += "/";
- } else if (child.equals("..")) {
- if (canonicalPath == null) throw new IllegalArgumentException("Cannot have .. at the start of an absolute path");
- int lastDir = canonicalPath.lastIndexOf('/');
- if (lastDir == -1) {
- if (startsWithDriveLetterOnWindows(canonicalPath)) {
- // do nothing, we should not delete the drive letter
- } else {
- canonicalPath = "";
- }
- } else {
- canonicalPath = canonicalPath.substring(0, lastDir);
- }
- } else if (canonicalPath == null) {
- canonicalPath = child;
- } else {
- canonicalPath += "/" + child;
- }
-
- return canonicalize(canonicalPath, remaining);
- }
-
- /**
- * Returns true if path matches against pattern The pattern is not a regular expression;
- * instead it follows rules similar to shell filename globbing. It may contain the following
- * metacharacters:
- * *: Glob - match any sequence chars (re: .*). If like begins with '.' then it doesn't.
- * ?: Matches a single char (re: .).
- * [set]: Matches a single char in a set (re: [...]).
- *
- */
- @JRubyMethod(name = {"fnmatch", "fnmatch?"}, required = 2, optional = 1, meta = true)
- public static IRubyObject fnmatch(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- int flags = args.length == 3 ? RubyNumeric.num2int(args[2]) : 0;
-
- ByteList pattern = args[0].convertToString().getByteList();
- ByteList path = args[1].convertToString().getByteList();
-
- if (org.jruby.util.Dir.fnmatch(pattern.bytes, pattern.begin, pattern.begin+pattern.realSize,
- path.bytes, path.begin, path.begin+path.realSize, flags) == 0) {
- return context.getRuntime().getTrue();
- }
- return context.getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "ftype", required = 1, meta = true)
- public static IRubyObject ftype(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- return context.getRuntime().newFileStat(filename.convertToString().toString(), true).ftype();
- }
-
- private static String inspectJoin(ThreadContext context, IRubyObject recv, RubyArray parent, RubyArray array) {
- Ruby runtime = context.getRuntime();
-
- // If already inspecting, there is no need to register/unregister again.
- if (runtime.isInspecting(parent)) return join(context, recv, array).toString();
-
- try {
- runtime.registerInspecting(parent);
- return join(context, recv, array).toString();
- } finally {
- runtime.unregisterInspecting(parent);
- }
- }
-
- private static RubyString join(ThreadContext context, IRubyObject recv, RubyArray ary) {
- IRubyObject[] args = ary.toJavaArray();
- boolean isTainted = false;
- StringBuilder buffer = new StringBuilder();
- Ruby runtime = context.getRuntime();
-
- for (int i = 0; i < args.length; i++) {
- if (args[i].isTaint()) {
- isTainted = true;
- }
- String element;
- if (args[i] instanceof RubyString) {
- element = args[i].toString();
- } else if (args[i] instanceof RubyArray) {
- if (runtime.isInspecting(args[i])) {
- element = "[...]";
- } else {
- element = inspectJoin(context, recv, ary, ((RubyArray)args[i]));
- }
- } else {
- element = args[i].convertToString().toString();
- }
-
- chomp(buffer);
- if (i > 0 && !element.startsWith("/") && !element.startsWith("\\")) {
- buffer.append("/");
- }
- buffer.append(element);
- }
-
- RubyString fixedStr = RubyString.newString(runtime, buffer.toString());
- fixedStr.setTaint(isTainted);
- return fixedStr;
- }
-
- /*
- * Fixme: This does not have exact same semantics as RubyArray.join, but they
- * probably could be consolidated (perhaps as join(args[], sep, doChomp)).
- */
- @JRubyMethod(rest = true, meta = true)
- public static RubyString join(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return join(context, recv, RubyArray.newArrayNoCopyLight(context.getRuntime(), args));
- }
-
- private static void chomp(StringBuilder buffer) {
- int lastIndex = buffer.length() - 1;
-
- while (lastIndex >= 0 && (buffer.lastIndexOf("/") == lastIndex || buffer.lastIndexOf("\\") == lastIndex)) {
- buffer.setLength(lastIndex);
- lastIndex--;
- }
- }
-
- @JRubyMethod(name = "lstat", required = 1, meta = true)
- public static IRubyObject lstat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- String f = filename.convertToString().toString();
- if(f.startsWith("file:") && f.indexOf('!') != -1) {
- f = f.substring(5, f.indexOf("!"));
- }
- return context.getRuntime().newFileStat(f, true);
- }
-
- @JRubyMethod(name = "stat", required = 1, meta = true)
- public static IRubyObject stat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- String f = filename.convertToString().toString();
- if(f.startsWith("file:") && f.indexOf('!') != -1) {
- f = f.substring(5, f.indexOf("!"));
- }
- return context.getRuntime().newFileStat(f, false);
- }
-
- @JRubyMethod(name = "atime", required = 1, meta = true)
- public static IRubyObject atime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- String f = filename.convertToString().toString();
- if(f.startsWith("file:") && f.indexOf('!') != -1) {
- f = f.substring(5, f.indexOf("!"));
- }
- return context.getRuntime().newFileStat(f, false).atime();
- }
-
- @JRubyMethod(name = "ctime", required = 1, meta = true)
- public static IRubyObject ctime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- String f = filename.convertToString().toString();
- if(f.startsWith("file:") && f.indexOf('!') != -1) {
- f = f.substring(5, f.indexOf("!"));
- }
- return context.getRuntime().newFileStat(f, false).ctime();
- }
-
- @JRubyMethod(required = 2, rest = true, meta = true)
- public static IRubyObject lchmod(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- int count = 0;
- RubyInteger mode = args[0].convertToInteger();
- for (int i = 1; i < args.length; i++) {
- IRubyObject filename = args[i];
-
- if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
- }
-
- boolean result = 0 == runtime.getPosix().lchmod(filename.toString(), (int)mode.getLongValue());
- if (result) {
- count++;
- }
- }
-
- return runtime.newFixnum(count);
- }
-
- @JRubyMethod(required = 3, rest = true, meta = true)
- public static IRubyObject lchown(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- int owner = !args[0].isNil() ? RubyNumeric.num2int(args[0]) : -1;
- int group = !args[1].isNil() ? RubyNumeric.num2int(args[1]) : -1;
- int count = 0;
-
- for (int i = 2; i < args.length; i++) {
- IRubyObject filename = args[i];
-
- if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
- }
-
- boolean result = 0 == runtime.getPosix().lchown(filename.toString(), owner, group);
- if (result) {
- count++;
- }
- }
-
- return runtime.newFixnum(count);
- }
-
- @JRubyMethod(required = 2, meta = true)
- public static IRubyObject link(ThreadContext context, IRubyObject recv, IRubyObject from, IRubyObject to) {
- Ruby runtime = context.getRuntime();
- RubyString fromStr = RubyString.stringValue(from);
- RubyString toStr = RubyString.stringValue(to);
- try {
- if (runtime.getPosix().link(
- fromStr.toString(),toStr.toString()) == -1) {
- // FIXME: When we get JNA3 we need to properly write this to errno.
- throw runtime.newErrnoEEXISTError("File exists - "
- + fromStr + " or " + toStr);
- }
- } catch (java.lang.UnsatisfiedLinkError ule) {
- throw runtime.newNotImplementedError("link() function is unimplemented on this machine");
- }
-
- return runtime.newFixnum(0);
- }
-
- @JRubyMethod(name = "mtime", required = 1, meta = true)
- public static IRubyObject mtime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
- return getLastModified(context.getRuntime(), filename.convertToString().toString());
- }
-
- @JRubyMethod(required = 2, meta = true)
- public static IRubyObject rename(ThreadContext context, IRubyObject recv, IRubyObject oldName, IRubyObject newName) {
- Ruby runtime = context.getRuntime();
- RubyString oldNameString = RubyString.stringValue(oldName);
- RubyString newNameString = RubyString.stringValue(newName);
- runtime.checkSafeString(oldNameString);
- runtime.checkSafeString(newNameString);
- JRubyFile oldFile = JRubyFile.create(runtime.getCurrentDirectory(), oldNameString.toString());
- JRubyFile newFile = JRubyFile.create(runtime.getCurrentDirectory(), newNameString.toString());
-
- if (!oldFile.exists() || !newFile.getParentFile().exists()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + oldNameString +
- " or " + newNameString);
- }
-
- JRubyFile dest = JRubyFile.create(runtime.getCurrentDirectory(), newNameString.toString());
-
- if (oldFile.renameTo(dest)) { // rename is successful
- return RubyFixnum.zero(runtime);
- }
-
- // rename via Java API call wasn't successful, let's try some tricks, similar to MRI
-
- if (newFile.exists()) {
- runtime.getPosix().chmod(newNameString.toString(), 0666);
- newFile.delete();
- }
-
- if (oldFile.renameTo(dest)) { // try to rename one more time
- return RubyFixnum.zero(runtime);
- }
-
- throw runtime.newErrnoEACCESError("Permission denied - " + oldNameString + " or " +
- newNameString);
- }
-
- @JRubyMethod(required = 1, meta = true)
- public static RubyArray split(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- RubyString filename = RubyString.stringValue(arg);
-
- return context.getRuntime().newArray(dirname(context, recv, filename),
- basename(context, recv, new IRubyObject[] { filename }));
- }
-
- @JRubyMethod(required = 2, meta = true)
- public static IRubyObject symlink(ThreadContext context, IRubyObject recv, IRubyObject from, IRubyObject to) {
- Ruby runtime = context.getRuntime();
- RubyString fromStr = RubyString.stringValue(from);
- RubyString toStr = RubyString.stringValue(to);
- try {
- if (runtime.getPosix().symlink(
- fromStr.toString(), toStr.toString()) == -1) {
- // FIXME: When we get JNA3 we need to properly write this to errno.
- throw runtime.newErrnoEEXISTError("File exists - "
- + fromStr + " or " + toStr);
- }
- } catch (java.lang.UnsatisfiedLinkError ule) {
- throw runtime.newNotImplementedError("symlink() function is unimplemented on this machine");
- }
-
- return runtime.newFixnum(0);
- }
-
- @JRubyMethod(required = 1, meta = true)
- public static IRubyObject readlink(ThreadContext context, IRubyObject recv, IRubyObject path) {
- Ruby runtime = context.getRuntime();
-
- try {
- String realPath = runtime.getPosix().readlink(path.toString());
-
- if (!RubyFileTest.exist_p(recv, path).isTrue()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + path);
- }
-
- if (!RubyFileTest.symlink_p(recv, path).isTrue()) {
- throw runtime.newErrnoEINVALError("invalid argument - " + path);
- }
-
- if (realPath == null) {
- //FIXME: When we get JNA3 we need to properly write this to errno.
- }
-
- return runtime.newString(realPath);
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- // Can we produce IOError which bypasses a close?
- @JRubyMethod(required = 2, meta = true)
- public static IRubyObject truncate(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
- Ruby runtime = context.getRuntime();
- RubyString filename = arg1.convertToString(); // TODO: SafeStringValue here
- RubyInteger newLength = arg2.convertToInteger();
-
- if (!new File(runtime.getCurrentDirectory(), filename.getByteList().toString()).exists()) {
- throw runtime.newErrnoENOENTError(
- "No such file or directory - " + filename.getByteList().toString());
- }
-
- if (newLength.getLongValue() < 0) {
- throw runtime.newErrnoEINVALError("invalid argument: " + filename);
- }
-
- IRubyObject[] args = new IRubyObject[] { filename, runtime.newString("r+") };
- RubyFile file = (RubyFile) open(context, recv, args, Block.NULL_BLOCK);
- file.truncate(context, newLength);
- file.close();
-
- return RubyFixnum.zero(runtime);
- }
-
- @JRubyMethod(meta = true, optional = 1)
- public static IRubyObject umask(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- int oldMask = 0;
- if (args.length == 0) {
- oldMask = runtime.getPosix().umask(0);
- runtime.getPosix().umask(oldMask);
- } else if (args.length == 1) {
- oldMask = runtime.getPosix().umask((int) args[0].convertToInteger().getLongValue());
- } else {
- runtime.newArgumentError("wrong number of arguments");
- }
-
- return runtime.newFixnum(oldMask);
- }
-
- /**
- * This method does NOT set atime, only mtime, since Java doesn't support anything else.
- */
- @JRubyMethod(required = 2, rest = true, meta = true)
- public static IRubyObject utime(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- // Ignore access_time argument since Java does not support it.
-
- long mtime;
- if (args[1] instanceof RubyTime) {
- mtime = ((RubyTime) args[1]).getJavaDate().getTime();
- } else if (args[1] instanceof RubyNumeric) {
- mtime = RubyNumeric.num2long(args[1]);
- } else if (args[1] == runtime.getNil()) {
- mtime = System.currentTimeMillis();
- } else {
- RubyTime time = (RubyTime) TypeConverter.convertToType(args[1], runtime.getTime(), MethodIndex.NO_INDEX,"to_time", true);
- mtime = time.getJavaDate().getTime();
- }
-
- for (int i = 2, j = args.length; i < j; i++) {
- RubyString filename = RubyString.stringValue(args[i]);
- runtime.checkSafeString(filename);
- JRubyFile fileToTouch = JRubyFile.create(runtime.getCurrentDirectory(),filename.toString());
-
- if (!fileToTouch.exists()) {
- throw runtime.newErrnoENOENTError(" No such file or directory - \"" + filename + "\"");
- }
-
- fileToTouch.setLastModified(mtime);
- }
-
- return runtime.newFixnum(args.length - 2);
- }
-
- @JRubyMethod(name = {"unlink", "delete"}, rest = true, meta = true)
- public static IRubyObject unlink(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- for (int i = 0; i < args.length; i++) {
- RubyString filename = RubyString.stringValue(args[i]);
- runtime.checkSafeString(filename);
- JRubyFile lToDelete = JRubyFile.create(runtime.getCurrentDirectory(),filename.toString());
-
- boolean isSymlink = RubyFileTest.symlink_p(recv, filename).isTrue();
- // Broken symlinks considered by exists() as non-existing,
- // so we need to check for symlinks explicitly.
- if (!lToDelete.exists() && !isSymlink) {
- throw runtime.newErrnoENOENTError(" No such file or directory - \"" + filename + "\"");
- }
-
- if (!lToDelete.delete()) {
- throw runtime.newErrnoEACCESError("Permission denied - \"" + filename + "\"");
- }
- }
-
- return runtime.newFixnum(args.length);
- }
-
- // Fast path since JNA stat is about 10x slower than this
- private static IRubyObject getLastModified(Ruby runtime, String path) {
- JRubyFile file = JRubyFile.create(runtime.getCurrentDirectory(), path);
-
- if (!file.exists()) {
- throw runtime.newErrnoENOENTError("No such file or directory - " + path);
- }
-
- return runtime.newTime(file.lastModified());
- }
-}
-/*
- ***** 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 Joey Gibson <joey@joeygibson.com>
- * Copyright (C) 2004 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.FileDescriptor;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.ext.posix.FileStat;
-import org.jruby.ext.posix.util.Platform;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.JRubyFile;
-
-/**
- * Implements File::Stat
- */
-@JRubyClass(name="File::Stat", include="Comparable")
-public class RubyFileStat extends RubyObject {
- private static final long serialVersionUID = 1L;
-
- private JRubyFile file;
- private FileStat stat;
-
- private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyFileStat(runtime, klass);
- }
- };
-
- public static RubyClass createFileStatClass(Ruby runtime) {
- // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
- final RubyClass fileStatClass = runtime.getFile().defineClassUnder("Stat",runtime.getObject(), ALLOCATOR);
- runtime.setFileStat(fileStatClass);
-
- fileStatClass.includeModule(runtime.fastGetModule("Comparable"));
- fileStatClass.defineAnnotatedMethods(RubyFileStat.class);
-
- return fileStatClass;
- }
-
- protected RubyFileStat(Ruby runtime, RubyClass clazz) {
- super(runtime, clazz);
-
- }
-
- public static RubyFileStat newFileStat(Ruby runtime, String filename, boolean lstat) {
- RubyFileStat stat = new RubyFileStat(runtime, runtime.getFileStat());
-
- stat.setup(filename, lstat);
-
- return stat;
- }
-
- public static RubyFileStat newFileStat(Ruby runtime, FileDescriptor descriptor) {
- RubyFileStat stat = new RubyFileStat(runtime, runtime.getFileStat());
-
- stat.setup(descriptor);
-
- return stat;
- }
-
- private void setup(FileDescriptor descriptor) {
- stat = getRuntime().getPosix().fstat(descriptor);
- }
-
- private void setup(String filename, boolean lstat) {
- if (Platform.IS_WINDOWS && filename.length() == 2
- && filename.charAt(1) == ':' && Character.isLetter(filename.charAt(0))) {
- filename += "/";
- }
-
- file = JRubyFile.create(getRuntime().getCurrentDirectory(), filename);
-
- if (lstat) {
- stat = getRuntime().getPosix().lstat(file.getAbsolutePath());
- } else {
- stat = getRuntime().getPosix().stat(file.getAbsolutePath());
- }
- }
-
- @JRubyMethod(name = "initialize", required = 1, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject fname, Block unusedBlock) {
- setup(fname.convertToString().toString(), false);
-
- return this;
- }
-
- @JRubyMethod(name = "atime")
- public IRubyObject atime() {
- return getRuntime().newTime(stat.atime() * 1000);
- }
-
- @JRubyMethod(name = "blksize")
- public RubyFixnum blksize() {
- return getRuntime().newFixnum(stat.blockSize());
- }
-
- @JRubyMethod(name = "blockdev?")
- public IRubyObject blockdev_p() {
- return getRuntime().newBoolean(stat.isBlockDev());
- }
-
- @JRubyMethod(name = "blocks")
- public IRubyObject blocks() {
- return getRuntime().newFixnum(stat.blocks());
- }
-
- @JRubyMethod(name = "chardev?")
- public IRubyObject chardev_p() {
- return getRuntime().newBoolean(stat.isCharDev());
- }
-
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject cmp(IRubyObject other) {
- if (!(other instanceof RubyFileStat)) getRuntime().getNil();
-
- long time1 = stat.mtime();
- long time2 = ((RubyFileStat) other).stat.mtime();
-
- if (time1 == time2) {
- return getRuntime().newFixnum(0);
- } else if (time1 < time2) {
- return getRuntime().newFixnum(-1);
- }
-
- return getRuntime().newFixnum(1);
- }
-
- @JRubyMethod(name = "ctime")
- public IRubyObject ctime() {
- return getRuntime().newTime(stat.ctime() * 1000);
- }
-
- @JRubyMethod(name = "dev")
- public IRubyObject dev() {
- return getRuntime().newFixnum(stat.dev());
- }
-
- @JRubyMethod(name = "dev_major")
- public IRubyObject devMajor() {
- return getRuntime().newFixnum(stat.major(stat.dev()));
- }
-
- @JRubyMethod(name = "dev_minor")
- public IRubyObject devMinor() {
- return getRuntime().newFixnum(stat.minor(stat.dev()));
- }
-
- @JRubyMethod(name = "directory?")
- public RubyBoolean directory_p() {
- return getRuntime().newBoolean(stat.isDirectory());
- }
-
- @JRubyMethod(name = "executable?")
- public IRubyObject executable_p() {
- return getRuntime().newBoolean(stat.isExecutable());
- }
-
- @JRubyMethod(name = "executable_real?")
- public IRubyObject executableReal_p() {
- return getRuntime().newBoolean(stat.isExecutableReal());
- }
-
- @JRubyMethod(name = "file?")
- public RubyBoolean file_p() {
- return getRuntime().newBoolean(stat.isFile());
- }
-
- @JRubyMethod(name = "ftype")
- public RubyString ftype() {
- return getRuntime().newString(stat.ftype());
- }
-
- @JRubyMethod(name = "gid")
- public IRubyObject gid() {
- return getRuntime().newFixnum(stat.gid());
- }
-
- @JRubyMethod(name = "grpowned?")
- public IRubyObject group_owned_p() {
- return getRuntime().newBoolean(stat.isGroupOwned());
- }
-
- @JRubyMethod(name = "initialize_copy", required = 1)
- public IRubyObject initialize_copy(IRubyObject original) {
- if (!(original instanceof RubyFileStat)) {
- throw getRuntime().newTypeError("wrong argument class");
- }
-
- RubyFileStat originalFileStat = (RubyFileStat) original;
-
- file = originalFileStat.file;
- stat = originalFileStat.stat;
-
- return this;
- }
-
- @JRubyMethod(name = "ino")
- public IRubyObject ino() {
- return getRuntime().newFixnum(stat.ino());
- }
-
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- StringBuilder buf = new StringBuilder("#<");
- buf.append(getMetaClass().getRealClass().getName());
- buf.append(" ");
- // FIXME: Obvious issue that not all platforms can display all attributes. Ugly hacks.
- // Using generic posix library makes pushing inspect behavior into specific system impls
- // rather painful.
- try { buf.append("dev=0x").append(Long.toHexString(stat.dev())).append(", "); } catch (Exception e) {}
- try { buf.append("ino=").append(stat.ino()).append(", "); } catch (Exception e) {}
- buf.append("mode=0").append(Integer.toOctalString(stat.mode())).append(", ");
- try { buf.append("nlink=").append(stat.nlink()).append(", "); } catch (Exception e) {}
- try { buf.append("uid=").append(stat.uid()).append(", "); } catch (Exception e) {}
- try { buf.append("gid=").append(stat.gid()).append(", "); } catch (Exception e) {}
- try { buf.append("rdev=0x").append(Long.toHexString(stat.rdev())).append(", "); } catch (Exception e) {}
- buf.append("size=").append(stat.st_size()).append(", ");
- try { buf.append("blksize=").append(stat.blockSize()).append(", "); } catch (Exception e) {}
- try { buf.append("blocks=").append(stat.blocks()).append(", "); } catch (Exception e) {}
-
- buf.append("atime=").append(atime()).append(", ");
- buf.append("mtime=").append(mtime()).append(", ");
- buf.append("ctime=").append(ctime());
- buf.append(">");
-
- return getRuntime().newString(buf.toString());
- }
-
- @JRubyMethod(name = "uid")
- public IRubyObject uid() {
- return getRuntime().newFixnum(stat.uid());
- }
-
- @JRubyMethod(name = "mode")
- public IRubyObject mode() {
- return getRuntime().newFixnum(stat.mode());
- }
-
- @JRubyMethod(name = "mtime")
- public IRubyObject mtime() {
- return getRuntime().newTime(stat.mtime() * 1000);
- }
-
- public IRubyObject mtimeEquals(IRubyObject other) {
- return getRuntime().newBoolean(stat.mtime() == newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
- }
-
- public IRubyObject mtimeGreaterThan(IRubyObject other) {
- return getRuntime().newBoolean(stat.mtime() > newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
- }
-
- public IRubyObject mtimeLessThan(IRubyObject other) {
- return getRuntime().newBoolean(stat.mtime() < newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
- }
-
- @JRubyMethod(name = "nlink")
- public IRubyObject nlink() {
- return getRuntime().newFixnum(stat.nlink());
- }
-
- @JRubyMethod(name = "owned?")
- public IRubyObject owned_p() {
- return getRuntime().newBoolean(stat.isOwned());
- }
-
- @JRubyMethod(name = "pipe?")
- public IRubyObject pipe_p() {
- return getRuntime().newBoolean(stat.isNamedPipe());
- }
-
- @JRubyMethod(name = "rdev")
- public IRubyObject rdev() {
- return getRuntime().newFixnum(stat.rdev());
- }
-
- @JRubyMethod(name = "rdev_major")
- public IRubyObject rdevMajor() {
- return getRuntime().newFixnum(stat.major(stat.rdev()));
- }
-
- @JRubyMethod(name = "rdev_minor")
- public IRubyObject rdevMinor() {
- return getRuntime().newFixnum(stat.minor(stat.rdev()));
- }
-
- @JRubyMethod(name = "readable?")
- public IRubyObject readable_p() {
- return getRuntime().newBoolean(stat.isReadable());
- }
-
- @JRubyMethod(name = "readable_real?")
- public IRubyObject readableReal_p() {
- return getRuntime().newBoolean(stat.isReadableReal());
- }
-
- @JRubyMethod(name = "setgid?")
- public IRubyObject setgid_p() {
- return getRuntime().newBoolean(stat.isSetgid());
- }
-
- @JRubyMethod(name = "setuid?")
- public IRubyObject setuid_p() {
- return getRuntime().newBoolean(stat.isSetuid());
- }
-
- @JRubyMethod(name = "size")
- public IRubyObject size() {
- return getRuntime().newFixnum(stat.st_size());
- }
-
- @JRubyMethod(name = "size?")
- public IRubyObject size_p() {
- long size = stat.st_size();
-
- if (size == 0) return getRuntime().getNil();
-
- return getRuntime().newFixnum(size);
- }
-
- @JRubyMethod(name = "socket?")
- public IRubyObject socket_p() {
- return getRuntime().newBoolean(stat.isSocket());
- }
-
- @JRubyMethod(name = "sticky?")
- public IRubyObject sticky_p() {
- return getRuntime().newBoolean(stat.isSticky());
- }
-
- @JRubyMethod(name = "symlink?")
- public IRubyObject symlink_p() {
- return getRuntime().newBoolean(stat.isSymlink());
- }
-
- @JRubyMethod(name = "writable?")
- public IRubyObject writable_p() {
- return getRuntime().newBoolean(stat.isWritable());
- }
-
- @JRubyMethod(name = "writable_real?")
- public IRubyObject writableReal_p() {
- return getRuntime().newBoolean(stat.isWritableReal());
- }
-
- @JRubyMethod(name = "zero?")
- public IRubyObject zero_p() {
- return getRuntime().newBoolean(stat.isEmpty());
- }
-}
-/***** 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) 2004-2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * 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.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.exceptions.RaiseException;
-
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.JRubyFile;
-
-@JRubyModule(name="FileTest")
-public class RubyFileTest {
- public static RubyModule createFileTestModule(Ruby runtime) {
- RubyModule fileTestModule = runtime.defineModule("FileTest");
- runtime.setFileTest(fileTestModule);
-
- fileTestModule.defineAnnotatedMethods(RubyFileTest.class);
-
- return fileTestModule;
- }
-
- @JRubyMethod(name = "blockdev?", required = 1, module = true)
- public static IRubyObject blockdev_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isBlockDev());
- }
-
- @JRubyMethod(name = "chardev?", required = 1, module = true)
- public static IRubyObject chardev_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isCharDev());
- }
-
- @JRubyMethod(name = "directory?", required = 1, module = true)
- public static IRubyObject directory_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isDirectory());
- }
-
- @JRubyMethod(name = "executable?", required = 1, module = true)
- public static IRubyObject executable_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isExecutable());
- }
-
- @JRubyMethod(name = "executable_real?", required = 1, module = true)
- public static IRubyObject executable_real_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isExecutableReal());
- }
-
- @JRubyMethod(name = {"exist?", "exists?"}, required = 1, module = true)
- public static IRubyObject exist_p(IRubyObject recv, IRubyObject filename) {
- if (Ruby.isSecurityRestricted()) {
- return recv.getRuntime().getFalse();
- }
-
- if(filename.convertToString().toString().startsWith("file:")) {
- String file = filename.convertToString().toString().substring(5);
- int bang = file.indexOf('!');
- if (bang == -1 || bang == file.length() - 1) {
- return recv.getRuntime().getFalse();
- }
- String jar = file.substring(0, bang);
- String after = file.substring(bang + 2);
- try {
- java.util.jar.JarFile jf = new java.util.jar.JarFile(jar);
- if(jf.getJarEntry(after) != null) {
- return recv.getRuntime().getTrue();
- } else {
- return recv.getRuntime().getFalse();
- }
- } catch(Exception e) {
- return recv.getRuntime().getFalse();
- }
- }
-
- return recv.getRuntime().newBoolean(file(filename).exists());
- }
-
- @JRubyMethod(name = "file?", required = 1, module = true)
- public static RubyBoolean file_p(IRubyObject recv, IRubyObject filename) {
- JRubyFile file = file(filename);
-
- return filename.getRuntime().newBoolean(file.exists() && file.isFile());
- }
-
- @JRubyMethod(name = "grpowned?", required = 1, module = true)
- public static IRubyObject grpowned_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isGroupOwned());
- }
-
- @JRubyMethod(name = "identical?", required = 2, module = true)
- public static IRubyObject identical_p(IRubyObject recv, IRubyObject filename1, IRubyObject filename2) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file1 = file(filename1);
- JRubyFile file2 = file(filename2);
-
- return runtime.newBoolean(file1.exists() && file2.exists() &&
- runtime.getPosix().stat(file1.getAbsolutePath()).isIdentical(runtime.getPosix().stat(file2.getAbsolutePath())));
- }
-
- @JRubyMethod(name = "owned?", required = 1, module = true)
- public static IRubyObject owned_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isOwned());
- }
-
- @JRubyMethod(name = "pipe?", required = 1, module = true)
- public static IRubyObject pipe_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isNamedPipe());
- }
-
- // We use file test since it is faster than a stat; also euid == uid in Java always
- @JRubyMethod(name = {"readable?", "readable_real?"}, required = 1, module = true)
- public static IRubyObject readable_p(IRubyObject recv, IRubyObject filename) {
- JRubyFile file = file(filename);
-
- return recv.getRuntime().newBoolean(file.exists() && file.canRead());
- }
-
- // Not exposed by filetest, but so similiar in nature that it is stored here
- public static IRubyObject rowned_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isROwned());
- }
-
- @JRubyMethod(name = "setgid?", required = 1, module = true)
- public static IRubyObject setgid_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSetgid());
- }
-
- @JRubyMethod(name = "setuid?", required = 1, module = true)
- public static IRubyObject setuid_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSetuid());
- }
-
- @JRubyMethod(name = "size", required = 1, module = true)
- public static IRubyObject size(IRubyObject recv, IRubyObject filename) {
- JRubyFile file = file(filename);
-
- if (!file.exists()) noFileError(filename);
-
- return recv.getRuntime().newFixnum(file.length());
- }
-
- @JRubyMethod(name = "size?", required = 1, module = true)
- public static IRubyObject size_p(IRubyObject recv, IRubyObject filename) {
- JRubyFile file = file(filename);
-
- if (!file.exists()) {
- return recv.getRuntime().getNil();
- }
-
- long length = file.length();
- if (length > 0) {
- return recv.getRuntime().newFixnum(length);
- } else {
- return recv.getRuntime().getNil();
- }
- }
-
- @JRubyMethod(name = "socket?", required = 1, module = true)
- public static IRubyObject socket_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSocket());
- }
-
- @JRubyMethod(name = "sticky?", required = 1, module = true)
- public static IRubyObject sticky_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSticky());
- }
-
- @JRubyMethod(name = "symlink?", required = 1, module = true)
- public static RubyBoolean symlink_p(IRubyObject recv, IRubyObject filename) {
- Ruby runtime = recv.getRuntime();
- JRubyFile file = file(filename);
-
- try {
- // Note: We can't use file.exists() to check whether the symlink
- // exists or not, because that method returns false for existing
- // but broken symlink. So, we try without the existence check,
- // but in the try-catch block.
- // MRI behavior: symlink? on broken symlink should return true.
- return runtime.newBoolean(runtime.getPosix().lstat(file.getAbsolutePath()).isSymlink());
- } catch (RaiseException re) {
- return runtime.getFalse();
- }
- }
-
- // We do both writable and writable_real through the same method because
- // in our java process effective and real userid will always be the same.
- @JRubyMethod(name = {"writable?", "writable_real?"}, required = 1, module = true)
- public static RubyBoolean writable_p(IRubyObject recv, IRubyObject filename) {
- return filename.getRuntime().newBoolean(file(filename).canWrite());
- }
-
- @JRubyMethod(name = "zero?", required = 1, module = true)
- public static RubyBoolean zero_p(IRubyObject recv, IRubyObject filename) {
- JRubyFile file = file(filename);
-
- return filename.getRuntime().newBoolean(file.exists() && file.length() == 0L);
- }
-
- private static JRubyFile file(IRubyObject path) {
- String filename = path.convertToString().toString();
-
- return JRubyFile.create(path.getRuntime().getCurrentDirectory(), filename);
- }
-
- private static void noFileError(IRubyObject filename) {
- throw filename.getRuntime().newErrnoENOENTError("No such file or directory - " +
- filename.convertToString());
- }
-}
-/*
- ***** 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-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
- * Copyright (C) 2006 Antti Karanta <antti.karanta@napa.fi>
- * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@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.math.BigInteger;
-import java.util.HashMap;
-import java.util.Map;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.java.MiniJava;
-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.UnmarshalStream;
-import org.jruby.util.Convert;
-import org.jruby.util.Numeric;
-import org.jruby.util.TypeCoercer;
-
-/**
- * Implementation of the Fixnum class.
- */
-@JRubyClass(name="Fixnum", parent="Integer", include="Precision")
-public class RubyFixnum extends RubyInteger {
-
- public static RubyClass createFixnumClass(Ruby runtime) {
- RubyClass fixnum = runtime.defineClass("Fixnum", runtime.getInteger(),
- ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setFixnum(fixnum);
- fixnum.index = ClassIndex.FIXNUM;
- fixnum.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyFixnum;
- }
- };
-
- fixnum.includeModule(runtime.getPrecision());
-
- fixnum.defineAnnotatedMethods(RubyFixnum.class);
-
- for (int i = 0; i < runtime.fixnumCache.length; i++) {
- runtime.fixnumCache[i] = new RubyFixnum(runtime, fixnum, i - 128);
- }
-
- return fixnum;
- }
-
- private final long value;
- private static final int BIT_SIZE = 64;
- public static final long SIGN_BIT = (1L << (BIT_SIZE - 1));
- public static final long MAX = (1L<<(BIT_SIZE - 1)) - 1;
- public static final long MIN = -1 * MAX - 1;
- public static final long MAX_MARSHAL_FIXNUM = (1L << 30) - 1; // 0x3fff_ffff
- public static final long MIN_MARSHAL_FIXNUM = - (1L << 30); // -0x4000_0000
-
- private static IRubyObject fixCoerce(IRubyObject x) {
- do {
- x = x.convertToInteger();
- } while (!(x instanceof RubyFixnum) && !(x instanceof RubyBignum));
- return x;
- }
-
- public RubyFixnum(Ruby runtime) {
- this(runtime, 0);
- }
-
- public RubyFixnum(Ruby runtime, long value) {
- super(runtime, runtime.getFixnum(), false);
- this.value = value;
- }
-
- private RubyFixnum(Ruby runtime, RubyClass klazz, long value) {
- super(runtime, klazz, false);
- this.value = value;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.FIXNUM;
- }
-
- /**
- * short circuit for Fixnum key comparison
- */
- @Override
- public final boolean eql(IRubyObject other) {
- return other instanceof RubyFixnum && value == ((RubyFixnum)other).value;
- }
-
- @Override
- public boolean isImmediate() {
- return true;
- }
-
- @Override
- public RubyClass getSingletonClass() {
- throw getRuntime().newTypeError("can't define singleton");
- }
-
- @Override
- public Class<?> getJavaClass() {
- // this precision-guessing needs to be thought out more, since in the
- // case of coercing to Object we generally want to get the same type
- // always
-// if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
-// return byte.class;
-// } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
-// return short.class;
-// } else if (value >= Character.MIN_VALUE && value <= Character.MAX_VALUE) {
-// return char.class;
-// } else if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
-// return int.class;
-// }
- return long.class;
- }
-
- @Override
- public double getDoubleValue() {
- return value;
- }
-
- @Override
- public long getLongValue() {
- return value;
- }
-
- private static final int CACHE_OFFSET = 128;
-
- public static RubyFixnum newFixnum(Ruby runtime, long value) {
- if (isInCacheRange(value)) {
- return runtime.fixnumCache[(int) value + CACHE_OFFSET];
- }
- return new RubyFixnum(runtime, value);
- }
-
- private static boolean isInCacheRange(long value) {
- return value <= 127 && value >= -128;
- }
-
- public RubyFixnum newFixnum(long newValue) {
- return newFixnum(getRuntime(), newValue);
- }
-
- public static RubyFixnum zero(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET];
- }
-
- public static RubyFixnum one(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET + 1];
- }
-
- public static RubyFixnum two(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET + 2];
- }
-
- public static RubyFixnum three(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET + 3];
- }
-
- public static RubyFixnum four(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET + 4];
- }
-
- public static RubyFixnum five(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET + 5];
- }
-
- public static RubyFixnum minus_one(Ruby runtime) {
- return runtime.fixnumCache[CACHE_OFFSET - 1];
- }
-
- @Override
- public RubyFixnum hash() {
- return newFixnum(hashCode());
- }
-
- @Override
- public final int hashCode() {
- return (int)(value ^ value >>> 32);
- }
-
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- if (other instanceof RubyFixnum) {
- RubyFixnum num = (RubyFixnum)other;
-
- if (num.value == value) {
- return true;
- }
- }
-
- return false;
- }
-
- /* ================
- * Instance Methods
- * ================
- */
-
- /** fix_to_s
- *
- */
- public RubyString to_s(IRubyObject[] args) {
- switch (args.length) {
- case 0: return to_s();
- case 1: return to_s(args[0]);
- default: throw getRuntime().newArgumentError(args.length, 1);
- }
- }
-
- @JRubyMethod
- @Override
- public RubyString to_s() {
- int base = 10;
- return getRuntime().newString(Convert.longToByteList(value, base));
- }
-
- @JRubyMethod
- public RubyString to_s(IRubyObject arg0) {
- int base = num2int(arg0);
- if (base < 2 || base > 36) {
- throw getRuntime().newArgumentError("illegal radix " + base);
- }
- return getRuntime().newString(Convert.longToByteList(value, base));
- }
-
- /** fix_id2name
- *
- */
- @JRubyMethod
- public IRubyObject id2name() {
- RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
-
- if (symbol != null) return getRuntime().newString(symbol.asJavaString());
-
- return getRuntime().getNil();
- }
-
- /** fix_to_sym
- *
- */
- @JRubyMethod
- public IRubyObject to_sym() {
- RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
-
- return symbol != null ? symbol : getRuntime().getNil();
- }
-
- /** fix_uminus
- *
- */
- @JRubyMethod(name = "-@")
- public IRubyObject op_uminus() {
- if (value == MIN) { // a gotcha
- return RubyBignum.newBignum(getRuntime(), BigInteger.valueOf(value).negate());
- }
- return RubyFixnum.newFixnum(getRuntime(), -value);
- }
-
- /** fix_plus
- *
- */
- @JRubyMethod(name = "+")
- public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return addFixnum(context, (RubyFixnum)other);
- }
- return addOther(context, other);
- }
-
- private IRubyObject addFixnum(ThreadContext context, RubyFixnum other) {
- long otherValue = other.value;
- long result = value + otherValue;
- if (additionOverflowed(value, otherValue, result)) {
- return addAsBignum(context, other);
- }
- return newFixnum(context.getRuntime(), result);
- }
-
- private static boolean additionOverflowed(long original, long other, long result) {
- return (~(original ^ other) & (original ^ result) & SIGN_BIT) != 0;
- }
-
- private static boolean subtractionOverflowed(long original, long other, long result) {
- return (~(original ^ ~other) & (original ^ result) & SIGN_BIT) != 0;
- }
-
- private IRubyObject addAsBignum(ThreadContext context, RubyFixnum other) {
- return RubyBignum.newBignum(context.getRuntime(), value).op_plus(context, other);
- }
-
- private IRubyObject addOther(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyBignum) {
- return ((RubyBignum) other).op_plus(context, this);
- }
- if (other instanceof RubyFloat) {
- return context.getRuntime().newFloat((double) value + ((RubyFloat) other).getDoubleValue());
- }
- return coerceBin(context, "+", other);
- }
-
- /** fix_minus
- *
- */
- @JRubyMethod(name = "-")
- public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return subtractFixnum(context, (RubyFixnum)other);
- }
- return subtractOther(context, other);
- }
-
- private IRubyObject subtractFixnum(ThreadContext context, RubyFixnum other) {
- long otherValue = other.value;
- long result = value - otherValue;
- if (subtractionOverflowed(value, otherValue, result)) {
- return subtractAsBignum(context, other);
- }
- return newFixnum(context.getRuntime(), result);
- }
-
- private IRubyObject subtractAsBignum(ThreadContext context, RubyFixnum other) {
- return RubyBignum.newBignum(context.getRuntime(), value).op_minus(context, other);
- }
-
- private IRubyObject subtractOther(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyBignum) {
- return RubyBignum.newBignum(context.getRuntime(), value).op_minus(context, other);
- } else if (other instanceof RubyFloat) {
- return context.getRuntime().newFloat((double) value - ((RubyFloat) other).getDoubleValue());
- }
- return coerceBin(context, "-", other);
- }
-
- /** fix_mul
- *
- */
- @JRubyMethod(name = "*")
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- if (other instanceof RubyFixnum) {
- long otherValue = ((RubyFixnum) other).value;
- if (value == 0) {
- return RubyFixnum.zero(runtime);
- }
- long result = value * otherValue;
- if (result / value != otherValue) {
- return RubyBignum.newBignum(runtime, value).op_mul(context, other);
- }
- return newFixnum(runtime, result);
- } else if (other instanceof RubyBignum) {
- return ((RubyBignum) other).op_mul(context, this);
- } else if (other instanceof RubyFloat) {
- return runtime.newFloat((double) value * ((RubyFloat) other).getDoubleValue());
- }
- return coerceBin(context, "*", other);
- }
-
- /** fix_div
- * here is terrible MRI gotcha:
- * 1.div 3.0 -> 0
- * 1 / 3.0 -> 0.3333333333333333
- *
- * MRI is also able to do it in one place by looking at current frame in rb_num_coerce_bin:
- * rb_funcall(x, ruby_frame->orig_func, 1, y);
- *
- * also note that RubyFloat doesn't override Numeric.div
- */
- @JRubyMethod(name = "div")
- public IRubyObject div_div(ThreadContext context, IRubyObject other) {
- return idiv(context, other, "div");
- }
-
- @JRubyMethod(name = "/")
- public IRubyObject op_div(ThreadContext context, IRubyObject other) {
- return idiv(context, other, "/");
- }
-
- @JRubyMethod(name = {"odd?"})
- public RubyBoolean odd_p() {
- if(value%2 != 0) {
- return getRuntime().getTrue();
- }
- return getRuntime().getFalse();
- }
-
- @JRubyMethod(name = {"even?"})
- public RubyBoolean even_p() {
- if(value%2 == 0) {
- return getRuntime().getTrue();
- }
- return getRuntime().getFalse();
- }
-
- @JRubyMethod
- public IRubyObject pred() {
- return getRuntime().newFixnum(value-1);
- }
-
- public IRubyObject idiv(ThreadContext context, IRubyObject other, String method) {
- if (other instanceof RubyFixnum) {
- long x = value;
- long y = ((RubyFixnum) other).value;
-
- if (y == 0) {
- throw context.getRuntime().newZeroDivisionError();
- }
-
- long div = x / y;
- long mod = x % y;
-
- if (mod < 0 && y > 0 || mod > 0 && y < 0) {
- div -= 1;
- }
-
- return context.getRuntime().newFixnum(div);
- }
- return coerceBin(context, method, other);
- }
-
- /** fix_mod
- *
- */
- @JRubyMethod(name = {"%", "modulo"})
- public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- // Java / and % are not the same as ruby
- long x = value;
- long y = ((RubyFixnum) other).value;
-
- if (y == 0) {
- throw context.getRuntime().newZeroDivisionError();
- }
-
- long mod = x % y;
-
- if (mod < 0 && y > 0 || mod > 0 && y < 0) {
- mod += y;
- }
-
- return context.getRuntime().newFixnum(mod);
- }
- return coerceBin(context, "%", other);
- }
-
- /** fix_divmod
- *
- */
- @JRubyMethod
- @Override
- public IRubyObject divmod(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- long x = value;
- long y = ((RubyFixnum) other).value;
- final Ruby runtime = context.getRuntime();
-
- if (y == 0) {
- throw runtime.newZeroDivisionError();
- }
-
- long div = x / y;
- long mod = x % y;
-
- if (mod < 0 && y > 0 || mod > 0 && y < 0) {
- div -= 1;
- mod += y;
- }
-
- IRubyObject fixDiv = RubyFixnum.newFixnum(runtime, div);
- IRubyObject fixMod = RubyFixnum.newFixnum(runtime, mod);
-
- return RubyArray.newArray(runtime, fixDiv, fixMod);
-
- }
- return coerceBin(context, "divmod", other);
- }
-
- /** fix_quo
- *
- */
- @JRubyMethod(name = "quo", compat = CompatVersion.RUBY1_8)
- public IRubyObject quo(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyFloat.newFloat(context.getRuntime(), (double) value / (double) ((RubyFixnum) other).value);
- }
- return coerceBin(context, "quo", other);
- }
-
- /** fix_pow
- *
- */
- @JRubyMethod(name = "**")
- public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
- if(other instanceof RubyFixnum) {
- long b = ((RubyFixnum) other).value;
-
- if (b == 0) {
- return RubyFixnum.one(context.getRuntime());
- }
- if (b == 1) {
- return this;
- }
- if (b > 0) {
- return RubyBignum.newBignum(context.getRuntime(), value).op_pow(context, other);
- }
- return RubyFloat.newFloat(context.getRuntime(), Math.pow(value, b));
- } else if (other instanceof RubyFloat) {
- return RubyFloat.newFloat(context.getRuntime(), Math.pow(value, ((RubyFloat) other)
- .getDoubleValue()));
- }
- return coerceBin(context, "**", other);
- }
-
- /** fix_pow
- *
- */
- @JRubyMethod(name = "**", compat = CompatVersion.RUBY1_9)
- public IRubyObject op_pow_19(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- long a = value;
- if (other instanceof RubyFixnum) {
- long b = ((RubyFixnum) other).value;
-
- if (b < 0) {
- return RubyRational.newRationalRaw(context.getRuntime(), this).callMethod(context, "**", other);
- }
-
- if (b == 0) return RubyFixnum.one(runtime);
- if (b == 1) return this;
-
- if (a == 0) {
- return b > 0 ? RubyFixnum.zero(runtime) : RubyNumeric.dbl2num(runtime, 1.0 / 0.0);
- }
- if (a == 1) return RubyFixnum.one(runtime);
- if (a == -1) {
- return b % 2 == 0 ? RubyFixnum.one(runtime) : RubyFixnum.minus_one(runtime);
- }
- return Numeric.int_pow(context, a, b);
- } else if (other instanceof RubyBignum) {
- if (other.callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue()) {
- return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
- }
- if (a == 0) return RubyFixnum.zero(runtime);
- if (a == 1) return RubyFixnum.one(runtime);
- if (a == -1) {
- return RubyInteger.even_p(context, other).isTrue() ? RubyFixnum.one(runtime) : RubyFixnum.minus_one(runtime);
- }
- RubyBignum.newBignum(runtime, RubyBignum.fix2big(this)).op_pow(context, other);
- } else if (other instanceof RubyFloat) {
- return RubyFloat.newFloat(context.getRuntime(), Math.pow(a, ((RubyFloat) other).getDoubleValue()));
- }
- return coerceBin(context, "**", other);
- }
-
-
- /** fix_abs
- *
- */
- @JRubyMethod
- public IRubyObject abs() {
- if (value < 0) {
- // A gotcha for Long.MIN_VALUE: value = -value
- if (value == Long.MIN_VALUE) {
- return RubyBignum.newBignum(
- getRuntime(), BigInteger.valueOf(value).negate());
- }
- return RubyFixnum.newFixnum(getRuntime(), -value);
- }
- return this;
- }
-
- /** fix_equal
- *
- */
- @JRubyMethod(name = "==")
- @Override
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyBoolean.newBoolean(context.getRuntime(), value == ((RubyFixnum) other).value);
- }
- return super.op_num_equal(context, other);
- }
-
- /** fix_cmp
- *
- */
- @JRubyMethod(name = "<=>")
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return compareFixnum(context, (RubyFixnum)other);
- }
- return coerceCmp(context, "<=>", other);
- }
-
- private IRubyObject compareFixnum(ThreadContext context, RubyFixnum other) {
- long otherValue = ((RubyFixnum) other).value;
- if (value == otherValue) {
- return RubyFixnum.zero(context.getRuntime());
- }
- if (value > otherValue) {
- return RubyFixnum.one(context.getRuntime());
- }
- return RubyFixnum.minus_one(context.getRuntime());
- }
-
- /** fix_gt
- *
- */
- @JRubyMethod(name = ">")
- public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyBoolean.newBoolean(context.getRuntime(), value > ((RubyFixnum) other).value);
- }
- return coerceRelOp(context, ">", other);
- }
-
- /** fix_ge
- *
- */
- @JRubyMethod(name = ">=")
- public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyBoolean.newBoolean(context.getRuntime(), value >= ((RubyFixnum) other).value);
- }
- return coerceRelOp(context, ">=", other);
- }
-
- /** fix_lt
- *
- */
- @JRubyMethod(name = "<")
- public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyBoolean.newBoolean(context.getRuntime(), value < ((RubyFixnum) other).value);
- }
-
- return coerceRelOp(context, "<", other);
- }
-
- /** fix_le
- *
- */
- @JRubyMethod(name = "<=")
- public IRubyObject op_le(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum) {
- return RubyBoolean.newBoolean(context.getRuntime(), value <= ((RubyFixnum) other).value);
- }
-
- return coerceRelOp(context, "<=", other);
- }
-
- /** fix_rev
- *
- */
- @JRubyMethod(name = "~")
- public IRubyObject op_neg() {
- return newFixnum(~value);
- }
-
- /** fix_and
- *
- */
- @JRubyMethod(name = "&")
- public IRubyObject op_and(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
- return newFixnum(context.getRuntime(), value & ((RubyFixnum) other).value);
- }
- return ((RubyBignum) other).op_and(context, this);
- }
-
- /** fix_or
- *
- */
- @JRubyMethod(name = "|")
- public IRubyObject op_or(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
- return newFixnum(context.getRuntime(), value | ((RubyFixnum) other).value);
- }
- return ((RubyBignum) other).op_or(context, this);
- }
-
- /** fix_xor
- *
- */
- @JRubyMethod(name = "^")
- public IRubyObject op_xor(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
- return newFixnum(context.getRuntime(), value ^ ((RubyFixnum) other).value);
- }
- return ((RubyBignum) other).op_xor(context, this);
- }
-
- /** fix_aref
- *
- */
- @JRubyMethod(name = "[]")
- public IRubyObject op_aref(IRubyObject other) {
- if(!(other instanceof RubyFixnum) && !((other = fixCoerce(other)) instanceof RubyFixnum)) {
- RubyBignum big = (RubyBignum) other;
- RubyObject tryFix = RubyBignum.bignorm(getRuntime(), big.getValue());
- if (!(tryFix instanceof RubyFixnum)) {
- return big.getValue().signum() == 0 || value >= 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.one(getRuntime());
- }
- }
-
- long otherValue = fix2long(other);
-
- if (otherValue < 0) return RubyFixnum.zero(getRuntime());
-
- if (BIT_SIZE - 1 < otherValue) {
- return value < 0 ? RubyFixnum.one(getRuntime()) : RubyFixnum.zero(getRuntime());
- }
-
- return (value & (1L << otherValue)) == 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.one(getRuntime());
- }
-
- /** fix_lshift
- *
- */
- @JRubyMethod(name = "<<")
- public IRubyObject op_lshift(IRubyObject other) {
- if (!(other instanceof RubyFixnum)) return RubyBignum.newBignum(getRuntime(), value).op_lshift(other);
-
- long width = ((RubyFixnum)other).getLongValue();
-
- return width < 0 ? rshift(-width) : lshift(width);
- }
-
- private IRubyObject lshift(long width) {
- if (width > BIT_SIZE - 1 || ((~0L << BIT_SIZE - width - 1) & value) != 0) {
- return RubyBignum.newBignum(getRuntime(), value).op_lshift(RubyFixnum.newFixnum(getRuntime(), width));
- }
- return RubyFixnum.newFixnum(getRuntime(), value << width);
- }
-
- /** fix_rshift
- *
- */
- @JRubyMethod(name = ">>")
- public IRubyObject op_rshift(IRubyObject other) {
- if (!(other instanceof RubyFixnum)) return RubyBignum.newBignum(getRuntime(), value).op_rshift(other);
-
- long width = ((RubyFixnum)other).getLongValue();
-
- if (width == 0) return this;
-
- return width < 0 ? lshift(-width) : rshift(width);
- }
-
- private IRubyObject rshift(long width) {
- if (width >= BIT_SIZE - 1) {
- return value < 0 ? RubyFixnum.minus_one(getRuntime()) : RubyFixnum.zero(getRuntime());
- }
- return RubyFixnum.newFixnum(getRuntime(), value >> width);
- }
-
- /** fix_to_f
- *
- */
- @JRubyMethod
- public IRubyObject to_f() {
- return RubyFloat.newFloat(getRuntime(), (double) value);
- }
-
- /** fix_size
- *
- */
- @JRubyMethod
- public IRubyObject size() {
- return newFixnum((long) ((BIT_SIZE + 7) / 8));
- }
-
- /** fix_zero_p
- *
- */
- @JRubyMethod(name = "zero?")
- public IRubyObject zero_p() {
- return RubyBoolean.newBoolean(getRuntime(), value == 0);
- }
-
- @JRubyMethod
- @Override
- public IRubyObject id() {
- if (value <= Long.MAX_VALUE / 2 && value >= Long.MIN_VALUE / 2) {
- return newFixnum(2 * value + 1);
- }
-
- return super.id();
- }
-
- @Override
- public IRubyObject taint(ThreadContext context) {
- return this;
- }
-
- @Override
- public IRubyObject freeze(ThreadContext context) {
- return this;
- }
-
- // Piece of mri rb_to_id
- @Override
- public String asJavaString() {
- getRuntime().getWarnings().warn(ID.FIXNUMS_NOT_SYMBOLS, "do not use Fixnums as Symbols");
-
- // FIXME: I think this chunk is equivalent to MRI id2name (and not our public method
- // id2name). Make into method if used more than once.
- RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
-
- if (symbol == null) {
- throw getRuntime().newArgumentError("" + value + " is not a symbol");
- }
-
- return symbol.asJavaString();
- }
-
- public static RubyFixnum unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- return input.getRuntime().newFixnum(input.unmarshalInt());
- }
-
- /* ================
- * Singleton Methods
- * ================
- */
-
- /** rb_fix_induced_from
- *
- */
- @JRubyMethod(meta = true)
- public static IRubyObject induced_from(IRubyObject recv, IRubyObject other) {
- return RubyNumeric.num2fix(other);
- }
-
- @Override
- public IRubyObject to_java() {
- return MiniJava.javaToRuby(getRuntime(), Long.valueOf(value));
- }
-
- @Override
- public IRubyObject as(Class javaClass) {
- return MiniJava.javaToRuby(getRuntime(), coerceToJavaType(getRuntime(), this, javaClass));
- }
-
- private static Object coerceToJavaType(Ruby ruby, RubyFixnum self, Class javaClass) {
- if (!Number.class.isAssignableFrom(javaClass)) {
- throw ruby.newTypeError(javaClass.getCanonicalName() + " is not a numeric type");
- }
-
- TypeCoercer coercer = JAVA_COERCERS.get(javaClass);
-
- if (coercer == null) {
- throw ruby.newTypeError("Cannot coerce Fixnum to " + javaClass.getCanonicalName());
- }
-
- return coercer.coerce(self);
- }
-
- private static final Map<Class, TypeCoercer> JAVA_COERCERS = new HashMap<Class, TypeCoercer>();
-
- static {
- TypeCoercer intCoercer = new TypeCoercer() {
- public Object coerce(IRubyObject self) {
- RubyFixnum fixnum = (RubyFixnum)self;
-
- if (fixnum.value > Integer.MAX_VALUE) {
- throw self.getRuntime().newRangeError("Fixnum " + fixnum.value + " is too large for Java int");
- }
-
- return Integer.valueOf((int)fixnum.value);
- }
- };
- JAVA_COERCERS.put(int.class, intCoercer);
- JAVA_COERCERS.put(Integer.class, intCoercer);
- }
-}
-/*
- ***** 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 Don Schwartz <schwardo@users.sourceforge.net>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@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 static org.jruby.util.Numeric.f_expt;
-import static org.jruby.util.Numeric.f_mul;
-import static org.jruby.util.Numeric.f_to_i;
-import static org.jruby.util.Numeric.frexp;
-import static org.jruby.util.Numeric.ldexp;
-
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.util.Locale;
-
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.MethodIndex;
-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;
-
-/**
- * A representation of a float object
- */
-@JRubyClass(name="Float", parent="Numeric", include="Precision")
-public class RubyFloat extends RubyNumeric {
-
- public static RubyClass createFloatClass(Ruby runtime) {
- RubyClass floatc = runtime.defineClass("Float", runtime.getNumeric(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setFloat(floatc);
- floatc.index = ClassIndex.FLOAT;
- floatc.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyFloat;
- }
- };
-
- floatc.getSingletonClass().undefineMethod("new");
- floatc.includeModule(runtime.getPrecision());
-
- // Java Doubles are 64 bit long:
- floatc.defineConstant("ROUNDS", RubyFixnum.newFixnum(runtime, 1));
- floatc.defineConstant("RADIX", RubyFixnum.newFixnum(runtime, 2));
- floatc.defineConstant("MANT_DIG", RubyFixnum.newFixnum(runtime, 53));
- floatc.defineConstant("DIG", RubyFixnum.newFixnum(runtime, 15));
- // Double.MAX_EXPONENT since Java 1.6
- floatc.defineConstant("MIN_EXP", RubyFixnum.newFixnum(runtime, -1021));
- // Double.MAX_EXPONENT since Java 1.6
- floatc.defineConstant("MAX_EXP", RubyFixnum.newFixnum(runtime, 1024));
- floatc.defineConstant("MIN_10_EXP", RubyFixnum.newFixnum(runtime, -307));
- floatc.defineConstant("MAX_10_EXP", RubyFixnum.newFixnum(runtime, 308));
- floatc.defineConstant("MIN", RubyFloat.newFloat(runtime, Double.MIN_VALUE));
- floatc.defineConstant("MAX", RubyFloat.newFloat(runtime, Double.MAX_VALUE));
- floatc.defineConstant("EPSILON", RubyFloat.newFloat(runtime, 2.2204460492503131e-16));
-
- floatc.defineAnnotatedMethods(RubyFloat.class);
-
- return floatc;
- }
-
- private final double value;
-
- public int getNativeTypeIndex() {
- return ClassIndex.FLOAT;
- }
-
- public RubyFloat(Ruby runtime) {
- this(runtime, 0.0);
- }
-
- public RubyFloat(Ruby runtime, double value) {
- super(runtime, runtime.getFloat());
- this.value = value;
- }
-
- public Class<?> getJavaClass() {
- // this needs to be thought out more along with the changes in RubyFixnum
- // since "to Object" coercion will generally want to produce the same
- // type every time
-// if (value >= Float.MIN_VALUE && value <= Float.MAX_VALUE) {
-// return float.class;
-// }
- return double.class;
- }
-
- /** Getter for property value.
- * @return Value of property value.
- */
- public double getValue() {
- return this.value;
- }
-
- public double getDoubleValue() {
- return value;
- }
-
- public long getLongValue() {
- return (long) value;
- }
-
- public RubyFloat convertToFloat() {
- return this;
- }
-
- protected int compareValue(RubyNumeric other) {
- double otherVal = other.getDoubleValue();
- return getValue() > otherVal ? 1 : getValue() < otherVal ? -1 : 0;
- }
-
- public static RubyFloat newFloat(Ruby runtime, double value) {
- return new RubyFloat(runtime, value);
- }
-
- /* ================
- * Instance Methods
- * ================
- */
-
- /** rb_flo_induced_from
- *
- */
- @JRubyMethod(required = 1, meta = true)
- public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject number) {
- if (number instanceof RubyFixnum || number instanceof RubyBignum || number instanceof RubyRational) {
- return number.callMethod(context, MethodIndex.TO_F, "to_f");
- } else if (number instanceof RubyFloat) {
- return number;
- }
- throw recv.getRuntime().newTypeError(
- "failed to convert " + number.getMetaClass() + " into Float");
- }
-
- private final static DecimalFormat FORMAT = new DecimalFormat("##############0.0##############",
- new DecimalFormatSymbols(Locale.ENGLISH));
-
- /** flo_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- if (Double.isInfinite(value)) {
- return RubyString.newString(getRuntime(), value < 0 ? "-Infinity" : "Infinity");
- }
-
- if (Double.isNaN(value)) {
- return RubyString.newString(getRuntime(), "NaN");
- }
-
- String val = ""+value;
-
- if(val.indexOf('E') != -1) {
- String v2 = FORMAT.format(value);
- int ix = v2.length()-1;
- while(v2.charAt(ix) == '0' && v2.charAt(ix-1) != '.') {
- ix--;
- }
- if(ix > 15 || "0.0".equals(v2.substring(0,ix+1))) {
- val = val.replaceFirst("E(\\d)","e+$1").replaceFirst("E-","e-");
- } else {
- val = v2.substring(0,ix+1);
- }
- }
-
- return RubyString.newString(getRuntime(), val);
- }
-
- /** flo_coerce
- *
- */
- @JRubyMethod(name = "coerce", required = 1)
- public IRubyObject coerce(IRubyObject other) {
- return getRuntime().newArray(RubyKernel.new_float(this, other), this);
- }
-
- /** flo_uminus
- *
- */
- @JRubyMethod(name = "-@")
- public IRubyObject op_uminus() {
- return RubyFloat.newFloat(getRuntime(), -value);
- }
-
- /** flo_plus
- *
- */
- @JRubyMethod(name = "+", required = 1)
- public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyFloat.newFloat(getRuntime(), value + ((RubyNumeric) other).getDoubleValue());
- default:
- return coerceBin(context, "+", other);
- }
- }
-
- /** flo_minus
- *
- */
- @JRubyMethod(name = "-", required = 1)
- public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyFloat.newFloat(getRuntime(), value - ((RubyNumeric) other).getDoubleValue());
- default:
- return coerceBin(context, "-", other);
- }
- }
-
- /** flo_mul
- *
- */
- @JRubyMethod(name = "*", required = 1)
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyFloat.newFloat(
- getRuntime(), value * ((RubyNumeric) other).getDoubleValue());
- default:
- return coerceBin(context, "*", other);
- }
- }
-
- /** flo_div
- *
- */
- @JRubyMethod(name = "/", required = 1)
- public IRubyObject op_fdiv(ThreadContext context, IRubyObject other) { // don't override Numeric#div !
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyFloat.newFloat(getRuntime(), value / ((RubyNumeric) other).getDoubleValue());
- default:
- return coerceBin(context, "/", other);
- }
- }
-
- /** flo_mod
- *
- */
- @JRubyMethod(name = {"%", "modulo"}, required = 1)
- public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double y = ((RubyNumeric) other).getDoubleValue();
- // Modelled after c ruby implementation (java /,% not same as ruby)
- double x = value;
-
- double mod = Math.IEEEremainder(x, y);
- if (y * mod < 0) {
- mod += y;
- }
-
- return RubyFloat.newFloat(getRuntime(), mod);
- default:
- return coerceBin(context, "%", other);
- }
- }
-
- /** flo_divmod
- *
- */
- @JRubyMethod(name = "divmod", required = 1)
- public IRubyObject divmod(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double y = ((RubyNumeric) other).getDoubleValue();
- double x = value;
-
- double mod = Math.IEEEremainder(x, y);
- // MRI behavior:
- if (Double.isNaN(mod)) {
- throw getRuntime().newFloatDomainError("NaN");
- }
- double div = Math.floor(x / y);
-
- if (y * mod < 0) {
- mod += y;
- }
- final Ruby runtime = getRuntime();
- IRubyObject car = dbl2num(runtime, div);
- RubyFloat cdr = RubyFloat.newFloat(runtime, mod);
- return RubyArray.newArray(runtime, car, cdr);
- default:
- return coerceBin(context, "divmod", other);
- }
- }
-
- /** flo_pow
- *
- */
- @JRubyMethod(name = "**", required = 1)
- public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyFloat.newFloat(getRuntime(), Math.pow(value, ((RubyNumeric) other)
- .getDoubleValue()));
- default:
- return coerceBin(context, "**", other);
- }
- }
-
- /** flo_eq
- *
- */
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if (Double.isNaN(value)) {
- return getRuntime().getFalse();
- }
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- return RubyBoolean.newBoolean(getRuntime(), value == ((RubyNumeric) other)
- .getDoubleValue());
- default:
- // Numeric.equal
- return super.op_num_equal(context, other);
- }
- }
-
- /** flo_cmp
- *
- */
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double b = ((RubyNumeric) other).getDoubleValue();
- return dbl_cmp(getRuntime(), value, b);
- default:
- return coerceCmp(context, "<=>", other);
- }
- }
-
- /** flo_gt
- *
- */
- @JRubyMethod(name = ">", required = 1)
- public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double b = ((RubyNumeric) other).getDoubleValue();
- return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value > b);
- default:
- return coerceRelOp(context, ">", other);
- }
- }
-
- /** flo_ge
- *
- */
- @JRubyMethod(name = ">=", required = 1)
- public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double b = ((RubyNumeric) other).getDoubleValue();
- return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value >= b);
- default:
- return coerceRelOp(context, ">=", other);
- }
- }
-
- /** flo_lt
- *
- */
- @JRubyMethod(name = "<", required = 1)
- public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double b = ((RubyNumeric) other).getDoubleValue();
- return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value < b);
- default:
- return coerceRelOp(context, "<", other);
- }
- }
-
- /** flo_le
- *
- */
- @JRubyMethod(name = "<=", required = 1)
- public IRubyObject op_le(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- case ClassIndex.FLOAT:
- double b = ((RubyNumeric) other).getDoubleValue();
- return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value <= b);
- default:
- return coerceRelOp(context, "<=", other);
- }
- }
-
- /** flo_eql
- *
- */
- @JRubyMethod(name = "eql?", required = 1)
- public IRubyObject eql_p(IRubyObject other) {
- if (other instanceof RubyFloat) {
- double b = ((RubyFloat) other).value;
- if (Double.isNaN(value) || Double.isNaN(b)) {
- return getRuntime().getFalse();
- }
- if (value == b) {
- return getRuntime().getTrue();
- }
- }
- return getRuntime().getFalse();
- }
-
- /** flo_hash
- *
- */
- @JRubyMethod(name = "hash")
- public RubyFixnum hash() {
- return getRuntime().newFixnum(hashCode());
- }
-
- public final int hashCode() {
- long l = Double.doubleToLongBits(value);
- return (int)(l ^ l >>> 32);
- }
-
- /** flo_fo
- *
- */
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f() {
- return this;
- }
-
- /** flo_abs
- *
- */
- @JRubyMethod(name = "abs")
- public IRubyObject abs() {
- if (Double.doubleToLongBits(value) < 0) {
- return RubyFloat.newFloat(getRuntime(), Math.abs(value));
- }
- return this;
- }
-
- /** flo_zero_p
- *
- */
- @JRubyMethod(name = "zero?")
- public IRubyObject zero_p() {
- return RubyBoolean.newBoolean(getRuntime(), value == 0.0);
- }
-
- /** flo_truncate
- *
- */
- @JRubyMethod(name = {"truncate", "to_i", "to_int"})
- public IRubyObject truncate() {
- double f = value;
- if (f > 0.0) f = Math.floor(f);
- if (f < 0.0) f = Math.ceil(f);
-
- return dbl2num(getRuntime(), f);
- }
-
- /** float_to_r, float_decode
- *
- */
- static final int DBL_MANT_DIG = 53;
- static final int FLT_RADIX = 2;
- @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
- public IRubyObject to_r(ThreadContext context) {
- long[]exp = new long[1];
- double f = frexp(value, exp);
- f = ldexp(f, DBL_MANT_DIG);
- long n = exp[0] - DBL_MANT_DIG;
- Ruby runtime = context.getRuntime();
- IRubyObject x = f_mul(context, f_to_i(context, runtime.newFloat(f)),
- f_expt(context,
- RubyFixnum.newFixnum(context.getRuntime(), FLT_RADIX),
- RubyFixnum.newFixnum(runtime, n)));
- return x;
- }
-
- /** floor
- *
- */
- @JRubyMethod(name = "floor")
- public IRubyObject floor() {
- return dbl2num(getRuntime(), Math.floor(value));
- }
-
- /** flo_ceil
- *
- */
- @JRubyMethod(name = "ceil")
- public IRubyObject ceil() {
- return dbl2num(getRuntime(), Math.ceil(value));
- }
-
- /** flo_round
- *
- */
- @JRubyMethod(name = "round")
- public IRubyObject round() {
- double f = value;
- if (f > 0.0) {
- f = Math.floor(f + 0.5);
- }
- if (f < 0.0) {
- f = Math.ceil(f - 0.5);
- }
- return dbl2num(getRuntime(), f);
- }
-
- /** flo_is_nan_p
- *
- */
- @JRubyMethod(name = "nan?")
- public IRubyObject nan_p() {
- return RubyBoolean.newBoolean(getRuntime(), Double.isNaN(value));
- }
-
- /** flo_is_infinite_p
- *
- */
- @JRubyMethod(name = "infinite?")
- public IRubyObject infinite_p() {
- if (Double.isInfinite(value)) {
- return RubyFixnum.newFixnum(getRuntime(), value < 0 ? -1 : 1);
- }
- return getRuntime().getNil();
- }
-
- /** flo_is_finite_p
- *
- */
- @JRubyMethod(name = "finite?")
- public IRubyObject finite_p() {
- if (Double.isInfinite(value) || Double.isNaN(value)) {
- return getRuntime().getFalse();
- }
- return getRuntime().getTrue();
- }
-
- public static void marshalTo(RubyFloat aFloat, MarshalStream output) throws java.io.IOException {
- output.registerLinkTarget(aFloat);
-
- String strValue = aFloat.toString();
-
- if (Double.isInfinite(aFloat.value)) {
- strValue = aFloat.value < 0 ? "-inf" : "inf";
- } else if (Double.isNaN(aFloat.value)) {
- strValue = "nan";
- }
- output.writeString(strValue);
- }
-
- public static RubyFloat unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- RubyFloat result = RubyFloat.newFloat(input.getRuntime(), org.jruby.util.Convert.byteListToDouble(input.unmarshalString(),false));
- 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) 2002 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>
- *
- * 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.common.IRubyWarnings.ID;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- * GC (Garbage Collection) Module
- *
- * Note: Since we rely on Java's memory model we can't provide the
- * kind of control over garbage collection that MRI provides.
- *
- * @author Anders
- */
-@JRubyModule(name="GC")
-public class RubyGC {
- public static RubyModule createGCModule(Ruby runtime) {
- RubyModule result = runtime.defineModule("GC");
- runtime.setGC(result);
-
- result.defineAnnotatedMethods(RubyGC.class);
-
- return result;
- }
-
- @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject start(IRubyObject recv) {
- System.gc();
- return recv.getRuntime().getNil();
- }
-
- @JRubyMethod
- public static IRubyObject garbage_collect(IRubyObject recv) {
- System.gc();
- return recv.getRuntime().getNil();
- }
-
- @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject enable(IRubyObject recv) {
- recv.getRuntime().getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "GC.enable will not work on JRuby", "GC.enable");
- return recv.getRuntime().getNil();
- }
-
- @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject disable(IRubyObject recv) {
- recv.getRuntime().getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "GC.disable will not work on JRuby", "GC.disable");
- return recv.getRuntime().getNil();
- }
-}
-/***** 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-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Tim Azzopardi <tim@tigerfive.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.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.util.io.STDIO;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.environment.OSEnvironmentReaderExcepton;
-import org.jruby.environment.OSEnvironment;
-import org.jruby.internal.runtime.ValueAccessor;
-import org.jruby.javasupport.JavaUtil;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Constants;
-import org.jruby.runtime.GlobalVariable;
-import org.jruby.runtime.IAccessor;
-import org.jruby.runtime.ReadonlyGlobalVariable;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.KCode;
-
-/** This class initializes global variables and constants.
- *
- * @author jpetersen
- */
-public class RubyGlobal {
-
- /**
- * Obligate string-keyed and string-valued hash, used for ENV and ENV_JAVA
- *
- */
- public static class StringOnlyRubyHash extends RubyHash {
-
- public StringOnlyRubyHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
- super(runtime, valueMap, defaultValue);
- }
-
- @Override
- public RubyHash to_hash() {
- Ruby runtime = getRuntime();
- RubyHash hash = RubyHash.newHash(runtime);
- hash.replace(runtime.getCurrentContext(), this);
- return hash;
- }
-
- @Override
- public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
- return super.op_aref(context, key.convertToString());
- }
-
- @Override
- public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
- if (!key.respondsTo("to_str")) {
- throw getRuntime().newTypeError("can't convert " + key.getMetaClass() + " into String");
- }
- if (!value.respondsTo("to_str") && !value.isNil()) {
- throw getRuntime().newTypeError("can't convert " + value.getMetaClass() + " into String");
- }
-
- if (value.isNil()) {
- return super.delete(context, key, org.jruby.runtime.Block.NULL_BLOCK);
- }
-
- //return super.aset(getRuntime().newString("sadfasdF"), getRuntime().newString("sadfasdF"));
- return super.op_aset(context, RuntimeHelpers.invoke(context, key, "to_str"),
- value.isNil() ? getRuntime().getNil() : RuntimeHelpers.invoke(context, value, "to_str"));
- }
-
- @JRubyMethod
- @Override
- public IRubyObject to_s(){
- return getRuntime().newString("ENV");
- }
- }
-
- public static void createGlobals(ThreadContext context, Ruby runtime) {
- runtime.defineGlobalConstant("TOPLEVEL_BINDING", runtime.newBinding());
-
- runtime.defineGlobalConstant("TRUE", runtime.getTrue());
- runtime.defineGlobalConstant("FALSE", runtime.getFalse());
- runtime.defineGlobalConstant("NIL", runtime.getNil());
-
- // define ARGV and $* for this runtime
- RubyArray argvArray = runtime.newArray();
- String[] argv = runtime.getInstanceConfig().getArgv();
- for (int i = 0; i < argv.length; i++) {
- argvArray.append(RubyString.newString(runtime, argv[i].getBytes()));
- }
- runtime.defineGlobalConstant("ARGV", argvArray);
- runtime.getGlobalVariables().defineReadonly("$*", new ValueAccessor(argvArray));
-
- IAccessor d = new ValueAccessor(runtime.newString(
- runtime.getInstanceConfig().displayedFileName()));
- runtime.getGlobalVariables().define("$PROGRAM_NAME", d);
- runtime.getGlobalVariables().define("$0", d);
-
- // Version information:
- IRubyObject version = runtime.newString(Constants.RUBY_VERSION).freeze(context);
- IRubyObject release = runtime.newString(Constants.COMPILE_DATE).freeze(context);
- IRubyObject platform = runtime.newString(Constants.PLATFORM).freeze(context);
- IRubyObject engine = runtime.newString(Constants.ENGINE).freeze(context);
-
- runtime.defineGlobalConstant("RUBY_VERSION", version);
- runtime.defineGlobalConstant("RUBY_PATCHLEVEL", runtime.newString(Constants.RUBY_PATCHLEVEL).freeze(context));
- runtime.defineGlobalConstant("RUBY_RELEASE_DATE", release);
- runtime.defineGlobalConstant("RUBY_PLATFORM", platform);
- runtime.defineGlobalConstant("RUBY_ENGINE", engine);
-
- runtime.defineGlobalConstant("VERSION", version);
- runtime.defineGlobalConstant("RELEASE_DATE", release);
- runtime.defineGlobalConstant("PLATFORM", platform);
-
- IRubyObject jrubyVersion = runtime.newString(Constants.VERSION).freeze(context);
- runtime.defineGlobalConstant("JRUBY_VERSION", jrubyVersion);
-
- GlobalVariable kcodeGV = new KCodeGlobalVariable(runtime, "$KCODE", runtime.newString("NONE"));
- runtime.defineVariable(kcodeGV);
- runtime.defineVariable(new GlobalVariable.Copy(runtime, "$-K", kcodeGV));
- IRubyObject defaultRS = runtime.newString(runtime.getInstanceConfig().getRecordSeparator()).freeze(context);
- GlobalVariable rs = new StringGlobalVariable(runtime, "$/", defaultRS);
- runtime.defineVariable(rs);
- runtime.setRecordSeparatorVar(rs);
- runtime.getGlobalVariables().setDefaultSeparator(defaultRS);
- runtime.defineVariable(new StringGlobalVariable(runtime, "$\\", runtime.getNil()));
- runtime.defineVariable(new StringGlobalVariable(runtime, "$,", runtime.getNil()));
-
- runtime.defineVariable(new LineNumberGlobalVariable(runtime, "$.", RubyFixnum.one(runtime)));
- runtime.defineVariable(new LastlineGlobalVariable(runtime, "$_"));
- runtime.defineVariable(new LastExitStatusVariable(runtime, "$?"));
-
- runtime.defineVariable(new ErrorInfoGlobalVariable(runtime, "$!", runtime.getNil()));
- runtime.defineVariable(new NonEffectiveGlobalVariable(runtime, "$=", runtime.getFalse()));
-
- if(runtime.getInstanceConfig().getInputFieldSeparator() == null) {
- runtime.defineVariable(new GlobalVariable(runtime, "$;", runtime.getNil()));
- } else {
- runtime.defineVariable(new GlobalVariable(runtime, "$;", RubyRegexp.newRegexp(runtime, runtime.getInstanceConfig().getInputFieldSeparator(), 0)));
- }
-
- Boolean verbose = runtime.getInstanceConfig().getVerbose();
- IRubyObject verboseValue = null;
- if (verbose == null) {
- verboseValue = runtime.getNil();
- } else if(verbose == Boolean.TRUE) {
- verboseValue = runtime.getTrue();
- } else {
- verboseValue = runtime.getFalse();
- }
- runtime.defineVariable(new VerboseGlobalVariable(runtime, "$VERBOSE", verboseValue));
-
- IRubyObject debug = runtime.newBoolean(runtime.getInstanceConfig().isDebug());
- runtime.defineVariable(new DebugGlobalVariable(runtime, "$DEBUG", debug));
- runtime.defineVariable(new DebugGlobalVariable(runtime, "$-d", debug));
-
- runtime.defineVariable(new SafeGlobalVariable(runtime, "$SAFE"));
-
- runtime.defineVariable(new BacktraceGlobalVariable(runtime, "$@"));
-
- IRubyObject stdin = new RubyIO(runtime, STDIO.IN);
- IRubyObject stdout = new RubyIO(runtime, STDIO.OUT);
- IRubyObject stderr = new RubyIO(runtime, STDIO.ERR);
-
- runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", stdin));
-
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", stdout));
- runtime.getGlobalVariables().alias("$>", "$stdout");
- runtime.getGlobalVariables().alias("$defout", "$stdout");
-
- runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", stderr));
- runtime.getGlobalVariables().alias("$deferr", "$stderr");
-
- runtime.defineGlobalConstant("STDIN", stdin);
- runtime.defineGlobalConstant("STDOUT", stdout);
- runtime.defineGlobalConstant("STDERR", stderr);
-
- runtime.defineVariable(new LoadedFeatures(runtime, "$\""));
- runtime.defineVariable(new LoadedFeatures(runtime, "$LOADED_FEATURES"));
-
- runtime.defineVariable(new LoadPath(runtime, "$:"));
- runtime.defineVariable(new LoadPath(runtime, "$-I"));
- runtime.defineVariable(new LoadPath(runtime, "$LOAD_PATH"));
-
- runtime.defineVariable(new MatchMatchGlobalVariable(runtime, "$&"));
- runtime.defineVariable(new PreMatchGlobalVariable(runtime, "$`"));
- runtime.defineVariable(new PostMatchGlobalVariable(runtime, "$'"));
- runtime.defineVariable(new LastMatchGlobalVariable(runtime, "$+"));
- runtime.defineVariable(new BackRefGlobalVariable(runtime, "$~"));
-
- // On platforms without a c-library accessable through JNA, getpid will return hashCode
- // as $$ used to. Using $$ to kill processes could take down many runtimes, but by basing
- // $$ on getpid() where available, we have the same semantics as MRI.
- runtime.getGlobalVariables().defineReadonly("$$", new ValueAccessor(runtime.newFixnum(runtime.getPosix().getpid())));
-
- // after defn of $stderr as the call may produce warnings
- defineGlobalEnvConstants(runtime);
-
- // Fixme: Do we need the check or does Main.java not call this...they should consolidate
- if (runtime.getGlobalVariables().get("$*").isNil()) {
- runtime.getGlobalVariables().defineReadonly("$*", new ValueAccessor(runtime.newArray()));
- }
-
- runtime.getGlobalVariables().defineReadonly("$-p",
- new ValueAccessor(runtime.getInstanceConfig().isAssumePrinting() ? runtime.getTrue() : runtime.getNil()));
- runtime.getGlobalVariables().defineReadonly("$-n",
- new ValueAccessor(runtime.getInstanceConfig().isAssumeLoop() ? runtime.getTrue() : runtime.getNil()));
- runtime.getGlobalVariables().defineReadonly("$-a",
- new ValueAccessor(runtime.getInstanceConfig().isSplit() ? runtime.getTrue() : runtime.getNil()));
- runtime.getGlobalVariables().defineReadonly("$-l",
- new ValueAccessor(runtime.getInstanceConfig().isProcessLineEnds() ? runtime.getTrue() : runtime.getNil()));
-
- // ARGF, $< object
- RubyArgsFile.initArgsFile(runtime);
- }
-
- private static void defineGlobalEnvConstants(Ruby runtime) {
-
- Map environmentVariableMap = null;
- OSEnvironment environment = new OSEnvironment();
- try {
- environmentVariableMap = environment.getEnvironmentVariableMap(runtime);
- } catch (OSEnvironmentReaderExcepton e) {
- // If the environment variables are not accessible shouldn't terminate
- runtime.getWarnings().warn(ID.MISCELLANEOUS, e.getMessage());
- }
-
- if (environmentVariableMap == null) {
- // if the environment variables can't be obtained, define an empty ENV
- environmentVariableMap = new HashMap();
- }
-
- StringOnlyRubyHash h1 = new StringOnlyRubyHash(runtime,
- environmentVariableMap, runtime.getNil());
- h1.getSingletonClass().defineAnnotatedMethods(StringOnlyRubyHash.class);
- runtime.defineGlobalConstant("ENV", h1);
-
- // Define System.getProperties() in ENV_JAVA
- Map systemProps = environment.getSystemPropertiesMap(runtime);
- runtime.defineGlobalConstant("ENV_JAVA", new StringOnlyRubyHash(
- runtime, systemProps, runtime.getNil()));
-
- }
-
- private static class NonEffectiveGlobalVariable extends GlobalVariable {
- public NonEffectiveGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- runtime.getWarnings().warn(ID.INEFFECTIVE_GLOBAL, "warning: variable " + name + " is no longer effective; ignored", name);
- return value;
- }
-
- @Override
- public IRubyObject get() {
- runtime.getWarnings().warn(ID.INEFFECTIVE_GLOBAL, "warning: variable " + name + " is no longer effective", name);
- return runtime.getFalse();
- }
- }
-
- private static class LastExitStatusVariable extends GlobalVariable {
- public LastExitStatusVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- IRubyObject lastExitStatus = runtime.getCurrentContext().getLastExitStatus();
- return lastExitStatus == null ? runtime.getNil() : lastExitStatus;
- }
-
- @Override
- public IRubyObject set(IRubyObject lastExitStatus) {
- runtime.getCurrentContext().setLastExitStatus(lastExitStatus);
-
- return lastExitStatus;
- }
- }
-
- private static class MatchMatchGlobalVariable extends GlobalVariable {
- public MatchMatchGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- return RubyRegexp.last_match(runtime.getCurrentContext().getCurrentFrame().getBackRef());
- }
- }
-
- private static class PreMatchGlobalVariable extends GlobalVariable {
- public PreMatchGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- return RubyRegexp.match_pre(runtime.getCurrentContext().getCurrentFrame().getBackRef());
- }
- }
-
- private static class PostMatchGlobalVariable extends GlobalVariable {
- public PostMatchGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- return RubyRegexp.match_post(runtime.getCurrentContext().getCurrentFrame().getBackRef());
- }
- }
-
- private static class LastMatchGlobalVariable extends GlobalVariable {
- public LastMatchGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- return RubyRegexp.match_last(runtime.getCurrentContext().getCurrentFrame().getBackRef());
- }
- }
-
- private static class BackRefGlobalVariable extends GlobalVariable {
- public BackRefGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, runtime.getNil());
- }
-
- @Override
- public IRubyObject get() {
- return RuntimeHelpers.getBackref(runtime, runtime.getCurrentContext());
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- RuntimeHelpers.setBackref(runtime, runtime.getCurrentContext(), value);
- return value;
- }
- }
-
- // Accessor methods.
-
- private static class LineNumberGlobalVariable extends GlobalVariable {
- public LineNumberGlobalVariable(Ruby runtime, String name, RubyFixnum value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- RubyArgsFile.setCurrentLineNumber(runtime.getGlobalVariables().get("$<"),RubyNumeric.fix2int(value));
- return super.set(value);
- }
- }
-
- private static class ErrorInfoGlobalVariable extends GlobalVariable {
- public ErrorInfoGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, null);
- set(value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- if (!value.isNil() &&
- !runtime.getException().isInstance(value) &&
- !(JavaUtil.isJavaObject(value) && JavaUtil.unwrapJavaObject(value) instanceof Exception)) {
- throw runtime.newTypeError("assigning non-exception to $!");
- }
-
- return runtime.getCurrentContext().setErrorInfo(value);
- }
-
- @Override
- public IRubyObject get() {
- return runtime.getCurrentContext().getErrorInfo();
- }
- }
-
- // FIXME: move out of this class!
- public static class StringGlobalVariable extends GlobalVariable {
- public StringGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- if (!value.isNil() && ! (value instanceof RubyString)) {
- throw runtime.newTypeError("value of " + name() + " must be a String");
- }
- return super.set(value);
- }
- }
-
- public static class KCodeGlobalVariable extends GlobalVariable {
- public KCodeGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject get() {
- return runtime.getKCode().kcode(runtime);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- runtime.setKCode(KCode.create(runtime, value.convertToString().toString()));
- return value;
- }
- }
-
- private static class SafeGlobalVariable extends GlobalVariable {
- public SafeGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, null);
- }
-
- @Override
- public IRubyObject get() {
- return runtime.newFixnum(runtime.getSafeLevel());
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
-// int level = RubyNumeric.fix2int(value);
-// if (level < runtime.getSafeLevel()) {
-// throw runtime.newSecurityError("tried to downgrade safe level from " +
-// runtime.getSafeLevel() + " to " + level);
-// }
-// runtime.setSafeLevel(level);
- // thread.setSafeLevel(level);
- runtime.getWarnings().warn(ID.SAFE_NOT_SUPPORTED, "SAFE levels are not supported in JRuby");
- return RubyFixnum.newFixnum(runtime, runtime.getSafeLevel());
- }
- }
-
- private static class VerboseGlobalVariable extends GlobalVariable {
- public VerboseGlobalVariable(Ruby runtime, String name, IRubyObject initialValue) {
- super(runtime, name, initialValue);
- set(initialValue);
- }
-
- @Override
- public IRubyObject get() {
- return runtime.getVerbose();
- }
-
- @Override
- public IRubyObject set(IRubyObject newValue) {
- if (newValue.isNil()) {
- runtime.setVerbose(newValue);
- } else {
- runtime.setVerbose(runtime.newBoolean(newValue.isTrue()));
- }
-
- return newValue;
- }
- }
-
- private static class DebugGlobalVariable extends GlobalVariable {
- public DebugGlobalVariable(Ruby runtime, String name, IRubyObject initialValue) {
- super(runtime, name, initialValue);
- set(initialValue);
- }
-
- @Override
- public IRubyObject get() {
- return runtime.getDebug();
- }
-
- @Override
- public IRubyObject set(IRubyObject newValue) {
- if (newValue.isNil()) {
- runtime.setDebug(newValue);
- } else {
- runtime.setDebug(runtime.newBoolean(newValue.isTrue()));
- }
-
- return newValue;
- }
- }
-
- private static class BacktraceGlobalVariable extends GlobalVariable {
- public BacktraceGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, null);
- }
-
- @Override
- public IRubyObject get() {
- IRubyObject errorInfo = runtime.getGlobalVariables().get("$!");
- IRubyObject backtrace = errorInfo.isNil() ? runtime.getNil() : errorInfo.callMethod(errorInfo.getRuntime().getCurrentContext(), "backtrace");
- //$@ returns nil if $!.backtrace is not an array
- if (!(backtrace instanceof RubyArray)) {
- backtrace = runtime.getNil();
- }
- return backtrace;
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- if (runtime.getGlobalVariables().get("$!").isNil()) {
- throw runtime.newArgumentError("$! not set.");
- }
- runtime.getGlobalVariables().get("$!").callMethod(value.getRuntime().getCurrentContext(), "set_backtrace", value);
- return value;
- }
- }
-
- private static class LastlineGlobalVariable extends GlobalVariable {
- public LastlineGlobalVariable(Ruby runtime, String name) {
- super(runtime, name, null);
- }
-
- @Override
- public IRubyObject get() {
- return RuntimeHelpers.getLastLine(runtime, runtime.getCurrentContext());
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- RuntimeHelpers.setLastLine(runtime, runtime.getCurrentContext(), value);
- return value;
- }
- }
-
- private static class InputGlobalVariable extends GlobalVariable {
- public InputGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- if (value == get()) {
- return value;
- }
-
- return super.set(value);
- }
- }
-
- private static class OutputGlobalVariable extends GlobalVariable {
- public OutputGlobalVariable(Ruby runtime, String name, IRubyObject value) {
- super(runtime, name, value);
- }
-
- @Override
- public IRubyObject set(IRubyObject value) {
- if (value == get()) {
- return value;
- }
- if (value instanceof RubyIO) {
- RubyIO io = (RubyIO)value;
-
- // HACK: in order to have stdout/err act like ttys and flush always,
- // we set anything assigned to stdout/stderr to sync
- io.getHandler().setSync(true);
- }
-
- if (!value.respondsTo("write")) {
- throw runtime.newTypeError(name() + " must have write method, " +
- value.getType().getName() + " given");
- }
-
- return super.set(value);
- }
- }
-
- private static class LoadPath extends ReadonlyGlobalVariable {
- public LoadPath(Ruby runtime, String name) {
- super(runtime, name, null);
- }
-
- /**
- * @see org.jruby.runtime.GlobalVariable#get()
- */
- @Override
- public IRubyObject get() {
- return runtime.getLoadService().getLoadPath();
- }
- }
-
- private static class LoadedFeatures extends ReadonlyGlobalVariable {
- public LoadedFeatures(Ruby runtime, String name) {
- super(runtime, name, null);
- }
-
- /**
- * @see org.jruby.runtime.GlobalVariable#get()
- */
- @Override
- public IRubyObject get() {
- return runtime.getLoadService().getLoadedFeatures();
- }
- }
-}
-/***** 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-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2006 Ola Bini <Ola.Bini@ki.se>
- * Copyright (C) 2006 Tim Azzopardi <tim@tigerfive.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2007 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.io.IOException;
-import java.util.AbstractCollection;
-import java.util.AbstractSet;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.javasupport.JavaUtil;
-import org.jruby.javasupport.util.RuntimeHelpers;
-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.TypeConverter;
-
-// Design overview:
-//
-// RubyHash is implemented as hash table with a singly-linked list of
-// RubyHash.RubyHashEntry objects for each bucket. RubyHashEntry objects
-// are also kept in a doubly-linked list which reflects their insertion
-// order and is used for iteration. For simplicity, this latter list is
-// circular; a dummy RubyHashEntry, RubyHash.head, is used to mark the
-// ends of the list.
-//
-// When an entry is removed from the table, it is also removed from the
-// doubly-linked list. However, while the reference to the previous
-// RubyHashEntry is cleared (to mark the entry as dead), the reference
-// to the next RubyHashEntry is preserved so that iterators are not
-// invalidated: any iterator with a reference to a dead entry can climb
-// back up into the list of live entries by chasing next references until
-// it finds a live entry (or head).
-//
-// Ordinarily, this scheme would require O(N) time to clear a hash (since
-// each RubyHashEntry would need to be visited and unlinked from the
-// iteration list), but RubyHash also maintains a generation count. Every
-// time the hash is cleared, the doubly-linked list is simply discarded and
-// the generation count incremented. Iterators check to see whether the
-// generation count has changed; if it has, they reset themselves back to
-// the new start of the list.
-//
-// This design means that iterators are never invalidated by changes to the
-// hashtable, and they do not need to modify the structure during their
-// lifecycle.
-//
-
-/** Implementation of the Hash class.
- *
- * Concurrency: no synchronization is required among readers, but
- * all users must synchronize externally with writers.
- *
- */
-@JRubyClass(name = "Hash", include="Enumerable")
-public class RubyHash extends RubyObject implements Map {
-
- public static RubyClass createHashClass(Ruby runtime) {
- RubyClass hashc = runtime.defineClass("Hash", runtime.getObject(), HASH_ALLOCATOR);
- runtime.setHash(hashc);
- hashc.index = ClassIndex.HASH;
- hashc.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyHash;
- }
- };
-
- hashc.includeModule(runtime.getEnumerable());
-
- hashc.defineAnnotatedMethods(RubyHash.class);
-
- return hashc;
- }
-
- private final static ObjectAllocator HASH_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyHash(runtime, klass);
- }
- };
-
- public int getNativeTypeIndex() {
- return ClassIndex.HASH;
- }
-
- /** rb_hash_s_create
- *
- */
- @JRubyMethod(name = "[]", rest = true, frame = true, meta = true)
- public static IRubyObject create(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyClass klass = (RubyClass) recv;
- Ruby runtime = context.getRuntime();
- RubyHash hash;
-
- if (args.length == 1) {
- IRubyObject tmp = TypeConverter.convertToTypeWithCheck(
- args[0], runtime.getHash(), MethodIndex.TO_HASH, "to_hash");
-
- if (!tmp.isNil()) {
- RubyHash otherHash = (RubyHash) tmp;
- return new RubyHash(runtime, klass, otherHash);
- }
- }
-
- if ((args.length & 1) != 0) {
- throw runtime.newArgumentError("odd number of args for Hash");
- }
-
- hash = (RubyHash)klass.allocate();
- for (int i=0; i < args.length; i+=2) hash.op_aset(context, args[i], args[i+1]);
-
- return hash;
- }
-
- /** rb_hash_new
- *
- */
- public static final RubyHash newHash(Ruby runtime) {
- return new RubyHash(runtime);
- }
-
- /** rb_hash_new
- *
- */
- public static final RubyHash newHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
- assert defaultValue != null;
-
- return new RubyHash(runtime, valueMap, defaultValue);
- }
-
- private RubyHashEntry[] table;
- private int size = 0;
- private int threshold;
-
- private static final int PROCDEFAULT_HASH_F = 1 << 10;
-
- private IRubyObject ifNone;
-
- private RubyHash(Ruby runtime, RubyClass klass, RubyHash other) {
- super(runtime, klass);
- this.ifNone = runtime.getNil();
- threshold = INITIAL_THRESHOLD;
- table = other.internalCopyTable(head);
- size = other.size;
- }
-
- public RubyHash(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- this.ifNone = runtime.getNil();
- alloc();
- }
-
- public RubyHash(Ruby runtime) {
- this(runtime, runtime.getNil());
- }
-
- public RubyHash(Ruby runtime, IRubyObject defaultValue) {
- super(runtime, runtime.getHash());
- this.ifNone = defaultValue;
- alloc();
- }
-
- /*
- * Constructor for internal usage (mainly for Array#|, Array#&, Array#- and Array#uniq)
- * it doesn't initialize ifNone field
- */
- RubyHash(Ruby runtime, boolean objectSpace) {
- super(runtime, runtime.getHash(), objectSpace);
- alloc();
- }
-
- // TODO should this be deprecated ? (to be efficient, internals should deal with RubyHash directly)
- public RubyHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
- super(runtime, runtime.getHash());
- this.ifNone = defaultValue;
- alloc();
-
- for (Iterator iter = valueMap.entrySet().iterator();iter.hasNext();) {
- Map.Entry e = (Map.Entry)iter.next();
- internalPut((IRubyObject)e.getKey(), (IRubyObject)e.getValue());
- }
- }
-
- private final void alloc() {
- threshold = INITIAL_THRESHOLD;
- generation++;
- head.nextAdded = head.prevAdded = head;
- table = new RubyHashEntry[MRI_HASH_RESIZE ? MRI_INITIAL_CAPACITY : JAVASOFT_INITIAL_CAPACITY];
- }
-
- /* ============================
- * Here are hash internals
- * (This could be extracted to a separate class but it's not too large though)
- * ============================
- */
-
- private static final int MRI_PRIMES[] = {
- 8 + 3, 16 + 3, 32 + 5, 64 + 3, 128 + 3, 256 + 27, 512 + 9, 1024 + 9, 2048 + 5, 4096 + 3,
- 8192 + 27, 16384 + 43, 32768 + 3, 65536 + 45, 131072 + 29, 262144 + 3, 524288 + 21, 1048576 + 7,
- 2097152 + 17, 4194304 + 15, 8388608 + 9, 16777216 + 43, 33554432 + 35, 67108864 + 15,
- 134217728 + 29, 268435456 + 3, 536870912 + 11, 1073741824 + 85, 0
- };
-
- private static final int JAVASOFT_INITIAL_CAPACITY = 8; // 16 ?
- private static final int MRI_INITIAL_CAPACITY = MRI_PRIMES[0];
-
- private static final int INITIAL_THRESHOLD = JAVASOFT_INITIAL_CAPACITY - (JAVASOFT_INITIAL_CAPACITY >> 2);
- private static final int MAXIMUM_CAPACITY = 1 << 30;
-
- private static final RubyHashEntry NO_ENTRY = new RubyHashEntry();
- private int generation = 0; // generation count for O(1) clears
- private final RubyHashEntry head = new RubyHashEntry();
- { head.prevAdded = head.nextAdded = head; }
-
- static final class RubyHashEntry implements Map.Entry {
- private IRubyObject key;
- private IRubyObject value;
- private RubyHashEntry next;
- private RubyHashEntry prevAdded;
- private RubyHashEntry nextAdded;
- private int hash;
-
- RubyHashEntry() {
- key = NEVER;
- }
-
- RubyHashEntry(int h, IRubyObject k, IRubyObject v, RubyHashEntry e, RubyHashEntry head) {
- key = k; value = v; next = e; hash = h;
- prevAdded = head.prevAdded;
- nextAdded = head;
- nextAdded.prevAdded = this;
- prevAdded.nextAdded = this;
- }
-
- public void detach() {
- if (prevAdded != null) {
- prevAdded.nextAdded = nextAdded;
- nextAdded.prevAdded = prevAdded;
- prevAdded = null;
- }
- }
-
- public boolean isLive() {
- return prevAdded != null;
- }
-
- public Object getKey() {
- return key;
- }
- public Object getJavaifiedKey(){
- return JavaUtil.convertRubyToJava(key);
- }
-
- public Object getValue() {
- return value;
- }
- public Object getJavaifiedValue() {
- return JavaUtil.convertRubyToJava(value);
- }
-
- public Object setValue(Object value) {
- IRubyObject oldValue = this.value;
- if (value instanceof IRubyObject) {
- this.value = (IRubyObject)value;
- } else {
- throw new UnsupportedOperationException("directEntrySet() doesn't support setValue for non IRubyObject instance entries, convert them manually or use entrySet() instead");
- }
- return oldValue;
- }
-
- public boolean equals(Object other){
- if(!(other instanceof RubyHashEntry)) return false;
- RubyHashEntry otherEntry = (RubyHashEntry)other;
- if(key == otherEntry.key || key.eql(otherEntry.key)){
- if(value == otherEntry.value || value.equals(otherEntry.value)) return true;
- }
- return false;
- }
-
- public int hashCode(){
- return key.hashCode() ^ value.hashCode();
- }
- }
-
- private static int JavaSoftHashValue(int h) {
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
-
- private static int JavaSoftBucketIndex(final int h, final int length) {
- return h & (length - 1);
- }
-
- private static int MRIHashValue(int h) {
- return h & HASH_SIGN_BIT_MASK;
- }
-
- private static final int HASH_SIGN_BIT_MASK = ~(1 << 31);
- private static int MRIBucketIndex(final int h, final int length) {
- return (h % length);
- }
-
- private final void resize(int newCapacity) {
- final RubyHashEntry[] oldTable = table;
- final RubyHashEntry[] newTable = new RubyHashEntry[newCapacity];
- for (int j = 0; j < oldTable.length; j++) {
- RubyHashEntry entry = oldTable[j];
- oldTable[j] = null;
- while (entry != null) {
- RubyHashEntry next = entry.next;
- int i = bucketIndex(entry.hash, newCapacity);
- entry.next = newTable[i];
- newTable[i] = entry;
- entry = next;
- }
- }
- table = newTable;
- }
-
- private final void JavaSoftCheckResize() {
- if (size > threshold) {
- int oldCapacity = table.length;
- if (oldCapacity == MAXIMUM_CAPACITY) {
- threshold = Integer.MAX_VALUE;
- return;
- }
- int newCapacity = table.length << 1;
- resize(newCapacity);
- threshold = newCapacity - (newCapacity >> 2);
- }
- }
-
- private static final int MIN_CAPA = 8;
- private static final int ST_DEFAULT_MAX_DENSITY = 5;
- private final void MRICheckResize() {
- if (size / table.length > ST_DEFAULT_MAX_DENSITY) {
- int forSize = table.length + 1; // size + 1;
- for (int i=0, newCapacity = MIN_CAPA; i < MRI_PRIMES.length; i++, newCapacity <<= 1) {
- if (newCapacity > forSize) {
- resize(MRI_PRIMES[i]);
- return;
- }
- }
- return; // suboptimal for large hashes (> 1073741824 + 85 entries) not very likely to happen
- }
- }
- // ------------------------------
- private static boolean MRI_HASH = true;
- private static boolean MRI_HASH_RESIZE = true;
-
- private static int hashValue(final int h) {
- return MRI_HASH ? MRIHashValue(h) : JavaSoftHashValue(h);
- }
-
- private static int bucketIndex(final int h, final int length) {
- return MRI_HASH ? MRIBucketIndex(h, length) : JavaSoftBucketIndex(h, length);
- }
-
- private void checkResize() {
- if (MRI_HASH_RESIZE) MRICheckResize(); else JavaSoftCheckResize();
- }
- // ------------------------------
- public static long collisions = 0;
-
- // put implementation
-
- private final void internalPut(final IRubyObject key, final IRubyObject value) {
- internalPut(key, value, true);
- }
-
- private final void internalPut(final IRubyObject key, final IRubyObject value, final boolean checkForExisting) {
- checkResize();
- final int hash = hashValue(key.hashCode());
- final int i = bucketIndex(hash, table.length);
-
- // if (table[i] != null) collisions++;
-
- if (checkForExisting) {
- for (RubyHashEntry entry = table[i]; entry != null; entry = entry.next) {
- IRubyObject k;
- if (entry.hash == hash && ((k = entry.key) == key || key.eql(k))) {
- entry.value = value;
- return;
- }
- }
- }
-
- table[i] = new RubyHashEntry(hash, key, value, table[i], head);
- size++;
- }
-
- // get implementation
-
- private final IRubyObject internalGet(IRubyObject key) { // specialized for value
- return internalGetEntry(key).value;
- }
-
- private final RubyHashEntry internalGetEntry(IRubyObject key) {
- final int hash = hashValue(key.hashCode());
- for (RubyHashEntry entry = table[bucketIndex(hash, table.length)]; entry != null; entry = entry.next) {
- IRubyObject k;
- if (entry.hash == hash && ((k = entry.key) == key || key.eql(k))) return entry;
- }
- return NO_ENTRY;
- }
-
- // delete implementation
-
-
- private final RubyHashEntry internalDelete(final IRubyObject key) {
- return internalDelete(hashValue(key.hashCode()), MATCH_KEY, key);
- }
-
- private final RubyHashEntry internalDeleteEntry(final RubyHashEntry entry) {
- // n.b. we need to recompute the hash in case the key object was modified
- return internalDelete(hashValue(entry.key.hashCode()), MATCH_ENTRY, entry);
- }
-
- private final RubyHashEntry internalDelete(final int hash, final EntryMatchType matchType, final Object obj) {
- final int i = bucketIndex(hash, table.length);
-
- RubyHashEntry entry = table[i];
- if (entry != null) {
- RubyHashEntry prior = null;
- for (; entry != null; prior = entry, entry = entry.next) {
- if (entry.hash == hash && matchType.matches(entry, obj)) {
- if (prior != null) {
- prior.next = entry.next;
- } else {
- table[i] = entry.next;
- }
- entry.detach();
- size--;
- return entry;
- }
- }
- }
-
- return NO_ENTRY;
- }
-
- private static abstract class EntryMatchType {
- public abstract boolean matches(final RubyHashEntry entry, final Object obj);
- }
-
- private static final EntryMatchType MATCH_KEY = new EntryMatchType() {
- public boolean matches(final RubyHashEntry entry, final Object obj) {
- final IRubyObject key = entry.key;
- return obj == key || (((IRubyObject)obj).eql(key));
- }
- };
-
- private static final EntryMatchType MATCH_ENTRY = new EntryMatchType() {
- public boolean matches(final RubyHashEntry entry, final Object obj) {
- return entry.equals(obj);
- }
- };
-
- private final RubyHashEntry[] internalCopyTable(RubyHashEntry destHead) {
- RubyHashEntry[]newTable = new RubyHashEntry[table.length];
-
- for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
- int i = bucketIndex(entry.hash, table.length);
- newTable[i] = new RubyHashEntry(entry.hash, entry.key, entry.value, newTable[i], destHead);
- }
- return newTable;
- }
-
- public static abstract class Visitor {
- public abstract void visit(IRubyObject key, IRubyObject value);
- }
-
- public void visitAll(Visitor visitor) {
- int startGeneration = generation;
- for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
- if (startGeneration != generation) {
- startGeneration = generation;
- entry = head.nextAdded;
- if (entry == head) break;
- }
- if (entry.isLive()) visitor.visit(entry.key, entry.value);
- }
- }
-
- /* ============================
- * End of hash internals
- * ============================
- */
-
- /* ================
- * Instance Methods
- * ================
- */
-
- /** rb_hash_initialize
- *
- */
- @JRubyMethod(name = "initialize", optional = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, final Block block) {
- modify();
-
- if (block.isGiven()) {
- if (args.length > 0) throw getRuntime().newArgumentError("wrong number of arguments");
- ifNone = getRuntime().newProc(Block.Type.PROC, block);
- flags |= PROCDEFAULT_HASH_F;
- } else {
- Arity.checkArgumentCount(getRuntime(), args, 0, 1);
- if (args.length == 1) ifNone = args[0];
- }
- return this;
- }
-
- /** rb_hash_default
- *
- */
- @Deprecated
- public IRubyObject default_value_get(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 0: return default_value_get(context);
- case 1: return default_value_get(context, args[0]);
- default: throw context.getRuntime().newArgumentError(args.length, 1);
- }
- }
- @JRubyMethod(name = "default", frame = true)
- public IRubyObject default_value_get(ThreadContext context) {
- if ((flags & PROCDEFAULT_HASH_F) != 0) {
- return getRuntime().getNil();
- }
- return ifNone;
- }
- @JRubyMethod(name = "default", frame = true)
- public IRubyObject default_value_get(ThreadContext context, IRubyObject arg) {
- if ((flags & PROCDEFAULT_HASH_F) != 0) {
- return RuntimeHelpers.invoke(context, ifNone, "call", this, arg);
- }
- return ifNone;
- }
-
- /** rb_hash_set_default
- *
- */
- @JRubyMethod(name = "default=", required = 1)
- public IRubyObject default_value_set(final IRubyObject defaultValue) {
- modify();
-
- ifNone = defaultValue;
- flags &= ~PROCDEFAULT_HASH_F;
-
- return ifNone;
- }
-
- /** rb_hash_default_proc
- *
- */
- @JRubyMethod(name = "default_proc", frame = true)
- public IRubyObject default_proc() {
- return (flags & PROCDEFAULT_HASH_F) != 0 ? ifNone : getRuntime().getNil();
- }
-
- /** rb_hash_modify
- *
- */
- public void modify() {
- testFrozen("hash");
- if (isTaint() && getRuntime().getSafeLevel() >= 4) {
- throw getRuntime().newSecurityError("Insecure: can't modify hash");
- }
- }
-
- /** inspect_hash
- *
- */
- private IRubyObject inspectHash(final ThreadContext context) {
- final ByteList buffer = new ByteList();
- buffer.append('{');
- final boolean[] firstEntry = new boolean[1];
-
- firstEntry[0] = true;
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (!firstEntry[0]) buffer.append(',').append(' ');
-
- buffer.append(inspect(context, key).getByteList());
- buffer.append('=').append('>');
- buffer.append(inspect(context, value).getByteList());
- firstEntry[0] = false;
- }
- });
- buffer.append('}');
- return getRuntime().newString(buffer);
- }
-
- /** rb_hash_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect(ThreadContext context) {
- if (size == 0) return getRuntime().newString("{}");
- if (getRuntime().isInspecting(this)) return getRuntime().newString("{...}");
-
- try {
- getRuntime().registerInspecting(this);
- return inspectHash(context);
- } finally {
- getRuntime().unregisterInspecting(this);
- }
- }
-
- /** rb_hash_size
- *
- */
- @JRubyMethod(name = {"size", "length"})
- public RubyFixnum rb_size() {
- return getRuntime().newFixnum(size);
- }
-
- /** rb_hash_empty_p
- *
- */
- @JRubyMethod(name = "empty?")
- public RubyBoolean empty_p() {
- return size == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- /** rb_hash_to_a
- *
- */
- @JRubyMethod(name = "to_a")
- public RubyArray to_a() {
- final Ruby runtime = getRuntime();
- final RubyArray result = RubyArray.newArray(runtime, size);
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- result.append(RubyArray.newArray(runtime, key, value));
- }
- });
-
- result.setTaint(isTaint());
- return result;
- }
-
- /** rb_hash_to_s & to_s_hash
- *
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- if (getRuntime().isInspecting(this)) return getRuntime().newString("{...}");
- try {
- getRuntime().registerInspecting(this);
- return to_a().to_s();
- } finally {
- getRuntime().unregisterInspecting(this);
- }
- }
-
- /** rb_hash_rehash
- *
- */
- @JRubyMethod(name = "rehash")
- public RubyHash rehash() {
- modify();
- final RubyHashEntry[] oldTable = table;
- final RubyHashEntry[] newTable = new RubyHashEntry[oldTable.length];
- for (int j = 0; j < oldTable.length; j++) {
- RubyHashEntry entry = oldTable[j];
- oldTable[j] = null;
- while (entry != null) {
- RubyHashEntry next = entry.next;
- entry.hash = entry.key.hashCode(); // update the hash value
- int i = bucketIndex(entry.hash, newTable.length);
- entry.next = newTable[i];
- newTable[i] = entry;
- entry = next;
- }
- }
- table = newTable;
- return this;
- }
-
- /** rb_hash_to_hash
- *
- */
- @JRubyMethod(name = "to_hash")
- public RubyHash to_hash() {
- return this;
- }
-
- public RubyHash convertToHash() {
- return this;
- }
-
- public final void fastASet(IRubyObject key, IRubyObject value) {
- internalPut(key, value);
- }
-
- @Deprecated
- public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
- return op_aset(getRuntime().getCurrentContext(), key, value);
- }
-
- /** rb_hash_aset
- *
- */
- @JRubyMethod(name = {"[]=", "store"}, required = 2)
- public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
- modify();
-
- if (!(key instanceof RubyString)) {
- internalPut(key, value);
- } else {
- final RubyHashEntry entry = internalGetEntry(key);
- if (entry != NO_ENTRY) {
- entry.value = value;
- } else {
- RubyString realKey = (RubyString)key;
-
- if (!realKey.isFrozen()) {
- realKey = realKey.strDup(context.getRuntime(), realKey.getMetaClass().getRealClass());;
- realKey.setFrozen(true);
- }
-
- internalPut(realKey, value, false);
- }
- }
-
- return value;
- }
-
- /**
- * Note: this is included as a compatibility measure for AR-JDBC
- * @deprecated use RubyHash.op_aset instead
- */
- public IRubyObject aset(IRubyObject key, IRubyObject value) {
- return op_aset(getRuntime().getCurrentContext(), key, value);
- }
-
- /**
- * Note: this is included as a compatibility measure for Mongrel+JRuby
- * @deprecated use RubyHash.op_aref instead
- */
- public IRubyObject aref(IRubyObject key) {
- return op_aref(getRuntime().getCurrentContext(), key);
- }
-
- public final IRubyObject fastARef(IRubyObject key) { // retuns null when not found to avoid unnecessary getRuntime().getNil() call
- return internalGet(key);
- }
-
- /** rb_hash_aref
- *
- */
- @JRubyMethod(name = "[]", required = 1)
- public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
- IRubyObject value;
- return ((value = internalGet(key)) == null) ? callMethod(context, MethodIndex.DEFAULT, "default", key) : value;
- }
-
- /** rb_hash_fetch
- *
- */
- @JRubyMethod(name = "fetch", required = 1, optional = 1, frame = true)
- public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
- if (args.length == 2 && block.isGiven()) {
- getRuntime().getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
- }
-
- IRubyObject value;
- if ((value = internalGet(args[0])) == null) {
- if (block.isGiven()) return block.yield(context, args[0]);
- if (args.length == 1) throw getRuntime().newIndexError("key not found");
- return args[1];
- }
- return value;
- }
-
- /** rb_hash_has_key
- *
- */
- @JRubyMethod(name = {"has_key?", "key?", "include?", "member?"}, required = 1)
- public RubyBoolean has_key_p(IRubyObject key) {
- return internalGetEntry(key) == NO_ENTRY ? getRuntime().getFalse() : getRuntime().getTrue();
- }
-
- private class Found extends RuntimeException {}
-
- private boolean hasValue(final ThreadContext context, final IRubyObject expected) {
- try {
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (equalInternal(context, value, expected)) {
- throw new Found();
- }
- }
- });
- return false;
- } catch (Found found) {
- return true;
- }
- }
-
- /** rb_hash_has_value
- *
- */
- @JRubyMethod(name = {"has_value?", "value?"}, required = 1)
- public RubyBoolean has_value_p(ThreadContext context, IRubyObject expected) {
- return getRuntime().newBoolean(hasValue(context, expected));
- }
-
- /** rb_hash_each
- *
- */
- @JRubyMethod(name = "each", frame = true)
- public RubyHash each(final ThreadContext context, final Block block) {
- final Ruby runtime = getRuntime();
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- // rb_assoc_new equivalent
- block.yield(context, RubyArray.newArray(runtime, key, value), null, null, false);
- }
- });
-
- return this;
- }
-
- /** rb_hash_each_pair
- *
- */
- @JRubyMethod(name = "each_pair", frame = true)
- public RubyHash each_pair(final ThreadContext context, final Block block) {
- final Ruby runtime = getRuntime();
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- // rb_yield_values(2,...) equivalent
- block.yield(context, RubyArray.newArray(runtime, key, value), null, null, true);
- }
- });
-
- return this;
- }
-
- /** rb_hash_each_value
- *
- */
- @JRubyMethod(name = "each_value", frame = true)
- public RubyHash each_value(final ThreadContext context, final Block block) {
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- block.yield(context, value);
- }
- });
-
- return this;
- }
-
- /** rb_hash_each_key
- *
- */
- @JRubyMethod(name = "each_key", frame = true)
- public RubyHash each_key(final ThreadContext context, final Block block) {
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- block.yield(context, key);
- }
- });
-
- return this;
- }
-
- /** rb_hash_sort
- *
- */
- @JRubyMethod(name = "sort", frame = true)
- public RubyArray sort(Block block) {
- return to_a().sort_bang(block);
- }
-
- private static class FoundKey extends RuntimeException {
- public IRubyObject key;
- FoundKey(IRubyObject key) {
- super();
- this.key = key;
- }
- }
-
- /** rb_hash_index
- *
- */
- @JRubyMethod(name = "index", required = 1)
- public IRubyObject index(ThreadContext context, IRubyObject expected) {
- IRubyObject key = internalIndex(context, expected);
- if (key != null) {
- return key;
- } else {
- return getRuntime().getNil();
- }
- }
-
- private IRubyObject internalIndex(final ThreadContext context, final IRubyObject expected) {
- try {
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (equalInternal(context, value, expected)) {
- throw new FoundKey(key);
- }
- }
- });
- return null;
- } catch (FoundKey found) {
- return found.key;
- }
- }
-
- /** rb_hash_indexes
- *
- */
- @JRubyMethod(name = {"indexes", "indices"}, rest = true)
- public RubyArray indices(ThreadContext context, IRubyObject[] indices) {
- return values_at(context, indices);
- }
-
- /** rb_hash_keys
- *
- */
- @JRubyMethod(name = "keys")
- public RubyArray keys() {
- final Ruby runtime = getRuntime();
- final RubyArray keys = RubyArray.newArray(runtime, size);
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- keys.append(key);
- }
- });
-
- return keys;
- }
-
- /** rb_hash_values
- *
- */
- @JRubyMethod(name = "values")
- public RubyArray rb_values() {
- final RubyArray values = RubyArray.newArray(getRuntime(), size);
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- values.append(value);
- }
- });
-
- return values;
- }
-
- /** rb_hash_equal
- *
- */
-
- private static final boolean EQUAL_CHECK_DEFAULT_VALUE = false;
-
- private static class Mismatch extends RuntimeException {}
-
- @Override
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(final ThreadContext context, final IRubyObject other) {
- if (this == other) return getRuntime().getTrue();
- if (!(other instanceof RubyHash)) {
- if (other.respondsTo("to_hash") && equalInternal(context, other, this)) return getRuntime().getTrue();
- return getRuntime().getFalse();
- }
-
- final RubyHash otherHash = (RubyHash)other;
- if (size != otherHash.size) return getRuntime().getFalse();
-
- final Ruby runtime = getRuntime();
-
- if (EQUAL_CHECK_DEFAULT_VALUE) {
- if (!equalInternal(context, ifNone, otherHash.ifNone) &&
- (flags & PROCDEFAULT_HASH_F) != (otherHash.flags & PROCDEFAULT_HASH_F)) return runtime.getFalse();
- }
-
- try {
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- IRubyObject otherValue = otherHash.internalGet(key);
- if (otherValue == null || !equalInternal(context, value, otherValue)) throw new Mismatch();
- }
- });
- return runtime.getTrue();
- } catch (Mismatch e) {
- return runtime.getFalse();
- }
- }
-
- /** rb_hash_shift
- *
- */
- @JRubyMethod(name = "shift")
- public IRubyObject shift(ThreadContext context) {
- modify();
-
- RubyHashEntry entry = head.nextAdded;
- if (entry != head) {
- RubyArray result = RubyArray.newArray(getRuntime(), entry.key, entry.value);
- internalDeleteEntry(entry);
- return result;
- }
-
- if ((flags & PROCDEFAULT_HASH_F) != 0) {
- return RuntimeHelpers.invoke(context, ifNone, "call", this, getRuntime().getNil());
- } else {
- return ifNone;
- }
- }
-
- public final boolean fastDelete(IRubyObject key) {
- return internalDelete(key) != NO_ENTRY;
- }
-
- /** rb_hash_delete
- *
- */
- @JRubyMethod(name = "delete", required = 1, frame = true)
- public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
- modify();
-
- final RubyHashEntry entry = internalDelete(key);
- if (entry != NO_ENTRY) return entry.value;
-
- if (block.isGiven()) return block.yield(context, key);
- return getRuntime().getNil();
- }
-
- /** rb_hash_select
- *
- */
- @JRubyMethod(name = "select", frame = true)
- public IRubyObject select(final ThreadContext context, final Block block) {
- final RubyArray result = getRuntime().newArray();
- final Ruby runtime = getRuntime();
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (block.yield(context, runtime.newArray(key, value), null, null, true).isTrue()) {
- result.append(runtime.newArray(key, value));
- }
- }
- });
-
- return result;
- }
-
- /** rb_hash_delete_if
- *
- */
- @JRubyMethod(name = "delete_if", frame = true)
- public RubyHash delete_if(final ThreadContext context, final Block block) {
- modify();
-
- final Ruby runtime = getRuntime();
- final RubyHash self = this;
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (block.yield(context, RubyArray.newArray(runtime, key, value), null, null, true).isTrue()) {
- self.delete(context, key, block);
- }
- }
- });
-
- return this;
- }
-
- /** rb_hash_reject
- *
- */
- @JRubyMethod(name = "reject", frame = true)
- public RubyHash reject(ThreadContext context, Block block) {
- return ((RubyHash)dup()).delete_if(context, block);
- }
-
- /** rb_hash_reject_bang
- *
- */
- @JRubyMethod(name = "reject!", frame = true)
- public IRubyObject reject_bang(ThreadContext context, Block block) {
- int n = size;
- delete_if(context, block);
- if (n == size) return getRuntime().getNil();
- return this;
- }
-
- /** rb_hash_clear
- *
- */
- @JRubyMethod(name = "clear")
- public RubyHash rb_clear() {
- modify();
-
- if (size > 0) {
- alloc();
- size = 0;
- }
-
- return this;
- }
-
- /** rb_hash_invert
- *
- */
- @JRubyMethod(name = "invert")
- public RubyHash invert(final ThreadContext context) {
- final RubyHash result = newHash(getRuntime());
-
- visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- result.op_aset(context, value, key);
- }
- });
-
- return result;
- }
-
- /** rb_hash_update
- *
- */
- @JRubyMethod(name = {"merge!", "update"}, required = 1, frame = true)
- public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
- modify();
-
- final Ruby runtime = getRuntime();
- final RubyHash otherHash = other.convertToHash();
- final RubyHash self = this;
- otherHash.visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- if (block.isGiven()) {
- IRubyObject existing = self.internalGet(key);
- if (existing != null)
- value = block.yield(context, RubyArray.newArrayNoCopy(runtime, new IRubyObject[]{key, existing, value}));
- }
- self.op_aset(context, key, value);
- }
- });
-
- return this;
- }
-
- /** rb_hash_merge
- *
- */
- @JRubyMethod(name = "merge", required = 1, frame = true)
- public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
- return ((RubyHash)dup()).merge_bang(context, other, block);
- }
-
- /** rb_hash_replace
- *
- */
- @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
- public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
- return replace(context, other);
- }
-
- /** rb_hash_replace
- *
- */
- @JRubyMethod(name = "replace", required = 1)
- public RubyHash replace(final ThreadContext context, IRubyObject other) {
- final RubyHash otherHash = other.convertToHash();
-
- if (this == otherHash) return this;
-
- rb_clear();
-
- final RubyHash self = this;
- otherHash.visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- self.op_aset(context, key, value);
- }
- });
-
- ifNone = otherHash.ifNone;
-
- if ((otherHash.flags & PROCDEFAULT_HASH_F) != 0) {
- flags |= PROCDEFAULT_HASH_F;
- } else {
- flags &= ~PROCDEFAULT_HASH_F;
- }
-
- return this;
- }
-
- /** rb_hash_values_at
- *
- */
- @JRubyMethod(name = "values_at", rest = true)
- public RubyArray values_at(ThreadContext context, IRubyObject[] args) {
- RubyArray result = RubyArray.newArray(getRuntime(), args.length);
- for (int i = 0; i < args.length; i++) {
- result.append(op_aref(context, args[i]));
- }
- return result;
- }
-
- public boolean hasDefaultProc() {
- return (flags & PROCDEFAULT_HASH_F) != 0;
- }
-
- public IRubyObject getIfNone(){
- return ifNone;
- }
-
- private static class VisitorIOException extends RuntimeException {
- VisitorIOException(Throwable cause) {
- super(cause);
- }
- }
-
- // FIXME: Total hack to get flash in Rails marshalling/unmarshalling in session ok...We need
- // to totally change marshalling to work with overridden core classes.
- public static void marshalTo(final RubyHash hash, final MarshalStream output) throws IOException {
- output.registerLinkTarget(hash);
- output.writeInt(hash.size);
- try {
- hash.visitAll(new Visitor() {
- public void visit(IRubyObject key, IRubyObject value) {
- try {
- output.dumpObject(key);
- output.dumpObject(value);
- } catch (IOException e) {
- throw new VisitorIOException(e);
- }
- }
- });
- } catch (VisitorIOException e) {
- throw (IOException)e.getCause();
- }
-
- if (!hash.ifNone.isNil()) output.dumpObject(hash.ifNone);
- }
-
- public static RubyHash unmarshalFrom(UnmarshalStream input, boolean defaultValue) throws IOException {
- RubyHash result = newHash(input.getRuntime());
- input.registerLinkTarget(result);
- int size = input.unmarshalInt();
- ThreadContext context = input.getRuntime().getCurrentContext();
- for (int i = 0; i < size; i++) {
- result.op_aset(context, input.unmarshalObject(), input.unmarshalObject());
- }
- if (defaultValue) result.default_value_set(input.unmarshalObject());
- return result;
- }
-
- public Class getJavaClass() {
- return Map.class;
- }
-
- // Satisfy java.util.Set interface (for Java integration)
-
- public int size() {
- return size;
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public boolean containsKey(Object key) {
- return internalGet(JavaUtil.convertJavaToRuby(getRuntime(), key)) != null;
- }
-
- public boolean containsValue(Object value) {
- return hasValue(getRuntime().getCurrentContext(), JavaUtil.convertJavaToRuby(getRuntime(), value));
- }
-
- public Object get(Object key) {
- return JavaUtil.convertRubyToJava(internalGet(JavaUtil.convertJavaToRuby(getRuntime(), key)));
- }
-
- public Object put(Object key, Object value) {
- internalPut(JavaUtil.convertJavaToRuby(getRuntime(), key), JavaUtil.convertJavaToRuby(getRuntime(), value));
- return value;
- }
-
- public Object remove(Object key) {
- IRubyObject rubyKey = JavaUtil.convertJavaToRuby(getRuntime(), key);
- return internalDelete(rubyKey).value;
- }
-
- public void putAll(Map map) {
- Ruby runtime = getRuntime();
- for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
- Object key = iter.next();
- internalPut(JavaUtil.convertJavaToRuby(runtime, key), JavaUtil.convertJavaToRuby(runtime, map.get(key)));
- }
- }
-
- public void clear() {
- rb_clear();
- }
-
- public boolean equals(Object other) {
- if (!(other instanceof RubyHash)) return false;
- if (this == other) return true;
- return op_equal(getRuntime().getCurrentContext(), (RubyHash)other).isTrue() ? true : false;
- }
-
- public Set keySet() {
- return new BaseSet(KEY_VIEW);
- }
-
- public Set directKeySet() {
- return new BaseSet(DIRECT_KEY_VIEW);
- }
-
- public Collection values() {
- return new BaseCollection(VALUE_VIEW);
- }
-
- public Collection directValues() {
- return new BaseCollection(DIRECT_VALUE_VIEW);
- }
-
- public Set entrySet() {
- return new BaseSet(ENTRY_VIEW);
- }
-
- public Set directEntrySet() {
- return new BaseSet(DIRECT_ENTRY_VIEW);
- }
-
- private class BaseSet extends AbstractSet {
- final EntryView view;
-
- public BaseSet(EntryView view) {
- this.view = view;
- }
-
- public Iterator iterator() {
- return new BaseIterator(view);
- }
-
- public boolean contains(Object o) {
- return view.contains(RubyHash.this, o);
- }
-
- public void clear() {
- RubyHash.this.clear();
- }
-
- public int size() {
- return RubyHash.this.size;
- }
-
- public boolean remove(Object o) {
- return view.remove(RubyHash.this, o);
- }
- }
-
- private class BaseCollection extends AbstractCollection {
- final EntryView view;
-
- public BaseCollection(EntryView view) {
- this.view = view;
- }
-
- public Iterator iterator() {
- return new BaseIterator(view);
- }
-
- public boolean contains(Object o) {
- return view.contains(RubyHash.this, o);
- }
-
- public void clear() {
- RubyHash.this.clear();
- }
-
- public int size() {
- return RubyHash.this.size;
- }
-
- public boolean remove(Object o) {
- return view.remove(RubyHash.this, o);
- }
- }
-
- private class BaseIterator implements Iterator {
- final private EntryView view;
- private RubyHashEntry entry;
- private boolean peeking;
- private int startGeneration;
-
- public BaseIterator(EntryView view) {
- this.view = view;
- this.entry = head;
- this.startGeneration = generation;
- }
-
- private void advance(boolean consume) {
- if (!peeking) {
- do {
- if (startGeneration != generation) {
- startGeneration = generation;
- entry = head;
- }
- entry = entry.nextAdded;
- } while (entry != head && !entry.isLive());
- }
- peeking = !consume;
- }
-
- public Object next() {
- advance(true);
- if (entry == head) {
- peeking = true; // remain where we are
- throw new NoSuchElementException();
- }
- return view.convertEntry(getRuntime(), entry);
- }
-
- // once hasNext has been called, we commit to next() returning
- // the entry it found, even if it were subsequently deleted
- public boolean hasNext() {
- advance(false);
- return entry != head;
- }
-
- public void remove() {
- if (entry == head) {
- throw new IllegalStateException("Iterator out of range");
- }
- internalDeleteEntry(entry);
- }
- }
-
- private static abstract class EntryView {
- public abstract Object convertEntry(Ruby runtime, RubyHashEntry value);
- public abstract boolean contains(RubyHash hash, Object o);
- public abstract boolean remove(RubyHash hash, Object o);
- }
-
- private static final EntryView DIRECT_KEY_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return entry.key;
- }
- public boolean contains(RubyHash hash, Object o) {
- if (!(o instanceof IRubyObject)) return false;
- return hash.internalGet((IRubyObject)o) != null;
- }
- public boolean remove(RubyHash hash, Object o) {
- if (!(o instanceof IRubyObject)) return false;
- return hash.internalDelete((IRubyObject)o) != NO_ENTRY;
- }
- };
-
- private static final EntryView KEY_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return JavaUtil.convertRubyToJava(entry.key, Object.class);
- }
- public boolean contains(RubyHash hash, Object o) {
- return hash.containsKey(o);
- }
- public boolean remove(RubyHash hash, Object o) {
- return hash.remove(o) != null;
- }
- };
-
- private static final EntryView DIRECT_VALUE_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return entry.value;
- }
- public boolean contains(RubyHash hash, Object o) {
- if (!(o instanceof IRubyObject)) return false;
- IRubyObject obj = (IRubyObject)o;
- return hash.hasValue(obj.getRuntime().getCurrentContext(), obj);
- }
- public boolean remove(RubyHash hash, Object o) {
- if (!(o instanceof IRubyObject)) return false;
- IRubyObject obj = (IRubyObject) o;
- IRubyObject key = hash.internalIndex(obj.getRuntime().getCurrentContext(), obj);
- if (key == null) return false;
- return hash.internalDelete(key) != NO_ENTRY;
- }
- };
-
- private final EntryView VALUE_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return JavaUtil.convertRubyToJava(entry.value, Object.class);
- }
- public boolean contains(RubyHash hash, Object o) {
- return hash.containsValue(o);
- }
- public boolean remove(RubyHash hash, Object o) {
- IRubyObject value = JavaUtil.convertJavaToRuby(hash.getRuntime(), o);
- IRubyObject key = hash.internalIndex(hash.getRuntime().getCurrentContext(), value);
- if (key == null) return false;
- return hash.internalDelete(key) != NO_ENTRY;
- }
- };
-
- private final EntryView DIRECT_ENTRY_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return entry;
- }
- public boolean contains(RubyHash hash, Object o) {
- if (!(o instanceof RubyHashEntry)) return false;
- RubyHashEntry entry = (RubyHashEntry)o;
- RubyHashEntry candidate = internalGetEntry(entry.key);
- return candidate != NO_ENTRY && entry.equals(candidate);
- }
- public boolean remove(RubyHash hash, Object o) {
- if (!(o instanceof RubyHashEntry)) return false;
- return hash.internalDeleteEntry((RubyHashEntry)o) != NO_ENTRY;
- }
- };
-
- private final EntryView ENTRY_VIEW = new EntryView() {
- public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
- return new ConvertingEntry(runtime, entry);
- }
- public boolean contains(RubyHash hash, Object o) {
- if (!(o instanceof ConvertingEntry)) return false;
- ConvertingEntry entry = (ConvertingEntry)o;
- RubyHashEntry candidate = hash.internalGetEntry(entry.entry.key);
- return candidate != NO_ENTRY && entry.entry.equals(candidate);
- }
- public boolean remove(RubyHash hash, Object o) {
- if (!(o instanceof ConvertingEntry)) return false;
- ConvertingEntry entry = (ConvertingEntry)o;
- return hash.internalDeleteEntry(entry.entry) != NO_ENTRY;
- }
- };
-
- private static class ConvertingEntry implements Map.Entry {
- private final RubyHashEntry entry;
- private final Ruby runtime;
-
- public ConvertingEntry(Ruby runtime, RubyHashEntry entry) {
- this.entry = entry;
- this.runtime = runtime;
- }
-
- public Object getKey() {
- return JavaUtil.convertRubyToJava(entry.key, Object.class);
- }
- public Object getValue() {
- return JavaUtil.convertRubyToJava(entry.value, Object.class);
- }
- public Object setValue(Object o) {
- return entry.setValue(JavaUtil.convertJavaToRuby(runtime, o));
- }
-
- public boolean equals(Object o) {
- if (!(o instanceof ConvertingEntry)) {
- return false;
- }
- ConvertingEntry other = (ConvertingEntry)o;
- return entry.equals(other.entry);
- }
- public int hashCode() {
- return entry.hashCode();
- }
- }
-}
-/***** 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 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2007 Koichiro Ohba <koichiro@meadowy.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 java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CodingErrorAction;
-import java.nio.charset.IllegalCharsetNameException;
-import java.nio.charset.MalformedInputException;
-import java.nio.charset.UnmappableCharacterException;
-import java.nio.charset.UnsupportedCharsetException;
-
-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.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-import org.jruby.util.ByteList;
-
-@JRubyClass(name="Iconv")
-public class RubyIconv extends RubyObject {
- //static private final String TRANSLIT = "//translit";
- static private final String IGNORE = "//ignore";
-
- private CharsetDecoder fromEncoding;
- private CharsetEncoder toEncoding;
-
- public RubyIconv(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private static final ObjectAllocator ICONV_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyIconv(runtime, klass);
- }
- };
-
- @JRubyModule(name="Iconv::Failure")
- public static class Failure {}
- @JRubyClass(name="Iconv::IllegalSequence", parent="ArgumentError", include="Iconv::Failure")
- public static class IllegalSequence {}
- @JRubyClass(name="Iconv::InvalidCharacter", parent="ArgumentError", include="Iconv::Failure")
- public static class InvalidCharacter {}
- @JRubyClass(name="Iconv::InvalidEncoding", parent="ArgumentError", include="Iconv::Failure")
- public static class InvalidEncoding {}
- @JRubyClass(name="Iconv::OutOfRange", parent="ArgumentError", include="Iconv::Failure")
- public static class OutOfRange {}
- @JRubyClass(name="Iconv::BrokenLibrary", parent="ArgumentError", include="Iconv::Failure")
- public static class BrokenLibrary {}
-
- public static void createIconv(Ruby runtime) {
- RubyClass iconvClass = runtime.defineClass("Iconv", runtime.getObject(), ICONV_ALLOCATOR);
-
- iconvClass.defineAnnotatedMethods(RubyIconv.class);
-
- RubyModule failure = iconvClass.defineModuleUnder("Failure");
- RubyClass argumentError = runtime.getArgumentError();
-
- String[] iconvErrors = {"IllegalSequence", "InvalidCharacter", "InvalidEncoding",
- "OutOfRange", "BrokenLibrary"};
-
- for (int i = 0; i < iconvErrors.length; i++) {
- RubyClass subClass = iconvClass.defineClassUnder(iconvErrors[i], argumentError, RubyFailure.ICONV_FAILURE_ALLOCATOR);
- subClass.defineAnnotatedMethods(RubyFailure.class);
- subClass.includeModule(failure);
- }
- }
-
- public static class RubyFailure extends RubyException {
- private IRubyObject success;
- private IRubyObject failed;
-
- public static RubyFailure newInstance(Ruby runtime, RubyClass excptnClass, String msg) {
- return new RubyFailure(runtime, excptnClass, msg);
- }
-
- protected static final ObjectAllocator ICONV_FAILURE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyFailure(runtime, klass);
- }
- };
-
- protected RubyFailure(Ruby runtime, RubyClass rubyClass) {
- this(runtime, rubyClass, null);
- }
-
- public RubyFailure(Ruby runtime, RubyClass rubyClass, String message) {
- super(runtime, rubyClass, message);
- }
-
- @JRubyMethod(name = "initialize", required = 1, optional = 2, frame = true)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- super.initialize(args, block);
- success = args.length >= 2 ? args[1] : getRuntime().getNil();
- failed = args.length == 3 ? args[2] : getRuntime().getNil();
-
- return this;
- }
-
- @JRubyMethod(name = "success")
- public IRubyObject success() {
- return success;
- }
-
- @JRubyMethod(name = "failed")
- public IRubyObject failed() {
- return failed;
- }
-
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- RubyModule rubyClass = getMetaClass();
- StringBuilder buffer = new StringBuilder("#<");
- buffer.append(rubyClass.getName()).append(": ").append(success.inspect().toString());
- buffer.append(", ").append(failed.inspect().toString()).append(">");
-
- return getRuntime().newString(buffer.toString());
- }
- }
-
- private static String getCharset(String encoding) {
- int index = encoding.indexOf("//");
- if (index == -1) return encoding;
- return encoding.substring(0, index);
- }
-
- /* Currently dead code, but useful when we figure out how to actually perform translit.
- private static boolean isTranslit(String encoding) {
- return encoding.toLowerCase().indexOf(TRANSLIT) != -1 ? true : false;
- }*/
-
- private static boolean isIgnore(String encoding) {
- return encoding.toLowerCase().indexOf(IGNORE) != -1 ? true : false;
- }
-
- @JRubyMethod(name = "open", required = 2, frame = true, meta = true)
- public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject to, IRubyObject from, Block block) {
- Ruby runtime = context.getRuntime();
-
- RubyIconv iconv = newIconv(context, recv, to, from);
-
- if (!block.isGiven()) return iconv;
-
- IRubyObject result = runtime.getNil();
- try {
- result = block.yield(context, iconv);
- } finally {
- iconv.close();
- }
-
- return result;
- }
-
- private static RubyIconv newIconv(ThreadContext context, IRubyObject recv,
- IRubyObject to, IRubyObject from) {
- RubyClass klazz = (RubyClass)recv;
-
- return (RubyIconv) klazz.newInstance(
- context, new IRubyObject[] {to, from}, Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "initialize", required = 2, frame = true)
- public IRubyObject initialize(IRubyObject arg1, IRubyObject arg2, Block unusedBlock) {
- Ruby runtime = getRuntime();
- if (!arg1.respondsTo("to_str")) {
- throw runtime.newTypeError("can't convert " + arg1.getMetaClass() + " into String");
- }
- if (!arg2.respondsTo("to_str")) {
- throw runtime.newTypeError("can't convert " + arg2.getMetaClass() + " into String");
- }
-
- String to = arg1.convertToString().toString();
- String from = arg2.convertToString().toString();
-
- try {
-
- fromEncoding = Charset.forName(getCharset(from)).newDecoder();
- toEncoding = Charset.forName(getCharset(to)).newEncoder();
-
- if (!isIgnore(from)) fromEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
- if (!isIgnore(to)) toEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
- } catch (IllegalCharsetNameException e) {
- throw runtime.newInvalidEncoding("invalid encoding");
- } catch (UnsupportedCharsetException e) {
- throw runtime.newInvalidEncoding("invalid encoding");
- } catch (Exception e) {
- throw runtime.newSystemCallError(e.toString());
- }
-
- return this;
- }
-
- @JRubyMethod(name = "close")
- public IRubyObject close() {
- toEncoding = null;
- fromEncoding = null;
- return RubyString.newEmptyString(getRuntime());
- }
-
- @JRubyMethod
- public IRubyObject iconv(IRubyObject str) {
- return iconv(str, 0, -1);
- }
-
- @JRubyMethod
- public IRubyObject iconv(IRubyObject str, IRubyObject startArg) {
- int start = 0;
- if (!startArg.isNil()) start = RubyNumeric.fix2int(startArg);
- return iconv(str, start, -1);
- }
-
- @JRubyMethod
- public IRubyObject iconv(IRubyObject str, IRubyObject startArg, IRubyObject endArg) {
- int start = 0;
- int end = -1;
-
- if (!startArg.isNil()) start = RubyNumeric.fix2int(startArg);
- if (!endArg.isNil()) end = RubyNumeric.fix2int(endArg);
-
- return iconv(str, start, end);
- }
-
- private IRubyObject iconv(IRubyObject str, int start, int end) {
- if (str.isNil()) {
- fromEncoding.reset();
- toEncoding.reset();
- return RubyString.newEmptyString(getRuntime());
- }
-
- return _iconv(str.convertToString(), start, end);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one, two or three arguments.
- */
- public IRubyObject iconv(IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return iconv(args[0]);
- case 2:
- return iconv(args[0], args[1]);
- case 3:
- return iconv(args[0], args[1], args[2]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- // FIXME: We are assuming that original string will be raw bytes. If -Ku is provided
- // this will not be true, but that is ok for now. Deal with that when someone needs it.
- private IRubyObject _iconv(RubyString str, int start, int end) {
- if (fromEncoding == null) {
- throw getRuntime().newArgumentError("closed iconv");
- }
-
- ByteList bytes = str.getByteList();
-
- // treat start and end as start...end for end >= 0, start..end for end < 0
- if (start < 0) {
- start += bytes.length();
- }
-
- if (end < 0) {
- end += 1 + bytes.length();
- } else if (end > bytes.length()) {
- end = bytes.length();
- }
-
- if (start < 0 || end < start) { // invalid ranges result in an empty string
- return RubyString.newEmptyString(getRuntime());
- }
-
- ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin() + start, end - start);
-
- try {
- CharBuffer cbuf = fromEncoding.decode(buf);
- buf = toEncoding.encode(cbuf);
- } catch (MalformedInputException e) {
- } catch (UnmappableCharacterException e) {
- } catch (CharacterCodingException e) {
- throw getRuntime().newInvalidEncoding("invalid sequence");
- } catch (IllegalStateException e) {
- }
- byte[] arr = buf.array();
-
- return getRuntime().newString(new ByteList(arr, 0, buf.limit()));
- }
-
- @JRubyMethod(name = "iconv", required = 2, rest = true, meta = true)
- public static IRubyObject iconv(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- return convertWithArgs(context, recv, args, "iconv");
- }
-
- @JRubyMethod(name = "conv", required = 3, rest = true, meta = true)
- public static IRubyObject conv(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- return convertWithArgs(context, recv, args, "conv").join(context, RubyString.newEmptyString(recv.getRuntime()));
- }
-
- @JRubyMethod(name = "charset_map", meta= true)
- public static IRubyObject charset_map_get(IRubyObject recv) {
- return recv.getRuntime().getCharsetMap();
- }
-
- private static String mapCharset(ThreadContext context, IRubyObject val) {
- RubyHash charset = val.getRuntime().getCharsetMap();
- if (charset.size() > 0) {
- RubyString key = val.callMethod(context, "downcase").convertToString();
- IRubyObject tryVal = charset.fastARef(key);
- if (tryVal != null) val = tryVal;
- }
-
- return val.convertToString().toString();
- }
-
- public static RubyArray convertWithArgs(ThreadContext context, IRubyObject recv, IRubyObject[] args, String function) {
- assert args.length >= 2;
-
- RubyArray array = context.getRuntime().newArray(args.length - 2);
- RubyIconv iconv = newIconv(context, recv, args[0], args[1]);
-
- try {
- for (int i = 2; i < args.length; i++) {
- array.append(iconv.iconv(args[i]));
- }
- } finally {
- iconv.close();
- }
-
- return array;
- }
-
- /*
- private static IRubyObject convert(String fromEncoding, String toEncoding, RubyString original)
- throws UnsupportedEncodingException {
- // Get all bytes from PLAIN string pretend they are not encoded in any way.
- byte[] string = original.getBytes();
- // Now create a string pretending it is from fromEncoding
- string = new String(string, fromEncoding).getBytes(toEncoding);
- // Finally recode back to PLAIN
- return RubyString.newString(original.getRuntime(), string);
- }
- */
-}
-/***** 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 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.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-
-import java.util.Set;
-import java.util.StringTokenizer;
-import org.jruby.ast.executable.Script;
-import org.jruby.exceptions.MainExitException;
-import org.jruby.runtime.Constants;
-import org.jruby.runtime.load.LoadService;
-import org.jruby.util.ClassCache;
-import org.jruby.util.JRubyFile;
-import org.jruby.util.KCode;
-import org.jruby.util.NormalizedFile;
-import org.jruby.util.SafePropertyAccessor;
-import org.objectweb.asm.Opcodes;
-
-public class RubyInstanceConfig {
-
- /**
- * The max count of active methods eligible for JIT-compilation.
- */
- private static final int JIT_MAX_METHODS_LIMIT = 4096;
-
- /**
- * The max size of JIT-compiled methods (full class size) allowed.
- */
- private static final int JIT_MAX_SIZE_LIMIT = Integer.MAX_VALUE;
-
- /**
- * The JIT threshold to the specified method invocation count.
- */
- private static final int JIT_THRESHOLD = 50;
-
- /** The version to use for generated classes. Set to current JVM version by default */
- public static final int JAVA_VERSION;
-
- /**
- * Default size for chained compilation.
- */
- private static final int CHAINED_COMPILE_LINE_COUNT_DEFAULT = 500;
-
- /**
- * The number of lines at which a method, class, or block body is split into
- * chained methods (to dodge 64k method-size limit in JVM).
- */
- public static final int CHAINED_COMPILE_LINE_COUNT
- = SafePropertyAccessor.getInt("jruby.compile.chainsize", CHAINED_COMPILE_LINE_COUNT_DEFAULT);
-
- public enum CompileMode {
- JIT, FORCE, OFF;
-
- public boolean shouldPrecompileCLI() {
- switch (this) {
- case JIT: case FORCE:
- return true;
- }
- return false;
- }
-
- public boolean shouldJIT() {
- switch (this) {
- case JIT: case FORCE:
- return true;
- }
- return false;
- }
-
- public boolean shouldPrecompileAll() {
- return this == FORCE;
- }
- }
- private InputStream input = System.in;
- private PrintStream output = System.out;
- private PrintStream error = System.err;
- private Profile profile = Profile.DEFAULT;
- private boolean objectSpaceEnabled
- = SafePropertyAccessor.getBoolean("jruby.objectspace.enabled", false);
-
- private CompileMode compileMode = CompileMode.JIT;
- private boolean runRubyInProcess = true;
- private String currentDirectory;
- private Map environment;
- private String[] argv = {};
-
- private final boolean jitLogging;
- private final boolean jitLoggingVerbose;
- private final int jitLogEvery;
- private final int jitThreshold;
- private final int jitMax;
- private final int jitMaxSize;
- private final boolean samplingEnabled;
- private CompatVersion compatVersion;
-
- private ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
- private ClassLoader loader = contextLoader == null ? RubyInstanceConfig.class.getClassLoader() : contextLoader;
-
- private ClassCache<Script> classCache;
-
- // from CommandlineParser
- private List<String> loadPaths = new ArrayList<String>();
- private Set<String> excludedMethods = new HashSet<String>();
- private StringBuffer inlineScript = new StringBuffer();
- private boolean hasInlineScript = false;
- private String scriptFileName = null;
- private List<String> requiredLibraries = new ArrayList<String>();
- private boolean benchmarking = false;
- private boolean argvGlobalsOn = false;
- private boolean assumeLoop = false;
- private boolean assumePrinting = false;
- private Map optionGlobals = new HashMap();
- private boolean processLineEnds = false;
- private boolean split = false;
- // This property is a Boolean, to allow three values, so it can match MRI's nil, false and true
- private Boolean verbose = Boolean.FALSE;
- private boolean debug = false;
- private boolean showVersion = false;
- private boolean showBytecode = false;
- private boolean showCopyright = false;
- private boolean endOfArguments = false;
- private boolean shouldRunInterpreter = true;
- private boolean shouldPrintUsage = false;
- private boolean shouldPrintProperties=false;
- private boolean yarv = false;
- private boolean rubinius = false;
- private boolean yarvCompile = false;
- private KCode kcode = KCode.NONE;
- private String recordSeparator = "\n";
- private boolean shouldCheckSyntax = false;
- private String inputFieldSeparator = null;
- private boolean managementEnabled = true;
-
- private int safeLevel = 0;
-
- private String jrubyHome;
-
- public static final boolean FASTEST_COMPILE_ENABLED
- = SafePropertyAccessor.getBoolean("jruby.compile.fastest");
- public static final boolean BOXED_COMPILE_ENABLED
- = FASTEST_COMPILE_ENABLED
- || SafePropertyAccessor.getBoolean("jruby.compile.boxed");
- public static final boolean FASTOPS_COMPILE_ENABLED
- = FASTEST_COMPILE_ENABLED
- || SafePropertyAccessor.getBoolean("jruby.compile.fastops");
- public static final boolean FRAMELESS_COMPILE_ENABLED
- = FASTEST_COMPILE_ENABLED
- || SafePropertyAccessor.getBoolean("jruby.compile.frameless");
- public static final boolean POSITIONLESS_COMPILE_ENABLED
- = FASTEST_COMPILE_ENABLED
- || SafePropertyAccessor.getBoolean("jruby.compile.positionless");
- public static final boolean THREADLESS_COMPILE_ENABLED
- = FASTEST_COMPILE_ENABLED
- || SafePropertyAccessor.getBoolean("jruby.compile.threadless");
- public static final boolean LAZYHANDLES_COMPILE = SafePropertyAccessor.getBoolean("jruby.compile.lazyHandles", false);
- public static final boolean FORK_ENABLED
- = SafePropertyAccessor.getBoolean("jruby.fork.enabled");
- public static final boolean POOLING_ENABLED
- = SafePropertyAccessor.getBoolean("jruby.thread.pool.enabled");
- public static final int POOL_MAX
- = SafePropertyAccessor.getInt("jruby.thread.pool.max", Integer.MAX_VALUE);
- public static final int POOL_MIN
- = SafePropertyAccessor.getInt("jruby.thread.pool.min", 0);
- public static final int POOL_TTL
- = SafePropertyAccessor.getInt("jruby.thread.pool.ttl", 60);
-
- public static final boolean NATIVE_NET_PROTOCOL
- = SafePropertyAccessor.getBoolean("jruby.native.net.protocol", false);
-
- public static boolean FULL_TRACE_ENABLED
- = SafePropertyAccessor.getBoolean("jruby.debug.fullTrace", false);
-
- public static final String COMPILE_EXCLUDE
- = SafePropertyAccessor.getProperty("jruby.jit.exclude");
- public static boolean nativeEnabled = true;
-
-
- public static interface LoadServiceCreator {
- LoadService create(Ruby runtime);
-
- LoadServiceCreator DEFAULT = new LoadServiceCreator() {
- public LoadService create(Ruby runtime) {
- return new LoadService(runtime);
- }
- };
- }
-
- private LoadServiceCreator creator = LoadServiceCreator.DEFAULT;
-
-
- static {
- String specVersion = null;
- try {
- specVersion = System.getProperty("jruby.bytecode.version");
- if (specVersion == null) {
- specVersion = System.getProperty("java.specification.version");
- }
- if (System.getProperty("jruby.native.enabled") != null) {
- nativeEnabled = Boolean.getBoolean("jruby.native.enabled");
- }
- } catch (SecurityException se) {
- nativeEnabled = false;
- specVersion = "1.5";
- }
-
- if (specVersion.equals("1.5")) {
- JAVA_VERSION = Opcodes.V1_5;
- } else {
- JAVA_VERSION = Opcodes.V1_6;
- }
- }
-
- public int characterIndex = 0;
-
- public RubyInstanceConfig() {
- if (Ruby.isSecurityRestricted())
- currentDirectory = "/";
- else {
- currentDirectory = JRubyFile.getFileProperty("user.dir");
- }
-
- samplingEnabled = SafePropertyAccessor.getBoolean("jruby.sampling.enabled", false);
- String compatString = SafePropertyAccessor.getProperty("jruby.compat.version", "RUBY1_8");
- if (compatString.equalsIgnoreCase("RUBY1_8")) {
- compatVersion = CompatVersion.RUBY1_8;
- } else if (compatString.equalsIgnoreCase("RUBY1_9")) {
- compatVersion = CompatVersion.RUBY1_9;
- } else {
- System.err.println("Compatibility version `" + compatString + "' invalid; use RUBY1_8 or RUBY1_9. Using RUBY1_8.");
- compatVersion = CompatVersion.RUBY1_8;
- }
-
- if (Ruby.isSecurityRestricted()) {
- compileMode = CompileMode.OFF;
- jitLogging = false;
- jitLoggingVerbose = false;
- jitLogEvery = 0;
- jitThreshold = -1;
- jitMax = 0;
- jitMaxSize = -1;
- managementEnabled = false;
- } else {
- String threshold = SafePropertyAccessor.getProperty("jruby.jit.threshold");
- String max = SafePropertyAccessor.getProperty("jruby.jit.max");
- String maxSize = SafePropertyAccessor.getProperty("jruby.jit.maxsize");
-
- if (COMPILE_EXCLUDE != null) {
- String[] elements = COMPILE_EXCLUDE.split(",");
- for (String element : elements) excludedMethods.add(element);
- }
-
- managementEnabled = SafePropertyAccessor.getBoolean("jruby.management.enabled", true);
- runRubyInProcess = SafePropertyAccessor.getBoolean("jruby.launch.inproc", true);
- boolean jitProperty = SafePropertyAccessor.getProperty("jruby.jit.enabled") != null;
- if (jitProperty) {
- error.print("jruby.jit.enabled property is deprecated; use jruby.compile.mode=(OFF|JIT|FORCE) for -C, default, and +C flags");
- compileMode = SafePropertyAccessor.getBoolean("jruby.jit.enabled") ? CompileMode.JIT : CompileMode.OFF;
- } else {
- String jitModeProperty = SafePropertyAccessor.getProperty("jruby.compile.mode", "JIT");
-
- if (jitModeProperty.equals("OFF")) {
- compileMode = CompileMode.OFF;
- } else if (jitModeProperty.equals("JIT")) {
- compileMode = CompileMode.JIT;
- } else if (jitModeProperty.equals("FORCE")) {
- compileMode = CompileMode.FORCE;
- } else {
- error.print("jruby.compile.mode property must be OFF, JIT, FORCE, or unset; defaulting to JIT");
- compileMode = CompileMode.JIT;
- }
- }
- jitLogging = SafePropertyAccessor.getBoolean("jruby.jit.logging");
- jitLoggingVerbose = SafePropertyAccessor.getBoolean("jruby.jit.logging.verbose");
- String logEvery = SafePropertyAccessor.getProperty("jruby.jit.logEvery");
- jitLogEvery = logEvery == null ? 0 : Integer.parseInt(logEvery);
- jitThreshold = threshold == null ?
- JIT_THRESHOLD : Integer.parseInt(threshold);
- jitMax = max == null ?
- JIT_MAX_METHODS_LIMIT : Integer.parseInt(max);
- jitMaxSize = maxSize == null ?
- JIT_MAX_SIZE_LIMIT : Integer.parseInt(maxSize);
- }
-
- // default ClassCache using jitMax as a soft upper bound
- classCache = new ClassCache<Script>(loader, jitMax);
-
- if (FORK_ENABLED) {
- error.print("WARNING: fork is highly unlikely to be safe or stable on the JVM. Have fun!\n");
- }
- }
-
- public LoadServiceCreator getLoadServiceCreator() {
- return creator;
- }
-
- public void setLoadServiceCreator(LoadServiceCreator creator) {
- this.creator = creator;
- }
-
- public LoadService createLoadService(Ruby runtime) {
- return this.creator.create(runtime);
- }
-
- public String getBasicUsageHelp() {
- StringBuilder sb = new StringBuilder();
- sb
- .append("Usage: jruby [switches] [--] [programfile] [arguments]\n")
- .append(" -0[octal] specify record separator (\0, if no argument)\n")
- .append(" -a autosplit mode with -n or -p (splits $_ into $F)\n")
- .append(" -b benchmark mode, times the script execution\n")
- .append(" -c check syntax only\n")
- .append(" -Cdirectory cd to directory, before executing your script\n")
- .append(" -d set debugging flags (set $DEBUG to true)\n")
- .append(" -e 'command' one line of script. Several -e's allowed. Omit [programfile]\n")
- .append(" -Fpattern split() pattern for autosplit (-a)\n")
- //.append(" -i[extension] edit ARGV files in place (make backup if extension supplied)\n")
- .append(" -Idirectory specify $LOAD_PATH directory (may be used more than once)\n")
- .append(" -J[java option] pass an option on to the JVM (e.g. -J-Xmx512m)\n")
- .append(" use --properties to list JRuby properties\n")
- .append(" run 'java -help' for a list of other Java options\n")
- .append(" -Kkcode specifies code-set (e.g. -Ku for Unicode\n")
- .append(" -l enable line ending processing\n")
- .append(" -n assume 'while gets(); ... end' loop around your script\n")
- .append(" -p assume loop like -n but print line also like sed\n")
- .append(" -rlibrary require the library, before executing your script\n")
- .append(" -s enable some switch parsing for switches after script name\n")
- .append(" -S look for the script in bin or using PATH environment variable\n")
- .append(" -T[level] turn on tainting checks\n")
- .append(" -v print version number, then turn on verbose mode\n")
- .append(" -w turn warnings on for your script\n")
- .append(" -W[level] set warning level; 0=silence, 1=medium, 2=verbose (default)\n")
- //.append(" -x[directory] strip off text before #!ruby line and perhaps cd to directory\n")
- .append(" -X[option] enable extended option (omit option to list)\n")
- .append(" --copyright print the copyright\n")
- .append(" --debug sets the execution mode most suitable for debugger functionality\n")
- .append(" --jdb runs JRuby process under JDB\n")
- .append(" --properties List all configuration Java properties (pass -J-Dproperty=value)\n")
- .append(" --sample run with profiling using the JVM's sampling profiler\n")
- .append(" --client use the non-optimizing \"client\" JVM (improves startup; default)\n")
- .append(" --server use the optimizing \"server\" JVM (improves perf)\n")
- .append(" --manage enable remote JMX management and monitoring of the VM and JRuby\n")
- .append(" --1.8 specify Ruby 1.8.x compatibility (default)\n")
- .append(" --1.9 specify Ruby 1.9.x compatibility\n")
- .append(" --bytecode show the JVM bytecode produced by compiling specified code\n")
- .append(" --version print the version\n");
-
- return sb.toString();
- }
-
- public String getExtendedHelp() {
- StringBuilder sb = new StringBuilder();
- sb
- .append("These flags are for extended JRuby options.\n")
- .append("Specify them by passing -X<option>\n")
- .append(" -O run with ObjectSpace disabled (default; improves performance)\n")
- .append(" +O run with ObjectSpace enabled (reduces performance)\n")
- .append(" -C disable all compilation\n")
- .append(" +C force compilation of all scripts before they are run (except eval)\n")
- .append(" -y read a YARV-compiled Ruby script and run that (EXPERIMENTAL)\n")
- .append(" -Y compile a Ruby script into YARV bytecodes and run this (EXPERIMENTAL)\n")
- .append(" -R read a Rubinius-compiled Ruby script and run that (EXPERIMENTAL)\n");
-
- return sb.toString();
- }
-
- public String getPropertyHelp() {
- StringBuilder sb = new StringBuilder();
- sb
- .append("These properties can be used to alter runtime behavior for perf or compatibility.\n")
- .append("Specify them by passing -J-D<property>=<value>\n")
- .append("\nCOMPILER SETTINGS:\n")
- .append(" jruby.compile.mode=JIT|FORCE|OFF\n")
- .append(" Set compilation mode. JIT is default; FORCE compiles all, OFF disables\n")
- .append(" jruby.compile.fastest=true|false\n")
- .append(" (EXPERIMENTAL) Turn on all experimental compiler optimizations\n")
- .append(" jruby.compile.boxed=true|false\n")
- .append(" (EXPERIMENTAL) Use boxed variables; this can speed up some methods. Default is false\n")
- .append(" jruby.compile.frameless=true|false\n")
- .append(" (EXPERIMENTAL) Turn on frameless compilation where possible\n")
- .append(" jruby.compile.positionless=true|false\n")
- .append(" (EXPERIMENTAL) Turn on compilation that avoids updating Ruby position info. Default is false\n")
- .append(" jruby.compile.threadless=true|false\n")
- .append(" (EXPERIMENTAL) Turn on compilation without polling for \"unsafe\" thread events. Default is false\n")
- .append(" jruby.compile.fastops=true|false\n")
- .append(" (EXPERIMENTAL) Turn on fast operators for Fixnum. Default is false\n")
- .append(" jruby.compile.chainsize=<line count>\n")
- .append(" Set the number of lines at which compiled bodies are \"chained\". Default is " + CHAINED_COMPILE_LINE_COUNT_DEFAULT + "\n")
- .append(" jruby.compile.lazyHandles=true|false\n")
- .append(" Generate method bindings (handles) for compiled methods lazily. Default is false.")
- .append("\nJIT SETTINGS:\n")
- .append(" jruby.jit.threshold=<invocation count>\n")
- .append(" Set the JIT threshold to the specified method invocation count. Default is " + JIT_THRESHOLD + ".\n")
- .append(" jruby.jit.max=<method count>\n")
- .append(" Set the max count of active methods eligible for JIT-compilation.\n")
- .append(" Default is " + JIT_MAX_METHODS_LIMIT + " per runtime. A value of 0 disables JIT, -1 disables max.\n")
- .append(" jruby.jit.maxsize=<jitted method size (full .class)>\n")
- .append(" Set the maximum full-class byte size allowed for jitted methods. Default is Integer.MAX_VALUE\n")
- .append(" jruby.jit.logging=true|false\n")
- .append(" Enable JIT logging (reports successful compilation). Default is false\n")
- .append(" jruby.jit.logging.verbose=true|false\n")
- .append(" Enable verbose JIT logging (reports failed compilation). Default is false\n")
- .append(" jruby.jit.logEvery=<method count>\n")
- .append(" Log a message every n methods JIT compiled. Default is 0 (off).\n")
- .append(" jruby.jit.exclude=<ClsOrMod,ClsOrMod::method_name,-::method_name>\n")
- .append(" Exclude methods from JIT by class/module short name, c/m::method_name,\n")
- .append(" or -::method_name for anon/singleton classes/modules. Comma-delimited.\n")
- .append("\nNATIVE SUPPORT:\n")
- .append(" jruby.native.enabled=true|false\n")
- .append(" Enable/disable native extensions (like JNA for non-Java APIs; Default is true\n")
- .append(" (This affects all JRuby instances in a given JVM)\n")
- .append(" jruby.native.verbose=true|false\n")
- .append(" Enable verbose logging of native extension loading. Default is false.\n")
- .append(" jruby.fork.enabled=true|false\n")
- .append(" (EXPERIMENTAL, maybe dangerous) Enable fork(2) on platforms that support it.\n")
- .append("\nTHREAD POOLING:\n")
- .append(" jruby.thread.pool.enabled=true|false\n")
- .append(" Enable reuse of native backing threads via a thread pool. Default is false.\n")
- .append(" jruby.thread.pool.min=<min thread count>\n")
- .append(" The minimum number of threads to keep alive in the pool. Default is 0.\n")
- .append(" jruby.thread.pool.max=<max thread count>\n")
- .append(" The maximum number of threads to allow in the pool. Default is unlimited.\n")
- .append(" jruby.thread.pool.ttl=<time to live, in seconds>\n")
- .append(" The maximum number of seconds to keep alive an idle thread. Default is 60.\n")
- .append("\nMISCELLANY:\n")
- .append(" jruby.compat.version=RUBY1_8|RUBY1_9\n")
- .append(" Specify the major Ruby version to be compatible with; Default is RUBY1_8\n")
- .append(" jruby.objectspace.enabled=true|false\n")
- .append(" Enable or disable ObjectSpace.each_object (default is disabled)\n")
- .append(" jruby.launch.inproc=true|false\n")
- .append(" Set in-process launching of e.g. system('ruby ...'). Default is true\n")
- .append(" jruby.bytecode.version=1.5|1.6\n")
- .append(" Set bytecode version for JRuby to generate. Default is current JVM version.\n")
- .append(" jruby.management.enabled=true|false\n")
- .append(" Set whether JMX management is enabled. Default is true.\n")
- .append(" jruby.debug.fullTrace=true|false\n")
- .append(" Set whether full traces are enabled (c-call/c-return). Default is false.\n");
-
- return sb.toString();
- }
-
- public String getVersionString() {
- String ver = Constants.RUBY_VERSION;
- switch (compatVersion) {
- case RUBY1_8:
- ver = Constants.RUBY_VERSION;
- break;
- case RUBY1_9:
- ver = Constants.RUBY1_9_VERSION;
- break;
- }
-
- String fullVersion = String.format(
- "jruby %s (ruby %s patchlevel %s) (%s rev %s) [%s-java]\n",
- Constants.VERSION, ver, Constants.RUBY_PATCHLEVEL,
- Constants.COMPILE_DATE, Constants.REVISION,
- SafePropertyAccessor.getProperty("os.arch", "unknown")
- );
-
- return fullVersion;
- }
-
- public String getCopyrightString() {
- return "JRuby - Copyright (C) 2001-2008 The JRuby Community (and contribs)\n";
- }
-
- public void processArguments(String[] arguments) {
- new ArgumentProcessor(arguments).processArguments();
- }
-
- public CompileMode getCompileMode() {
- return compileMode;
- }
-
- public void setCompileMode(CompileMode compileMode) {
- this.compileMode = compileMode;
- }
-
- public boolean isJitLogging() {
- return jitLogging;
- }
-
- public boolean isJitLoggingVerbose() {
- return jitLoggingVerbose;
- }
-
- public int getJitLogEvery() {
- return jitLogEvery;
- }
-
- public boolean isSamplingEnabled() {
- return samplingEnabled;
- }
-
- public int getJitThreshold() {
- return jitThreshold;
- }
-
- public int getJitMax() {
- return jitMax;
- }
-
- public int getJitMaxSize() {
- return jitMaxSize;
- }
-
- public boolean isRunRubyInProcess() {
- return runRubyInProcess;
- }
-
- public void setRunRubyInProcess(boolean flag) {
- this.runRubyInProcess = flag;
- }
-
- public void setInput(InputStream newInput) {
- input = newInput;
- }
-
- public InputStream getInput() {
- return input;
- }
-
- public CompatVersion getCompatVersion() {
- return compatVersion;
- }
-
- public void setOutput(PrintStream newOutput) {
- output = newOutput;
- }
-
- public PrintStream getOutput() {
- return output;
- }
-
- public void setError(PrintStream newError) {
- error = newError;
- }
-
- public PrintStream getError() {
- return error;
- }
-
- public void setCurrentDirectory(String newCurrentDirectory) {
- currentDirectory = newCurrentDirectory;
- }
-
- public String getCurrentDirectory() {
- return currentDirectory;
- }
-
- public void setProfile(Profile newProfile) {
- profile = newProfile;
- }
-
- public Profile getProfile() {
- return profile;
- }
-
- public void setObjectSpaceEnabled(boolean newObjectSpaceEnabled) {
- objectSpaceEnabled = newObjectSpaceEnabled;
- }
-
- public boolean isObjectSpaceEnabled() {
- return objectSpaceEnabled;
- }
-
- public void setEnvironment(Map newEnvironment) {
- environment = newEnvironment;
- }
-
- public Map getEnvironment() {
- return environment;
- }
-
- public ClassLoader getLoader() {
- return loader;
- }
-
- public void setLoader(ClassLoader loader) {
- // Setting the loader needs to reset the class cache
- if(this.loader != loader) {
- this.classCache = new ClassCache<Script>(loader, this.classCache.getMax());
- }
- this.loader = loader;
- }
-
- public String[] getArgv() {
- return argv;
- }
-
- public void setArgv(String[] argv) {
- this.argv = argv;
- }
-
- public String getJRubyHome() {
- if (jrubyHome == null) {
- if (Ruby.isSecurityRestricted()) {
- return "SECURITY RESTRICTED";
- }
- jrubyHome = verifyHome(SafePropertyAccessor.getProperty("jruby.home",
- SafePropertyAccessor.getProperty("user.home") + "/.jruby"));
-
- try {
- // This comment also in rbConfigLibrary
- // Our shell scripts pass in non-canonicalized paths, but even if we didn't
- // anyone who did would become unhappy because Ruby apps expect no relative
- // operators in the pathname (rubygems, for example).
- jrubyHome = new NormalizedFile(jrubyHome).getCanonicalPath();
- } catch (IOException e) { }
-
- jrubyHome = new NormalizedFile(jrubyHome).getAbsolutePath();
- }
- return jrubyHome;
- }
-
- public void setJRubyHome(String home) {
- jrubyHome = verifyHome(home);
- }
-
- // We require the home directory to be absolute
- private String verifyHome(String home) {
- if (home.equals(".")) {
- home = System.getProperty("user.dir");
- }
- if (!home.startsWith("file:")) {
- NormalizedFile f = new NormalizedFile(home);
- if (!f.isAbsolute()) {
- home = f.getAbsolutePath();
- }
- f.mkdirs();
- }
- return home;
- }
-
- private class ArgumentProcessor {
- private String[] arguments;
- private int argumentIndex = 0;
-
- public ArgumentProcessor(String[] arguments) {
- this.arguments = arguments;
- }
-
- public void processArguments() {
- while (argumentIndex < arguments.length && isInterpreterArgument(arguments[argumentIndex])) {
- processArgument();
- argumentIndex++;
- }
-
- if (!hasInlineScript && scriptFileName == null) {
- if (argumentIndex < arguments.length) {
- setScriptFileName(arguments[argumentIndex]); //consume the file name
- argumentIndex++;
- }
- }
-
- processArgv();
- }
-
- private void processArgv() {
- List<String> arglist = new ArrayList<String>();
- for (; argumentIndex < arguments.length; argumentIndex++) {
- String arg = arguments[argumentIndex];
- if (argvGlobalsOn && arg.startsWith("-")) {
- arg = arg.substring(1);
- if (arg.indexOf('=') > 0) {
- String[] keyvalue = arg.split("=", 2);
- optionGlobals.put(keyvalue[0], keyvalue[1]);
- } else {
- optionGlobals.put(arg, null);
- }
- } else {
- argvGlobalsOn = false;
- arglist.add(arg);
- }
- }
-
- // Remaining arguments are for the script itself
- argv = arglist.toArray(new String[arglist.size()]);
- }
-
- private boolean isInterpreterArgument(String argument) {
- return (argument.charAt(0) == '-' || argument.charAt(0) == '+') && !endOfArguments;
- }
-
- private String getArgumentError(String additionalError) {
- return "jruby: invalid argument\n" + additionalError + "\n";
- }
-
- private void processArgument() {
- String argument = arguments[argumentIndex];
- FOR : for (characterIndex = 1; characterIndex < argument.length(); characterIndex++) {
- switch (argument.charAt(characterIndex)) {
- case '0': {
- String temp = grabOptionalValue();
- if (null == temp) {
- recordSeparator = "\u0000";
- } else if (temp.equals("0")) {
- recordSeparator = "\n\n";
- } else if (temp.equals("777")) {
- recordSeparator = "\uFFFF"; // Specify something that can't separate
- } else {
- try {
- int val = Integer.parseInt(temp, 8);
- recordSeparator = "" + (char) val;
- } catch (Exception e) {
- MainExitException mee = new MainExitException(1, getArgumentError(" -0 must be followed by either 0, 777, or a valid octal value"));
- mee.setUsageError(true);
- throw mee;
- }
- }
- break FOR;
- }
- case 'a':
- split = true;
- break;
- case 'b':
- benchmarking = true;
- break;
- case 'c':
- shouldCheckSyntax = true;
- break;
- case 'C':
- try {
- String saved = grabValue(getArgumentError(" -C must be followed by a directory expression"));
- File base = new File(currentDirectory);
- File newDir = new File(saved);
- if (newDir.isAbsolute()) {
- currentDirectory = newDir.getCanonicalPath();
- } else {
- currentDirectory = new File(base, newDir.getPath()).getCanonicalPath();
- }
- if (!(new File(currentDirectory).isDirectory())) {
- MainExitException mee = new MainExitException(1, "jruby: Can't chdir to " + saved + " (fatal)");
- mee.setUsageError(true);
- throw mee;
- }
- } catch (IOException e) {
- MainExitException mee = new MainExitException(1, getArgumentError(" -C must be followed by a valid directory"));
- mee.setUsageError(true);
- throw mee;
- }
- break;
- case 'd':
- debug = true;
- verbose = Boolean.TRUE;
- break;
- case 'e':
- inlineScript.append(grabValue(getArgumentError(" -e must be followed by an expression to evaluate")));
- inlineScript.append('\n');
- hasInlineScript = true;
- break FOR;
- case 'F':
- inputFieldSeparator = grabValue(getArgumentError(" -F must be followed by a pattern for input field separation"));
- break;
- case 'h':
- shouldPrintUsage = true;
- shouldRunInterpreter = false;
- break;
- // FIXME: -i flag not supported
-// case 'i' :
-// break;
- case 'I':
- String s = grabValue(getArgumentError("-I must be followed by a directory name to add to lib path"));
- String[] ls = s.split(java.io.File.pathSeparator);
- for (int i = 0; i < ls.length; i++) {
- loadPaths.add(ls[i]);
- }
- break FOR;
- case 'K':
- // FIXME: No argument seems to work for -K in MRI plus this should not
- // siphon off additional args 'jruby -K ~/scripts/foo'. Also better error
- // processing.
- String eArg = grabValue(getArgumentError("provide a value for -K"));
- kcode = KCode.create(null, eArg);
- break;
- case 'l':
- processLineEnds = true;
- break;
- case 'n':
- assumeLoop = true;
- break;
- case 'p':
- assumePrinting = true;
- assumeLoop = true;
- break;
- case 'r':
- requiredLibraries.add(grabValue(getArgumentError("-r must be followed by a package to require")));
- break FOR;
- case 's' :
- argvGlobalsOn = true;
- break;
- case 'S':
- runBinScript();
- break FOR;
- case 'T' :{
- String temp = grabOptionalValue();
- int value = 1;
-
- if(temp!=null) {
- try {
- value = Integer.parseInt(temp, 8);
- } catch(Exception e) {
- value = 1;
- }
- }
-
- safeLevel = value;
-
- break FOR;
- }
- case 'v':
- verbose = Boolean.TRUE;
- setShowVersion(true);
- break;
- case 'w':
- verbose = Boolean.TRUE;
- break;
- case 'W': {
- String temp = grabOptionalValue();
- int value = 2;
- if (null != temp) {
- if (temp.equals("2")) {
- value = 2;
- } else if (temp.equals("1")) {
- value = 1;
- } else if (temp.equals("0")) {
- value = 0;
- } else {
- MainExitException mee = new MainExitException(1, getArgumentError(" -W must be followed by either 0, 1, 2 or nothing"));
- mee.setUsageError(true);
- throw mee;
- }
- }
- switch (value) {
- case 0:
- verbose = null;
- break;
- case 1:
- verbose = Boolean.FALSE;
- break;
- case 2:
- verbose = Boolean.TRUE;
- break;
- }
-
-
- break FOR;
- }
- // FIXME: -x flag not supported
-// case 'x' :
-// break;
- case 'X':
- String extendedOption = grabOptionalValue();
-
- if (extendedOption == null) {
- throw new MainExitException(0, "jruby: missing extended option, listing available options\n" + getExtendedHelp());
- } else if (extendedOption.equals("-O")) {
- objectSpaceEnabled = false;
- } else if (extendedOption.equals("+O")) {
- objectSpaceEnabled = true;
- } else if (extendedOption.equals("-C")) {
- compileMode = CompileMode.OFF;
- } else if (extendedOption.equals("+C")) {
- compileMode = CompileMode.FORCE;
- } else if (extendedOption.equals("-y")) {
- yarv = true;
- } else if (extendedOption.equals("-Y")) {
- yarvCompile = true;
- } else if (extendedOption.equals("-R")) {
- rubinius = true;
- } else {
- MainExitException mee =
- new MainExitException(1, "jruby: invalid extended option " + extendedOption + " (-X will list valid options)\n");
- mee.setUsageError(true);
-
- throw mee;
- }
- break FOR;
- case '-':
- if (argument.equals("--command") || argument.equals("--bin")) {
- characterIndex = argument.length();
- runBinScript();
- break;
- } else if (argument.equals("--compat")) {
- characterIndex = argument.length();
- compatVersion = CompatVersion.getVersionFromString(grabValue(getArgumentError("--compat must be RUBY1_8 or RUBY1_9")));
- if (compatVersion == null) {
- compatVersion = CompatVersion.RUBY1_8;
- }
- break FOR;
- } else if (argument.equals("--copyright")) {
- setShowCopyright(true);
- shouldRunInterpreter = false;
- break FOR;
- } else if (argument.equals("--debug")) {
- compileMode = CompileMode.OFF;
- FULL_TRACE_ENABLED = true;
- System.setProperty("jruby.reflection", "true");
- break FOR;
- } else if (argument.equals("--jdb")) {
- debug = true;
- verbose = Boolean.TRUE;
- break;
- } else if (argument.equals("--help")) {
- shouldPrintUsage = true;
- shouldRunInterpreter = false;
- break;
- } else if (argument.equals("--properties")) {
- shouldPrintProperties = true;
- shouldRunInterpreter = false;
- break;
- } else if (argument.equals("--version")) {
- setShowVersion(true);
- break FOR;
- } else if (argument.equals("--bytecode")) {
- setShowBytecode(true);
- break FOR;
- } else {
- if (argument.equals("--")) {
- // ruby interpreter compatibilty
- // Usage: ruby [switches] [--] [programfile] [arguments])
- endOfArguments = true;
- break;
- }
- }
- default:
- throw new MainExitException(1, "jruby: unknown option " + argument);
- }
- }
- }
-
- private void runBinScript() {
- String scriptName = grabValue("jruby: provide a bin script to execute");
- if (scriptName.equals("irb")) {
- scriptName = "jirb";
- }
-
- scriptFileName = scriptName;
-
- if (!new File(scriptFileName).exists()) {
- try {
- String jrubyHome = JRubyFile.create(System.getProperty("user.dir"), JRubyFile.getFileProperty("jruby.home")).getCanonicalPath();
- scriptFileName = JRubyFile.create(jrubyHome + JRubyFile.separator + "bin", scriptName).getCanonicalPath();
- } catch (IOException io) {
- MainExitException mee = new MainExitException(1, "jruby: Can't determine script filename");
- mee.setUsageError(true);
- throw mee;
- }
- }
-
- // route 'gem' through ruby code in case we're running out of the complete jar
- if (scriptName.equals("gem") || !new File(scriptFileName).exists()) {
- requiredLibraries.add("jruby/commands");
- inlineScript.append("JRuby::Commands." + scriptName);
- inlineScript.append("\n");
- hasInlineScript = true;
- }
- endOfArguments = true;
- }
-
- private String grabValue(String errorMessage) {
- characterIndex++;
- if (characterIndex < arguments[argumentIndex].length()) {
- return arguments[argumentIndex].substring(characterIndex);
- }
- argumentIndex++;
- if (argumentIndex < arguments.length) {
- return arguments[argumentIndex];
- }
-
- MainExitException mee = new MainExitException(1, errorMessage);
- mee.setUsageError(true);
-
- throw mee;
- }
-
- private String grabOptionalValue() {
- characterIndex++;
- if (characterIndex < arguments[argumentIndex].length()) {
- return arguments[argumentIndex].substring(characterIndex);
- }
- return null;
- }
- }
-
- public byte[] inlineScript() {
- return inlineScript.toString().getBytes();
- }
-
- public List<String> requiredLibraries() {
- return requiredLibraries;
- }
-
- public List<String> loadPaths() {
- return loadPaths;
- }
-
- public boolean shouldRunInterpreter() {
- if(isShowVersion() && (hasInlineScript || scriptFileName != null)) {
- return true;
- }
- return isShouldRunInterpreter();
- }
-
- public boolean shouldPrintUsage() {
- return shouldPrintUsage;
- }
-
- public boolean shouldPrintProperties() {
- return shouldPrintProperties;
- }
-
- private boolean isSourceFromStdin() {
- return getScriptFileName() == null;
- }
-
- public boolean isInlineScript() {
- return hasInlineScript;
- }
-
- public InputStream getScriptSource() {
- try {
- // KCode.NONE is used because KCODE does not affect parse in Ruby 1.8
- // if Ruby 2.0 encoding pragmas are implemented, this will need to change
- if (hasInlineScript) {
- return new ByteArrayInputStream(inlineScript());
- } else if (isSourceFromStdin()) {
- // can't use -v and stdin
- if (isShowVersion()) {
- return null;
- }
- return getInput();
- } else {
- File file = JRubyFile.create(getCurrentDirectory(), getScriptFileName());
- return new BufferedInputStream(new FileInputStream(file));
- }
- } catch (IOException e) {
- throw new MainExitException(1, "Error opening script file: " + e.getMessage());
- }
- }
-
- public String displayedFileName() {
- if (hasInlineScript) {
- if (scriptFileName != null) {
- return scriptFileName;
- } else {
- return "-e";
- }
- } else if (isSourceFromStdin()) {
- return "-";
- } else {
- return getScriptFileName();
- }
- }
-
- private void setScriptFileName(String scriptFileName) {
- this.scriptFileName = scriptFileName;
- }
-
- public String getScriptFileName() {
- return scriptFileName;
- }
-
- public boolean isBenchmarking() {
- return benchmarking;
- }
-
- public boolean isAssumeLoop() {
- return assumeLoop;
- }
-
- public boolean isAssumePrinting() {
- return assumePrinting;
- }
-
- public boolean isProcessLineEnds() {
- return processLineEnds;
- }
-
- public boolean isSplit() {
- return split;
- }
-
- public boolean isVerbose() {
- return verbose == Boolean.TRUE;
- }
-
- public Boolean getVerbose() {
- return verbose;
- }
-
- public boolean isDebug() {
- return debug;
- }
-
- public boolean isShowVersion() {
- return showVersion;
- }
-
- public boolean isShowBytecode() {
- return showBytecode;
- }
-
- public boolean isShowCopyright() {
- return showCopyright;
- }
-
- protected void setShowVersion(boolean showVersion) {
- this.showVersion = showVersion;
- }
-
- protected void setShowBytecode(boolean showBytecode) {
- this.showBytecode = showBytecode;
- }
-
- protected void setShowCopyright(boolean showCopyright) {
- this.showCopyright = showCopyright;
- }
-
- public boolean isShouldRunInterpreter() {
- return shouldRunInterpreter;
- }
-
- public boolean isShouldCheckSyntax() {
- return shouldCheckSyntax;
- }
-
- public boolean isYARVEnabled() {
- return yarv;
- }
-
- public String getInputFieldSeparator() {
- return inputFieldSeparator;
- }
-
- public boolean isRubiniusEnabled() {
- return rubinius;
- }
-
- public boolean isYARVCompileEnabled() {
- return yarvCompile;
- }
-
- public KCode getKCode() {
- return kcode;
- }
-
- public String getRecordSeparator() {
- return recordSeparator;
- }
-
- public int getSafeLevel() {
- return safeLevel;
- }
-
- public void setRecordSeparator(String recordSeparator) {
- this.recordSeparator = recordSeparator;
- }
-
- public ClassCache getClassCache() {
- return classCache;
- }
-
- public void setClassCache(ClassCache classCache) {
- this.classCache = classCache;
- }
-
- public Map getOptionGlobals() {
- return optionGlobals;
- }
-
- public boolean isManagementEnabled() {
- return managementEnabled;
- }
-
- public Set getExcludedMethods() {
- return excludedMethods;
- }
-
-}
-/*
- **** 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 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-2004 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.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.BlockBody;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.ByteList;
-
-/** Implementation of the Integer class.
- *
- * @author jpetersen
- */
-@JRubyClass(name="Integer", parent="Numeric", include="Precision")
-public abstract class RubyInteger extends RubyNumeric {
-
- public static RubyClass createIntegerClass(Ruby runtime) {
- RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
- ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setInteger(integer);
- integer.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyInteger;
- }
- };
-
- integer.getSingletonClass().undefineMethod("new");
-
- integer.includeModule(runtime.getPrecision());
-
- integer.defineAnnotatedMethods(RubyInteger.class);
-
- return integer;
- }
-
- public RubyInteger(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
- }
-
- public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
- super(runtime, rubyClass, useObjectSpace);
- }
-
- public RubyInteger convertToInteger() {
- return this;
- }
-
- // conversion
- protected RubyFloat toFloat() {
- return RubyFloat.newFloat(getRuntime(), getDoubleValue());
- }
-
- /* ================
- * Instance Methods
- * ================
- */
-
- /** int_int_p
- *
- */
- @JRubyMethod(name = "integer?")
- public IRubyObject integer_p() {
- return getRuntime().getTrue();
- }
-
- /** int_upto
- *
- */
- @JRubyMethod(name = "upto", frame = true)
- public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
- final Ruby runtime = getRuntime();
-
- if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
- RubyFixnum toFixnum = (RubyFixnum) to;
- final long toValue = toFixnum.getLongValue();
- final long fromValue = getLongValue();
-
- if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
- final IRubyObject nil = runtime.getNil();
- for (long i = fromValue; i <= toValue; i++) {
- block.yield(context, nil);
- }
- } else {
- for (long i = fromValue; i <= toValue; i++) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- }
- } else {
- RubyNumeric i = this;
-
- while (true) {
- if (i.callMethod(context, MethodIndex.OP_GT, ">", to).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
- }
- }
- return this;
- }
-
- /** int_downto
- *
- */
- // TODO: Make callCoerced work in block context...then fix downto, step, and upto.
- @JRubyMethod(name = "downto", frame = true)
- public IRubyObject downto(ThreadContext context, IRubyObject to, Block block) {
- final Ruby runtime = getRuntime();
-
- if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
- RubyFixnum toFixnum = (RubyFixnum) to;
- final long toValue = toFixnum.getLongValue();
- if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
- final IRubyObject nil = runtime.getNil();
- for (long i = getLongValue(); i >= toValue; i--) {
- block.yield(context, nil);
- }
- } else {
- for (long i = getLongValue(); i >= toValue; i--) {
- block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
- }
- }
- } else {
- RubyNumeric i = this;
-
- while (true) {
- if (i.callMethod(context, MethodIndex.OP_LT, "<", to).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_MINUS, "-", RubyFixnum.one(getRuntime()));
- }
- }
- return this;
- }
-
- @JRubyMethod(name = "times", frame = true)
- public IRubyObject times(ThreadContext context, Block block) {
- final Ruby runtime = context.getRuntime();
-
- if (this instanceof RubyFixnum) {
- final long value = getLongValue();
- if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
- final IRubyObject nil = runtime.getNil();
- for (long i = 0; i < value; i++) {
- block.yield(context, nil);
- }
- } else {
- for (long i = 0; i < value; i++) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- }
- } else {
- RubyNumeric i = RubyFixnum.zero(runtime);
- while (true) {
- if (!i.callMethod(context, MethodIndex.OP_LT, "<", this).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
- }
- }
-
- return this;
- }
-
- /** int_succ
- *
- */
- @JRubyMethod(name = {"succ", "next"})
- public IRubyObject succ(ThreadContext context) {
- if (this instanceof RubyFixnum) {
- return RubyFixnum.newFixnum(getRuntime(), getLongValue() + 1L);
- } else {
- return callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(getRuntime()));
- }
- }
-
- /** int_chr
- *
- */
- @JRubyMethod(name = "chr")
- public RubyString chr() {
- if (getLongValue() < 0 || getLongValue() > 0xff) {
- throw getRuntime().newRangeError(this.toString() + " out of char range");
- }
- return RubyString.newString(getRuntime(), new ByteList(new byte[]{(byte)getLongValue()}, false));
- }
-
- /** int_to_i
- *
- */
- @JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "round", "truncate"})
- public RubyInteger to_i() {
- return this;
- }
-
- /** integer_to_r
- *
- */
- @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
- public IRubyObject to_r(ThreadContext context) {
- return RubyRational.newRationalCanonicalize(context, this);
- }
-
-
- @JRubyMethod(name = {"odd?"})
- public static RubyBoolean odd_p(ThreadContext context, IRubyObject recv) {
- if(recv.callMethod(context, "%", recv.getRuntime().newFixnum(2)) != RubyFixnum.zero(recv.getRuntime())) {
- return recv.getRuntime().getTrue();
- }
- return recv.getRuntime().getFalse();
- }
-
- @JRubyMethod(name = {"even?"})
- public static RubyBoolean even_p(ThreadContext context, IRubyObject recv) {
- if(recv.callMethod(context, "%", recv.getRuntime().newFixnum(2)) == RubyFixnum.zero(recv.getRuntime())) {
- return recv.getRuntime().getTrue();
- }
- return recv.getRuntime().getFalse();
- }
-
- @JRubyMethod
- public static IRubyObject pred(ThreadContext context, IRubyObject recv) {
- return recv.callMethod(context, "-", recv.getRuntime().newFixnum(1));
- }
-
-
- /* ================
- * Singleton Methods
- * ================
- */
-
- /** rb_int_induced_from
- *
- */
- @JRubyMethod(name = "induced_from", meta = true)
- public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
- if (other instanceof RubyFixnum || other instanceof RubyBignum) {
- return other;
- } else if (other instanceof RubyFloat || other instanceof RubyRational) {
- return other.callMethod(context, MethodIndex.TO_I, "to_i");
- } else {
- throw recv.getRuntime().newTypeError(
- "failed to convert " + other.getMetaClass().getName() + " into Integer");
- }
- }
-}
-/*
- **** 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) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2002-2006 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) 2006 Evan Buswell <ebuswell@gmail.com>
- * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@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.EOFException;
-import java.io.FileDescriptor;
-import java.io.FilterInputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
-import java.nio.channels.Channel;
-import java.nio.channels.Channels;
-import java.nio.channels.FileChannel;
-import java.nio.channels.Pipe;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import org.jruby.anno.FrameField;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.ext.posix.util.FieldAccess;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallType;
-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.util.ByteList;
-import org.jruby.util.io.Stream;
-import org.jruby.util.io.ModeFlags;
-import org.jruby.util.ShellLauncher;
-import org.jruby.util.TypeConverter;
-import org.jruby.util.io.BadDescriptorException;
-import org.jruby.util.io.ChannelStream;
-import org.jruby.util.io.InvalidValueException;
-import org.jruby.util.io.PipeException;
-import org.jruby.util.io.FileExistsException;
-import org.jruby.util.io.STDIO;
-import org.jruby.util.io.OpenFile;
-import org.jruby.util.io.ChannelDescriptor;
-
-import static org.jruby.CompatVersion.*;
-
-/**
- *
- * @author jpetersen
- */
-@JRubyClass(name="IO", include="Enumerable")
-public class RubyIO extends RubyObject {
- protected OpenFile openFile;
- protected List<RubyThread> blockingThreads;
-
- public void registerDescriptor(ChannelDescriptor descriptor) {
- getRuntime().getDescriptors().put(new Integer(descriptor.getFileno()), new WeakReference<ChannelDescriptor>(descriptor));
- }
-
- public void unregisterDescriptor(int aFileno) {
- getRuntime().getDescriptors().remove(new Integer(aFileno));
- }
-
- public ChannelDescriptor getDescriptorByFileno(int aFileno) {
- Reference<ChannelDescriptor> reference = getRuntime().getDescriptors().get(new Integer(aFileno));
- if (reference == null) {
- return null;
- }
- return reference.get();
- }
-
- // FIXME can't use static; would interfere with other runtimes in the same JVM
- protected static AtomicInteger filenoIndex = new AtomicInteger(2);
-
- public static int getNewFileno() {
- return filenoIndex.incrementAndGet();
- }
-
- // This should only be called by this and RubyFile.
- // It allows this object to be created without a IOHandler.
- public RubyIO(Ruby runtime, RubyClass type) {
- super(runtime, type);
-
- openFile = new OpenFile();
- }
-
- public RubyIO(Ruby runtime, OutputStream outputStream) {
- super(runtime, runtime.getIO());
-
- // We only want IO objects with valid streams (better to error now).
- if (outputStream == null) {
- throw runtime.newIOError("Opening invalid stream");
- }
-
- openFile = new OpenFile();
-
- try {
- openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(outputStream), getNewFileno(), new FileDescriptor())));
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- }
-
- openFile.setMode(OpenFile.WRITABLE | OpenFile.APPEND);
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- public RubyIO(Ruby runtime, InputStream inputStream) {
- super(runtime, runtime.getIO());
-
- if (inputStream == null) {
- throw runtime.newIOError("Opening invalid stream");
- }
-
- openFile = new OpenFile();
-
- try {
- openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(inputStream), getNewFileno(), new FileDescriptor())));
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- }
-
- openFile.setMode(OpenFile.READABLE);
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- public RubyIO(Ruby runtime, Channel channel) {
- super(runtime, runtime.getIO());
-
- // We only want IO objects with valid streams (better to error now).
- if (channel == null) {
- throw runtime.newIOError("Opening invalid stream");
- }
-
- openFile = new OpenFile();
-
- try {
- openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(channel, getNewFileno(), new FileDescriptor())));
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- }
-
- openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- public RubyIO(Ruby runtime, ShellLauncher.POpenProcess process, ModeFlags modes) {
- super(runtime, runtime.getIO());
-
- openFile = new OpenFile();
-
- openFile.setMode(modes.getOpenFileFlags() | OpenFile.SYNC);
- openFile.setProcess(process);
-
- try {
- if (openFile.isReadable()) {
- Channel inChannel;
- if (process.getInput() != null) {
- // NIO-based
- inChannel = process.getInput();
- } else {
- // Stream-based
- inChannel = Channels.newChannel(process.getInputStream());
- }
-
- ChannelDescriptor main = new ChannelDescriptor(
- inChannel,
- getNewFileno(),
- new FileDescriptor());
- main.setCanBeSeekable(false);
-
- openFile.setMainStream(new ChannelStream(getRuntime(), main));
- registerDescriptor(main);
- }
-
- if (openFile.isWritable()) {
- Channel outChannel;
- if (process.getOutput() != null) {
- // NIO-based
- outChannel = process.getOutput();
- } else {
- outChannel = Channels.newChannel(process.getOutputStream());
- }
-
- ChannelDescriptor pipe = new ChannelDescriptor(
- outChannel,
- getNewFileno(),
- new FileDescriptor());
- pipe.setCanBeSeekable(false);
-
- if (openFile.getMainStream() != null) {
- openFile.setPipeStream(new ChannelStream(getRuntime(), pipe));
- } else {
- openFile.setMainStream(new ChannelStream(getRuntime(), pipe));
- }
-
- registerDescriptor(pipe);
- }
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- }
- }
-
- public RubyIO(Ruby runtime, STDIO stdio) {
- super(runtime, runtime.getIO());
-
- openFile = new OpenFile();
-
- try {
- switch (stdio) {
- case IN:
- openFile.setMainStream(
- new ChannelStream(
- runtime,
- // special constructor that accepts stream, not channel
- new ChannelDescriptor(runtime.getIn(), 0, new ModeFlags(ModeFlags.RDONLY), FileDescriptor.in),
- FileDescriptor.in));
- break;
- case OUT:
- openFile.setMainStream(
- new ChannelStream(
- runtime,
- new ChannelDescriptor(Channels.newChannel(runtime.getOut()), 1, new ModeFlags(ModeFlags.WRONLY | ModeFlags.APPEND), FileDescriptor.out),
- FileDescriptor.out));
- openFile.getMainStream().setSync(true);
- break;
- case ERR:
- openFile.setMainStream(
- new ChannelStream(
- runtime,
- new ChannelDescriptor(Channels.newChannel(runtime.getErr()), 2, new ModeFlags(ModeFlags.WRONLY | ModeFlags.APPEND), FileDescriptor.err),
- FileDescriptor.err));
- openFile.getMainStream().setSync(true);
- break;
- }
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- }
-
- openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- }
-
- public static RubyIO newIO(Ruby runtime, Channel channel) {
- return new RubyIO(runtime, channel);
- }
-
- public OpenFile getOpenFile() {
- return openFile;
- }
-
- protected OpenFile getOpenFileChecked() {
- openFile.checkClosed(getRuntime());
- return openFile;
- }
-
- private static ObjectAllocator IO_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyIO(runtime, klass);
- }
- };
-
- public static RubyClass createIOClass(Ruby runtime) {
- RubyClass ioClass = runtime.defineClass("IO", runtime.getObject(), IO_ALLOCATOR);
- ioClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyIO;
- }
- };
-
- ioClass.includeModule(runtime.getEnumerable());
-
- // TODO: Implement tty? and isatty. We have no real capability to
- // determine this from java, but if we could set tty status, then
- // we could invoke jruby differently to allow stdin to return true
- // on this. This would allow things like cgi.rb to work properly.
-
- ioClass.defineAnnotatedMethods(RubyIO.class);
-
- // Constants for seek
- ioClass.fastSetConstant("SEEK_SET", runtime.newFixnum(Stream.SEEK_SET));
- ioClass.fastSetConstant("SEEK_CUR", runtime.newFixnum(Stream.SEEK_CUR));
- ioClass.fastSetConstant("SEEK_END", runtime.newFixnum(Stream.SEEK_END));
-
- return ioClass;
- }
-
- public OutputStream getOutStream() {
- return getOpenFileChecked().getMainStream().newOutputStream();
- }
-
- public InputStream getInStream() {
- return getOpenFileChecked().getMainStream().newInputStream();
- }
-
- public Channel getChannel() {
- if (getOpenFileChecked().getMainStream() instanceof ChannelStream) {
- return ((ChannelStream) openFile.getMainStream()).getDescriptor().getChannel();
- } else {
- return null;
- }
- }
-
- public Stream getHandler() {
- return getOpenFileChecked().getMainStream();
- }
-
- @JRubyMethod(name = "reopen", required = 1, optional = 1)
- public IRubyObject reopen(ThreadContext context, IRubyObject[] args) throws InvalidValueException {
- Ruby runtime = context.getRuntime();
-
- if (args.length < 1) {
- throw runtime.newArgumentError("wrong number of arguments");
- }
-
- IRubyObject tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getIO(),
- MethodIndex.getIndex("to_io"), "to_io");
-
- if (!tmp.isNil()) {
- try {
- RubyIO ios = (RubyIO) tmp;
-
- if (ios.openFile == this.openFile) {
- return this;
- }
-
- OpenFile originalFile = ios.getOpenFileChecked();
- OpenFile selfFile = getOpenFileChecked();
-
- long pos = 0;
- if (originalFile.isReadable()) {
- pos = originalFile.getMainStream().fgetpos();
- }
-
- if (originalFile.getPipeStream() != null) {
- originalFile.getPipeStream().fflush();
- } else if (originalFile.isWritable()) {
- originalFile.getMainStream().fflush();
- }
-
- if (selfFile.isWritable()) {
- selfFile.getWriteStream().fflush();
- }
-
- selfFile.setMode(originalFile.getMode());
- selfFile.setProcess(originalFile.getProcess());
- selfFile.setLineNumber(originalFile.getLineNumber());
- selfFile.setPath(originalFile.getPath());
- selfFile.setFinalizer(originalFile.getFinalizer());
-
- ChannelDescriptor selfDescriptor = selfFile.getMainStream().getDescriptor();
- ChannelDescriptor originalDescriptor = originalFile.getMainStream().getDescriptor();
-
- // confirm we're not reopening self's channel
- if (selfDescriptor.getChannel() != originalDescriptor.getChannel()) {
- // check if we're a stdio IO, and ensure we're not badly mutilated
- if (selfDescriptor.getFileno() >=0 && selfDescriptor.getFileno() <= 2) {
- selfFile.getMainStream().clearerr();
-
- // dup2 new fd into self to preserve fileno and references to it
- originalDescriptor.dup2Into(selfDescriptor);
-
- // re-register, since fileno points at something new now
- registerDescriptor(selfDescriptor);
- } else {
- Stream pipeFile = selfFile.getPipeStream();
- int mode = selfFile.getMode();
- selfFile.getMainStream().fclose();
- selfFile.setPipeStream(null);
-
- // TODO: turn off readable? am I reading this right?
- // This only seems to be used while duping below, since modes gets
- // reset to actual modes afterward
- //fptr->mode &= (m & FMODE_READABLE) ? ~FMODE_READABLE : ~FMODE_WRITABLE;
-
- if (pipeFile != null) {
- selfFile.setMainStream(ChannelStream.fdopen(runtime, originalDescriptor, new ModeFlags()));
- selfFile.setPipeStream(pipeFile);
- } else {
- selfFile.setMainStream(
- new ChannelStream(
- runtime,
- originalDescriptor.dup2(selfDescriptor.getFileno())));
-
- // re-register the descriptor
- registerDescriptor(selfFile.getMainStream().getDescriptor());
-
- // since we're not actually duping the incoming channel into our handler, we need to
- // copy the original sync behavior from the other handler
- selfFile.getMainStream().setSync(selfFile.getMainStream().isSync());
- }
- selfFile.setMode(mode);
- }
-
- // TODO: anything threads attached to original fd are notified of the close...
- // see rb_thread_fd_close
-
- if (originalFile.isReadable() && pos >= 0) {
- selfFile.seek(pos, Stream.SEEK_SET);
- originalFile.seek(pos, Stream.SEEK_SET);
- }
- }
-
- if (selfFile.getPipeStream() != null && selfDescriptor.getFileno() != selfFile.getPipeStream().getDescriptor().getFileno()) {
- int fd = selfFile.getPipeStream().getDescriptor().getFileno();
-
- if (originalFile.getPipeStream() == null) {
- selfFile.getPipeStream().fclose();
- selfFile.setPipeStream(null);
- } else if (fd != originalFile.getPipeStream().getDescriptor().getFileno()) {
- selfFile.getPipeStream().fclose();
- ChannelDescriptor newFD2 = originalFile.getPipeStream().getDescriptor().dup2(fd);
- selfFile.setPipeStream(ChannelStream.fdopen(runtime, newFD2, getIOModes(runtime, "w")));
-
- // re-register, since fileno points at something new now
- registerDescriptor(newFD2);
- }
- }
-
- // TODO: restore binary mode
- // if (fptr->mode & FMODE_BINMODE) {
- // rb_io_binmode(io);
- // }
-
- // TODO: set our metaclass to target's class (i.e. scary!)
-
- } catch (IOException ex) { // TODO: better error handling
- throw runtime.newIOError("could not reopen: " + ex.getMessage());
- } catch (BadDescriptorException ex) {
- throw runtime.newIOError("could not reopen: " + ex.getMessage());
- } catch (PipeException ex) {
- throw runtime.newIOError("could not reopen: " + ex.getMessage());
- }
- } else {
- IRubyObject pathString = args[0].convertToString();
-
- // TODO: check safe, taint on incoming string
-
- if (openFile == null) {
- openFile = new OpenFile();
- }
-
- try {
- ModeFlags modes;
- if (args.length > 1) {
- IRubyObject modeString = args[1].convertToString();
- modes = getIOModes(runtime, modeString.toString());
-
- openFile.setMode(modes.getOpenFileFlags());
- } else {
- modes = getIOModes(runtime, "r");
- }
-
- String path = pathString.toString();
-
- // Ruby code frequently uses a platform check to choose "NUL:" on windows
- // but since that check doesn't work well on JRuby, we help it out
-
- openFile.setPath(path);
-
- if (openFile.getMainStream() == null) {
- try {
- openFile.setMainStream(ChannelStream.fopen(runtime, path, modes));
- } catch (FileExistsException fee) {
- throw runtime.newErrnoEEXISTError(path);
- }
-
- registerDescriptor(openFile.getMainStream().getDescriptor());
- if (openFile.getPipeStream() != null) {
- openFile.getPipeStream().fclose();
- unregisterDescriptor(openFile.getPipeStream().getDescriptor().getFileno());
- openFile.setPipeStream(null);
- }
- return this;
- } else {
- // TODO: This is an freopen in MRI, this is close, but not quite the same
- openFile.getMainStream().freopen(path, getIOModes(runtime, openFile.getModeAsString(runtime)));
-
- // re-register
- registerDescriptor(openFile.getMainStream().getDescriptor());
-
- if (openFile.getPipeStream() != null) {
- // TODO: pipe handler to be reopened with path and "w" mode
- }
- }
- } catch (PipeException pe) {
- throw runtime.newErrnoEPIPEError();
- } catch (IOException ex) {
- throw runtime.newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw runtime.newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw runtime.newErrnoEINVALError();
- }
- }
-
- // A potentially previously close IO is being 'reopened'.
- return this;
- }
-
- public static ModeFlags getIOModes(Ruby runtime, String modesString) throws InvalidValueException {
- return new ModeFlags(getIOModesIntFromString(runtime, modesString));
- }
-
- public static int getIOModesIntFromString(Ruby runtime, String modesString) {
- int modes = 0;
- int length = modesString.length();
-
- if (length == 0) {
- throw runtime.newArgumentError("illegal access mode");
- }
-
- switch (modesString.charAt(0)) {
- case 'r' :
- modes |= ModeFlags.RDONLY;
- break;
- case 'a' :
- modes |= ModeFlags.APPEND | ModeFlags.WRONLY | ModeFlags.CREAT;
- break;
- case 'w' :
- modes |= ModeFlags.WRONLY | ModeFlags.TRUNC | ModeFlags.CREAT;
- break;
- default :
- throw runtime.newArgumentError("illegal access mode " + modes);
- }
-
- for (int n = 1; n < length; n++) {
- switch (modesString.charAt(n)) {
- case 'b':
- modes |= ModeFlags.BINARY;
- break;
- case '+':
- modes = (modes & ~ModeFlags.ACCMODE) | ModeFlags.RDWR;
- break;
- default:
- throw runtime.newArgumentError("illegal access mode " + modes);
- }
- }
-
- return modes;
- }
-
- private static ByteList getSeparatorFromArgs(Ruby runtime, IRubyObject[] args, int idx) {
- IRubyObject sepVal;
-
- if (args.length > idx) {
- sepVal = args[idx];
- } else {
- sepVal = runtime.getRecordSeparatorVar().get();
- }
-
- ByteList separator = sepVal.isNil() ? null : sepVal.convertToString().getByteList();
-
- if (separator != null && separator.realSize == 0) {
- separator = Stream.PARAGRAPH_DELIMETER;
- }
-
- return separator;
- }
-
- private ByteList getSeparatorForGets(Ruby runtime, IRubyObject[] args) {
- return getSeparatorFromArgs(runtime, args, 0);
- }
-
- public IRubyObject getline(Ruby runtime, ByteList separator) {
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkReadable(runtime);
- myOpenFile.setReadBuffered();
-
- boolean isParagraph = separator == Stream.PARAGRAPH_DELIMETER;
- separator = (separator == Stream.PARAGRAPH_DELIMETER) ?
- Stream.PARAGRAPH_SEPARATOR : separator;
-
- if (isParagraph) {
- swallow('\n');
- }
-
- if (separator == null) {
- IRubyObject str = readAll(null);
- if (((RubyString)str).getByteList().length() == 0) {
- return runtime.getNil();
- }
- incrementLineno(runtime, myOpenFile);
- return str;
- } else if (separator.length() == 1) {
- return getlineFast(runtime, separator.get(0));
- } else {
- Stream readStream = myOpenFile.getMainStream();
- int c = -1;
- int n = -1;
- int newline = separator.get(separator.length() - 1) & 0xFF;
-
- ByteList buf = new ByteList(0);
- boolean update = false;
-
- while (true) {
- do {
- readCheck(readStream);
- readStream.clearerr();
-
- try {
- n = readStream.getline(buf, (byte) newline);
- c = buf.length() > 0 ? buf.get(buf.length() - 1) & 0xff : -1;
- } catch (EOFException e) {
- n = -1;
- }
-
- if (n == -1) {
- if (!readStream.isBlocking() && (readStream instanceof ChannelStream)) {
- if(!(waitReadable(((ChannelStream)readStream).getDescriptor()))) {
- throw runtime.newIOError("bad file descriptor: " + openFile.getPath());
- }
-
- continue;
- } else {
- break;
- }
- }
-
- update = true;
- } while (c != newline); // loop until we see the nth separator char
-
- // if we hit EOF, we're done
- if (n == -1) {
- break;
- }
-
- // if we've found the last char of the separator,
- // and we've found at least as many characters as separator length,
- // and the last n characters of our buffer match the separator, we're done
- if (c == newline && buf.length() >= separator.length() &&
- 0 == ByteList.memcmp(buf.unsafeBytes(), buf.begin + buf.realSize - separator.length(), separator.unsafeBytes(), separator.begin, separator.realSize)) {
- break;
- }
- }
-
- if (isParagraph) {
- if (c != -1) {
- swallow('\n');
- }
- }
-
- if (!update) {
- return runtime.getNil();
- } else {
- incrementLineno(runtime, myOpenFile);
- RubyString str = RubyString.newString(runtime, buf);
- str.setTaint(true);
-
- return str;
- }
- }
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (EOFException e) {
- return runtime.getNil();
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- private void incrementLineno(Ruby runtime, OpenFile myOpenFile) {
- int lineno = myOpenFile.getLineNumber() + 1;
- myOpenFile.setLineNumber(lineno);
- runtime.getGlobalVariables().set("$.", runtime.newFixnum(lineno));
- // this is for a range check, near as I can tell
- RubyNumeric.int2fix(runtime, myOpenFile.getLineNumber());
- }
-
- protected boolean swallow(int term) throws IOException, BadDescriptorException {
- Stream readStream = openFile.getMainStream();
- int c;
-
- do {
- readCheck(readStream);
-
- try {
- c = readStream.fgetc();
- } catch (EOFException e) {
- c = -1;
- }
-
- if (c != term) {
- readStream.ungetc(c);
- return true;
- }
- } while (c != -1);
-
- return false;
- }
-
- public IRubyObject getlineFast(Ruby runtime, int delim) throws IOException, BadDescriptorException {
- Stream readStream = openFile.getMainStream();
- int c = -1;
-
- ByteList buf = new ByteList(0);
- boolean update = false;
- do {
- readCheck(readStream);
- readStream.clearerr();
- int n;
- try {
- n = readStream.getline(buf, (byte) delim);
- c = buf.length() > 0 ? buf.get(buf.length() - 1) & 0xff : -1;
- } catch (EOFException e) {
- n = -1;
- }
-
- if (n == -1) {
- if (!readStream.isBlocking() && (readStream instanceof ChannelStream)) {
- if(!(waitReadable(((ChannelStream)readStream).getDescriptor()))) {
- throw runtime.newIOError("bad file descriptor: " + openFile.getPath());
- }
- continue;
- } else {
- break;
- }
- }
-
- update = true;
- } while (c != delim);
-
- if (!update) {
- return runtime.getNil();
- } else {
- incrementLineno(runtime, openFile);
- RubyString str = RubyString.newString(runtime, buf);
- str.setTaint(true);
- return str;
- }
- }
- // IO class methods.
-
- @JRubyMethod(name = {"new", "for_fd"}, rest = true, frame = true, meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyClass klass = (RubyClass)recv;
-
- if (block.isGiven()) {
- String className = klass.getName();
- context.getRuntime().getWarnings().warn(
- ID.BLOCK_NOT_ACCEPTED,
- className + "::new() does not take block; use " + className + "::open() instead",
- className + "::open()");
- }
-
- return klass.newInstance(context, args, block);
- }
-
- @JRubyMethod(name = "initialize", required = 1, optional = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- int argCount = args.length;
- ModeFlags modes;
-
- int fileno = RubyNumeric.fix2int(args[0]);
-
- try {
- ChannelDescriptor descriptor = getDescriptorByFileno(fileno);
-
- if (descriptor == null) {
- throw getRuntime().newErrnoEBADFError();
- }
-
- descriptor.checkOpen();
-
- if (argCount == 2) {
- if (args[1] instanceof RubyFixnum) {
- modes = new ModeFlags(RubyFixnum.fix2long(args[1]));
- } else {
- modes = getIOModes(getRuntime(), args[1].convertToString().toString());
- }
- } else {
- // use original modes
- modes = descriptor.getOriginalModes();
- }
-
- openFile.setMode(modes.getOpenFileFlags());
-
- openFile.setMainStream(fdopen(descriptor, modes));
- } catch (BadDescriptorException ex) {
- throw getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException ive) {
- throw getRuntime().newErrnoEINVALError();
- }
-
- return this;
- }
-
- protected Stream fdopen(ChannelDescriptor existingDescriptor, ModeFlags modes) throws InvalidValueException {
- // See if we already have this descriptor open.
- // If so then we can mostly share the handler (keep open
- // file, but possibly change the mode).
-
- if (existingDescriptor == null) {
- // redundant, done above as well
-
- // this seems unlikely to happen unless it's a totally bogus fileno
- // ...so do we even need to bother trying to create one?
-
- // IN FACT, we should probably raise an error, yes?
- throw getRuntime().newErrnoEBADFError();
-
-// if (mode == null) {
-// mode = "r";
-// }
-//
-// try {
-// openFile.setMainStream(streamForFileno(getRuntime(), fileno));
-// } catch (BadDescriptorException e) {
-// throw getRuntime().newErrnoEBADFError();
-// } catch (IOException e) {
-// throw getRuntime().newErrnoEBADFError();
-// }
-// //modes = new IOModes(getRuntime(), mode);
-//
-// registerStream(openFile.getMainStream());
- } else {
- // We are creating a new IO object that shares the same
- // IOHandler (and fileno).
- return ChannelStream.fdopen(getRuntime(), existingDescriptor, modes);
- }
- }
-
- @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, meta = true)
- public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
- RubyClass klass = (RubyClass)recv;
-
- RubyIO io = (RubyIO)klass.newInstance(context, args, block);
-
- if (block.isGiven()) {
- try {
- return block.yield(context, io);
- } finally {
- try {
- io.getMetaClass().invoke(context, io, "close", IRubyObject.NULL_ARRAY, CallType.FUNCTIONAL, Block.NULL_BLOCK);
- } catch (RaiseException re) {
- RubyException rubyEx = re.getException();
- if (rubyEx.kind_of_p(context, runtime.getStandardError()).isTrue()) {
- // MRI behavior: swallow StandardErorrs
- } else {
- throw re;
- }
- }
- }
- }
-
- return io;
- }
-
- // This appears to be some windows-only mode. On a java platform this is a no-op
- @JRubyMethod(name = "binmode")
- public IRubyObject binmode() {
- return this;
- }
-
- /** @deprecated will be removed in 1.2 */
- protected void checkInitialized() {
- if (openFile == null) {
- throw getRuntime().newIOError("uninitialized stream");
- }
- }
-
- /** @deprecated will be removed in 1.2 */
- protected void checkClosed() {
- if (openFile.getMainStream() == null && openFile.getPipeStream() == null) {
- throw getRuntime().newIOError("closed stream");
- }
- }
-
- @JRubyMethod(name = "syswrite", required = 1)
- public IRubyObject syswrite(ThreadContext context, IRubyObject obj) {
- Ruby runtime = context.getRuntime();
-
- try {
- RubyString string = obj.asString();
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkWritable(runtime);
-
- Stream writeStream = myOpenFile.getWriteStream();
-
- if (myOpenFile.isWriteBuffered()) {
- runtime.getWarnings().warn(ID.SYSWRITE_BUFFERED_IO, "syswrite for buffered IO");
- }
-
- if (!writeStream.getDescriptor().isWritable()) {
- myOpenFile.checkClosed(runtime);
- }
-
- int read = writeStream.getDescriptor().write(string.getByteList());
-
- if (read == -1) {
- // TODO? I think this ends up propagating from normal Java exceptions
- // sys_fail(openFile.getPath())
- }
-
- return runtime.newFixnum(read);
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (IOException e) {
- throw runtime.newSystemCallError(e.getMessage());
- }
- }
-
- @JRubyMethod(name = "write_nonblock", required = 1)
- public IRubyObject write_nonblock(ThreadContext context, IRubyObject obj) {
- // MRI behavior: always check whether the file is writable
- // or not, even if we are to write 0 bytes.
- OpenFile myOpenFile = getOpenFileChecked();
- try {
- myOpenFile.checkWritable(context.getRuntime());
- } catch (IOException ex) {
- throw context.getRuntime().newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException ex) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (PipeException ex) {
- throw context.getRuntime().newErrnoEPIPEError();
- }
-
- // TODO: Obviously, we're not doing a non-blocking write here
- return write(context, obj);
- }
-
- /** io_write
- *
- */
- @JRubyMethod(name = "write", required = 1)
- public IRubyObject write(ThreadContext context, IRubyObject obj) {
- Ruby runtime = context.getRuntime();
-
- runtime.secure(4);
-
- RubyString str = obj.asString();
-
- // TODO: Ruby reuses this logic for other "write" behavior by checking if it's an IO and calling write again
-
- if (str.getByteList().length() == 0) {
- return runtime.newFixnum(0);
- }
-
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkWritable(runtime);
-
- int written = fwrite(str.getByteList());
-
- if (written == -1) {
- // TODO: sys fail
- }
-
- // if not sync, we switch to write buffered mode
- if (!myOpenFile.isSync()) {
- myOpenFile.setWriteBuffered();
- }
-
- return runtime.newFixnum(written);
- } catch (IOException ex) {
- throw runtime.newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw runtime.newErrnoEBADFError();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- }
- }
-
- protected boolean waitWritable(ChannelDescriptor descriptor) throws IOException {
- Channel channel = descriptor.getChannel();
- if (channel == null || !(channel instanceof SelectableChannel)) {
- return false;
- }
-
- Selector selector = Selector.open();
-
- ((SelectableChannel) channel).configureBlocking(false);
- int real_ops = ((SelectableChannel) channel).validOps() & SelectionKey.OP_WRITE;
- SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
-
- if (key == null) {
- ((SelectableChannel) channel).register(selector, real_ops, descriptor);
- } else {
- key.interestOps(key.interestOps()|real_ops);
- }
-
- while(selector.select() == 0);
-
- for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
- SelectionKey skey = (SelectionKey) i.next();
- if ((skey.interestOps() & skey.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
- if(skey.attachment() == descriptor) {
- return true;
- }
- }
- }
- return false;
- }
-
- protected boolean waitReadable(ChannelDescriptor descriptor) throws IOException {
- Channel channel = descriptor.getChannel();
- if (channel == null || !(channel instanceof SelectableChannel)) {
- return false;
- }
-
- Selector selector = Selector.open();
-
- ((SelectableChannel) channel).configureBlocking(false);
- int real_ops = ((SelectableChannel) channel).validOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT);
- SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
-
- if (key == null) {
- ((SelectableChannel) channel).register(selector, real_ops, descriptor);
- } else {
- key.interestOps(key.interestOps()|real_ops);
- }
-
- while(selector.select() == 0);
-
- for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
- SelectionKey skey = (SelectionKey) i.next();
- if ((skey.interestOps() & skey.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) {
- if(skey.attachment() == descriptor) {
- return true;
- }
- }
- }
- return false;
- }
-
- protected int fwrite(ByteList buffer) {
- int n, r, l, offset = 0;
- boolean eagain = false;
- Stream writeStream = openFile.getWriteStream();
-
- int len = buffer.length();
-
- if ((n = len) <= 0) return n;
-
- try {
- if (openFile.isSync()) {
- openFile.fflush(writeStream);
-
- // TODO: why is this guarded?
- // if (!rb_thread_fd_writable(fileno(f))) {
- // rb_io_check_closed(fptr);
- // }
-
- while(offset<len) {
- l = n;
-
- // TODO: Something about pipe buffer length here
-
- r = writeStream.getDescriptor().write(buffer,offset,l);
-
- if(r == len) {
- return len; //Everything written
- }
-
- if (0 <= r) {
- offset += r;
- n -= r;
- eagain = true;
- }
-
- if(eagain && waitWritable(writeStream.getDescriptor())) {
- openFile.checkClosed(getRuntime());
- if(offset >= buffer.length()) {
- return -1;
- }
- eagain = false;
- } else {
- return -1;
- }
- }
-
-
- // TODO: all this stuff...some pipe logic, some async thread stuff
- // retry:
- // l = n;
- // if (PIPE_BUF < l &&
- // !rb_thread_critical &&
- // !rb_thread_alone() &&
- // wsplit_p(fptr)) {
- // l = PIPE_BUF;
- // }
- // TRAP_BEG;
- // r = write(fileno(f), RSTRING(str)->ptr+offset, l);
- // TRAP_END;
- // if (r == n) return len;
- // if (0 <= r) {
- // offset += r;
- // n -= r;
- // errno = EAGAIN;
- // }
- // if (rb_io_wait_writable(fileno(f))) {
- // rb_io_check_closed(fptr);
- // if (offset < RSTRING(str)->len)
- // goto retry;
- // }
- // return -1L;
- }
-
- // TODO: handle errors in buffered write by retrying until finished or file is closed
- return writeStream.fwrite(buffer);
- // while (errno = 0, offset += (r = fwrite(RSTRING(str)->ptr+offset, 1, n, f)), (n -= r) > 0) {
- // if (ferror(f)
- // ) {
- // if (rb_io_wait_writable(fileno(f))) {
- // rb_io_check_closed(fptr);
- // clearerr(f);
- // if (offset < RSTRING(str)->len)
- // continue;
- // }
- // return -1L;
- // }
- // }
-
-// return len - n;
- } catch (IOException ex) {
- throw getRuntime().newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw getRuntime().newErrnoEBADFError();
- }
- }
-
- /** rb_io_addstr
- *
- */
- @JRubyMethod(name = "<<", required = 1)
- public IRubyObject op_append(ThreadContext context, IRubyObject anObject) {
- // Claims conversion is done via 'to_s' in docs.
- callMethod(context, "write", anObject);
-
- return this;
- }
-
- @JRubyMethod(name = "fileno", alias = "to_i")
- public RubyFixnum fileno(ThreadContext context) {
- return context.getRuntime().newFixnum(getOpenFileChecked().getMainStream().getDescriptor().getFileno());
- }
-
- /** Returns the current line number.
- *
- * @return the current line number.
- */
- @JRubyMethod(name = "lineno")
- public RubyFixnum lineno(ThreadContext context) {
- return context.getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
- }
-
- /** Sets the current line number.
- *
- * @param newLineNumber The new line number.
- */
- @JRubyMethod(name = "lineno=", required = 1)
- public RubyFixnum lineno_set(ThreadContext context, IRubyObject newLineNumber) {
- getOpenFileChecked().setLineNumber(RubyNumeric.fix2int(newLineNumber));
-
- return context.getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
- }
-
- /** Returns the current sync mode.
- *
- * @return the current sync mode.
- */
- @JRubyMethod(name = "sync")
- public RubyBoolean sync(ThreadContext context) {
- return context.getRuntime().newBoolean(getOpenFileChecked().getMainStream().isSync());
- }
-
- /**
- * <p>Return the process id (pid) of the process this IO object
- * spawned. If no process exists (popen was not called), then
- * nil is returned. This is not how it appears to be defined
- * but ruby 1.8 works this way.</p>
- *
- * @return the pid or nil
- */
- @JRubyMethod(name = "pid")
- public IRubyObject pid(ThreadContext context) {
- OpenFile myOpenFile = getOpenFileChecked();
-
- if (myOpenFile.getProcess() == null) {
- return context.getRuntime().getNil();
- }
-
- // Of course this isn't particularly useful.
- int pid = myOpenFile.getProcess().hashCode();
-
- return context.getRuntime().newFixnum(pid);
- }
-
- /**
- * @deprecated
- * @return
- */
- public boolean writeDataBuffered() {
- return openFile.getMainStream().writeDataBuffered();
- }
-
- @JRubyMethod(name = {"pos", "tell"})
- public RubyFixnum pos(ThreadContext context) {
- try {
- return context.getRuntime().newFixnum(getOpenFileChecked().getMainStream().fgetpos());
- } catch (InvalidValueException ex) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (BadDescriptorException bde) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (IOException e) {
- throw context.getRuntime().newIOError(e.getMessage());
- }
- }
-
- @JRubyMethod(name = "pos=", required = 1)
- public RubyFixnum pos_set(ThreadContext context, IRubyObject newPosition) {
- long offset = RubyNumeric.num2long(newPosition);
-
- if (offset < 0) {
- throw context.getRuntime().newSystemCallError("Negative seek offset");
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- try {
- myOpenFile.getMainStream().lseek(offset, Stream.SEEK_SET);
- } catch (BadDescriptorException e) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (IOException e) {
- throw context.getRuntime().newIOError(e.getMessage());
- }
-
- myOpenFile.getMainStream().clearerr();
-
- return context.getRuntime().newFixnum(offset);
- }
-
- /** Print some objects to the stream.
- *
- */
- @JRubyMethod(name = "print", rest = true, reads = FrameField.LASTLINE)
- public IRubyObject print(ThreadContext context, IRubyObject[] args) {
- if (args.length == 0) {
- args = new IRubyObject[] { context.getCurrentFrame().getLastLine() };
- }
-
- Ruby runtime = context.getRuntime();
- IRubyObject fs = runtime.getGlobalVariables().get("$,");
- IRubyObject rs = runtime.getGlobalVariables().get("$\\");
-
- for (int i = 0; i < args.length; i++) {
- if (i > 0 && !fs.isNil()) {
- callMethod(context, "write", fs);
- }
- if (args[i].isNil()) {
- callMethod(context, "write", runtime.newString("nil"));
- } else {
- callMethod(context, "write", args[i]);
- }
- }
- if (!rs.isNil()) {
- callMethod(context, "write", rs);
- }
-
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "printf", required = 1, rest = true)
- public IRubyObject printf(ThreadContext context, IRubyObject[] args) {
- callMethod(context, "write", RubyKernel.sprintf(context, this, args));
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "putc", required = 1, backtrace = true)
- public IRubyObject putc(ThreadContext context, IRubyObject object) {
- int c = RubyNumeric.num2chr(object);
-
- try {
- getOpenFileChecked().getMainStream().fputc(c);
- } catch (BadDescriptorException e) {
- return RubyFixnum.zero(context.getRuntime());
- } catch (IOException e) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- return object;
- }
-
- public RubyFixnum seek(ThreadContext context, IRubyObject[] args) {
- long offset = RubyNumeric.num2long(args[0]);
- int whence = Stream.SEEK_SET;
-
- if (args.length > 1) {
- whence = RubyNumeric.fix2int(args[1].convertToInteger());
- }
-
- return doSeek(context, offset, whence);
- }
-
- @JRubyMethod(name = "seek")
- public RubyFixnum seek(ThreadContext context, IRubyObject arg0) {
- long offset = RubyNumeric.num2long(arg0);
- int whence = Stream.SEEK_SET;
-
- return doSeek(context, offset, whence);
- }
-
- @JRubyMethod(name = "seek")
- public RubyFixnum seek(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- long offset = RubyNumeric.num2long(arg0);
- int whence = RubyNumeric.fix2int(arg1.convertToInteger());
-
- return doSeek(context, offset, whence);
- }
-
- private RubyFixnum doSeek(ThreadContext context, long offset, int whence) {
- OpenFile myOpenFile = getOpenFileChecked();
-
- try {
- myOpenFile.seek(offset, whence);
- } catch (BadDescriptorException ex) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (IOException e) {
- throw context.getRuntime().newIOError(e.getMessage());
- }
-
- myOpenFile.getMainStream().clearerr();
-
- return RubyFixnum.zero(context.getRuntime());
- }
-
- // This was a getOpt with one mandatory arg, but it did not work
- // so I am parsing it for now.
- @JRubyMethod(name = "sysseek", required = 1, optional = 1)
- public RubyFixnum sysseek(ThreadContext context, IRubyObject[] args) {
- long offset = RubyNumeric.num2long(args[0]);
- long pos;
- int whence = Stream.SEEK_SET;
-
- if (args.length > 1) {
- whence = RubyNumeric.fix2int(args[1].convertToInteger());
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- try {
-
- if (myOpenFile.isReadable() && myOpenFile.isReadBuffered()) {
- throw context.getRuntime().newIOError("sysseek for buffered IO");
- }
- if (myOpenFile.isWritable() && myOpenFile.isWriteBuffered()) {
- context.getRuntime().getWarnings().warn(ID.SYSSEEK_BUFFERED_IO, "sysseek for buffered IO");
- }
-
- pos = myOpenFile.getMainStream().getDescriptor().lseek(offset, whence);
- } catch (BadDescriptorException ex) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (IOException e) {
- throw context.getRuntime().newIOError(e.getMessage());
- }
-
- myOpenFile.getMainStream().clearerr();
-
- return context.getRuntime().newFixnum(pos);
- }
-
- @JRubyMethod(name = "rewind")
- public RubyFixnum rewind(ThreadContext context) {
- OpenFile myOpenfile = getOpenFileChecked();
-
- try {
- myOpenfile.getMainStream().lseek(0L, Stream.SEEK_SET);
- myOpenfile.getMainStream().clearerr();
-
- // TODO: This is some goofy global file value from MRI..what to do?
-// if (io == current_file) {
-// gets_lineno -= fptr->lineno;
-// }
- } catch (BadDescriptorException e) {
- throw context.getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw context.getRuntime().newErrnoEINVALError();
- } catch (PipeException e) {
- throw context.getRuntime().newErrnoESPIPEError();
- } catch (IOException e) {
- throw context.getRuntime().newIOError(e.getMessage());
- }
-
- // Must be back on first line on rewind.
- myOpenfile.setLineNumber(0);
-
- return RubyFixnum.zero(context.getRuntime());
- }
-
- @JRubyMethod(name = "fsync")
- public RubyFixnum fsync(ThreadContext context) {
- Ruby runtime = context.getRuntime();
-
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkWritable(runtime);
-
- myOpenFile.getWriteStream().sync();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- }
-
- return RubyFixnum.zero(runtime);
- }
-
- /** Sets the current sync mode.
- *
- * @param newSync The new sync mode.
- */
- @JRubyMethod(name = "sync=", required = 1)
- public IRubyObject sync_set(IRubyObject newSync) {
- getOpenFileChecked().setSync(newSync.isTrue());
- getOpenFileChecked().getMainStream().setSync(newSync.isTrue());
-
- return this;
- }
-
- @JRubyMethod(name = {"eof?", "eof"})
- public RubyBoolean eof_p(ThreadContext context) {
- Ruby runtime = context.getRuntime();
-
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkReadable(runtime);
- myOpenFile.setReadBuffered();
-
- if (myOpenFile.getMainStream().feof()) {
- return runtime.getTrue();
- }
-
- if (myOpenFile.getMainStream().readDataBuffered()) {
- return runtime.getFalse();
- }
-
- readCheck(myOpenFile.getMainStream());
-
- myOpenFile.getMainStream().clearerr();
-
- int c = myOpenFile.getMainStream().fgetc();
-
- if (c != -1) {
- myOpenFile.getMainStream().ungetc(c);
- return runtime.getFalse();
- }
-
- myOpenFile.checkClosed(runtime);
-
- myOpenFile.getMainStream().clearerr();
-
- return runtime.getTrue();
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- @JRubyMethod(name = {"tty?", "isatty"})
- public RubyBoolean tty_p(ThreadContext context) {
- return context.getRuntime().newBoolean(context.getRuntime().getPosix().isatty(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor()));
- }
-
- @JRubyMethod(name = "initialize_copy", required = 1)
- @Override
- public IRubyObject initialize_copy(IRubyObject original){
- Ruby runtime = getRuntime();
-
- if (this == original) return this;
-
- RubyIO originalIO = (RubyIO) TypeConverter.convertToTypeWithCheck(original, runtime.getIO(), MethodIndex.TO_IO, "to_io");
-
- OpenFile originalFile = originalIO.getOpenFileChecked();
- OpenFile newFile = openFile;
-
- try {
- // TODO: I didn't see where MRI has this check, but it seems to be the right place
- originalFile.checkClosed(runtime);
-
- if (originalFile.getPipeStream() != null) {
- originalFile.getPipeStream().fflush();
- originalFile.getMainStream().lseek(0, Stream.SEEK_CUR);
- } else if (originalFile.isWritable()) {
- originalFile.getMainStream().fflush();
- } else {
- originalFile.getMainStream().lseek(0, Stream.SEEK_CUR);
- }
-
- newFile.setMode(originalFile.getMode());
- newFile.setProcess(originalFile.getProcess());
- newFile.setLineNumber(originalFile.getLineNumber());
- newFile.setPath(originalFile.getPath());
- newFile.setFinalizer(originalFile.getFinalizer());
-
- ModeFlags modes;
- if (newFile.isReadable()) {
- if (newFile.isWritable()) {
- if (newFile.getPipeStream() != null) {
- modes = new ModeFlags(ModeFlags.RDONLY);
- } else {
- modes = new ModeFlags(ModeFlags.RDWR);
- }
- } else {
- modes = new ModeFlags(ModeFlags.RDONLY);
- }
- } else {
- if (newFile.isWritable()) {
- modes = new ModeFlags(ModeFlags.WRONLY);
- } else {
- modes = originalFile.getMainStream().getModes();
- }
- }
-
- ChannelDescriptor descriptor = originalFile.getMainStream().getDescriptor().dup();
-
- newFile.setMainStream(ChannelStream.fdopen(runtime, descriptor, modes));
-
- // TODO: the rest of this...seeking to same position is unnecessary since we share a channel
- // but some of this may be needed?
-
-// fseeko(fptr->f, ftello(orig->f), SEEK_SET);
-// if (orig->f2) {
-// if (fileno(orig->f) != fileno(orig->f2)) {
-// fd = ruby_dup(fileno(orig->f2));
-// }
-// fptr->f2 = rb_fdopen(fd, "w");
-// fseeko(fptr->f2, ftello(orig->f2), SEEK_SET);
-// }
-// if (fptr->mode & FMODE_BINMODE) {
-// rb_io_binmode(dest);
-// }
-
- // Register the new descriptor
- registerDescriptor(newFile.getMainStream().getDescriptor());
- } catch (IOException ex) {
- throw runtime.newIOError("could not init copy: " + ex);
- } catch (BadDescriptorException ex) {
- throw runtime.newIOError("could not init copy: " + ex);
- } catch (PipeException ex) {
- throw runtime.newIOError("could not init copy: " + ex);
- } catch (InvalidValueException ex) {
- throw runtime.newIOError("could not init copy: " + ex);
- }
-
- return this;
- }
-
- /** Closes the IO.
- *
- * @return The IO.
- */
- @JRubyMethod(name = "closed?")
- public RubyBoolean closed_p(ThreadContext context) {
- return context.getRuntime().newBoolean(openFile.getMainStream() == null && openFile.getPipeStream() == null);
- }
-
- /**
- * <p>Closes all open resources for the IO. It also removes
- * it from our magical all open file descriptor pool.</p>
- *
- * @return The IO.
- */
- @JRubyMethod(name = "close")
- public IRubyObject close() {
- Ruby runtime = getRuntime();
-
- if (runtime.getSafeLevel() >= 4 && isTaint()) {
- throw runtime.newSecurityError("Insecure: can't close");
- }
-
- openFile.checkClosed(runtime);
- return close2(runtime);
- }
-
- protected IRubyObject close2(Ruby runtime) {
- if (openFile == null) return runtime.getNil();
-
- // These would be used when we notify threads...if we notify threads
- interruptBlockingThreads();
-
- ChannelDescriptor main, pipe;
- if (openFile.getPipeStream() != null) {
- pipe = openFile.getPipeStream().getDescriptor();
- } else {
- if (openFile.getMainStream() == null) {
- return runtime.getNil();
- }
- pipe = null;
- }
-
- main = openFile.getMainStream().getDescriptor();
-
- // cleanup, raising errors if any
- openFile.cleanup(runtime, true);
-
- // TODO: notify threads waiting on descriptors/IO? probably not...
-
- if (openFile.getProcess() != null) {
- try {
- IRubyObject processResult = RubyProcess.RubyStatus.newProcessStatus(runtime, openFile.getProcess().waitFor());
- runtime.getGlobalVariables().set("$?", processResult);
- } catch (InterruptedException ie) {
- // TODO: do something here?
- }
- }
-
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "close_write")
- public IRubyObject close_write(ThreadContext context) throws BadDescriptorException {
- try {
- if (context.getRuntime().getSafeLevel() >= 4 && isTaint()) {
- throw context.getRuntime().newSecurityError("Insecure: can't close");
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- if (myOpenFile.getPipeStream() == null && myOpenFile.isReadable()) {
- throw context.getRuntime().newIOError("closing non-duplex IO for writing");
- }
-
- if (myOpenFile.getPipeStream() == null) {
- close();
- } else{
- myOpenFile.getPipeStream().fclose();
- myOpenFile.setPipeStream(null);
- myOpenFile.setMode(myOpenFile.getMode() & ~OpenFile.WRITABLE);
- // TODO
- // n is result of fclose; but perhaps having a SysError below is enough?
- // if (n != 0) rb_sys_fail(fptr->path);
- }
- } catch (IOException ioe) {
- // hmmmm
- }
- return this;
- }
-
- @JRubyMethod(name = "close_read")
- public IRubyObject close_read(ThreadContext context) throws BadDescriptorException {
- Ruby runtime = context.getRuntime();
-
- try {
- if (runtime.getSafeLevel() >= 4 && isTaint()) {
- throw runtime.newSecurityError("Insecure: can't close");
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- if (myOpenFile.getPipeStream() == null && myOpenFile.isWritable()) {
- throw runtime.newIOError("closing non-duplex IO for reading");
- }
-
- if (myOpenFile.getPipeStream() == null) {
- close();
- } else{
- myOpenFile.getMainStream().fclose();
- myOpenFile.setMode(myOpenFile.getMode() & ~OpenFile.READABLE);
- myOpenFile.setMainStream(myOpenFile.getPipeStream());
- myOpenFile.setPipeStream(null);
- // TODO
- // n is result of fclose; but perhaps having a SysError below is enough?
- // if (n != 0) rb_sys_fail(fptr->path);
- }
- } catch (IOException ioe) {
- // I believe Ruby bails out with a "bug" if closing fails
- throw runtime.newIOErrorFromException(ioe);
- }
- return this;
- }
-
- /** Flushes the IO output stream.
- *
- * @return The IO.
- */
- @JRubyMethod(name = "flush")
- public RubyIO flush() {
- try {
- getOpenFileChecked().getWriteStream().fflush();
- } catch (BadDescriptorException e) {
- throw getRuntime().newErrnoEBADFError();
- } catch (IOException e) {
- throw getRuntime().newIOError(e.getMessage());
- }
-
- return this;
- }
-
- /** Read a line.
- *
- */
- @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
- public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- ByteList separator = getSeparatorForGets(runtime, args);
-
- IRubyObject result = getline(runtime, separator);
-
- if (!result.isNil()) context.getCurrentFrame().setLastLine(result);
-
- return result;
- }
-
- public boolean getBlocking() {
- return ((ChannelStream) openFile.getMainStream()).isBlocking();
- }
-
- @JRubyMethod(name = "fcntl", required = 2)
- public IRubyObject fcntl(ThreadContext context, IRubyObject cmd, IRubyObject arg) {
- // TODO: This version differs from ioctl by checking whether fcntl exists
- // and raising notimplemented if it doesn't; perhaps no difference for us?
- return ctl(context.getRuntime(), cmd, arg);
- }
-
- @JRubyMethod(name = "ioctl", required = 1, optional = 1)
- public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
- IRubyObject cmd = args[0];
- IRubyObject arg;
-
- if (args.length == 2) {
- arg = args[1];
- } else {
- arg = context.getRuntime().getNil();
- }
-
- return ctl(context.getRuntime(), cmd, arg);
- }
-
- public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
- long realCmd = cmd.convertToInteger().getLongValue();
- long nArg = 0;
-
- // FIXME: Arg may also be true, false, and nil and still be valid. Strangely enough,
- // protocol conversion is not happening in Ruby on this arg?
- if (arg.isNil() || arg == runtime.getFalse()) {
- nArg = 0;
- } else if (arg instanceof RubyFixnum) {
- nArg = RubyFixnum.fix2long(arg);
- } else if (arg == runtime.getTrue()) {
- nArg = 1;
- } else {
- throw runtime.newNotImplementedError("JRuby does not support string for second fcntl/ioctl argument yet");
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- // Fixme: Only F_SETFL is current supported
- if (realCmd == 1L) { // cmd is F_SETFL
- boolean block = true;
-
- if ((nArg & ModeFlags.NONBLOCK) == ModeFlags.NONBLOCK) {
- block = false;
- }
-
- try {
- myOpenFile.getMainStream().setBlocking(block);
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- } else {
- throw runtime.newNotImplementedError("JRuby only supports F_SETFL for fcntl/ioctl currently");
- }
-
- return runtime.newFixnum(0);
- }
-
- private static final ByteList NIL_BYTELIST = ByteList.create("nil");
- private static final ByteList RECURSIVE_BYTELIST = ByteList.create("[...]");
-
- @JRubyMethod(name = "puts", rest = true)
- public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- assert runtime.getGlobalVariables().getDefaultSeparator() instanceof RubyString;
- RubyString separator = (RubyString) runtime.getGlobalVariables().getDefaultSeparator();
-
- if (args.length == 0) {
- write(context, separator.getByteList());
- return runtime.getNil();
- }
-
- for (int i = 0; i < args.length; i++) {
- ByteList line;
-
- if (args[i].isNil()) {
- line = NIL_BYTELIST;
- } else if (runtime.isInspecting(args[i])) {
- line = RECURSIVE_BYTELIST;
- } else if (args[i] instanceof RubyArray) {
- inspectPuts(context, (RubyArray) args[i]);
- continue;
- } else {
- line = args[i].asString().getByteList();
- }
-
- write(context, line);
-
- if (line.length() == 0 || !line.endsWith(separator.getByteList())) {
- write(context, separator.getByteList());
- }
- }
- return runtime.getNil();
- }
-
- protected void write(ThreadContext context, ByteList byteList) {
- callMethod(context, "write", RubyString.newStringShared(context.getRuntime(), byteList));
- }
-
- private IRubyObject inspectPuts(ThreadContext context, RubyArray array) {
- try {
- context.getRuntime().registerInspecting(array);
- return puts(context, array.toJavaArray());
- } finally {
- context.getRuntime().unregisterInspecting(array);
- }
- }
-
- /** Read a line.
- *
- */
- @JRubyMethod(name = "readline", optional = 1, writes = FrameField.LASTLINE)
- public IRubyObject readline(ThreadContext context, IRubyObject[] args) {
- IRubyObject line = gets(context, args);
-
- if (line.isNil()) throw context.getRuntime().newEOFError();
-
- return line;
- }
-
- /** Read a byte. On EOF returns nil.
- *
- */
- @JRubyMethod(name = "getc")
- public IRubyObject getc() {
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkReadable(getRuntime());
- myOpenFile.setReadBuffered();
-
- Stream stream = myOpenFile.getMainStream();
-
- readCheck(stream);
- stream.clearerr();
-
- int c = myOpenFile.getMainStream().fgetc();
-
- if (c == -1) {
- // TODO: check for ferror, clear it, and try once more up above readCheck
-// if (ferror(f)) {
-// clearerr(f);
-// if (!rb_io_wait_readable(fileno(f)))
-// rb_sys_fail(fptr->path);
-// goto retry;
-// }
- return getRuntime().getNil();
- }
-
- return getRuntime().newFixnum(c);
- } catch (PipeException ex) {
- throw getRuntime().newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } catch (BadDescriptorException e) {
- throw getRuntime().newErrnoEBADFError();
- } catch (EOFException e) {
- throw getRuntime().newEOFError();
- } catch (IOException e) {
- throw getRuntime().newIOError(e.getMessage());
- }
- }
-
- private void readCheck(Stream stream) {
- if (!stream.readDataBuffered()) {
- openFile.checkClosed(getRuntime());
- }
- }
-
- /**
- * <p>Pushes char represented by int back onto IOS.</p>
- *
- * @param number to push back
- */
- @JRubyMethod(name = "ungetc", required = 1)
- public IRubyObject ungetc(IRubyObject number) {
- int ch = RubyNumeric.fix2int(number);
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- if (!myOpenFile.isReadBuffered()) {
- throw getRuntime().newIOError("unread stream");
- }
-
- try {
- myOpenFile.checkReadable(getRuntime());
- myOpenFile.setReadBuffered();
-
- if (myOpenFile.getMainStream().ungetc(ch) == -1 && ch != -1) {
- throw getRuntime().newIOError("ungetc failed");
- }
- } catch (PipeException ex) {
- throw getRuntime().newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } catch (BadDescriptorException e) {
- throw getRuntime().newErrnoEBADFError();
- } catch (EOFException e) {
- throw getRuntime().newEOFError();
- } catch (IOException e) {
- throw getRuntime().newIOError(e.getMessage());
- }
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "read_nonblock", required = 1, optional = 1)
- public IRubyObject read_nonblock(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- openFile.checkClosed(runtime);
-
- if(!(openFile.getMainStream() instanceof ChannelStream)) {
- // cryptic for the uninitiated...
- throw runtime.newNotImplementedError("read_nonblock only works with Nio based handlers");
- }
- try {
- int maxLength = RubyNumeric.fix2int(args[0]);
- if (maxLength < 0) {
- throw runtime.newArgumentError("negative length " + maxLength + " given");
- }
- ByteList buf = ((ChannelStream)openFile.getMainStream()).readnonblock(RubyNumeric.fix2int(args[0]));
- IRubyObject strbuf = RubyString.newString(runtime, buf == null ? new ByteList(ByteList.NULL_ARRAY) : buf);
- if(args.length > 1) {
- args[1].callMethod(context, MethodIndex.OP_LSHIFT, "<<", strbuf);
- return args[1];
- }
-
- return strbuf;
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (EOFException e) {
- return runtime.getNil();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- @JRubyMethod(name = "readpartial", required = 1, optional = 1)
- public IRubyObject readpartial(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- openFile.checkClosed(runtime);
-
- if(!(openFile.getMainStream() instanceof ChannelStream)) {
- // cryptic for the uninitiated...
- throw runtime.newNotImplementedError("readpartial only works with Nio based handlers");
- }
- try {
- int maxLength = RubyNumeric.fix2int(args[0]);
- if (maxLength < 0) {
- throw runtime.newArgumentError("negative length " + maxLength + " given");
- }
- ByteList buf = ((ChannelStream)openFile.getMainStream()).readpartial(RubyNumeric.fix2int(args[0]));
- IRubyObject strbuf = RubyString.newString(runtime, buf == null ? new ByteList(ByteList.NULL_ARRAY) : buf);
- if(args.length > 1) {
- args[1].callMethod(context, MethodIndex.OP_LSHIFT, "<<", strbuf);
- return args[1];
- }
-
- return strbuf;
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (EOFException e) {
- return runtime.getNil();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- @JRubyMethod(name = "sysread", required = 1, optional = 1)
- public IRubyObject sysread(ThreadContext context, IRubyObject[] args) {
- int len = (int)RubyNumeric.num2long(args[0]);
- if (len < 0) throw getRuntime().newArgumentError("Negative size");
-
- try {
- RubyString str;
- ByteList buffer;
- if (args.length == 1 || args[1].isNil()) {
- if (len == 0) {
- return RubyString.newStringShared(getRuntime(), ByteList.EMPTY_BYTELIST);
- }
-
- buffer = new ByteList(len);
- str = RubyString.newString(getRuntime(), buffer);
- } else {
- str = args[1].convertToString();
- str.modify(len);
-
- if (len == 0) {
- return str;
- }
-
- buffer = str.getByteList();
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- myOpenFile.checkReadable(getRuntime());
-
- if (myOpenFile.getMainStream().readDataBuffered()) {
- throw getRuntime().newIOError("sysread for buffered IO");
- }
-
- // TODO: Ruby locks the string here
-
- context.getThread().beforeBlockingCall();
- myOpenFile.checkClosed(getRuntime());
-
- // TODO: Ruby re-checks that the buffer string hasn't been modified
-
- int bytesRead = myOpenFile.getMainStream().getDescriptor().read(len, str.getByteList());
-
- // TODO: Ruby unlocks the string here
-
- // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
-
- if (bytesRead == -1 || (bytesRead == 0 && len > 0)) {
- throw getRuntime().newEOFError();
- }
-
- str.setTaint(true);
-
- return str;
- } catch (BadDescriptorException e) {
- throw getRuntime().newErrnoEBADFError();
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- } catch (PipeException e) {
- throw getRuntime().newErrnoEPIPEError();
- } catch (EOFException e) {
- throw getRuntime().newEOFError();
- } catch (IOException e) {
- // All errors to sysread should be SystemCallErrors, but on a closed stream
- // Ruby returns an IOError. Java throws same exception for all errors so
- // we resort to this hack...
- if ("File not open".equals(e.getMessage())) {
- throw getRuntime().newIOError(e.getMessage());
- }
- throw getRuntime().newSystemCallError(e.getMessage());
- } finally {
- context.getThread().afterBlockingCall();
- }
- }
-
- public IRubyObject read(IRubyObject[] args) {
- ThreadContext context = getRuntime().getCurrentContext();
-
- switch (args.length) {
- case 0: return read(context);
- case 1: return read(context, args[0]);
- case 2: return read(context, args[0], args[1]);
- default: throw getRuntime().newArgumentError(args.length, 2);
- }
- }
-
- @JRubyMethod(name = "read")
- public IRubyObject read(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- OpenFile myOpenFile = getOpenFileChecked();
-
- try {
- myOpenFile.checkReadable(runtime);
- myOpenFile.setReadBuffered();
-
- return readAll(getRuntime().getNil());
- } catch (PipeException ex) {
- throw getRuntime().newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } catch (EOFException ex) {
- throw getRuntime().newEOFError();
- } catch (IOException ex) {
- throw getRuntime().newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw getRuntime().newErrnoEBADFError();
- }
- }
-
- @JRubyMethod(name = "read")
- public IRubyObject read(ThreadContext context, IRubyObject arg0) {
- if (arg0.isNil()) {
- return read(context);
- }
-
- OpenFile myOpenFile = getOpenFileChecked();
-
- int length = RubyNumeric.num2int(arg0);
-
- if (length < 0) {
- throw getRuntime().newArgumentError("negative length " + length + " given");
- }
-
- RubyString str = null;
-
- return readNotAll(context, myOpenFile, length, str);
- }
-
- @JRubyMethod(name = "read")
- public IRubyObject read(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- OpenFile myOpenFile = getOpenFileChecked();
-
- if (arg0.isNil()) {
- try {
- myOpenFile.checkReadable(getRuntime());
- myOpenFile.setReadBuffered();
-
- return readAll(arg1);
- } catch (PipeException ex) {
- throw getRuntime().newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw getRuntime().newErrnoEINVALError();
- } catch (EOFException ex) {
- throw getRuntime().newEOFError();
- } catch (IOException ex) {
- throw getRuntime().newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw getRuntime().newErrnoEBADFError();
- }
- }
-
- int length = RubyNumeric.num2int(arg0);
-
- if (length < 0) {
- throw getRuntime().newArgumentError("negative length " + length + " given");
- }
-
- RubyString str = null;
-// ByteList buffer = null;
- if (arg1.isNil()) {
-// buffer = new ByteList(length);
-// str = RubyString.newString(getRuntime(), buffer);
- } else {
- str = arg1.convertToString();
- str.modify(length);
-
- if (length == 0) {
- return str;
- }
-
-// buffer = str.getByteList();
- }
-
- return readNotAll(context, myOpenFile, length, str);
- }
-
- private IRubyObject readNotAll(ThreadContext context, OpenFile myOpenFile, int length, RubyString str) {
- Ruby runtime = context.getRuntime();
-
- try {
- myOpenFile.checkReadable(runtime);
- myOpenFile.setReadBuffered();
-
- if (myOpenFile.getMainStream().feof()) {
- return runtime.getNil();
- }
-
- // TODO: Ruby locks the string here
-
- // READ_CHECK from MRI io.c
- readCheck(myOpenFile.getMainStream());
-
- // TODO: check buffer length again?
- // if (RSTRING(str)->len != len) {
- // rb_raise(rb_eRuntimeError, "buffer string modified");
- // }
-
- // TODO: read into buffer using all the fread logic
- // int read = openFile.getMainStream().fread(buffer);
- ByteList newBuffer = myOpenFile.getMainStream().fread(length);
-
- // TODO: Ruby unlocks the string here
-
- // TODO: change this to check number read into buffer once that's working
- // if (read == 0) {
-
- if (newBuffer == null || newBuffer.length() == 0) {
- if (myOpenFile.getMainStream() == null) {
- return runtime.getNil();
- }
-
- if (myOpenFile.getMainStream().feof()) {
- // truncate buffer string to zero, if provided
- if (str != null) {
- str.setValue(ByteList.EMPTY_BYTELIST.dup());
- }
-
- return runtime.getNil();
- }
-
- // Removed while working on JRUBY-2386, since fixes for that
- // modified EOF logic such that this check is not really valid.
- // We expect that an EOFException will be thrown now in EOF
- // cases.
-// if (length > 0) {
-// // I think this is only partly correct; sys fail based on errno in Ruby
-// throw getRuntime().newEOFError();
-// }
- }
-
-
- // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
-
- // FIXME: I don't like the null checks here
- if (str == null) {
- if (newBuffer == null) {
- str = RubyString.newEmptyString(runtime);
- } else {
- str = RubyString.newString(runtime, newBuffer);
- }
- } else {
- if (newBuffer == null) {
- str.empty();
- } else {
- str.setValue(newBuffer);
- }
- }
- str.setTaint(true);
-
- return str;
- } catch (EOFException ex) {
- throw runtime.newEOFError();
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (IOException ex) {
- throw runtime.newIOErrorFromException(ex);
- } catch (BadDescriptorException ex) {
- throw runtime.newErrnoEBADFError();
- }
- }
-
- protected IRubyObject readAll(IRubyObject buffer) throws BadDescriptorException, EOFException, IOException {
- Ruby runtime = getRuntime();
- // TODO: handle writing into original buffer better
-
- RubyString str = null;
- if (buffer instanceof RubyString) {
- str = (RubyString)buffer;
- }
-
- // TODO: ruby locks the string here
-
- // READ_CHECK from MRI io.c
- if (openFile.getMainStream().readDataBuffered()) {
- openFile.checkClosed(runtime);
- }
-
- ByteList newBuffer = openFile.getMainStream().readall();
-
- // TODO same zero-length checks as file above
-
- if (str == null) {
- if (newBuffer == null) {
- str = RubyString.newEmptyString(runtime);
- } else {
- str = RubyString.newString(runtime, newBuffer);
- }
- } else {
- if (newBuffer == null) {
- str.empty();
- } else {
- str.setValue(newBuffer);
- }
- }
-
- str.taint(runtime.getCurrentContext());
-
- return str;
-// long bytes = 0;
-// long n;
-//
-// if (siz == 0) siz = BUFSIZ;
-// if (NIL_P(str)) {
-// str = rb_str_new(0, siz);
-// }
-// else {
-// rb_str_resize(str, siz);
-// }
-// for (;;) {
-// rb_str_locktmp(str);
-// READ_CHECK(fptr->f);
-// n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr);
-// rb_str_unlocktmp(str);
-// if (n == 0 && bytes == 0) {
-// if (!fptr->f) break;
-// if (feof(fptr->f)) break;
-// if (!ferror(fptr->f)) break;
-// rb_sys_fail(fptr->path);
-// }
-// bytes += n;
-// if (bytes < siz) break;
-// siz += BUFSIZ;
-// rb_str_resize(str, siz);
-// }
-// if (bytes != siz) rb_str_resize(str, bytes);
-// OBJ_TAINT(str);
-//
-// return str;
- }
-
- // TODO: There's a lot of complexity here due to error handling and
- // nonblocking IO; much of this goes away, but for now I'm just
- // having read call ChannelStream.fread directly.
-// protected int fread(int len, ByteList buffer) {
-// long n = len;
-// int c;
-// int saved_errno;
-//
-// while (n > 0) {
-// c = read_buffered_data(ptr, n, fptr->f);
-// if (c < 0) goto eof;
-// if (c > 0) {
-// ptr += c;
-// if ((n -= c) <= 0) break;
-// }
-// rb_thread_wait_fd(fileno(fptr->f));
-// rb_io_check_closed(fptr);
-// clearerr(fptr->f);
-// TRAP_BEG;
-// c = getc(fptr->f);
-// TRAP_END;
-// if (c == EOF) {
-// eof:
-// if (ferror(fptr->f)) {
-// switch (errno) {
-// case EINTR:
-// #if defined(ERESTART)
-// case ERESTART:
-// #endif
-// clearerr(fptr->f);
-// continue;
-// case EAGAIN:
-// #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
-// case EWOULDBLOCK:
-// #endif
-// if (len > n) {
-// clearerr(fptr->f);
-// }
-// saved_errno = errno;
-// rb_warning("nonblocking IO#read is obsolete; use IO#readpartial or IO#sysread");
-// errno = saved_errno;
-// }
-// if (len == n) return 0;
-// }
-// break;
-// }
-// *ptr++ = c;
-// n--;
-// }
-// return len - n;
-//
-// }
-
- /** Read a byte. On EOF throw EOFError.
- *
- */
- @JRubyMethod(name = "readchar")
- public IRubyObject readchar() {
- IRubyObject c = getc();
-
- if (c.isNil()) throw getRuntime().newEOFError();
-
- return c;
- }
-
- @JRubyMethod
- public IRubyObject stat(ThreadContext context) {
- openFile.checkClosed(context.getRuntime());
- return context.getRuntime().newFileStat(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor());
- }
-
- /**
- * <p>Invoke a block for each byte.</p>
- */
- @JRubyMethod(name = "each_byte", frame = true)
- public IRubyObject each_byte(ThreadContext context, Block block) {
- Ruby runtime = context.getRuntime();
-
- try {
- OpenFile myOpenFile = getOpenFileChecked();
-
- while (true) {
- myOpenFile.checkReadable(runtime);
- myOpenFile.setReadBuffered();
-
- // TODO: READ_CHECK from MRI
-
- int c = myOpenFile.getMainStream().fgetc();
-
- if (c == -1) {
- // TODO: check for error, clear it, and wait until readable before trying once more
-// if (ferror(f)) {
-// clearerr(f);
-// if (!rb_io_wait_readable(fileno(f)))
-// rb_sys_fail(fptr->path);
-// continue;
-// }
- break;
- }
-
- assert c < 256;
- block.yield(context, getRuntime().newFixnum(c));
- }
-
- // TODO: one more check for error
-// if (ferror(f)) rb_sys_fail(fptr->path);
- return this;
- } catch (PipeException ex) {
- throw runtime.newErrnoEPIPEError();
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (BadDescriptorException e) {
- throw runtime.newErrnoEBADFError();
- } catch (EOFException e) {
- return runtime.getNil();
- } catch (IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- /**
- * <p>Invoke a block for each line.</p>
- */
- @JRubyMethod(name = {"each_line", "each"}, optional = 1, frame = true)
- public RubyIO each_line(ThreadContext context, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
- ByteList separator = getSeparatorForGets(runtime, args);
-
- for (IRubyObject line = getline(runtime, separator); !line.isNil();
- line = getline(runtime, separator)) {
- block.yield(context, line);
- }
-
- return this;
- }
-
-
- @JRubyMethod(name = "readlines", optional = 1)
- public RubyArray readlines(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- IRubyObject[] separatorArgs = args.length > 0 ? new IRubyObject[] { args[0] } : IRubyObject.NULL_ARRAY;
- ByteList separator = getSeparatorForGets(runtime, separatorArgs);
- RubyArray result = runtime.newArray();
- IRubyObject line;
-
- while (! (line = getline(runtime, separator)).isNil()) {
- result.append(line);
- }
- return result;
- }
-
- @JRubyMethod(name = "to_io")
- public RubyIO to_io() {
- return this;
- }
-
- @Override
- public String toString() {
- return "RubyIO(" + openFile.getMode() + ", " + openFile.getMainStream().getDescriptor().getFileno() + ")";
- }
-
- /* class methods for IO */
-
- /** rb_io_s_foreach
- *
- */
- @JRubyMethod(name = "foreach", required = 1, optional = 1, frame = true, meta = true)
- public static IRubyObject foreach(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
- int count = args.length;
- IRubyObject filename = args[0].convertToString();
- runtime.checkSafeString(filename);
-
- ByteList separator = getSeparatorFromArgs(runtime, args, 1);
-
- RubyIO io = (RubyIO)RubyFile.open(context, runtime.getFile(), new IRubyObject[] { filename }, Block.NULL_BLOCK);
-
- if (!io.isNil()) {
- try {
- IRubyObject str = io.getline(runtime, separator);
- while (!str.isNil()) {
- block.yield(context, str);
- str = io.getline(runtime, separator);
- }
- } finally {
- io.close();
- }
- }
-
- return runtime.getNil();
- }
-
- private static RubyIO convertToIO(ThreadContext context, IRubyObject obj) {
- return (RubyIO)TypeConverter.convertToType(obj, context.getRuntime().getIO(), MethodIndex.TO_IO, "to_io");
- }
-
- private static boolean registerSelect(ThreadContext context, Selector selector, IRubyObject obj, RubyIO ioObj, int ops) throws IOException {
- Channel channel = ioObj.getChannel();
- if (channel == null || !(channel instanceof SelectableChannel)) {
- return false;
- }
-
- ((SelectableChannel) channel).configureBlocking(false);
- int real_ops = ((SelectableChannel) channel).validOps() & ops;
- SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
-
- if (key == null) {
- ((SelectableChannel) channel).register(selector, real_ops, obj);
- } else {
- key.interestOps(key.interestOps()|real_ops);
- }
-
- return true;
- }
-
- @JRubyMethod(name = "select", required = 1, optional = 3, meta = true)
- public static IRubyObject select(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return select_static(context, context.getRuntime(), args);
- }
-
- private static void checkArrayType(Ruby runtime, IRubyObject obj) {
- if (!(obj instanceof RubyArray)) {
- throw runtime.newTypeError("wrong argument type "
- + obj.getMetaClass().getName() + " (expected Array)");
- }
- }
-
- public static IRubyObject select_static(ThreadContext context, Ruby runtime, IRubyObject[] args) {
- try {
- Set pending = new HashSet();
- Set unselectable_reads = new HashSet();
- Set unselectable_writes = new HashSet();
- Selector selector = Selector.open();
- if (!args[0].isNil()) {
- // read
- checkArrayType(runtime, args[0]);
- for (Iterator i = ((RubyArray) args[0]).getList().iterator(); i.hasNext(); ) {
- IRubyObject obj = (IRubyObject) i.next();
- RubyIO ioObj = convertToIO(context, obj);
- if (registerSelect(context, selector, obj, ioObj, SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) {
- if (ioObj.writeDataBuffered()) {
- pending.add(obj);
- }
- } else {
- if (( ioObj.openFile.getMode() & OpenFile.READABLE ) != 0) {
- unselectable_reads.add(obj);
- }
- }
- }
- }
-
- if (args.length > 1 && !args[1].isNil()) {
- // write
- checkArrayType(runtime, args[1]);
- for (Iterator i = ((RubyArray) args[1]).getList().iterator(); i.hasNext(); ) {
- IRubyObject obj = (IRubyObject) i.next();
- RubyIO ioObj = convertToIO(context, obj);
- if (!registerSelect(context, selector, obj, ioObj, SelectionKey.OP_WRITE)) {
- if (( ioObj.openFile.getMode() & OpenFile.WRITABLE ) != 0) {
- unselectable_writes.add(obj);
- }
- }
- }
- }
-
- if (args.length > 2 && !args[2].isNil()) {
- checkArrayType(runtime, args[2]);
- // Java's select doesn't do anything about this, so we leave it be.
- }
-
- final boolean has_timeout = ( args.length > 3 && !args[3].isNil() );
- long timeout = 0;
- if(has_timeout) {
- IRubyObject timeArg = args[3];
- if (timeArg instanceof RubyFloat) {
- timeout = Math.round(((RubyFloat) timeArg).getDoubleValue() * 1000);
- } else if (timeArg instanceof RubyFixnum) {
- timeout = Math.round(((RubyFixnum) timeArg).getDoubleValue() * 1000);
- } else { // TODO: MRI also can hadle Bignum here
- throw runtime.newTypeError("can't convert "
- + timeArg.getMetaClass().getName() + " into time interval");
- }
-
- if (timeout < 0) {
- throw runtime.newArgumentError("negative timeout given");
- }
- }
-
- if (pending.isEmpty() && unselectable_reads.isEmpty() && unselectable_writes.isEmpty()) {
- if (has_timeout) {
- if (timeout==0) {
- selector.selectNow();
- } else {
- selector.select(timeout);
- }
- } else {
- selector.select();
- }
- } else {
- selector.selectNow();
- }
-
- List r = new ArrayList();
- List w = new ArrayList();
- List e = new ArrayList();
- for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
- SelectionKey key = (SelectionKey) i.next();
- if ((key.interestOps() & key.readyOps()
- & (SelectionKey.OP_READ|SelectionKey.OP_ACCEPT|SelectionKey.OP_CONNECT)) != 0) {
- r.add(key.attachment());
- pending.remove(key.attachment());
- }
- if ((key.interestOps() & key.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
- w.add(key.attachment());
- }
- }
- r.addAll(pending);
- r.addAll(unselectable_reads);
- w.addAll(unselectable_writes);
-
- // make all sockets blocking as configured again
- for (Iterator i = selector.keys().iterator(); i.hasNext(); ) {
- SelectionKey key = (SelectionKey) i.next();
- SelectableChannel channel = key.channel();
- synchronized(channel.blockingLock()) {
- RubyIO originalIO = (RubyIO) TypeConverter.convertToType(
- (IRubyObject) key.attachment(), runtime.getIO(),
- MethodIndex.TO_IO, "to_io");
- boolean blocking = originalIO.getBlocking();
- key.cancel();
- channel.configureBlocking(blocking);
- }
- }
- selector.close();
-
- if (r.size() == 0 && w.size() == 0 && e.size() == 0) {
- return runtime.getNil();
- }
-
- List ret = new ArrayList();
-
- ret.add(RubyArray.newArray(runtime, r));
- ret.add(RubyArray.newArray(runtime, w));
- ret.add(RubyArray.newArray(runtime, e));
-
- return RubyArray.newArray(runtime, ret);
- } catch(IOException e) {
- throw runtime.newIOError(e.getMessage());
- }
- }
-
- public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 0: throw context.getRuntime().newArgumentError(0, 1);
- case 1: return read(context, recv, args[0], block);
- case 2: return read(context, recv, args[0], args[1], block);
- case 3: return read(context, recv, args[0], args[1], args[2], block);
- default: throw context.getRuntime().newArgumentError(args.length, 3);
- }
- }
-
- @JRubyMethod(name = "read", meta = true)
- public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
- IRubyObject[] fileArguments = new IRubyObject[] {arg0};
- RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
-
- try {
- return file.read(context);
- } finally {
- file.close();
- }
- }
-
- @JRubyMethod(name = "read", meta = true)
- public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
- IRubyObject[] fileArguments = new IRubyObject[] {arg0};
- RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
-
- try {
- if (!arg1.isNil()) {
- return file.read(context, arg1);
- } else {
- return file.read(context);
- }
- } finally {
- file.close();
- }
- }
-
- @JRubyMethod(name = "read", meta = true)
- public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- IRubyObject[] fileArguments = new IRubyObject[]{arg0};
- RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
-
- if (!arg2.isNil()) {
- file.seek(context, arg2);
- }
-
- try {
- if (!arg1.isNil()) {
- return file.read(context, arg1);
- } else {
- return file.read(context);
- }
- } finally {
- file.close();
- }
- }
-
- @JRubyMethod(name = "readlines", required = 1, optional = 1, meta = true)
- public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- int count = args.length;
-
- IRubyObject[] fileArguments = new IRubyObject[]{ args[0].convertToString() };
- IRubyObject[] separatorArguments = count >= 2 ? new IRubyObject[]{args[1]} : IRubyObject.NULL_ARRAY;
- RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
- try {
- return file.readlines(context, separatorArguments);
- } finally {
- file.close();
- }
- }
-
- @JRubyMethod(name = "popen", required = 1, optional = 1, meta = true)
- public static IRubyObject popen(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
- int mode;
-
- IRubyObject cmdObj = args[0].convertToString();
- runtime.checkSafeString(cmdObj);
-
- if ("-".equals(cmdObj.toString())) {
- throw runtime.newNotImplementedError("popen(\"-\") is unimplemented");
- }
-
- try {
- if (args.length == 1) {
- mode = ModeFlags.RDONLY;
- } else if (args[1] instanceof RubyFixnum) {
- mode = RubyFixnum.num2int(args[1]);
- } else {
- mode = getIOModesIntFromString(runtime, args[1].convertToString().toString());
- }
-
- ModeFlags modes = new ModeFlags(mode);
-
- ShellLauncher.POpenProcess process = ShellLauncher.popen(runtime, cmdObj, modes);
- RubyIO io = new RubyIO(runtime, process, modes);
-
- if (block.isGiven()) {
- try {
- return block.yield(context, io);
- } finally {
- if (io.openFile.isOpen()) {
- io.close();
- }
- runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, (process.waitFor() * 256)));
- }
- }
- return io;
- } catch (InvalidValueException ex) {
- throw runtime.newErrnoEINVALError();
- } catch (IOException e) {
- throw runtime.newIOErrorFromException(e);
- } catch (InterruptedException e) {
- throw runtime.newThreadError("unexpected interrupt");
- }
- }
-
- // NIO based pipe
- @JRubyMethod(name = "pipe", meta = true)
- public static IRubyObject pipe(ThreadContext context, IRubyObject recv) throws Exception {
- // TODO: This isn't an exact port of MRI's pipe behavior, so revisit
- Ruby runtime = context.getRuntime();
- Pipe pipe = Pipe.open();
-
- RubyIO source = new RubyIO(runtime, pipe.source());
- RubyIO sink = new RubyIO(runtime, pipe.sink());
-
- sink.openFile.getMainStream().setSync(true);
- return runtime.newArrayNoCopy(new IRubyObject[] { source, sink });
- }
-
- @JRubyMethod(name = "copy_stream", meta = true, compat = RUBY1_9)
- public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv,
- IRubyObject stream1, IRubyObject stream2) throws IOException {
- RubyIO io1 = (RubyIO)stream1;
- RubyIO io2 = (RubyIO)stream2;
-
- ChannelDescriptor d1 = io1.openFile.getMainStream().getDescriptor();
- if (!d1.isSeekable()) {
- throw context.getRuntime().newTypeError("only supports file-to-file copy");
- }
- ChannelDescriptor d2 = io2.openFile.getMainStream().getDescriptor();
- if (!d2.isSeekable()) {
- throw context.getRuntime().newTypeError("only supports file-to-file copy");
- }
-
- FileChannel f1 = (FileChannel)d1.getChannel();
- FileChannel f2 = (FileChannel)d2.getChannel();
-
- long size = f1.size();
-
- f1.transferTo(f2.position(), size, f2);
-
- return context.getRuntime().newFixnum(size);
- }
-
- /**
- * Add a thread to the list of blocking threads for this IO.
- *
- * @param thread A thread blocking on this IO
- */
- public synchronized void addBlockingThread(RubyThread thread) {
- if (blockingThreads == null) {
- blockingThreads = new ArrayList<RubyThread>(1);
- }
- blockingThreads.add(thread);
- }
-
- /**
- * Remove a thread from the list of blocking threads for this IO.
- *
- * @param thread A thread blocking on this IO
- */
- public synchronized void removeBlockingThread(RubyThread thread) {
- if (blockingThreads == null) {
- return;
- }
- for (int i = 0; i < blockingThreads.size(); i++) {
- if (blockingThreads.get(i) == thread) {
- // not using remove(Object) here to avoid the equals() call
- blockingThreads.remove(i);
- }
- }
- }
-
- /**
- * Fire an IOError in all threads blocking on this IO object
- */
- protected synchronized void interruptBlockingThreads() {
- if (blockingThreads == null) {
- return;
- }
- for (int i = 0; i < blockingThreads.size(); i++) {
- RubyThread thread = blockingThreads.get(i);
-
- // raise will also wake the thread from selection
- thread.raise(new IRubyObject[] {getRuntime().newIOError("stream closed").getException()}, Block.NULL_BLOCK);
- }
- }
-}
-/*
- **** 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 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 java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.anno.JRubyClass;
-
-import org.jruby.ast.ArgsNode;
-import org.jruby.ast.ArgumentNode;
-import org.jruby.ast.ListNode;
-import org.jruby.ast.LocalAsgnNode;
-import org.jruby.javasupport.Java;
-import org.jruby.javasupport.JavaObject;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.load.Library;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-
-import org.jruby.ast.Node;
-import org.jruby.compiler.ASTInspector;
-import org.jruby.compiler.ASTCompiler;
-import org.jruby.compiler.impl.StandardASMCompiler;
-import org.jruby.internal.runtime.methods.MethodArgs;
-import org.jruby.javasupport.JavaUtil;
-import org.jruby.runtime.InterpretedBlock;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.util.TypeConverter;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.util.TraceClassVisitor;
-
-/**
- * Module which defines JRuby-specific methods for use.
- */
-@JRubyModule(name="JRuby")
-public class RubyJRuby {
- public static RubyModule createJRuby(Ruby runtime) {
- ThreadContext context = runtime.getCurrentContext();
- runtime.getKernel().callMethod(context, "require", runtime.newString("java"));
- RubyModule jrubyModule = runtime.defineModule("JRuby");
-
- jrubyModule.defineAnnotatedMethods(RubyJRuby.class);
-
- RubyClass compiledScriptClass = jrubyModule.defineClassUnder("CompiledScript",runtime.getObject(), runtime.getObject().getAllocator());
-
- compiledScriptClass.attr_accessor(context, new IRubyObject[]{runtime.newSymbol("name"), runtime.newSymbol("class_name"), runtime.newSymbol("original_script"), runtime.newSymbol("code")});
- compiledScriptClass.defineAnnotatedMethods(JRubyCompiledScript.class);
-
- return jrubyModule;
- }
-
- public static RubyModule createJRubyExt(Ruby runtime) {
- runtime.getKernel().callMethod(runtime.getCurrentContext(),"require", runtime.newString("java"));
- RubyModule mJRubyExt = runtime.getOrCreateModule("JRuby").defineModuleUnder("Extensions");
-
- mJRubyExt.defineAnnotatedMethods(JRubyExtensions.class);
-
- runtime.getObject().includeModule(mJRubyExt);
-
- return mJRubyExt;
- }
-
- public static class ExtLibrary implements Library {
- public void load(Ruby runtime, boolean wrap) throws IOException {
- RubyJRuby.createJRubyExt(runtime);
-
- runtime.getMethod().defineAnnotatedMethods(MethodExtensions.class);
- }
- }
-
- public static class TypeLibrary implements Library {
- public void load(Ruby runtime, boolean wrap) throws IOException {
- RubyModule jrubyType = runtime.defineModule("Type");
- jrubyType.defineAnnotatedMethods(TypeLibrary.class);
- }
-
- @JRubyMethod(module = true)
- public static IRubyObject coerce_to(ThreadContext context, IRubyObject self, IRubyObject object, IRubyObject clazz, IRubyObject method) {
- Ruby ruby = object.getRuntime();
-
- if (!(clazz instanceof RubyClass)) {
- throw ruby.newTypeError(clazz, ruby.getClassClass());
- }
- if (!(method instanceof RubySymbol)) {
- throw ruby.newTypeError(method, ruby.getSymbol());
- }
-
- RubyClass rubyClass = (RubyClass)clazz;
- RubySymbol methodSym = (RubySymbol)method;
-
- return TypeConverter.convertToTypeOrRaise(object, rubyClass, methodSym.asJavaString());
- }
- }
-
- @JRubyMethod(name = "runtime", frame = true, module = true)
- public static IRubyObject runtime(IRubyObject recv, Block unusedBlock) {
- return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), recv.getRuntime()), Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "objectspace", frame = true, module = true)
- public static IRubyObject getObjectSpaceEnabled(IRubyObject recv, Block b) {
- Ruby runtime = recv.getRuntime();
- return RubyBoolean.newBoolean(
- runtime, runtime.isObjectSpaceEnabled());
- }
-
- @JRubyMethod(name = "objectspace=", required = 1, frame = true, module = true)
- public static IRubyObject setObjectSpaceEnabled(
- IRubyObject recv, IRubyObject arg, Block b) {
- Ruby runtime = recv.getRuntime();
- runtime.setObjectSpaceEnabled(arg.isTrue());
- return runtime.getNil();
- }
-
- @JRubyMethod(name = {"parse", "ast_for"}, optional = 3, frame = true, module = true)
- public static IRubyObject parse(IRubyObject recv, IRubyObject[] args, Block block) {
- if(block.isGiven()) {
- if(block.getBody() instanceof org.jruby.runtime.CompiledBlock) {
- throw new RuntimeException("Cannot compile an already compiled block. Use -J-Djruby.jit.enabled=false to avoid this problem.");
- }
- Arity.checkArgumentCount(recv.getRuntime(),args,0,0);
- return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), ((InterpretedBlock)block.getBody()).getIterNode().getBodyNode()), Block.NULL_BLOCK);
- } else {
- Arity.checkArgumentCount(recv.getRuntime(),args,1,3);
- String filename = "-";
- boolean extraPositionInformation = false;
- RubyString content = args[0].convertToString();
- if(args.length>1) {
- filename = args[1].convertToString().toString();
- if(args.length>2) {
- extraPositionInformation = args[2].isTrue();
- }
- }
- return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(),
- recv.getRuntime().parse(content.getByteList(), filename, null, 0, extraPositionInformation)), Block.NULL_BLOCK);
- }
- }
-
- @JRubyMethod(name = "compile", optional = 3, frame = true, module = true)
- public static IRubyObject compile(IRubyObject recv, IRubyObject[] args, Block block) {
- Node node;
- String filename;
- RubyString content;
- if(block.isGiven()) {
- Arity.checkArgumentCount(recv.getRuntime(),args,0,0);
- if(block.getBody() instanceof org.jruby.runtime.CompiledBlock) {
- throw new RuntimeException("Cannot compile an already compiled block. Use -J-Djruby.jit.enabled=false to avoid this problem.");
- }
- content = RubyString.newEmptyString(recv.getRuntime());
- Node bnode = ((InterpretedBlock)block.getBody()).getIterNode().getBodyNode();
- node = new org.jruby.ast.RootNode(bnode.getPosition(), block.getBinding().getDynamicScope(), bnode);
- filename = "__block_" + node.getPosition().getFile();
- } else {
- Arity.checkArgumentCount(recv.getRuntime(),args,1,3);
- filename = "-";
- boolean extraPositionInformation = false;
- content = args[0].convertToString();
- if(args.length>1) {
- filename = args[1].convertToString().toString();
- if(args.length>2) {
- extraPositionInformation = args[2].isTrue();
- }
- }
-
- node = recv.getRuntime().parse(content.getByteList(), filename, null, 0, extraPositionInformation);
- }
-
- String classname;
- if (filename.equals("-e")) {
- classname = "__dash_e__";
- } else {
- classname = filename.replace('\\', '/').replaceAll(".rb", "").replaceAll("-","_dash_");
- }
-
- ASTInspector inspector = new ASTInspector();
- inspector.inspect(node);
-
- StandardASMCompiler asmCompiler = new StandardASMCompiler(classname, filename);
- ASTCompiler compiler = new ASTCompiler();
- compiler.compileRoot(node, asmCompiler, inspector);
- byte[] bts = asmCompiler.getClassByteArray();
-
- IRubyObject compiledScript = ((RubyModule)recv).fastGetConstant("CompiledScript").callMethod(recv.getRuntime().getCurrentContext(),"new");
- compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "name=", recv.getRuntime().newString(filename));
- compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "class_name=", recv.getRuntime().newString(classname));
- compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "original_script=", content);
- compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "code=", Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), bts), Block.NULL_BLOCK));
-
- return compiledScript;
- }
-
- @JRubyMethod(name = "reference", required = 1, module = true)
- public static IRubyObject reference(IRubyObject recv, IRubyObject obj) {
- return Java.wrap(recv.getRuntime().getJavaSupport().getJavaUtilitiesModule(),
- JavaObject.wrap(recv.getRuntime(), obj));
- }
-
- @JRubyMethod(name = "dereference", required = 1, module = true)
- public static IRubyObject dereference(ThreadContext context, IRubyObject recv, IRubyObject obj) {
- if (!(obj.dataGetStruct() instanceof JavaObject)) {
- throw context.getRuntime().newTypeError("got " + obj + ", expected wrapped Java object");
- }
-
- Object unwrapped = JavaUtil.unwrapJavaObject(obj);
-
- if (!(unwrapped instanceof IRubyObject)) {
- throw context.getRuntime().newTypeError("got " + obj + ", expected Java-wrapped Ruby object");
- }
-
- return (IRubyObject)unwrapped;
- }
-
- @JRubyClass(name="JRuby::CompiledScript")
- public static class JRubyCompiledScript {
- @JRubyMethod(name = "to_s")
- public static IRubyObject compiled_script_to_s(IRubyObject recv) {
- return recv.getInstanceVariables().fastGetInstanceVariable("@original_script");
- }
-
- @JRubyMethod(name = "inspect")
- public static IRubyObject compiled_script_inspect(IRubyObject recv) {
- return recv.getRuntime().newString("#<JRuby::CompiledScript " + recv.getInstanceVariables().fastGetInstanceVariable("@name") + ">");
- }
-
- @JRubyMethod(name = "inspect_bytecode")
- public static IRubyObject compiled_script_inspect_bytecode(IRubyObject recv) {
- StringWriter sw = new StringWriter();
- ClassReader cr = new ClassReader((byte[])org.jruby.javasupport.JavaUtil.convertRubyToJava(recv.getInstanceVariables().fastGetInstanceVariable("@code"),byte[].class));
- TraceClassVisitor cv = new TraceClassVisitor(new PrintWriter(sw));
- cr.accept(cv, ClassReader.SKIP_DEBUG);
- return recv.getRuntime().newString(sw.toString());
- }
- }
-
- @JRubyModule(name="JRubyExtensions")
- public static class JRubyExtensions {
- @JRubyMethod(name = "steal_method", required = 2, module = true)
- public static IRubyObject steal_method(IRubyObject recv, IRubyObject type, IRubyObject methodName) {
- RubyModule to_add = null;
- if(recv instanceof RubyModule) {
- to_add = (RubyModule)recv;
- } else {
- to_add = recv.getSingletonClass();
- }
- String name = methodName.toString();
- if(!(type instanceof RubyModule)) {
- throw recv.getRuntime().newArgumentError("First argument must be a module/class");
- }
-
- DynamicMethod method = ((RubyModule)type).searchMethod(name);
- if(method == null || method.isUndefined()) {
- throw recv.getRuntime().newArgumentError("No such method " + name + " on " + type);
- }
-
- to_add.addMethod(name, method);
- return recv.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "steal_methods", required = 1, rest = true, module = true)
- public static IRubyObject steal_methods(IRubyObject recv, IRubyObject[] args) {
- IRubyObject type = args[0];
- for(int i=1;i<args.length;i++) {
- steal_method(recv, type, args[i]);
- }
- return recv.getRuntime().getNil();
- }
- }
-
- public static class MethodExtensions {
- @JRubyMethod(name = "args")
- public static IRubyObject methodArgs(IRubyObject recv) {
- Ruby ruby = recv.getRuntime();
- RubyMethod rubyMethod = (RubyMethod)recv;
-
- DynamicMethod method = rubyMethod.method;
-
- if (method instanceof MethodArgs) {
- MethodArgs interpMethod = (MethodArgs)method;
- ArgsNode args = interpMethod.getArgsNode();
- RubyArray argsArray = RubyArray.newArray(ruby);
-
- RubyArray reqArray = RubyArray.newArray(ruby);
- ListNode requiredArgs = args.getArgs();
- for (int i = 0; requiredArgs != null && i < requiredArgs.size(); i++) {
- ArgumentNode arg = (ArgumentNode)requiredArgs.get(i);
- reqArray.append(RubySymbol.newSymbol(ruby, arg.getName()));
- }
- argsArray.append(reqArray);
-
- RubyArray optArray = RubyArray.newArray(ruby);
- ListNode optArgs = args.getOptArgs();
- for (int i = 0; optArgs != null && i < optArgs.size(); i++) {
- LocalAsgnNode arg = (LocalAsgnNode)optArgs.get(i);
- optArray.append(RubySymbol.newSymbol(ruby, arg.getName()));
- }
- argsArray.append(optArray);
-
- if (args.getRestArgNode() != null) {
- argsArray.append(RubySymbol.newSymbol(ruby, args.getRestArgNode().getName()));
- } else {
- argsArray.append(ruby.getNil());
- }
-
- if (args.getBlockArgNode() != null) {
- argsArray.append(RubySymbol.newSymbol(ruby, args.getBlockArgNode().getName()));
- } else {
- argsArray.append(ruby.getNil());
- }
-
- return argsArray;
- }
-
- throw ruby.newTypeError("Method args are only available for standard interpreted or jitted methods");
- }
- }
-}
-/*
- ***** 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-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004-2005 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) 2006 Evan Buswell <evan@heron.sytes.net>
- * Copyright (C) 2006 Ola Bini <ola@ologix.com>
- * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.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.ByteArrayOutputStream;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import static org.jruby.anno.FrameField.*;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-
-import org.jruby.ast.util.ArgsUtil;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.evaluator.ASTInterpreter;
-import org.jruby.exceptions.JumpException;
-import org.jruby.exceptions.MainExitException;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.internal.runtime.JumpTarget;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallType;
-import org.jruby.runtime.Frame;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.Visibility;
-import static org.jruby.runtime.Visibility.*;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.load.IAutoloadMethod;
-import org.jruby.runtime.load.LoadService;
-import org.jruby.util.IdUtil;
-import org.jruby.util.ShellLauncher;
-import org.jruby.util.TypeConverter;
-
-/**
- * Note: For CVS history, see KernelModule.java.
- */
-@JRubyModule(name="Kernel")
-public class RubyKernel {
- public final static Class<?> IRUBY_OBJECT = IRubyObject.class;
-
- public static RubyModule createKernelModule(Ruby runtime) {
- RubyModule module = runtime.defineModule("Kernel");
- runtime.setKernel(module);
-
- module.defineAnnotatedMethods(RubyKernel.class);
- module.defineAnnotatedMethods(RubyObject.class);
-
- runtime.setRespondToMethod(module.searchMethod("respond_to?"));
-
- module.setFlag(RubyObject.USER7_F, false); //Kernel is the only Module that doesn't need an implementor
-
- return module;
- }
-
- @JRubyMethod(name = "at_exit", frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject at_exit(ThreadContext context, IRubyObject recv, Block block) {
- return context.getRuntime().pushExitBlock(context.getRuntime().newProc(Block.Type.PROC, block));
- }
-
- @JRubyMethod(name = "autoload?", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject autoload_p(ThreadContext context, final IRubyObject recv, IRubyObject symbol) {
- Ruby runtime = context.getRuntime();
- RubyModule module = recv instanceof RubyModule ? (RubyModule) recv : runtime.getObject();
- String name = module.getName() + "::" + symbol.asJavaString();
-
- IAutoloadMethod autoloadMethod = runtime.getLoadService().autoloadFor(name);
- if (autoloadMethod == null) return runtime.getNil();
-
- return runtime.newString(autoloadMethod.file());
- }
-
- @JRubyMethod(name = "autoload", required = 2, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject autoload(final IRubyObject recv, IRubyObject symbol, final IRubyObject file) {
- Ruby runtime = recv.getRuntime();
- final LoadService loadService = runtime.getLoadService();
- String nonInternedName = symbol.asJavaString();
-
- if (!IdUtil.isValidConstantName(nonInternedName)) {
- throw runtime.newNameError("autoload must be constant name", nonInternedName);
- }
-
- RubyString fileString = file.convertToString();
-
- if (fileString.isEmpty()) {
- throw runtime.newArgumentError("empty file name");
- }
-
- final String baseName = symbol.asJavaString().intern(); // interned, OK for "fast" methods
- final RubyModule module = recv instanceof RubyModule ? (RubyModule) recv : runtime.getObject();
- String nm = module.getName() + "::" + baseName;
-
- IRubyObject existingValue = module.fastFetchConstant(baseName);
- if (existingValue != null && existingValue != RubyObject.UNDEF) return runtime.getNil();
-
- module.fastStoreConstant(baseName, RubyObject.UNDEF);
-
- loadService.addAutoload(nm, new IAutoloadMethod() {
- public String file() {
- return file.toString();
- }
- /**
- * @see org.jruby.runtime.load.IAutoloadMethod#load(Ruby, String)
- */
- public IRubyObject load(Ruby runtime, String name) {
- boolean required = loadService.require(file());
-
- // File to be loaded by autoload has already been or is being loaded.
- if (!required) return null;
-
- return module.fastGetConstant(baseName);
- }
- });
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "method_missing", rest = true, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject method_missing(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
-
- if (args.length == 0 || !(args[0] instanceof RubySymbol)) throw runtime.newArgumentError("no id given");
-
- Visibility lastVis = context.getLastVisibility();
- CallType lastCallType = context.getLastCallType();
-
- // create a lightweight thunk
- IRubyObject msg = new RubyNameError.RubyNameErrorMessage(runtime,
- recv,
- args[0],
- lastVis,
- lastCallType);
- final IRubyObject[]exArgs;
- final RubyClass exc;
- if (lastCallType != CallType.VARIABLE) {
- exc = runtime.getNoMethodError();
- exArgs = new IRubyObject[]{msg, args[0], RubyArray.newArrayNoCopy(runtime, args, 1)};
- } else {
- exc = runtime.getNameError();
- exArgs = new IRubyObject[]{msg, args[0]};
- }
-
- throw new RaiseException((RubyException)exc.newInstance(context, exArgs, Block.NULL_BLOCK));
- }
-
- @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- String arg = args[0].convertToString().toString();
- Ruby runtime = context.getRuntime();
-
- if (arg.startsWith("|")) {
- String command = arg.substring(1);
- // exec process, create IO with process
- return RubyIO.popen(context, runtime.getIO(), new IRubyObject[] {runtime.newString(command)}, block);
- }
-
- return RubyFile.open(context, runtime.getFile(), args, block);
- }
-
- @JRubyMethod(name = "getc", module = true, visibility = PRIVATE)
- public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
- context.getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "getc is obsolete; use STDIN.getc instead", "getc", "STDIN.getc");
- IRubyObject defin = context.getRuntime().getGlobalVariables().get("$stdin");
- return defin.callMethod(context, "getc");
- }
-
- @JRubyMethod(name = "gets", optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return RubyArgsFile.gets(context, context.getRuntime().getGlobalVariables().get("$<"), args);
- }
-
- @JRubyMethod(name = "abort", optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject abort(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- if(args.length == 1) {
- context.getRuntime().getGlobalVariables().get("$stderr").callMethod(context,"puts",args[0]);
- }
- throw new MainExitException(1,true);
- }
-
- @JRubyMethod(name = "Array", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject new_array(ThreadContext context, IRubyObject recv, IRubyObject object) {
- IRubyObject value = object.checkArrayType();
-
- if (value.isNil()) {
- if (object.getMetaClass().searchMethod("to_a").getImplementationClass() != context.getRuntime().getKernel()) {
- value = object.callMethod(context, MethodIndex.TO_A, "to_a");
- if (!(value instanceof RubyArray)) throw context.getRuntime().newTypeError("`to_a' did not return Array");
- return value;
- } else {
- return context.getRuntime().newArray(object);
- }
- }
- return value;
- }
-
- @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_complex(ThreadContext context, IRubyObject recv) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert");
- }
- @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_complex(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert", arg);
- }
- @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_complex(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert", arg0, arg1);
- }
-
- @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_rational(ThreadContext context, IRubyObject recv) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert");
- }
- @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_rational(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert", arg);
- }
- @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static IRubyObject new_rational(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert", arg0, arg1);
- }
-
- @JRubyMethod(name = "Float", module = true, visibility = PRIVATE)
- public static IRubyObject new_float(IRubyObject recv, IRubyObject object) {
- if(object instanceof RubyFixnum){
- return RubyFloat.newFloat(object.getRuntime(), ((RubyFixnum)object).getDoubleValue());
- }else if(object instanceof RubyFloat){
- return object;
- }else if(object instanceof RubyBignum){
- return RubyFloat.newFloat(object.getRuntime(), RubyBignum.big2dbl((RubyBignum)object));
- }else if(object instanceof RubyString){
- if(((RubyString)object).getByteList().realSize == 0){ // rb_cstr_to_dbl case
- throw recv.getRuntime().newArgumentError("invalid value for Float(): " + object.inspect());
- }
- return RubyNumeric.str2fnum(recv.getRuntime(),(RubyString)object,true);
- }else if(object.isNil()){
- throw recv.getRuntime().newTypeError("can't convert nil into Float");
- } else {
- RubyFloat rFloat = (RubyFloat)TypeConverter.convertToType(object, recv.getRuntime().getFloat(), MethodIndex.TO_F, "to_f");
- if (Double.isNaN(rFloat.getDoubleValue())) throw recv.getRuntime().newArgumentError("invalid value for Float()");
- return rFloat;
- }
- }
-
- @JRubyMethod(name = "Integer", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject new_integer(ThreadContext context, IRubyObject recv, IRubyObject object) {
- if (object instanceof RubyFloat) {
- double val = ((RubyFloat)object).getDoubleValue();
- if (val > (double) RubyFixnum.MAX && val < (double) RubyFixnum.MIN) {
- return RubyNumeric.dbl2num(context.getRuntime(),((RubyFloat)object).getDoubleValue());
- }
- } else if (object instanceof RubyFixnum || object instanceof RubyBignum) {
- return object;
- } else if (object instanceof RubyString) {
- return RubyNumeric.str2inum(context.getRuntime(),(RubyString)object,0,true);
- }
-
- IRubyObject tmp = TypeConverter.convertToType(object, context.getRuntime().getInteger(), MethodIndex.TO_INT, "to_int", false);
- if (tmp.isNil()) return object.convertToInteger(MethodIndex.TO_I, "to_i");
- return tmp;
- }
-
- @JRubyMethod(name = "String", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject new_string(ThreadContext context, IRubyObject recv, IRubyObject object) {
- return TypeConverter.convertToType(object, context.getRuntime().getString(), MethodIndex.TO_S, "to_s");
- }
-
- @JRubyMethod(name = "p", rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject p(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- IRubyObject defout = runtime.getGlobalVariables().get("$>");
-
- for (int i = 0; i < args.length; i++) {
- if (args[i] != null) {
- defout.callMethod(context, "write", RubyObject.inspect(context, args[i]));
- defout.callMethod(context, "write", runtime.newString("\n"));
- }
- }
-
- if (defout instanceof RubyFile) {
- ((RubyFile)defout).flush();
- }
-
- return context.getRuntime().getNil();
- }
-
- /** rb_f_putc
- */
- @JRubyMethod(name = "putc", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject putc(ThreadContext context, IRubyObject recv, IRubyObject ch) {
- IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
- return defout.callMethod(context, "putc", ch);
- }
-
- @JRubyMethod(name = "puts", rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject puts(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
-
- defout.callMethod(context, "puts", args);
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "print", rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject print(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
-
- defout.callMethod(context, "print", args);
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "printf", rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject printf(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- if (args.length != 0) {
- IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
-
- if (!(args[0] instanceof RubyString)) {
- defout = args[0];
- args = ArgsUtil.popArray(args);
- }
-
- defout.callMethod(context, "write", RubyKernel.sprintf(recv, args));
- }
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "readline", optional = 1, module = true, visibility = PRIVATE)
- 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, module = true, visibility = PRIVATE)
- public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return RubyArgsFile.readlines(context, context.getRuntime().getGlobalVariables().get("$<"), args);
- }
-
- /** Returns value of $_.
- *
- * @throws TypeError if $_ is not a String or nil.
- * @return value of $_ as String.
- */
- private static RubyString getLastlineString(ThreadContext context, Ruby runtime) {
- IRubyObject line = context.getPreviousFrame().getLastLine();
-
- if (line.isNil()) {
- throw runtime.newTypeError("$_ value need to be String (nil given).");
- } else if (!(line instanceof RubyString)) {
- throw runtime.newTypeError("$_ value need to be String (" + line.getMetaClass().getName() + " given).");
- } else {
- return (RubyString) line;
- }
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the one or two-arg versions.
- */
- public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- return getLastlineString(context, context.getRuntime()).sub_bang(context, args, block);
- }
-
- @JRubyMethod(name = "sub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE)
- public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
- return getLastlineString(context, context.getRuntime()).sub_bang(context, arg0, block);
- }
-
- @JRubyMethod(name = "sub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE)
- public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
- return getLastlineString(context, context.getRuntime()).sub_bang(context, arg0, arg1, block);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the one or two-arg versions.
- */
- public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.sub_bang(context, args, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- @JRubyMethod(name = "sub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.sub_bang(context, arg0, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- @JRubyMethod(name = "sub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.sub_bang(context, arg0, arg1, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the one or two-arg versions.
- */
- public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- return getLastlineString(context, context.getRuntime()).gsub_bang(context, args, block);
- }
-
- @JRubyMethod(name = "gsub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
- return getLastlineString(context, context.getRuntime()).gsub_bang(context, arg0, block);
- }
-
- @JRubyMethod(name = "gsub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
- return getLastlineString(context, context.getRuntime()).gsub_bang(context, arg0, arg1, block);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the one or two-arg versions.
- */
- public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.gsub_bang(context, args, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- @JRubyMethod(name = "gsub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.gsub_bang(context, arg0, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- @JRubyMethod(name = "gsub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
- RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
-
- if (!str.gsub_bang(context, arg0, arg1, block).isNil()) {
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- @JRubyMethod(name = "chop!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chop_bang(ThreadContext context, IRubyObject recv, Block block) {
- return getLastlineString(context, context.getRuntime()).chop_bang();
- }
-
- @JRubyMethod(name = "chop", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chop(ThreadContext context, IRubyObject recv, Block block) {
- RubyString str = getLastlineString(context, context.getRuntime());
-
- if (str.getByteList().realSize > 0) {
- str = (RubyString) str.dup();
- str.chop_bang();
- context.getPreviousFrame().setLastLine(str);
- }
-
- return str;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the zero or one-arg versions.
- */
- public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- return getLastlineString(context, context.getRuntime()).chomp_bang(args);
- }
-
- @JRubyMethod(name = "chomp!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv) {
- return getLastlineString(context, context.getRuntime()).chomp_bang();
- }
-
- @JRubyMethod(name = "chomp!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
- return getLastlineString(context, context.getRuntime()).chomp_bang(arg0);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the zero or one-arg versions.
- */
- public static IRubyObject chomp(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyString str = getLastlineString(context, context.getRuntime());
- RubyString dup = (RubyString) str.dup();
-
- if (dup.chomp_bang(args).isNil()) {
- return str;
- }
-
- context.getPreviousFrame().setLastLine(dup);
- return dup;
- }
-
- @JRubyMethod(name = "chomp", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chomp(ThreadContext context, IRubyObject recv) {
- RubyString str = getLastlineString(context, context.getRuntime());
- RubyString dup = (RubyString) str.dup();
-
- if (dup.chomp_bang().isNil()) {
- return str;
- }
-
- context.getPreviousFrame().setLastLine(dup);
- return dup;
- }
-
- @JRubyMethod(name = "chomp", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
- public static IRubyObject chomp(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
- RubyString str = getLastlineString(context, context.getRuntime());
- RubyString dup = (RubyString) str.dup();
-
- if (dup.chomp_bang(arg0).isNil()) {
- return str;
- }
-
- context.getPreviousFrame().setLastLine(dup);
- return dup;
- }
-
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- *
- * @param context The thread context for the current thread
- * @param recv The receiver of the method (usually a class that has included Kernel)
- * @return
- * @deprecated Use the versions with zero, one, or two args.
- */
- public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return getLastlineString(context, context.getRuntime()).split(context, args);
- }
-
- @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
- public static IRubyObject split(ThreadContext context, IRubyObject recv) {
- return getLastlineString(context, context.getRuntime()).split(context);
- }
-
- @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
- public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
- return getLastlineString(context, context.getRuntime()).split(context, arg0);
- }
-
- @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
- public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
- return getLastlineString(context, context.getRuntime()).split(context, arg0, arg1);
- }
-
- @JRubyMethod(name = "scan", required = 1, frame = true, module = true, visibility = PRIVATE, reads = {LASTLINE, BACKREF}, writes = {LASTLINE, BACKREF})
- public static IRubyObject scan(ThreadContext context, IRubyObject recv, IRubyObject pattern, Block block) {
- return getLastlineString(context, context.getRuntime()).scan(context, pattern, block);
- }
-
- @JRubyMethod(name = "select", required = 1, optional = 3, module = true, visibility = PRIVATE)
- public static IRubyObject select(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return RubyIO.select_static(context, context.getRuntime(), args);
- }
-
- @JRubyMethod(name = "sleep", optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject sleep(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- long milliseconds;
-
- if (args.length == 0) {
- // Zero sleeps forever
- milliseconds = 0;
- } else {
- if (!(args[0] instanceof RubyNumeric)) {
- throw context.getRuntime().newTypeError("can't convert " + args[0].getMetaClass().getName() + "into time interval");
- }
- milliseconds = (long) (args[0].convertToFloat().getDoubleValue() * 1000);
- if (milliseconds < 0) {
- throw context.getRuntime().newArgumentError("time interval must be positive");
- } else if (milliseconds == 0) {
- // Explicit zero in MRI returns immediately
- return context.getRuntime().newFixnum(0);
- }
- }
- long startTime = System.currentTimeMillis();
-
- RubyThread rubyThread = context.getThread();
-
- do {
- long loopStartTime = System.currentTimeMillis();
- try {
- rubyThread.sleep(milliseconds);
- } catch (InterruptedException iExcptn) {
- }
- milliseconds -= (System.currentTimeMillis() - loopStartTime);
- } while (milliseconds > 0);
-
- return context.getRuntime().newFixnum(Math.round((System.currentTimeMillis() - startTime) / 1000.0));
- }
-
- // FIXME: Add at_exit and finalizers to exit, then make exit_bang not call those.
- @JRubyMethod(name = "exit", optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject exit(IRubyObject recv, IRubyObject[] args) {
- exit(recv.getRuntime(), args, false);
- return recv.getRuntime().getNil(); // not reached
- }
-
- @JRubyMethod(name = "exit!", optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject exit_bang(IRubyObject recv, IRubyObject[] args) {
- exit(recv.getRuntime(), args, true);
- return recv.getRuntime().getNil(); // not reached
- }
-
- private static void exit(Ruby runtime, IRubyObject[] args, boolean hard) {
- runtime.secure(4);
-
- int status = 1;
- if (args.length > 0) {
- RubyObject argument = (RubyObject)args[0];
- if (argument instanceof RubyFixnum) {
- status = RubyNumeric.fix2int(argument);
- } else {
- status = argument.isFalse() ? 1 : 0;
- }
- }
-
- if (hard) {
- throw new MainExitException(status, true);
- } else {
- throw runtime.newSystemExit(status);
- }
- }
-
-
- /** Returns an Array with the names of all global variables.
- *
- */
- @JRubyMethod(name = "global_variables", module = true, visibility = PRIVATE)
- public static RubyArray global_variables(ThreadContext context, IRubyObject recv) {
- Ruby runtime = context.getRuntime();
- RubyArray globalVariables = runtime.newArray();
-
- for (String globalVariableName : runtime.getGlobalVariables().getNames()) {
- globalVariables.append(runtime.newString(globalVariableName));
- }
-
- return globalVariables;
- }
-
- /** Returns an Array with the names of all local variables.
- *
- */
- @JRubyMethod(name = "local_variables", module = true, visibility = PRIVATE)
- public static RubyArray local_variables(ThreadContext context, IRubyObject recv) {
- final Ruby runtime = context.getRuntime();
- RubyArray localVariables = runtime.newArray();
-
- for (String name: context.getCurrentScope().getAllNamesInScope()) {
- if (IdUtil.isLocal(name)) localVariables.append(runtime.newString(name));
- }
-
- return localVariables;
- }
-
- @JRubyMethod(name = "binding", frame = true, module = true, visibility = PRIVATE)
- public static RubyBinding binding(ThreadContext context, IRubyObject recv, Block block) {
- return RubyBinding.newBinding(context.getRuntime());
- }
-
- @JRubyMethod(name = {"block_given?", "iterator?"}, frame = true, module = true, visibility = PRIVATE)
- public static RubyBoolean block_given_p(ThreadContext context, IRubyObject recv, Block block) {
- return context.getRuntime().newBoolean(context.getPreviousFrame().getBlock().isGiven());
- }
-
-
- @Deprecated
- public static IRubyObject sprintf(IRubyObject recv, IRubyObject[] args) {
- return sprintf(recv.getRuntime().getCurrentContext(), recv, args);
- }
-
- @JRubyMethod(name = {"sprintf", "format"}, required = 1, rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject sprintf(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- if (args.length == 0) {
- throw context.getRuntime().newArgumentError("sprintf must have at least one argument");
- }
-
- RubyString str = RubyString.stringValue(args[0]);
-
- RubyArray newArgs = context.getRuntime().newArrayNoCopy(args);
- newArgs.shift();
-
- return str.op_format(context, newArgs);
- }
-
- @JRubyMethod(name = {"raise", "fail"}, optional = 3, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject raise(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- // FIXME: Pass block down?
- Ruby runtime = context.getRuntime();
-
- if (args.length == 0) {
- IRubyObject lastException = runtime.getGlobalVariables().get("$!");
- if (lastException.isNil()) {
- throw new RaiseException(runtime, runtime.getRuntimeError(), "", false);
- }
- throw new RaiseException((RubyException) lastException);
- }
-
- IRubyObject exception;
-
- if (args.length == 1) {
- if (args[0] instanceof RubyString) {
- throw new RaiseException((RubyException)runtime.getRuntimeError().newInstance(context, args, block));
- }
-
- if (!args[0].respondsTo("exception")) {
- throw runtime.newTypeError("exception class/object expected");
- }
- exception = args[0].callMethod(context, "exception");
- } else {
- if (!args[0].respondsTo("exception")) {
- throw runtime.newTypeError("exception class/object expected");
- }
-
- exception = args[0].callMethod(context, "exception", args[1]);
- }
-
- if (!runtime.fastGetClass("Exception").isInstance(exception)) {
- throw runtime.newTypeError("exception object expected");
- }
-
- if (args.length == 3) {
- ((RubyException) exception).set_backtrace(args[2]);
- }
-
- if (runtime.getDebug().isTrue()) {
- printExceptionSummary(context, runtime, (RubyException) exception);
- }
-
- throw new RaiseException((RubyException) exception);
- }
-
- private static void printExceptionSummary(ThreadContext context, Ruby runtime, RubyException rEx) {
- Frame currentFrame = context.getCurrentFrame();
-
- String msg = String.format("Exception `%s' at %s:%s - %s\n",
- rEx.getMetaClass(),
- currentFrame.getFile(), currentFrame.getLine() + 1,
- rEx.to_s());
-
- IRubyObject errorStream = runtime.getGlobalVariables().get("$stderr");
- errorStream.callMethod(context, "write", runtime.newString(msg));
- }
-
- /**
- * Require.
- * MRI allows to require ever .rb files or ruby extension dll (.so or .dll depending on system).
- * we allow requiring either .rb files or jars.
- * @param recv ruby object used to call require (any object will do and it won't be used anyway).
- * @param name the name of the file to require
- **/
- @JRubyMethod(name = "require", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject require(IRubyObject recv, IRubyObject name, Block block) {
- Ruby runtime = recv.getRuntime();
-
- if (runtime.getLoadService().require(name.convertToString().toString())) {
- return runtime.getTrue();
- }
- return runtime.getFalse();
- }
-
- @JRubyMethod(name = "load", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject load(IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = recv.getRuntime();
- RubyString file = args[0].convertToString();
- boolean wrap = args.length == 2 ? args[1].isTrue() : false;
-
- runtime.getLoadService().load(file.getByteList().toString(), wrap);
-
- return runtime.getTrue();
- }
-
- @JRubyMethod(name = "eval", required = 1, optional = 3, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject eval(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
-
- // string to eval
- RubyString src = args[0].convertToString();
- runtime.checkSafeString(src);
-
- IRubyObject scope = args.length > 1 && !args[1].isNil() ? args[1] : null;
- String file;
- if (args.length > 2) {
- file = args[2].convertToString().toString();
- } else if (scope == null) {
- file = "(eval)";
- } else {
- file = null;
- }
- int line;
- if (args.length > 3) {
- line = (int) args[3].convertToInteger().getLongValue();
- } else if (scope == null) {
- line = 0;
- } else {
- line = -1;
- }
- if (scope == null) scope = RubyBinding.newBindingForEval(context);
-
- return ASTInterpreter.evalWithBinding(context, src, scope, file, line);
- }
-
- @JRubyMethod(name = "callcc", frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject callcc(ThreadContext context, IRubyObject recv, Block block) {
- Ruby runtime = context.getRuntime();
- runtime.getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "Kernel#callcc: Continuations are not implemented in JRuby and will not work", "Kernel#callcc");
- IRubyObject cc = runtime.getContinuation().callMethod(context, "new");
- cc.dataWrapStruct(block);
- return block.yield(context, cc);
- }
-
- @JRubyMethod(name = "caller", optional = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- int level = args.length > 0 ? RubyNumeric.fix2int(args[0]) : 1;
-
- if (level < 0) {
- throw context.getRuntime().newArgumentError("negative level(" + level + ')');
- }
-
- return context.createCallerBacktrace(context.getRuntime(), level);
- }
-
- @JRubyMethod(name = "catch", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject rbCatch(ThreadContext context, IRubyObject recv, IRubyObject tag, Block block) {
- CatchTarget target = new CatchTarget(tag.asJavaString());
- try {
- context.pushCatch(target);
- return block.yield(context, tag);
- } catch (JumpException.ThrowJump tj) {
- if (tj.getTarget() == target) return (IRubyObject) tj.getValue();
-
- throw tj;
- } finally {
- context.popCatch();
- }
- }
-
- public static class CatchTarget implements JumpTarget {
- private final String tag;
- public CatchTarget(String tag) { this.tag = tag; }
- public String getTag() { return tag; }
- }
-
- @JRubyMethod(name = "throw", required = 1, frame = true, optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject rbThrow(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = context.getRuntime();
-
- String tag = args[0].asJavaString();
- CatchTarget[] catches = context.getActiveCatches();
-
- String message = "uncaught throw `" + tag + "'";
-
- // Ordering of array traversal not important, just intuitive
- for (int i = catches.length - 1 ; i >= 0 ; i--) {
- if (tag.equals(catches[i].getTag())) {
- //Catch active, throw for catch to handle
- throw new JumpException.ThrowJump(catches[i], args.length > 1 ? args[1] : runtime.getNil());
- }
- }
-
- // No catch active for this throw
- RubyThread currentThread = context.getThread();
- if (currentThread == runtime.getThreadService().getMainThread()) {
- throw runtime.newNameError(message, tag);
- } else {
- throw runtime.newThreadError(message + " in thread 0x" + Integer.toHexString(RubyInteger.fix2int(currentThread.id())));
- }
- }
-
- @JRubyMethod(name = "trap", required = 1, frame = true, optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject trap(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- context.getRuntime().getLoadService().require("jsignal");
- return RuntimeHelpers.invoke(context, recv, "__jtrap", args, block);
- }
-
- @JRubyMethod(name = "warn", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject warn(ThreadContext context, IRubyObject recv, IRubyObject message) {
- Ruby runtime = context.getRuntime();
-
- if (!runtime.getVerbose().isNil()) {
- IRubyObject out = runtime.getGlobalVariables().get("$stderr");
- RuntimeHelpers.invoke(context, out, "puts", message);
- }
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "set_trace_func", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject set_trace_func(ThreadContext context, IRubyObject recv, IRubyObject trace_func, Block block) {
- if (trace_func.isNil()) {
- context.getRuntime().setTraceFunction(null);
- } else if (!(trace_func instanceof RubyProc)) {
- throw context.getRuntime().newTypeError("trace_func needs to be Proc.");
- } else {
- context.getRuntime().setTraceFunction((RubyProc) trace_func);
- }
- return trace_func;
- }
-
- @JRubyMethod(name = "trace_var", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject trace_var(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- if (args.length == 0) throw context.getRuntime().newArgumentError(0, 1);
- RubyProc proc = null;
- String var = args.length > 1 ? args[0].toString() : null;
- // ignore if it's not a global var
- if (var.charAt(0) != '$') return context.getRuntime().getNil();
- if (args.length == 1) proc = RubyProc.newProc(context.getRuntime(), block, Block.Type.PROC);
- if (args.length == 2) {
- proc = (RubyProc)TypeConverter.convertToType(args[1], context.getRuntime().getProc(), 0, "to_proc", true);
- }
-
- context.getRuntime().getGlobalVariables().setTraceVar(var, proc);
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "untrace_var", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject untrace_var(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- if (args.length == 0) throw context.getRuntime().newArgumentError(0, 1);
- String var = args.length >= 1 ? args[0].toString() : null;
-
- // ignore if it's not a global var
- if (var.charAt(0) != '$') return context.getRuntime().getNil();
-
- if (args.length > 1) {
- ArrayList<IRubyObject> success = new ArrayList<IRubyObject>();
- for (int i = 1; i < args.length; i++) {
- if (context.getRuntime().getGlobalVariables().untraceVar(var, args[i])) {
- success.add(args[i]);
- }
- }
- return RubyArray.newArray(context.getRuntime(), success);
- } else {
- context.getRuntime().getGlobalVariables().untraceVar(var);
- }
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "singleton_method_added", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject singleton_method_added(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "singleton_method_removed", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject singleton_method_removed(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "singleton_method_undefined", required = 1, frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject singleton_method_undefined(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = {"proc", "lambda"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_8)
- public static RubyProc proc(ThreadContext context, IRubyObject recv, Block block) {
- return context.getRuntime().newProc(Block.Type.LAMBDA, block);
- }
-
- @Deprecated
- public static RubyProc proc(IRubyObject recv, Block block) {
- return recv.getRuntime().newProc(Block.Type.LAMBDA, block);
- }
-
- @JRubyMethod(name = {"lambda"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static RubyProc lambda(ThreadContext context, IRubyObject recv, Block block) {
- return context.getRuntime().newProc(Block.Type.LAMBDA, block);
- }
-
- @JRubyMethod(name = {"proc"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
- public static RubyProc proc_1_9(ThreadContext context, IRubyObject recv, Block block) {
- return context.getRuntime().newProc(Block.Type.PROC, block);
- }
-
- @JRubyMethod(name = "loop", frame = true, module = true, visibility = PRIVATE)
- public static IRubyObject loop(ThreadContext context, IRubyObject recv, Block block) {
- while (true) {
- block.yield(context, context.getRuntime().getNil());
-
- context.pollThreadEvents();
- }
- }
-
- @JRubyMethod(name = "test", required = 2, optional = 1, module = true, visibility = PRIVATE)
- public static IRubyObject test(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- if (args.length == 0) throw context.getRuntime().newArgumentError("wrong number of arguments");
-
- int cmd;
- if (args[0] instanceof RubyFixnum) {
- cmd = (int)((RubyFixnum) args[0]).getLongValue();
- } else if (args[0] instanceof RubyString &&
- ((RubyString) args[0]).getByteList().length() > 0) {
- // MRI behavior: use first byte of string value if len > 0
- cmd = ((RubyString) args[0]).getByteList().charAt(0);
- } else {
- cmd = (int) args[0].convertToInteger().getLongValue();
- }
-
- // MRI behavior: raise ArgumentError for 'unknown command' before
- // checking number of args.
- switch(cmd) {
- case 'A': case 'b': case 'c': case 'C': case 'd': case 'e': case 'f': case 'g': case 'G':
- case 'k': case 'M': case 'l': case 'o': case 'O': case 'p': case 'r': case 'R': case 's':
- case 'S': case 'u': case 'w': case 'W': case 'x': case 'X': case 'z': case '=': case '<':
- case '>': case '-':
- break;
- default:
- throw context.getRuntime().newArgumentError("unknown command ?" + (char) cmd);
- }
-
- // MRI behavior: now check arg count
-
- switch(cmd) {
- case '-': case '=': case '<': case '>':
- if (args.length != 3) throw context.getRuntime().newArgumentError(args.length, 3);
- break;
- default:
- if (args.length != 2) throw context.getRuntime().newArgumentError(args.length, 2);
- break;
- }
-
- switch (cmd) {
- case 'A': // ?A | Time | Last access time for file1
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).atime();
- case 'b': // ?b | boolean | True if file1 is a block device
- return RubyFileTest.blockdev_p(recv, args[1]);
- case 'c': // ?c | boolean | True if file1 is a character device
- return RubyFileTest.chardev_p(recv, args[1]);
- case 'C': // ?C | Time | Last change time for file1
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).ctime();
- case 'd': // ?d | boolean | True if file1 exists and is a directory
- return RubyFileTest.directory_p(recv, args[1]);
- case 'e': // ?e | boolean | True if file1 exists
- return RubyFileTest.exist_p(recv, args[1]);
- case 'f': // ?f | boolean | True if file1 exists and is a regular file
- return RubyFileTest.file_p(recv, args[1]);
- case 'g': // ?g | boolean | True if file1 has the \CF{setgid} bit
- return RubyFileTest.setgid_p(recv, args[1]);
- case 'G': // ?G | boolean | True if file1 exists and has a group ownership equal to the caller's group
- return RubyFileTest.grpowned_p(recv, args[1]);
- case 'k': // ?k | boolean | True if file1 exists and has the sticky bit set
- return RubyFileTest.sticky_p(recv, args[1]);
- case 'M': // ?M | Time | Last modification time for file1
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtime();
- case 'l': // ?l | boolean | True if file1 exists and is a symbolic link
- return RubyFileTest.symlink_p(recv, args[1]);
- case 'o': // ?o | boolean | True if file1 exists and is owned by the caller's effective uid
- return RubyFileTest.owned_p(recv, args[1]);
- case 'O': // ?O | boolean | True if file1 exists and is owned by the caller's real uid
- return RubyFileTest.rowned_p(recv, args[1]);
- case 'p': // ?p | boolean | True if file1 exists and is a fifo
- return RubyFileTest.pipe_p(recv, args[1]);
- case 'r': // ?r | boolean | True if file1 is readable by the effective uid/gid of the caller
- return RubyFileTest.readable_p(recv, args[1]);
- case 'R': // ?R | boolean | True if file is readable by the real uid/gid of the caller
- // FIXME: Need to implement an readable_real_p in FileTest
- return RubyFileTest.readable_p(recv, args[1]);
- case 's': // ?s | int/nil | If file1 has nonzero size, return the size, otherwise nil
- return RubyFileTest.size_p(recv, args[1]);
- case 'S': // ?S | boolean | True if file1 exists and is a socket
- return RubyFileTest.socket_p(recv, args[1]);
- case 'u': // ?u | boolean | True if file1 has the setuid bit set
- return RubyFileTest.setuid_p(recv, args[1]);
- case 'w': // ?w | boolean | True if file1 exists and is writable by effective uid/gid
- return RubyFileTest.writable_p(recv, args[1]);
- case 'W': // ?W | boolean | True if file1 exists and is writable by the real uid/gid
- // FIXME: Need to implement an writable_real_p in FileTest
- return RubyFileTest.writable_p(recv, args[1]);
- case 'x': // ?x | boolean | True if file1 exists and is executable by the effective uid/gid
- return RubyFileTest.executable_p(recv, args[1]);
- case 'X': // ?X | boolean | True if file1 exists and is executable by the real uid/gid
- return RubyFileTest.executable_real_p(recv, args[1]);
- case 'z': // ?z | boolean | True if file1 exists and has a zero length
- return RubyFileTest.zero_p(recv, args[1]);
- case '=': // ?= | boolean | True if the modification times of file1 and file2 are equal
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeEquals(args[2]);
- case '<': // ?< | boolean | True if the modification time of file1 is prior to that of file2
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeLessThan(args[2]);
- case '>': // ?> | boolean | True if the modification time of file1 is after that of file2
- return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeGreaterThan(args[2]);
- case '-': // ?- | boolean | True if file1 and file2 are identical
- return RubyFileTest.identical_p(recv, args[1], args[2]);
- default:
- throw new InternalError("unreachable code reached!");
- }
- }
-
- @JRubyMethod(name = "`", required = 1, module = true, visibility = PRIVATE)
- public static IRubyObject backquote(ThreadContext context, IRubyObject recv, IRubyObject aString) {
- Ruby runtime = context.getRuntime();
- ByteArrayOutputStream output = new ByteArrayOutputStream();
-
- RubyString string = aString.convertToString();
- int resultCode = ShellLauncher.runAndWait(runtime, new IRubyObject[] {string}, output);
-
- runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, resultCode));
-
- return RubyString.newString(runtime, output.toByteArray());
- }
-
- @JRubyMethod(name = "srand", optional = 1, module = true, visibility = PRIVATE)
- public static RubyInteger srand(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- long oldRandomSeed = runtime.getRandomSeed();
-
- if (args.length > 0) {
- RubyInteger integerSeed = args[0].convertToInteger(MethodIndex.TO_INT, "to_int");
- runtime.setRandomSeed(integerSeed.getLongValue());
- } else {
- // Not sure how well this works, but it works much better than
- // just currentTimeMillis by itself.
- runtime.setRandomSeed(System.currentTimeMillis() ^
- recv.hashCode() ^ runtime.incrementRandomSeedSequence() ^
- runtime.getRandom().nextInt(Math.max(1, Math.abs((int)runtime.getRandomSeed()))));
- }
- runtime.getRandom().setSeed(runtime.getRandomSeed());
- return runtime.newFixnum(oldRandomSeed);
- }
-
- @JRubyMethod(name = "rand", optional = 1, module = true, visibility = PRIVATE)
- public static RubyNumeric rand(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- long ceil;
- if (args.length == 0) {
- ceil = 0;
- } else if (args.length == 1) {
- if (args[0] instanceof RubyBignum) {
- byte[] bigCeilBytes = ((RubyBignum) args[0]).getValue().toByteArray();
- BigInteger bigCeil = new BigInteger(bigCeilBytes).abs();
-
- byte[] randBytes = new byte[bigCeilBytes.length];
- runtime.getRandom().nextBytes(randBytes);
-
- BigInteger result = new BigInteger(randBytes).abs().mod(bigCeil);
-
- return new RubyBignum(runtime, result);
- }
-
- RubyInteger integerCeil = (RubyInteger)RubyKernel.new_integer(context, recv, args[0]);
- ceil = Math.abs(integerCeil.getLongValue());
- } else {
- throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for 1)");
- }
-
- if (ceil == 0) {
- return RubyFloat.newFloat(runtime, runtime.getRandom().nextDouble());
- }
- if (ceil > Integer.MAX_VALUE) {
- return runtime.newFixnum(Math.abs(runtime.getRandom().nextLong()) % ceil);
- }
-
- return runtime.newFixnum(runtime.getRandom().nextInt((int) ceil));
- }
-
- @JRubyMethod(name = "syscall", required = 1, optional = 9, module = true, visibility = PRIVATE)
- public static IRubyObject syscall(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- throw context.getRuntime().newNotImplementedError("Kernel#syscall is not implemented in JRuby");
- }
-
- @JRubyMethod(name = {"system"}, required = 1, rest = true, module = true, visibility = PRIVATE)
- public static RubyBoolean system(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- int resultCode;
- try {
- resultCode = ShellLauncher.runAndWait(runtime, args);
- } catch (Exception e) {
- resultCode = 127;
- }
- runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, resultCode));
- return runtime.newBoolean(resultCode == 0);
- }
-
- @JRubyMethod(name = {"exec"}, required = 1, rest = true, module = true, visibility = PRIVATE)
- public static IRubyObject exec(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- int resultCode;
- try {
- // TODO: exec should replace the current process.
- // This could be possible with JNA.
- resultCode = ShellLauncher.execAndWait(runtime, args);
- } catch (Exception e) {
- throw runtime.newErrnoENOENTError("cannot execute");
- }
-
- return exit(recv, new IRubyObject[] {runtime.newFixnum(resultCode)});
- }
-
- @JRubyMethod(name = "fork", module = true, visibility = PRIVATE)
- public static IRubyObject fork(ThreadContext context, IRubyObject recv, Block block) {
- Ruby runtime = context.getRuntime();
-
- if (!RubyInstanceConfig.FORK_ENABLED) {
- throw runtime.newNotImplementedError("fork is unsafe and disabled by default on JRuby");
- }
-
- if (block.isGiven()) {
- int pid = runtime.getPosix().fork();
-
- if (pid == 0) {
- try {
- block.yield(context, runtime.getNil());
- } catch (RaiseException re) {
- if (re.getException() instanceof RubySystemExit) {
- throw re;
- }
- return exit_bang(recv, new IRubyObject[] {RubyFixnum.minus_one(runtime)});
- } catch (Throwable t) {
- return exit_bang(recv, new IRubyObject[] {RubyFixnum.minus_one(runtime)});
- }
- return exit_bang(recv, new IRubyObject[] {RubyFixnum.zero(runtime)});
- } else {
- return runtime.newFixnum(pid);
- }
- } else {
- int result = runtime.getPosix().fork();
-
- if (result == -1) {
- return runtime.getNil();
- }
-
- return runtime.newFixnum(result);
- }
- }
-
- @JRubyMethod(frame = true, module = true)
- public static IRubyObject tap(ThreadContext context, IRubyObject recv, Block block) {
- block.yield(context, recv);
- return recv;
- }
-}
-/***** 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 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.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyClass(name="LocalJumpError",parent="StandardError")
-public class RubyLocalJumpError extends RubyException {
- private static ObjectAllocator LOCALJUMPERROR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyLocalJumpError(runtime, klass);
- }
- };
-
- public static RubyClass createLocalJumpErrorClass(Ruby runtime, RubyClass standardErrorClass) {
- RubyClass nameErrorClass = runtime.defineClass("LocalJumpError", standardErrorClass, LOCALJUMPERROR_ALLOCATOR);
-
- nameErrorClass.defineAnnotatedMethods(RubyLocalJumpError.class);
-
- return nameErrorClass;
- }
-
- private RubyLocalJumpError(Ruby runtime, RubyClass exceptionClass) {
- super(runtime, exceptionClass);
- }
-
- public RubyLocalJumpError(Ruby runtime, RubyClass exceptionClass, String message, String reason, IRubyObject exitValue) {
- super(runtime, exceptionClass, message);
- fastSetInternalVariable("reason", runtime.newSymbol(reason));
- fastSetInternalVariable("exit_value", exitValue);
- }
-
- @JRubyMethod(name = "reason")
- public IRubyObject reason() {
- return fastGetInternalVariable("reason");
- }
-
- @JRubyMethod(name = "exit_value")
- public IRubyObject exit_value() {
- return fastGetInternalVariable("exit_value");
- }
-}
-/***** 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>
- * Copyright (C) 2002-2007 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2003 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.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.Constants;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.ThreadContext;
-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.IOInputStream;
-import org.jruby.util.IOOutputStream;
-
-/**
- * Marshal module
- *
- * @author Anders
- */
-@JRubyModule(name="Marshal")
-public class RubyMarshal {
-
- public static RubyModule createMarshalModule(Ruby runtime) {
- RubyModule module = runtime.defineModule("Marshal");
- runtime.setMarshal(module);
-
- module.defineAnnotatedMethods(RubyMarshal.class);
- module.defineConstant("MAJOR_VERSION", runtime.newFixnum(Constants.MARSHAL_MAJOR));
- module.defineConstant("MINOR_VERSION", runtime.newFixnum(Constants.MARSHAL_MINOR));
-
- return module;
- }
-
- @JRubyMethod(name = "dump", required = 1, optional = 2, frame = true, module = true)
- public static IRubyObject dump(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- if (args.length < 1) {
- throw recv.getRuntime().newArgumentError("wrong # of arguments(at least 1)");
- }
- IRubyObject objectToDump = args[0];
-
- IRubyObject io = null;
- int depthLimit = -1;
-
- if (args.length >= 2) {
- if (args[1].respondsTo("write")) {
- io = args[1];
- } else if (args[1] instanceof RubyFixnum) {
- depthLimit = (int) ((RubyFixnum) args[1]).getLongValue();
- } else {
- throw recv.getRuntime().newTypeError("Instance of IO needed");
- }
- if (args.length == 3) {
- depthLimit = (int) ((RubyFixnum) args[2]).getLongValue();
- }
- }
-
- try {
- if (io != null) {
- dumpToStream(objectToDump, outputStream(io), depthLimit);
- return io;
- }
- ByteArrayOutputStream stringOutput = new ByteArrayOutputStream();
- dumpToStream(objectToDump, stringOutput, depthLimit);
-
- return RubyString.newString(recv.getRuntime(), new ByteList(stringOutput.toByteArray(),false));
-
- } catch (IOException ioe) {
- throw recv.getRuntime().newIOErrorFromException(ioe);
- }
-
- }
-
- private static OutputStream outputStream(IRubyObject out) {
- setBinmodeIfPossible(out);
- if (out instanceof RubyIO) {
- return ((RubyIO) out).getOutStream();
- }
- return new IOOutputStream(out);
- }
-
- private static void setBinmodeIfPossible(IRubyObject io) {
- if (io.respondsTo("binmode")) {
- io.callMethod(io.getRuntime().getCurrentContext(), "binmode");
- }
- }
-
- @JRubyMethod(name = {"load", "restore"}, required = 1, optional = 1, frame = true, module = true)
- public static IRubyObject load(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- try {
- if (args.length < 1) {
- throw recv.getRuntime().newArgumentError("wrong number of arguments (0 for 1)");
- }
-
- if (args.length > 2) {
- throw recv.getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 2)");
- }
-
- IRubyObject in = null;
- IRubyObject proc = null;
-
- switch (args.length) {
- case 2:
- proc = args[1];
- case 1:
- in = args[0];
- }
-
- InputStream rawInput;
- if (in != null && in.respondsTo("read")) {
- rawInput = inputStream(in);
- } else if (in != null && in.respondsTo("to_str")) {
- RubyString inString = (RubyString) RuntimeHelpers.invoke(context, in, "to_str");
- ByteList bytes = inString.getByteList();
- rawInput = new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- } else {
- throw recv.getRuntime().newTypeError("instance of IO needed");
- }
-
- UnmarshalStream input = new UnmarshalStream(recv.getRuntime(), rawInput, proc);
-
- return input.unmarshalObject();
-
- } catch (EOFException ee) {
- throw recv.getRuntime().newEOFError();
- } catch (IOException ioe) {
- throw recv.getRuntime().newIOErrorFromException(ioe);
- }
- }
-
- private static InputStream inputStream(IRubyObject in) {
- setBinmodeIfPossible(in);
- if (in instanceof RubyIO) {
- return ((RubyIO) in).getInStream();
- }
- return new IOInputStream(in);
- }
-
- private static void dumpToStream(IRubyObject object, OutputStream rawOutput, int depthLimit)
- throws IOException
- {
- MarshalStream output = new MarshalStream(object.getRuntime(), rawOutput, depthLimit);
- output.dumpObject(object);
- }
-}
-/***** 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) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 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.util.Iterator;
-
-import org.joni.NameEntry;
-import org.joni.Regex;
-import org.joni.Region;
-import org.joni.exception.JOniException;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Arity;
-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.ByteList;
-
-/**
- * @author olabini
- */
-@JRubyClass(name="MatchData")
-public class RubyMatchData extends RubyObject {
- Region regs; // captures
- int begin; // begin and end are used when not groups defined
- int end;
- RubyString str;
- Regex pattern;
-
- public static RubyClass createMatchDataClass(Ruby runtime) {
- // TODO: Is NOT_ALLOCATABLE_ALLOCATOR ok here, since you can't actually instantiate MatchData directly?
- RubyClass matchDataClass = runtime.defineClass("MatchData", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setMatchData(matchDataClass);
- runtime.defineGlobalConstant("MatchingData", matchDataClass);
- matchDataClass.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyMatchData;
- }
- };
-
- matchDataClass.getMetaClass().undefineMethod("new");
-
- matchDataClass.defineAnnotatedMethods(RubyMatchData.class);
-
- return matchDataClass;
- }
-
- public RubyMatchData(Ruby runtime) {
- super(runtime, runtime.getMatchData());
- }
-
- public final static int MATCH_BUSY = USER2_F;
-
- // rb_match_busy
- public final void use() {
- flags |= MATCH_BUSY;
- }
-
- public final boolean used() {
- return (flags & MATCH_BUSY) != 0;
- }
-
- private RubyArray match_array(Ruby runtime, int start) {
- if (regs == null) {
- if (start != 0) return runtime.newEmptyArray();
- if (begin == -1) {
- return getRuntime().newArray(runtime.getNil());
- } else {
- RubyString ss = str.makeShared(runtime, begin, end - begin);
- if (isTaint()) ss.setTaint(true);
- return getRuntime().newArray(ss);
- }
- } else {
- RubyArray arr = getRuntime().newArray(regs.numRegs - start);
- for (int i=start; i<regs.numRegs; i++) {
- if (regs.beg[i] == -1) {
- arr.append(getRuntime().getNil());
- } else {
- RubyString ss = str.makeShared(runtime, regs.beg[i], regs.end[i] - regs.beg[i]);
- if (isTaint()) ss.setTaint(true);
- arr.append(ss);
- }
- }
- return arr;
- }
-
- }
-
- public IRubyObject group(long n) {
- return RubyRegexp.nth_match((int)n, this);
- }
-
- public IRubyObject group(int n) {
- return RubyRegexp.nth_match(n, this);
- }
-
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- if (pattern == null) return anyToString();
-
- RubyString result = getRuntime().newString();
- result.cat((byte)'#').cat((byte)'<');
- result.append(getMetaClass().getRealClass().to_s());
-
- NameEntry[]names = new NameEntry[regs == null ? 1 : regs.numRegs];
-
- if (pattern.numberOfNames() > 0) {
- for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
- NameEntry e = i.next();
- for (int num : e.getBackRefs()) names[num] = e;
- }
- }
-
- for (int i=0; i<names.length; i++) {
- result.cat((byte)' ');
- if (i > 0) {
- NameEntry e = names[i];
- if (e != null) {
- result.cat(e.name, e.nameP, e.nameEnd - e.nameP);
- } else {
- result.cat((byte)('0' + i));
- }
- result.cat((byte)':');
- }
- IRubyObject v = RubyRegexp.nth_match(i, this);
- if (v.isNil()) {
- result.cat("nil".getBytes());
- } else {
- result.append(v.inspect());
- }
- }
-
- return result.cat((byte)'>');
- }
-
- /** match_to_a
- *
- */
- @JRubyMethod(name = "to_a")
- @Override
- public RubyArray to_a() {
- return match_array(getRuntime(), 0);
- }
-
- @JRubyMethod(name = "values_at", required = 1, rest = true)
- public IRubyObject values_at(IRubyObject[] args) {
- return to_a().values_at(args);
- }
-
- @JRubyMethod(name = "select", frame = true)
- public IRubyObject select(ThreadContext context, Block block) {
- return block.yield(context, to_a());
- }
-
- /** match_captures
- *
- */
- @JRubyMethod(name = "captures")
- public IRubyObject captures(ThreadContext context) {
- return match_array(context.getRuntime(), 1);
- }
-
- private int nameToBackrefNumber(RubyString str) {
- ByteList value = str.getByteList();
- try {
- return pattern.nameToBackrefNumber(value.bytes, value.begin, value.begin + value.realSize, regs);
- } catch (JOniException je) {
- throw getRuntime().newIndexError(je.getMessage());
- }
- }
-
- final int backrefNumber(IRubyObject obj) {
- if (obj instanceof RubySymbol) {
- return nameToBackrefNumber((RubyString)((RubySymbol)obj).id2name());
- } else if (obj instanceof RubyString) {
- return nameToBackrefNumber((RubyString)obj);
- } else {
- return RubyNumeric.num2int(obj);
- }
- }
-
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public IRubyObject op_aref(IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return op_aref(args[0]);
- case 2:
- return op_aref(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** match_aref
- *
- */
- @JRubyMethod(name = "[]")
- public IRubyObject op_aref(IRubyObject idx) {
- IRubyObject result = op_arefCommon(idx);
- return result == null ? ((RubyArray)to_a()).aref(idx) : result;
- }
-
- /** match_aref
- *
- */
- @JRubyMethod(name = "[]")
- public IRubyObject op_aref(IRubyObject idx, IRubyObject rest) {
- IRubyObject result;
- return !rest.isNil() || (result = op_arefCommon(idx)) == null ? ((RubyArray)to_a()).aref(idx, rest) : result;
- }
-
- private IRubyObject op_arefCommon(IRubyObject idx) {
- if (idx instanceof RubyFixnum) {
- int num = RubyNumeric.fix2int(idx);
- if (num >= 0) return RubyRegexp.nth_match(num, this);
- } else {
- if (idx instanceof RubySymbol) {
- return RubyRegexp.nth_match(nameToBackrefNumber((RubyString)((RubySymbol)idx).id2name()), this);
- } else if (idx instanceof RubyString) {
- return RubyRegexp.nth_match(nameToBackrefNumber((RubyString)idx), this);
- }
- }
- return null;
- }
-
- /** match_size
- *
- */
- @JRubyMethod(name = {"size", "length"})
- public IRubyObject size() {
- return regs == null ? RubyFixnum.one(getRuntime()) : RubyFixnum.newFixnum(getRuntime(), regs.numRegs);
- }
-
- /** match_begin
- *
- */
- @JRubyMethod(name = "begin", required = 1)
- public IRubyObject begin(IRubyObject index) {
- int i = backrefNumber(index);
-
- if (regs == null) {
- if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
- if (begin < 0) return getRuntime().getNil();
- return RubyFixnum.newFixnum(getRuntime(), begin);
- } else {
- if (i < 0 || regs.numRegs <= i) throw getRuntime().newIndexError("index " + i + " out of matches");
- if (regs.beg[i] < 0) return getRuntime().getNil();
- return RubyFixnum.newFixnum(getRuntime(), regs.beg[i]);
- }
- }
-
- /** match_end
- *
- */
- @JRubyMethod(name = "end", required = 1)
- public IRubyObject end(IRubyObject index) {
- int i = backrefNumber(index);
-
- if (regs == null) {
- if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
- if (end < 0) return getRuntime().getNil();
- return RubyFixnum.newFixnum(getRuntime(), end);
- } else {
- if (i < 0 || regs.numRegs <= i) throw getRuntime().newIndexError("index " + i + " out of matches");
- if (regs.end[i] < 0) return getRuntime().getNil();
- return RubyFixnum.newFixnum(getRuntime(), regs.end[i]);
- }
- }
-
- /** match_offset
- *
- */
- @JRubyMethod(name = "offset", required = 1)
- public IRubyObject offset(IRubyObject index) {
- int i = backrefNumber(index);
- Ruby runtime = getRuntime();
-
- if (regs == null) {
- if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
- if (begin < 0) return runtime.newArray(runtime.getNil(), runtime.getNil());
- return runtime.newArray(RubyFixnum.newFixnum(runtime, begin),RubyFixnum.newFixnum(runtime, end));
- } else {
- if (i < 0 || regs.numRegs <= i) throw runtime.newIndexError("index " + i + " out of matches");
- if (regs.beg[i] < 0) return runtime.newArray(runtime.getNil(), runtime.getNil());
- return runtime.newArray(RubyFixnum.newFixnum(runtime, regs.beg[i]),RubyFixnum.newFixnum(runtime, regs.end[i]));
- }
- }
-
- /** match_pre_match
- *
- */
- @JRubyMethod(name = "pre_match")
- public IRubyObject pre_match(ThreadContext context) {
- RubyString ss;
-
- if (regs == null) {
- if(begin == -1) return context.getRuntime().getNil();
- ss = str.makeShared(context.getRuntime(), 0, begin);
- } else {
- if(regs.beg[0] == -1) return context.getRuntime().getNil();
- ss = str.makeShared(context.getRuntime(), 0, regs.beg[0]);
- }
-
- if (isTaint()) ss.setTaint(true);
- return ss;
- }
-
- /** match_post_match
- *
- */
- @JRubyMethod(name = "post_match")
- public IRubyObject post_match(ThreadContext context) {
- RubyString ss;
-
- if (regs == null) {
- if (begin == -1) return context.getRuntime().getNil();
- ss = str.makeShared(context.getRuntime(), end, str.getByteList().length() - end);
- } else {
- if (regs.beg[0] == -1) return context.getRuntime().getNil();
- ss = str.makeShared(context.getRuntime(), regs.end[0], str.getByteList().length() - regs.end[0]);
- }
-
- if(isTaint()) ss.setTaint(true);
- return ss;
- }
-
- /** match_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- IRubyObject ss = RubyRegexp.last_match(this);
- if (ss.isNil()) ss = RubyString.newEmptyString(getRuntime());
- if (isTaint()) ss.setTaint(true);
- return ss;
- }
-
- /** match_string
- *
- */
- @JRubyMethod(name = "string")
- public IRubyObject string() {
- return str; //str is frozen
- }
-
- @JRubyMethod(name = "initialize_copy", required = 1)
- public IRubyObject initialize_copy(IRubyObject original) {
- if (this == original) return this;
-
- if (!(getMetaClass() == original.getMetaClass())){ // MRI also does a pointer comparison here
- throw getRuntime().newTypeError("wrong argument class");
- }
-
- RubyMatchData origMatchData = (RubyMatchData)original;
- str = origMatchData.str;
- regs = origMatchData.regs;
-
- 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 Chad Fowler <chadfowler@chadfowler.com>
- * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-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.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyModule(name="Math")
-public class RubyMath {
- /** Create the Math module and add it to the Ruby runtime.
- *
- */
- public static RubyModule createMathModule(Ruby runtime) {
- RubyModule result = runtime.defineModule("Math");
- runtime.setMath(result);
-
- result.defineConstant("E", RubyFloat.newFloat(runtime, Math.E));
- result.defineConstant("PI", RubyFloat.newFloat(runtime, Math.PI));
-
- result.defineAnnotatedMethods(RubyMath.class);
-
- return result;
- }
-
-
- private static void domainCheck(IRubyObject recv, double value, String msg) {
- if (Double.isNaN(value)) {
- throw recv.getRuntime().newErrnoEDOMError(msg);
- }
- }
-
- private static double chebylevSerie(double x, double coef[]) {
- double b0, b1, b2, twox;
- int i;
- b1 = 0.0;
- b0 = 0.0;
- b2 = 0.0;
- twox = 2.0 * x;
- for (i = coef.length-1; i >= 0; i--) {
- b2 = b1;
- b1 = b0;
- b0 = twox * b1 - b2 + coef[i];
- }
- return 0.5*(b0 - b2);
- }
-
- private static double sign(double x, double y) {
- double abs = ((x < 0) ? -x : x);
- return (y < 0.0) ? -abs : abs;
- }
-
- @JRubyMethod(name = "atan2", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat atan2(IRubyObject recv, IRubyObject x, IRubyObject y) {
- double valuea = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double valueb = ((RubyFloat)RubyKernel.new_float(recv,y)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(), Math.atan2(valuea, valueb));
- }
-
- @JRubyMethod(name = "cos", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat cos(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),Math.cos(value));
- }
-
- @JRubyMethod(name = "sin", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat sin(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),Math.sin(value));
- }
-
- @JRubyMethod(name = "tan", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat tan(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),Math.tan(value));
- }
-
- @JRubyMethod(name = "asin", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat asin(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result = Math.asin(value);
- domainCheck(recv, result, "asin");
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- @JRubyMethod(name = "acos", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat acos(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result = Math.acos(value);
- domainCheck(recv, result, "acos");
- return RubyFloat.newFloat(recv.getRuntime(), result);
- }
-
- @JRubyMethod(name = "atan", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat atan(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),Math.atan(value));
- }
-
- @JRubyMethod(name = "cosh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat cosh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),(Math.exp(value) + Math.exp(-value)) / 2.0);
- }
-
- @JRubyMethod(name = "sinh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat sinh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),(Math.exp(value) - Math.exp(-value)) / 2.0);
- }
-
- @JRubyMethod(name = "tanh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat tanh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(), Math.tanh(value));
- }
-
- @JRubyMethod(name = "acosh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat acosh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result;
- if (Double.isNaN(value) || value < 1) {
- result = Double.NaN;
- } else if (value < 94906265.62) {
- result = Math.log(value + Math.sqrt(value * value - 1.0));
- } else{
- result = 0.69314718055994530941723212145818 + Math.log(value);
- }
-
- domainCheck(recv, result, "acosh");
-
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- private static final double ASINH_COEF[] = {
- -.12820039911738186343372127359268e+0,
- -.58811761189951767565211757138362e-1,
- .47274654322124815640725249756029e-2,
- -.49383631626536172101360174790273e-3,
- .58506207058557412287494835259321e-4,
- -.74669983289313681354755069217188e-5,
- .10011693583558199265966192015812e-5,
- -.13903543858708333608616472258886e-6,
- .19823169483172793547317360237148e-7,
- -.28847468417848843612747272800317e-8,
- .42672965467159937953457514995907e-9,
- -.63976084654366357868752632309681e-10,
- .96991686089064704147878293131179e-11,
- -.14844276972043770830246658365696e-11,
- .22903737939027447988040184378983e-12,
- -.35588395132732645159978942651310e-13,
- .55639694080056789953374539088554e-14,
- -.87462509599624678045666593520162e-15,
- .13815248844526692155868802298129e-15,
- -.21916688282900363984955142264149e-16,
- .34904658524827565638313923706880e-17
- };
-
- @JRubyMethod(name = "asinh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat asinh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double y = Math.abs(value);
- double result;
-
- if (Double.isNaN(value)) {
- result = Double.NaN;
- } else if (y <= 1.05367e-08) {
- result = value;
- } else if (y <= 1.0) {
- result = value * (1.0 + chebylevSerie(2.0 * value * value - 1.0, ASINH_COEF));
- } else if (y < 94906265.62) {
- result = Math.log(value + Math.sqrt(value * value + 1.0));
- } else {
- result = 0.69314718055994530941723212145818 + Math.log(y);
- if (value < 0) result *= -1;
- }
-
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- private static final double ATANH_COEF[] = {
- .9439510239319549230842892218633e-1,
- .4919843705578615947200034576668e-1,
- .2102593522455432763479327331752e-2,
- .1073554449776116584640731045276e-3,
- .5978267249293031478642787517872e-5,
- .3505062030889134845966834886200e-6,
- .2126374343765340350896219314431e-7,
- .1321694535715527192129801723055e-8,
- .8365875501178070364623604052959e-10,
- .5370503749311002163881434587772e-11,
- .3486659470157107922971245784290e-12,
- .2284549509603433015524024119722e-13,
- .1508407105944793044874229067558e-14,
- .1002418816804109126136995722837e-15,
- .6698674738165069539715526882986e-17,
- .4497954546494931083083327624533e-18
- };
-
- @JRubyMethod(name = "atanh", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat atanh(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double y = Math.abs(value);
- double result;
-
- if (Double.isNaN(value)) {
- result = Double.NaN;
- } else if (y < 1.82501e-08) {
- result = value;
- } else if (y <= 0.5) {
- result = value * (1.0 + chebylevSerie(8.0 * value * value - 1.0, ATANH_COEF));
- } else if (y < 1.0) {
- result = 0.5 * Math.log((1.0 + value) / (1.0 - value));
- } else if (y == 1.0) {
- result = value * Double.POSITIVE_INFINITY;
- } else {
- result = Double.NaN;
- }
-
- domainCheck(recv, result, "atanh");
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- @JRubyMethod(name = "exp", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat exp(IRubyObject recv, IRubyObject exponent) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,exponent)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),Math.exp(value));
- }
-
- /** Returns the natural logarithm of x.
- *
- */
- @JRubyMethod(name = "log", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat log(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result = Math.log(value);
- domainCheck(recv, result, "log");
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- /** Returns the base 10 logarithm of x.
- *
- */
- @JRubyMethod(name = "log10", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat log10(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result = Math.log(value) / Math.log(10);
- domainCheck(recv, result, "log10");
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- @JRubyMethod(name = "sqrt", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat sqrt(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result;
-
- if (value < 0) {
- result = Double.NaN;
- } else{
- result = Math.sqrt(value);
- }
-
- domainCheck(recv, result, "sqrt");
- return RubyFloat.newFloat(recv.getRuntime(), result);
- }
-
- @JRubyMethod(name = "hypot", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat hypot(IRubyObject recv, IRubyObject x, IRubyObject y) {
- double valuea = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double valueb = ((RubyFloat)RubyKernel.new_float(recv,y)).getDoubleValue();
- double result;
-
- if (Math.abs(valuea) > Math.abs(valueb)) {
- result = valueb / valuea;
- result = Math.abs(valuea) * Math.sqrt(1 + result * result);
- } else if (valueb != 0) {
- result = valuea / valueb;
- result = Math.abs(valueb) * Math.sqrt(1 + result * result);
- } else {
- result = 0;
- }
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
-
- /*
- * x = mantissa * 2 ** exponent
- *
- * Where mantissa is in the range of [.5, 1)
- *
- */
- @JRubyMethod(name = "frexp", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyArray frexp(IRubyObject recv, IRubyObject other) {
- double mantissa = ((RubyFloat)RubyKernel.new_float(recv,other)).getDoubleValue();
- short sign = 1;
- long exponent = 0;
-
- if (mantissa != 0.0) {
- // Make mantissa same sign so we only have one code path.
- if (mantissa < 0) {
- mantissa = -mantissa;
- sign = -1;
- }
-
- // Increase value to hit lower range.
- for (; mantissa < 0.5; mantissa *= 2.0, exponent -=1) { }
-
- // Decrease value to hit upper range.
- for (; mantissa >= 1.0; mantissa *= 0.5, exponent +=1) { }
- }
-
- return RubyArray.newArray(recv.getRuntime(),
- RubyFloat.newFloat(recv.getRuntime(), sign * mantissa),
- RubyNumeric.int2fix(recv.getRuntime(), exponent));
- }
-
- /*
- * r = x * 2 ** y
- */
- @JRubyMethod(name = "ldexp", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat ldexp(IRubyObject recv, IRubyObject mantissa, IRubyObject exponent) {
- double mantissaValue = ((RubyFloat)RubyKernel.new_float(recv, mantissa)).getDoubleValue();
- return RubyFloat.newFloat(recv.getRuntime(),mantissaValue * Math.pow(2.0, RubyNumeric.num2int(exponent)));
- }
-
- private static final double ERFC_COEF[] = {
- -.490461212346918080399845440334e-1,
- -.142261205103713642378247418996e0,
- .100355821875997955757546767129e-1,
- -.576876469976748476508270255092e-3,
- .274199312521960610344221607915e-4,
- -.110431755073445076041353812959e-5,
- .384887554203450369499613114982e-7,
- -.118085825338754669696317518016e-8,
- .323342158260509096464029309534e-10,
- -.799101594700454875816073747086e-12,
- .179907251139614556119672454866e-13,
- -.371863548781869263823168282095e-15,
- .710359900371425297116899083947e-17,
- -.126124551191552258324954248533e-18
- };
-
- @JRubyMethod(name = "erf", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat erf(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
-
- double result;
- double y = Math.abs(value);
-
- if (y <= 1.49012e-08) {
- result = 2 * value / 1.77245385090551602729816748334;
- } else if (y <= 1) {
- result = value * (1 + chebylevSerie(2 * value * value - 1, ERFC_COEF));
- } else if (y < 6.013687357) {
- result = sign(1 - erfc(recv, RubyFloat.newFloat(recv.getRuntime(),y)).getDoubleValue(), value);
- } else {
- result = sign(1, value);
- }
- return RubyFloat.newFloat(recv.getRuntime(),result);
- }
-
- private static final double ERFC2_COEF[] = {
- -.69601346602309501127391508262e-1,
- -.411013393626208934898221208467e-1,
- .391449586668962688156114370524e-2,
- -.490639565054897916128093545077e-3,
- .715747900137703638076089414183e-4,
- -.115307163413123283380823284791e-4,
- .199467059020199763505231486771e-5,
- -.364266647159922287393611843071e-6,
- .694437261000501258993127721463e-7,
- -.137122090210436601953460514121e-7,
- .278838966100713713196386034809e-8,
- -.581416472433116155186479105032e-9,
- .123892049175275318118016881795e-9,
- -.269063914530674343239042493789e-10,
- .594261435084791098244470968384e-11,
- -.133238673575811957928775442057e-11,
- .30280468061771320171736972433e-12,
- -.696664881494103258879586758895e-13,
- .162085454105392296981289322763e-13,
- -.380993446525049199987691305773e-14,
- .904048781597883114936897101298e-15,
- -.2164006195089607347809812047e-15,
- .522210223399585498460798024417e-16,
- -.126972960236455533637241552778e-16,
- .310914550427619758383622741295e-17,
- -.766376292032038552400956671481e-18,
- .190081925136274520253692973329e-18
- };
-
- private static final double ERFCC_COEF[] = {
- .715179310202924774503697709496e-1,
- -.265324343376067157558893386681e-1,
- .171115397792085588332699194606e-2,
- -.163751663458517884163746404749e-3,
- .198712935005520364995974806758e-4,
- -.284371241276655508750175183152e-5,
- .460616130896313036969379968464e-6,
- -.822775302587920842057766536366e-7,
- .159214187277090112989358340826e-7,
- -.329507136225284321486631665072e-8,
- .72234397604005554658126115389e-9,
- -.166485581339872959344695966886e-9,
- .401039258823766482077671768814e-10,
- -.100481621442573113272170176283e-10,
- .260827591330033380859341009439e-11,
- -.699111056040402486557697812476e-12,
- .192949233326170708624205749803e-12,
- -.547013118875433106490125085271e-13,
- .158966330976269744839084032762e-13,
- -.47268939801975548392036958429e-14,
- .14358733767849847867287399784e-14,
- -.444951056181735839417250062829e-15,
- .140481088476823343737305537466e-15,
- -.451381838776421089625963281623e-16,
- .147452154104513307787018713262e-16,
- -.489262140694577615436841552532e-17,
- .164761214141064673895301522827e-17,
- -.562681717632940809299928521323e-18,
- .194744338223207851429197867821e-18
- };
-
- @JRubyMethod(name = "erfc", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static RubyFloat erfc(IRubyObject recv, IRubyObject x) {
- double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
- double result;
- double y = Math.abs(value);
-
- if (value <= -6.013687357) {
- result = 2;
- } else if (y < 1.49012e-08) {
- result = 1 - 2 * value / 1.77245385090551602729816748334;
- } else {
- double ysq = y*y;
- if (y < 1) {
- result = 1 - value * (1 + chebylevSerie(2 * ysq - 1, ERFC_COEF));
- } else if (y <= 4.0) {
- result = Math.exp(-ysq)/y*(0.5+chebylevSerie((8.0 / ysq - 5.0) / 3.0, ERFC2_COEF));
- if (value < 0) result = 2.0 - result;
- if (value < 0) result = 2.0 - result;
- if (value < 0) result = 2.0 - result;
- } else {
- result = Math.exp(-ysq) / y * (0.5 + chebylevSerie(8.0 / ysq - 1, ERFCC_COEF));
- if (value < 0) result = 2.0 - result;
- }
- }
- return RubyFloat.newFloat(recv.getRuntime(),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 Alan Moore <alan_moore@gmx.net>
- * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- * Copyright (C) 2004 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 org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.exceptions.JumpException;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallbackFactory;
-import org.jruby.runtime.MethodBlock;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- * The RubyMethod class represents a RubyMethod object.
- *
- * You can get such a method by calling the "method" method of an object.
- *
- * Note: This was renamed from Method.java
- *
- * @author jpetersen
- * @since 0.2.3
- */
-@JRubyClass(name="Method")
-public class RubyMethod extends RubyObject {
- protected RubyModule implementationModule;
- protected String methodName;
- protected RubyModule originModule;
- protected String originName;
- protected DynamicMethod method;
- protected IRubyObject receiver;
-
- protected RubyMethod(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
- }
-
- /** Create the RubyMethod class and add it to the Ruby runtime.
- *
- */
- public static RubyClass createMethodClass(Ruby runtime) {
- // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
- RubyClass methodClass = runtime.defineClass("Method", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setMethod(methodClass);
-
- methodClass.defineAnnotatedMethods(RubyMethod.class);
-
- return methodClass;
- }
-
- public static RubyMethod newMethod(
- RubyModule implementationModule,
- String methodName,
- RubyModule originModule,
- String originName,
- DynamicMethod method,
- IRubyObject receiver) {
- Ruby runtime = implementationModule.getRuntime();
- RubyMethod newMethod = new RubyMethod(runtime, runtime.getMethod());
-
- newMethod.implementationModule = implementationModule;
- newMethod.methodName = methodName;
- newMethod.originModule = originModule;
- newMethod.originName = originName;
- newMethod.method = method.getRealMethod();
- newMethod.receiver = receiver;
-
- return newMethod;
- }
-
- /** Call the method.
- *
- */
- @JRubyMethod(name = {"call", "[]"})
- public IRubyObject call(ThreadContext context, Block block) {
- return method.call(context, receiver, implementationModule, methodName, block);
- }
- @JRubyMethod(name = {"call", "[]"})
- public IRubyObject call(ThreadContext context, IRubyObject arg, Block block) {
- return method.call(context, receiver, implementationModule, methodName, arg, block);
- }
- @JRubyMethod(name = {"call", "[]"})
- public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- return method.call(context, receiver, implementationModule, methodName, arg0, arg1, block);
- }
- @JRubyMethod(name = {"call", "[]"})
- public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- return method.call(context, receiver, implementationModule, methodName, arg0, arg1, arg2, block);
- }
- @JRubyMethod(name = {"call", "[]"}, rest = true)
- public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
- return method.call(context, receiver, implementationModule, methodName, args, block);
- }
-
- /** Returns the number of arguments a method accepted.
- *
- * @return the number of arguments of a method.
- */
- @JRubyMethod(name = "arity")
- public RubyFixnum arity() {
- return getRuntime().newFixnum(method.getArity().getValue());
- }
-
- @JRubyMethod(name = "==", required = 1)
- @Override
- public RubyBoolean op_equal(ThreadContext context, IRubyObject other) {
- if (!(other instanceof RubyMethod)) return context.getRuntime().getFalse();
- RubyMethod otherMethod = (RubyMethod)other;
- return context.getRuntime().newBoolean(implementationModule == otherMethod.implementationModule &&
- originModule == otherMethod.originModule &&
- receiver == otherMethod.receiver &&
- method.getRealMethod() == otherMethod.method.getRealMethod());
- }
-
- @JRubyMethod(name = "clone")
- @Override
- public RubyMethod rbClone() {
- return newMethod(implementationModule, methodName, originModule, originName, method, receiver);
- }
-
- /** Create a Proc object.
- *
- */
- @JRubyMethod(name = "to_proc", frame = true)
- public IRubyObject to_proc(ThreadContext context, Block unusedBlock) {
- Ruby runtime = context.getRuntime();
- CallbackFactory f = runtime.callbackFactory(RubyMethod.class);
- Block block = MethodBlock.createMethodBlock(context, context.getCurrentScope(),
- f.getBlockMethod("bmcall"), this, runtime.getTopSelf());
-
- while (true) {
- try {
- // FIXME: We should not be regenerating this over and over
- return mproc(context, block);
- } catch (JumpException.BreakJump bj) {
- return (IRubyObject) bj.getValue();
- } catch (JumpException.ReturnJump rj) {
- return (IRubyObject) rj.getValue();
- } catch (JumpException.RetryJump rj) {
- // Execute iterateMethod again.
- }
- }
- }
-
- /** Create a Proc object which is called like a ruby method.
- *
- * Used by the RubyMethod#to_proc method.
- *
- */
- private IRubyObject mproc(ThreadContext context, Block block) {
- try {
- context.preMproc();
-
- return RubyKernel.proc(context, context.getRuntime().getNil(), block);
- } finally {
- context.postMproc();
- }
- }
-
- /** Delegate a block call to a bound method call.
- *
- * Used by the RubyMethod#to_proc method.
- *
- */
- public static IRubyObject bmcall(IRubyObject blockArg, IRubyObject arg1,
- IRubyObject self, Block unusedBlock) {
- ThreadContext context = blockArg.getRuntime().getCurrentContext();
-
- if (blockArg instanceof RubyArray) {
- // ENEBO: Very wrong
- return ((RubyMethod) arg1).call(context, ((RubyArray) blockArg).toJavaArray(), Block.NULL_BLOCK);
- }
- // ENEBO: Very wrong
- return ((RubyMethod) arg1).call(context, new IRubyObject[] { blockArg }, Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "unbind", frame = true)
- public RubyUnboundMethod unbind(Block unusedBlock) {
- RubyUnboundMethod unboundMethod =
- RubyUnboundMethod.newUnboundMethod(implementationModule, methodName, originModule, originName, method);
- unboundMethod.infectBy(this);
-
- return unboundMethod;
- }
-
- @JRubyMethod(name = {"inspect", "to_s"})
- @Override
- public IRubyObject inspect() {
- StringBuilder buf = new StringBuilder("#<");
- char delimeter = '#';
-
- buf.append(getMetaClass().getRealClass().getName()).append(": ");
-
- if (implementationModule.isSingleton()) {
- IRubyObject attached = ((MetaClass) implementationModule).getAttached();
- if (receiver == null) {
- buf.append(implementationModule.inspect().toString());
- } else if (receiver == attached) {
- buf.append(attached.inspect().toString());
- delimeter = '.';
- } else {
- buf.append(receiver.inspect().toString());
- buf.append('(').append(attached.inspect().toString()).append(')');
- delimeter = '.';
- }
- } else {
- buf.append(originModule.getName());
-
- if (implementationModule != originModule) {
- buf.append('(').append(implementationModule.getName()).append(')');
- }
- }
-
- buf.append(delimeter).append(methodName).append('>');
-
- RubyString str = getRuntime().newString(buf.toString());
- str.setTaint(isTaint());
- return str;
- }
-}
-
-/*
- **** 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-2007 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.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyConstant;
-import org.jruby.anno.JavaMethodDescriptor;
-import org.jruby.anno.TypePopulator;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.compiler.ASTInspector;
-import org.jruby.internal.runtime.methods.AliasMethod;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-import org.jruby.internal.runtime.methods.FullFunctionCallbackMethod;
-import org.jruby.internal.runtime.methods.SimpleCallbackMethod;
-import org.jruby.internal.runtime.methods.MethodMethod;
-import org.jruby.internal.runtime.methods.ProcMethod;
-import org.jruby.internal.runtime.methods.UndefinedMethod;
-import org.jruby.internal.runtime.methods.WrapperMethod;
-import org.jruby.parser.StaticScope;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CacheMap;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.Visibility;
-import static org.jruby.runtime.Visibility.*;
-import static org.jruby.anno.FrameField.*;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.builtin.Variable;
-import org.jruby.runtime.callback.Callback;
-import org.jruby.runtime.component.VariableEntry;
-import org.jruby.runtime.marshal.MarshalStream;
-import org.jruby.runtime.marshal.UnmarshalStream;
-import org.jruby.util.ClassProvider;
-import org.jruby.util.IdUtil;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.internal.runtime.methods.JavaMethod;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.MethodFactory;
-import org.jruby.runtime.MethodIndex;
-
-/**
- *
- * @author jpetersen
- */
-@JRubyClass(name="Module")
-public class RubyModule extends RubyObject {
- private static final boolean DEBUG = false;
-
- public static RubyClass createModuleClass(Ruby runtime, RubyClass moduleClass) {
- moduleClass.index = ClassIndex.MODULE;
- moduleClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyModule;
- }
- };
-
- moduleClass.defineAnnotatedMethods(RubyModule.class);
- moduleClass.defineAnnotatedMethods(ModuleKernelMethods.class);
-
- return moduleClass;
- }
-
- public static class ModuleKernelMethods {
- @JRubyMethod
- public static IRubyObject autoload(IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
- return RubyKernel.autoload(recv, arg0, arg1);
- }
-
- @JRubyMethod(name = "autoload?")
- public static IRubyObject autoload_p(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
- return RubyKernel.autoload_p(context, recv, arg0);
- }
- }
-
- static ObjectAllocator MODULE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyModule(runtime, klass);
- }
- };
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.MODULE;
- }
-
- @Override
- public boolean isModule() {
- return true;
- }
-
- @Override
- public boolean isClass() {
- return false;
- }
-
- public boolean isSingleton() {
- return false;
- }
-
- // superClass may be null.
- protected RubyClass superClass;
-
- public int index;
-
- public static class KindOf {
- public static final KindOf DEFAULT_KIND_OF = new KindOf();
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj.getMetaClass().hasModuleInHierarchy(type);
- }
- }
-
- public boolean isInstance(IRubyObject object) {
- return kindOf.isKindOf(object, this);
- }
-
- public KindOf kindOf = KindOf.DEFAULT_KIND_OF;
-
- public final int id;
-
- // Containing class...The parent of Object is null. Object should always be last in chain.
- public RubyModule parent;
-
- // ClassId is the name of the class/module sans where it is located.
- // If it is null, then it an anonymous class.
- protected String classId;
-
-
- // CONSTANT TABLE
-
- // Lock used for variableTable/constantTable writes. The RubyObject variableTable
- // write methods are overridden here to use this lock rather than Java
- // synchronization for faster concurrent writes for modules/classes.
- protected final ReentrantLock variableWriteLock = new ReentrantLock();
-
- protected transient volatile ConstantTableEntry[] constantTable =
- new ConstantTableEntry[CONSTANT_TABLE_DEFAULT_CAPACITY];
-
- protected transient int constantTableSize;
-
- protected transient int constantTableThreshold =
- (int)(CONSTANT_TABLE_DEFAULT_CAPACITY * CONSTANT_TABLE_LOAD_FACTOR);
-
- private final Map<String, DynamicMethod> methods = new ConcurrentHashMap<String, DynamicMethod>(12, 0.75f, 1);
-
- // ClassProviders return Java class/module (in #defineOrGetClassUnder and
- // #defineOrGetModuleUnder) when class/module is opened using colon syntax.
- private transient List<ClassProvider> classProviders;
-
- /** separate path for MetaClass construction
- *
- */
- protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
- super(runtime, metaClass, objectSpace);
- id = runtime.allocModuleId();
- // if (parent == null) parent = runtime.getObject();
- setFlag(USER7_F, !isClass());
- }
-
- /** used by MODULE_ALLOCATOR and RubyClass constructors
- *
- */
- protected RubyModule(Ruby runtime, RubyClass metaClass) {
- this(runtime, metaClass, runtime.isObjectSpaceEnabled());
- }
-
- /** standard path for Module construction
- *
- */
- protected RubyModule(Ruby runtime) {
- this(runtime, runtime.getModule());
- }
-
- public boolean needsImplementer() {
- return getFlag(USER7_F);
- }
-
- /** rb_module_new
- *
- */
- public static RubyModule newModule(Ruby runtime) {
- return new RubyModule(runtime);
- }
-
- /** rb_module_new/rb_define_module_id/rb_name_class/rb_set_class_path
- *
- */
- public static RubyModule newModule(Ruby runtime, String name, RubyModule parent, boolean setParent) {
- RubyModule module = newModule(runtime);
- module.setBaseName(name);
- if (setParent) module.setParent(parent);
- parent.setConstant(name, module);
- return module;
- }
-
- // synchronized method per JRUBY-1173 (unsafe Double-Checked Locking)
- // FIXME: synchronization is still wrong in CP code
- public synchronized void addClassProvider(ClassProvider provider) {
- if (classProviders == null) {
- List<ClassProvider> cp = Collections.synchronizedList(new ArrayList<ClassProvider>());
- cp.add(provider);
- classProviders = cp;
- } else {
- synchronized(classProviders) {
- if (!classProviders.contains(provider)) {
- classProviders.add(provider);
- }
- }
- }
- }
-
- public void removeClassProvider(ClassProvider provider) {
- if (classProviders != null) {
- classProviders.remove(provider);
- }
- }
-
- private RubyClass searchProvidersForClass(String name, RubyClass superClazz) {
- if (classProviders != null) {
- synchronized(classProviders) {
- RubyClass clazz;
- for (ClassProvider classProvider: classProviders) {
- if ((clazz = classProvider.defineClassUnder(this, name, superClazz)) != null) {
- return clazz;
- }
- }
- }
- }
- return null;
- }
-
- private RubyModule searchProvidersForModule(String name) {
- if (classProviders != null) {
- synchronized(classProviders) {
- RubyModule module;
- for (ClassProvider classProvider: classProviders) {
- if ((module = classProvider.defineModuleUnder(this, name)) != null) {
- return module;
- }
- }
- }
- }
- return null;
- }
-
- /** Getter for property superClass.
- * @return Value of property superClass.
- */
- public RubyClass getSuperClass() {
- return superClass;
- }
-
- protected void setSuperClass(RubyClass superClass) {
- this.superClass = superClass;
- }
-
- public RubyModule getParent() {
- return parent;
- }
-
- public void setParent(RubyModule parent) {
- this.parent = parent;
- }
-
- public Map<String, DynamicMethod> getMethods() {
- return methods;
- }
-
-
- // note that addMethod now does its own put, so any change made to
- // functionality here should be made there as well
- private void putMethod(String name, DynamicMethod method) {
- getMethods().put(name, method);
- }
-
- /**
- * Is this module one that in an included one (e.g. an IncludedModuleWrapper).
- */
- public boolean isIncluded() {
- return false;
- }
-
- public RubyModule getNonIncludedClass() {
- return this;
- }
-
- public String getBaseName() {
- return classId;
- }
-
- public void setBaseName(String name) {
- classId = name;
- }
-
- private volatile String bareName;
- private volatile String fullName;
-
- /**
- * Generate a fully-qualified class name or a #-style name for anonymous and singleton classes.
- *
- * Ruby C equivalent = "classname"
- *
- * @return The generated class name
- */
- public String getName() {
- if (fullName == null) {
- fullName = calculateFullName();
- }
- return fullName;
- }
-
- private String calculateFullName() {
- if (getBaseName() == null) {
- if (bareName == null) {
- if (isClass()) {
- bareName = "#<" + "Class" + ":01x" + Integer.toHexString(System.identityHashCode(this)) + ">";
- } else {
- bareName = "#<" + "Module" + ":01x" + Integer.toHexString(System.identityHashCode(this)) + ">";
- }
- }
-
- return bareName;
- }
-
- String result = getBaseName();
- RubyClass objectClass = getRuntime().getObject();
-
- for (RubyModule p = this.getParent(); p != null && p != objectClass; p = p.getParent()) {
- String pName = p.getBaseName();
- // This is needed when the enclosing class or module is a singleton.
- // In that case, we generated a name such as null::Foo, which broke
- // Marshalling, among others. The correct thing to do in this situation
- // is to insert the generate the name of form #<Class:01xasdfasd> if
- // it's a singleton module/class, which this code accomplishes.
- if(pName == null) {
- pName = p.getName();
- }
- result = pName + "::" + result;
- }
-
- return result;
- }
-
- /**
- * Create a wrapper to use for including the specified module into this one.
- *
- * Ruby C equivalent = "include_class_new"
- *
- * @return The module wrapper
- */
- public IncludedModuleWrapper newIncludeClass(RubyClass superClazz) {
- IncludedModuleWrapper includedModule = new IncludedModuleWrapper(getRuntime(), superClazz, this);
-
- // include its parent (and in turn that module's parents)
- if (getSuperClass() != null) {
- includedModule.includeModule(getSuperClass());
- }
-
- return includedModule;
- }
- /**
- * Finds a class that is within the current module (or class).
- *
- * @param name to be found in this module (or class)
- * @return the class or null if no such class
- */
- public RubyClass getClass(String name) {
- IRubyObject module;
- if ((module = getConstantAt(name)) instanceof RubyClass) {
- return (RubyClass)module;
- }
- return null;
- }
-
- public RubyClass fastGetClass(String internedName) {
- IRubyObject module;
- if ((module = fastGetConstantAt(internedName)) instanceof RubyClass) {
- return (RubyClass)module;
- }
- return null;
- }
-
- /**
- * Include a new module in this module or class.
- *
- * @param arg The module to include
- */
- public synchronized void includeModule(IRubyObject arg) {
- assert arg != null;
-
- testFrozen("module");
- if (!isTaint()) {
- getRuntime().secure(4);
- }
-
- if (!(arg instanceof RubyModule)) {
- throw getRuntime().newTypeError("Wrong argument type " + arg.getMetaClass().getName() +
- " (expected Module).");
- }
-
- RubyModule module = (RubyModule) arg;
-
- // Make sure the module we include does not already exist
- if (isSame(module)) {
- return;
- }
-
- infectBy(module);
-
- doIncludeModule(module);
- }
-
- public void defineMethod(String name, Callback method) {
- Visibility visibility = name.equals("initialize") ?
- PRIVATE : PUBLIC;
- addMethod(name, new FullFunctionCallbackMethod(this, method, visibility));
- }
-
- public void defineAnnotatedMethod(Class clazz, String name) {
- // FIXME: This is probably not very efficient, since it loads all methods for each call
- boolean foundMethod = false;
- for (Method method : clazz.getDeclaredMethods()) {
- if (method.getName().equals(name) && defineAnnotatedMethod(method, MethodFactory.createFactory(getRuntime().getJRubyClassLoader()))) {
- foundMethod = true;
- }
- }
-
- if (!foundMethod) {
- throw new RuntimeException("No JRubyMethod present for method " + name + "on class " + clazz.getName());
- }
- }
-
- public void defineAnnotatedConstants(Class clazz) {
- Field[] declaredFields = clazz.getDeclaredFields();
- for (Field field : declaredFields) {
- if(Modifier.isStatic(field.getModifiers())) {
- defineAnnotatedConstant(field);
- }
- }
- }
-
- public boolean defineAnnotatedConstant(Field field) {
- JRubyConstant jrubyConstant = field.getAnnotation(JRubyConstant.class);
-
- if (jrubyConstant == null) return false;
-
- String[] names = jrubyConstant.value();
- if(names.length == 0) {
- names = new String[]{field.getName()};
- }
-
- Class tp = field.getType();
- IRubyObject realVal;
-
- try {
- if(tp == Integer.class || tp == Integer.TYPE || tp == Short.class || tp == Short.TYPE || tp == Byte.class || tp == Byte.TYPE) {
- realVal = RubyNumeric.int2fix(getRuntime(), field.getInt(null));
- } else if(tp == Boolean.class || tp == Boolean.TYPE) {
- realVal = field.getBoolean(null) ? getRuntime().getTrue() : getRuntime().getFalse();
- } else {
- realVal = getRuntime().getNil();
- }
- } catch(Exception e) {
- realVal = getRuntime().getNil();
- }
-
-
- for(String name : names) {
- this.fastSetConstant(name, realVal);
- }
-
- return true;
- }
-
- public void defineAnnotatedMethods(Class clazz) {
- defineAnnotatedMethodsIndividually(clazz);
- }
-
- public static class MethodClumper {
- Map<String, List<JavaMethodDescriptor>> annotatedMethods = new HashMap<String, List<JavaMethodDescriptor>>();
- Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods = new HashMap<String, List<JavaMethodDescriptor>>();
- Map<String, List<JavaMethodDescriptor>> annotatedMethods1_8 = new HashMap<String, List<JavaMethodDescriptor>>();
- Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods1_8 = new HashMap<String, List<JavaMethodDescriptor>>();
- Map<String, List<JavaMethodDescriptor>> annotatedMethods1_9 = new HashMap<String, List<JavaMethodDescriptor>>();
- Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods1_9 = new HashMap<String, List<JavaMethodDescriptor>>();
-
- public void clump(Class cls) {
- Method[] declaredMethods = cls.getDeclaredMethods();
- for (Method method: declaredMethods) {
- JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
- if (anno == null) continue;
-
- JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
-
- String name = anno.name().length == 0 ? method.getName() : anno.name()[0];
-
- List<JavaMethodDescriptor> methodDescs;
- Map<String, List<JavaMethodDescriptor>> methodsHash = null;
- if (desc.isStatic) {
- if (anno.compat() == CompatVersion.RUBY1_8) {
- methodsHash = staticAnnotatedMethods1_8;
- } else if (anno.compat() == CompatVersion.RUBY1_9) {
- methodsHash = staticAnnotatedMethods1_9;
- } else {
- methodsHash = staticAnnotatedMethods;
- }
- } else {
- if (anno.compat() == CompatVersion.RUBY1_8) {
- methodsHash = annotatedMethods1_8;
- } else if (anno.compat() == CompatVersion.RUBY1_9) {
- methodsHash = annotatedMethods1_9;
- } else {
- methodsHash = annotatedMethods;
- }
- }
-
- methodDescs = methodsHash.get(name);
- if (methodDescs == null) {
- methodDescs = new ArrayList<JavaMethodDescriptor>();
- methodsHash.put(name, methodDescs);
- }
-
- methodDescs.add(desc);
- }
- }
-
- public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods() {
- return annotatedMethods;
- }
-
- public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods1_8() {
- return annotatedMethods1_8;
- }
-
- public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods1_9() {
- return annotatedMethods1_9;
- }
-
- public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods() {
- return staticAnnotatedMethods;
- }
-
- public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods1_8() {
- return staticAnnotatedMethods1_8;
- }
-
- public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods1_9() {
- return staticAnnotatedMethods1_9;
- }
- }
-
- public void defineAnnotatedMethodsIndividually(Class clazz) {
- String x = clazz.getSimpleName();
- TypePopulator populator = null;
-
- if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
- // we need full traces, use default (slow) populator
- if (DEBUG) System.out.println("trace mode, using default populator");
- populator = TypePopulator.DEFAULT;
- } else {
- try {
- String qualifiedName = "org.jruby.gen." + clazz.getCanonicalName().replace('.', '$');
-
- if (DEBUG) System.out.println("looking for " + qualifiedName + "$Populator");
-
- Class populatorClass = Class.forName(qualifiedName + "$Populator");
- populator = (TypePopulator)populatorClass.newInstance();
- } catch (Throwable t) {
- if (DEBUG) System.out.println("Could not find it, using default populator");
- populator = TypePopulator.DEFAULT;
- }
- }
-
- populator.populate(this, clazz);
- }
-
- @Deprecated
- private void defineAnnotatedMethodsIndexed(Class clazz) {
- MethodFactory methodFactory = MethodFactory.createFactory(getRuntime().getJRubyClassLoader());
- methodFactory.defineIndexedAnnotatedMethods(this, clazz, methodDefiningCallback);
- }
-
- private static MethodFactory.MethodDefiningCallback methodDefiningCallback = new MethodFactory.MethodDefiningCallback() {
- public void define(RubyModule module, JavaMethodDescriptor desc, DynamicMethod dynamicMethod) {
- JRubyMethod jrubyMethod = desc.anno;
- if (jrubyMethod.frame()) {
- for (String name : jrubyMethod.name()) {
- ASTInspector.FRAME_AWARE_METHODS.add(name);
- }
- }
- if(jrubyMethod.compat() == CompatVersion.BOTH ||
- module.getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
- RubyModule singletonClass;
-
- if (jrubyMethod.meta()) {
- singletonClass = module.getSingletonClass();
- dynamicMethod.setImplementationClass(singletonClass);
-
- String baseName;
- if (jrubyMethod.name().length == 0) {
- baseName = desc.name;
- singletonClass.addMethod(baseName, dynamicMethod);
- } else {
- baseName = jrubyMethod.name()[0];
- for (String name : jrubyMethod.name()) {
- singletonClass.addMethod(name, dynamicMethod);
- }
- }
-
- if (jrubyMethod.alias().length > 0) {
- for (String alias : jrubyMethod.alias()) {
- singletonClass.defineAlias(alias, baseName);
- }
- }
- } else {
- String baseName;
- if (jrubyMethod.name().length == 0) {
- baseName = desc.name;
- module.addMethod(baseName, dynamicMethod);
- } else {
- baseName = jrubyMethod.name()[0];
- for (String name : jrubyMethod.name()) {
- module.addMethod(name, dynamicMethod);
- }
- }
-
- if (jrubyMethod.alias().length > 0) {
- for (String alias : jrubyMethod.alias()) {
- module.defineAlias(alias, baseName);
- }
- }
-
- if (jrubyMethod.module()) {
- singletonClass = module.getSingletonClass();
- // module/singleton methods are all defined public
- DynamicMethod moduleMethod = dynamicMethod.dup();
- moduleMethod.setVisibility(PUBLIC);
-
- if (jrubyMethod.name().length == 0) {
- baseName = desc.name;
- singletonClass.addMethod(desc.name, moduleMethod);
- } else {
- baseName = jrubyMethod.name()[0];
- for (String name : jrubyMethod.name()) {
- singletonClass.addMethod(name, moduleMethod);
- }
- }
-
- if (jrubyMethod.alias().length > 0) {
- for (String alias : jrubyMethod.alias()) {
- singletonClass.defineAlias(alias, baseName);
- }
- }
- }
- }
- }
- }
- };
-
- public boolean defineAnnotatedMethod(String name, List<JavaMethodDescriptor> methods, MethodFactory methodFactory) {
- JavaMethodDescriptor desc = methods.get(0);
- if (methods.size() == 1) {
- return defineAnnotatedMethod(desc, methodFactory);
- } else {
- DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, methods);
- methodDefiningCallback.define(this, desc, dynamicMethod);
-
- return true;
- }
- }
-
- public boolean defineAnnotatedMethod(Method method, MethodFactory methodFactory) {
- JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
-
- if (jrubyMethod == null) return false;
-
- if(jrubyMethod.compat() == CompatVersion.BOTH ||
- getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
- JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
- DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc);
- methodDefiningCallback.define(this, desc, dynamicMethod);
-
- return true;
- }
- return false;
- }
-
- public boolean defineAnnotatedMethod(JavaMethodDescriptor desc, MethodFactory methodFactory) {
- JRubyMethod jrubyMethod = desc.anno;
-
- if (jrubyMethod == null) return false;
-
- if(jrubyMethod.compat() == CompatVersion.BOTH ||
- getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
- DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc);
- methodDefiningCallback.define(this, desc, dynamicMethod);
-
- return true;
- }
- return false;
- }
-
- public void defineFastMethod(String name, Callback method) {
- Visibility visibility = name.equals("initialize") ?
- PRIVATE : PUBLIC;
- addMethod(name, new SimpleCallbackMethod(this, method, visibility));
- }
-
- public void defineFastMethod(String name, Callback method, Visibility visibility) {
- addMethod(name, new SimpleCallbackMethod(this, method, visibility));
- }
-
- public void definePrivateMethod(String name, Callback method) {
- addMethod(name, new FullFunctionCallbackMethod(this, method, PRIVATE));
- }
-
- public void defineFastPrivateMethod(String name, Callback method) {
- addMethod(name, new SimpleCallbackMethod(this, method, PRIVATE));
- }
-
- public void defineFastProtectedMethod(String name, Callback method) {
- addMethod(name, new SimpleCallbackMethod(this, method, PROTECTED));
- }
-
- public void undefineMethod(String name) {
- addMethod(name, UndefinedMethod.getInstance());
- }
-
- /** rb_undef
- *
- */
- public void undef(ThreadContext context, String name) {
- Ruby runtime = context.getRuntime();
-
- if (this == runtime.getObject()) runtime.secure(4);
-
- if (runtime.getSafeLevel() >= 4 && !isTaint()) {
- throw new SecurityException("Insecure: can't undef");
- }
- testFrozen("module");
- if (name.equals("__id__") || name.equals("__send__")) {
- runtime.getWarnings().warn(ID.UNDEFINING_BAD, "undefining `"+ name +"' may cause serious problem");
- }
- DynamicMethod method = searchMethod(name);
- if (method.isUndefined()) {
- String s0 = " class";
- RubyModule c = this;
-
- if (c.isSingleton()) {
- IRubyObject obj = ((MetaClass)c).getAttached();
-
- if (obj != null && obj instanceof RubyModule) {
- c = (RubyModule) obj;
- s0 = "";
- }
- } else if (c.isModule()) {
- s0 = " module";
- }
-
- throw runtime.newNameError("Undefined method " + name + " for" + s0 + " '" + c.getName() + "'", name);
- }
- addMethod(name, UndefinedMethod.getInstance());
-
- if (isSingleton()) {
- IRubyObject singleton = ((MetaClass)this).getAttached();
- singleton.callMethod(context, "singleton_method_undefined", runtime.newSymbol(name));
- } else {
- callMethod(context, "method_undefined", runtime.newSymbol(name));
- }
- }
-
- @JRubyMethod(name = "include?", required = 1)
- public IRubyObject include_p(ThreadContext context, IRubyObject arg) {
- if (!arg.isModule()) {
- throw context.getRuntime().newTypeError(arg, context.getRuntime().getModule());
- }
-
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if ((p instanceof IncludedModuleWrapper) && ((IncludedModuleWrapper) p).getNonIncludedClass() == arg) {
- return context.getRuntime().getTrue();
- }
- }
-
- return context.getRuntime().getFalse();
- }
-
- // TODO: Consider a better way of synchronizing
- public void addMethod(String name, DynamicMethod method) {
- Ruby runtime = getRuntime();
-
- if (this == runtime.getObject()) runtime.secure(4);
-
- if (runtime.getSafeLevel() >= 4 && !isTaint()) {
- throw runtime.newSecurityError("Insecure: can't define method");
- }
- testFrozen("class/module");
-
- // We can safely reference methods here instead of doing getMethods() since if we
- // are adding we are not using a IncludedModuleWrapper.
- synchronized(getMethods()) {
- // If we add a method which already is cached in this class, then we should update the
- // cachemap so it stays up to date.
- DynamicMethod existingMethod = getMethods().put(name, method);
- if (existingMethod != null) {
- runtime.getCacheMap().remove(existingMethod);
- }
- }
- }
-
- public void removeMethod(ThreadContext context, String name) {
- Ruby runtime = context.getRuntime();
-
- if (this == runtime.getObject()) runtime.secure(4);
-
- if (runtime.getSafeLevel() >= 4 && !isTaint()) {
- throw runtime.newSecurityError("Insecure: can't remove method");
- }
- testFrozen("class/module");
-
- // We can safely reference methods here instead of doing getMethods() since if we
- // are adding we are not using a IncludedModuleWrapper.
- synchronized(getMethods()) {
- DynamicMethod method = (DynamicMethod) getMethods().remove(name);
- if (method == null) {
- throw runtime.newNameError("method '" + name + "' not defined in " + getName(), name);
- }
-
- runtime.getCacheMap().remove(method);
- }
-
- if (isSingleton()) {
- IRubyObject singleton = ((MetaClass)this).getAttached();
- singleton.callMethod(context, "singleton_method_removed", runtime.newSymbol(name));
- } else {
- callMethod(context, "method_removed", runtime.newSymbol(name));
- }
- }
-
- /**
- * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
- *
- * @param name The name of the method to search for
- * @return The method, or UndefinedMethod if not found
- */
- public DynamicMethod searchMethod(String name) {
- DynamicMethod method = getMethods().get(name);
-
- if (method != null) return method;
-
- return superClass == null ? UndefinedMethod.getInstance() : superClass.searchMethod(name);
- }
-
- /**
- * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
- *
- * @param name The name of the method to search for
- * @return The method, or UndefinedMethod if not found
- */
- public DynamicMethod retrieveMethod(String name) {
- return getMethods().get(name);
- }
-
- /**
- * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
- *
- * @param name The name of the method to search for
- * @return The method, or UndefinedMethod if not found
- */
- public RubyModule findImplementer(RubyModule clazz) {
- for (RubyModule searchModule = this; searchModule != null; searchModule = searchModule.getSuperClass()) {
- if (searchModule.isSame(clazz)) {
- return searchModule;
- }
- }
-
- return null;
- }
-
- public void addModuleFunction(String name, DynamicMethod method) {
- addMethod(name, method);
- getSingletonClass().addMethod(name, method);
- }
-
- /** rb_define_module_function
- *
- */
- public void defineModuleFunction(String name, Callback method) {
- definePrivateMethod(name, method);
- getSingletonClass().defineMethod(name, method);
- }
-
- /** rb_define_module_function
- *
- */
- public void definePublicModuleFunction(String name, Callback method) {
- defineMethod(name, method);
- getSingletonClass().defineMethod(name, method);
- }
-
- /** rb_define_module_function
- *
- */
- public void defineFastModuleFunction(String name, Callback method) {
- defineFastPrivateMethod(name, method);
- getSingletonClass().defineFastMethod(name, method);
- }
-
- /** rb_define_module_function
- *
- */
- public void defineFastPublicModuleFunction(String name, Callback method) {
- defineFastMethod(name, method);
- getSingletonClass().defineFastMethod(name, method);
- }
-
- /** rb_alias
- *
- */
- public synchronized void defineAlias(String name, String oldName) {
- testFrozen("module");
- if (oldName.equals(name)) {
- return;
- }
- Ruby runtime = getRuntime();
- if (this == runtime.getObject()) {
- runtime.secure(4);
- }
- DynamicMethod method = searchMethod(oldName);
- DynamicMethod oldMethod = searchMethod(name);
- if (method.isUndefined()) {
- if (isModule()) {
- method = runtime.getObject().searchMethod(oldName);
- }
-
- if (method.isUndefined()) {
- throw runtime.newNameError("undefined method `" + oldName + "' for " +
- (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
- }
- }
- CacheMap cacheMap = runtime.getCacheMap();
- cacheMap.remove(method);
- cacheMap.remove(oldMethod);
- if (oldMethod != oldMethod.getRealMethod()) {
- cacheMap.remove(oldMethod.getRealMethod());
- }
- putMethod(name, new AliasMethod(this, method, oldName));
- }
-
- public synchronized void defineAliases(List<String> aliases, String oldName) {
- testFrozen("module");
- Ruby runtime = getRuntime();
- if (this == runtime.getObject()) {
- runtime.secure(4);
- }
- DynamicMethod method = searchMethod(oldName);
- if (method.isUndefined()) {
- if (isModule()) {
- method = runtime.getObject().searchMethod(oldName);
- }
-
- if (method.isUndefined()) {
- throw runtime.newNameError("undefined method `" + oldName + "' for " +
- (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
- }
- }
- CacheMap cacheMap = runtime.getCacheMap();
- cacheMap.remove(method);
- for (String name: aliases) {
- if (oldName.equals(name)) continue;
- DynamicMethod oldMethod = searchMethod(name);
- cacheMap.remove(oldMethod);
- if (oldMethod != oldMethod.getRealMethod()) {
- cacheMap.remove(oldMethod.getRealMethod());
- }
- putMethod(name, new AliasMethod(this, method, oldName));
- }
- }
-
- /** this method should be used only by interpreter or compiler
- *
- */
- public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz) {
- // This method is intended only for defining new classes in Ruby code,
- // so it uses the allocator of the specified superclass or default to
- // the Object allocator. It should NOT be used to define classes that require a native allocator.
-
- Ruby runtime = getRuntime();
- IRubyObject classObj = getConstantAt(name);
- RubyClass clazz;
-
- if (classObj != null) {
- if (!(classObj instanceof RubyClass)) throw runtime.newTypeError(name + " is not a class");
- clazz = (RubyClass)classObj;
-
- if (superClazz != null) {
- RubyClass tmp = clazz.getSuperClass();
- while (tmp != null && tmp.isIncluded()) tmp = tmp.getSuperClass(); // need to skip IncludedModuleWrappers
- if (tmp != null) tmp = tmp.getRealClass();
- if (tmp != superClazz) throw runtime.newTypeError("superclass mismatch for class " + name);
- // superClazz = null;
- }
-
- if (runtime.getSafeLevel() >= 4) throw runtime.newTypeError("extending class prohibited");
- } else if (classProviders != null && (clazz = searchProvidersForClass(name, superClazz)) != null) {
- // reopen a java class
- } else {
- if (superClazz == null) superClazz = runtime.getObject();
- clazz = RubyClass.newClass(runtime, superClazz, name, superClazz.getAllocator(), this, true);
- }
-
- return clazz;
- }
-
- /** this method should be used only by interpreter or compiler
- *
- */
- public RubyModule defineOrGetModuleUnder(String name) {
- // This method is intended only for defining new modules in Ruby code
- Ruby runtime = getRuntime();
- IRubyObject moduleObj = getConstantAt(name);
- RubyModule module;
- if (moduleObj != null) {
- if (!moduleObj.isModule()) throw runtime.newTypeError(name + " is not a module");
- if (runtime.getSafeLevel() >= 4) throw runtime.newSecurityError("extending module prohibited");
- module = (RubyModule)moduleObj;
- } else if (classProviders != null && (module = searchProvidersForModule(name)) != null) {
- // reopen a java module
- } else {
- module = RubyModule.newModule(runtime, name, this, true);
- }
- return module;
- }
-
- /** rb_define_class_under
- * this method should be used only as an API to define/open nested classes
- */
- public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator) {
- return getRuntime().defineClassUnder(name, superClass, allocator, this);
- }
-
- /** rb_define_module_under
- * this method should be used only as an API to define/open nested module
- */
- public RubyModule defineModuleUnder(String name) {
- return getRuntime().defineModuleUnder(name, this);
- }
-
- // FIXME: create AttrReaderMethod, AttrWriterMethod, for faster attr access
- private void addAccessor(ThreadContext context, String internedName, boolean readable, boolean writeable) {
- assert internedName == internedName.intern() : internedName + " is not interned";
-
- final Ruby runtime = context.getRuntime();
-
- // Check the visibility of the previous frame, which will be the frame in which the class is being eval'ed
- Visibility attributeScope = context.getCurrentVisibility();
- if (attributeScope == PRIVATE) {
- //FIXME warning
- } else if (attributeScope == MODULE_FUNCTION) {
- attributeScope = PRIVATE;
- // FIXME warning
- }
- final String variableName = ("@" + internedName).intern();
- if (readable) {
- // FIXME: should visibility be set to current visibility?
- addMethod(internedName, new JavaMethod(this, PUBLIC) {
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
- if (args.length != 0) Arity.raiseArgumentError(runtime, args.length, 0, 0);
-
- IRubyObject variable = self.getInstanceVariables().fastGetInstanceVariable(variableName);
-
- return variable == null ? runtime.getNil() : variable;
- }
-
- @Override
- public Arity getArity() {
- return Arity.noArguments();
- }
- });
- callMethod(context, "method_added", runtime.fastNewSymbol(internedName));
- }
- if (writeable) {
- internedName = (internedName + "=").intern();
- // FIXME: should visibility be set to current visibility?
- addMethod(internedName, new JavaMethod(this, PUBLIC) {
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
- // ENEBO: Can anyone get args to be anything but length 1?
- if (args.length != 1) Arity.raiseArgumentError(runtime, args.length, 1, 1);
-
- return self.getInstanceVariables().fastSetInstanceVariable(variableName, args[0]);
- }
-
- @Override
- public Arity getArity() {
- return Arity.singleArgument();
- }
- });
- callMethod(context, "method_added", runtime.fastNewSymbol(internedName));
- }
- }
-
- /** set_method_visibility
- *
- */
- public void setMethodVisibility(IRubyObject[] methods, Visibility visibility) {
- if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
- throw getRuntime().newSecurityError("Insecure: can't change method visibility");
- }
-
- for (int i = 0; i < methods.length; i++) {
- exportMethod(methods[i].asJavaString(), visibility);
- }
- }
-
- /** rb_export_method
- *
- */
- public void exportMethod(String name, Visibility visibility) {
- if (this == getRuntime().getObject()) {
- getRuntime().secure(4);
- }
-
- DynamicMethod method = searchMethod(name);
-
- if (method.isUndefined()) {
- throw getRuntime().newNameError("undefined method '" + name + "' for " +
- (isModule() ? "module" : "class") + " '" + getName() + "'", name);
- }
-
- if (method.getVisibility() != visibility) {
- if (this == method.getImplementationClass()) {
- method.setVisibility(visibility);
- } else {
- // FIXME: Why was this using a FullFunctionCallbackMethod before that did callSuper?
- addMethod(name, new WrapperMethod(this, method, visibility));
- }
- }
- }
-
- /**
- * MRI: rb_method_boundp
- *
- */
- public boolean isMethodBound(String name, boolean checkVisibility) {
- DynamicMethod method = searchMethod(name);
- if (!method.isUndefined()) {
- return !(checkVisibility && method.getVisibility() == PRIVATE);
- }
- return false;
- }
-
- public IRubyObject newMethod(IRubyObject receiver, String name, boolean bound) {
- DynamicMethod method = searchMethod(name);
- if (method.isUndefined()) {
- throw getRuntime().newNameError("undefined method `" + name +
- "' for class `" + this.getName() + "'", name);
- }
-
- RubyModule implementationModule = method.getImplementationClass();
- RubyModule originModule = this;
- while (originModule != implementationModule && originModule.isSingleton()) {
- originModule = ((MetaClass)originModule).getRealClass();
- }
-
- RubyMethod newMethod = null;
- if (bound) {
- newMethod = RubyMethod.newMethod(implementationModule, name, originModule, name, method, receiver);
- } else {
- newMethod = RubyUnboundMethod.newUnboundMethod(implementationModule, name, originModule, name, method);
- }
- newMethod.infectBy(this);
-
- return newMethod;
- }
-
- @JRubyMethod(name = "define_method", frame = true, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject define_method(ThreadContext context, IRubyObject arg0, Block block) {
- Ruby runtime = context.getRuntime();
- String name = arg0.asJavaString().intern();
- DynamicMethod newMethod = null;
- Visibility visibility = context.getCurrentVisibility();
-
- if (visibility == MODULE_FUNCTION) visibility = PRIVATE;
- RubyProc proc = runtime.newProc(Block.Type.LAMBDA, block);
-
- // a normal block passed to define_method changes to do arity checking; make it a lambda
- proc.getBlock().type = Block.Type.LAMBDA;
-
- newMethod = createProcMethod(name, visibility, proc);
-
- RuntimeHelpers.addInstanceMethod(this, name, newMethod, context.getPreviousVisibility(), context, runtime);
-
- return proc;
- }
-
- @JRubyMethod(name = "define_method", frame = true, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject define_method(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- Ruby runtime = context.getRuntime();
- IRubyObject body;
- String name = arg0.asJavaString().intern();
- DynamicMethod newMethod = null;
- Visibility visibility = context.getCurrentVisibility();
-
- if (visibility == MODULE_FUNCTION) visibility = PRIVATE;
- if (runtime.getProc().isInstance(arg1)) {
- // double-testing args.length here, but it avoids duplicating the proc-setup code in two places
- RubyProc proc = (RubyProc)arg1;
- body = proc;
-
- newMethod = createProcMethod(name, visibility, proc);
- } else if (runtime.getMethod().isInstance(arg1)) {
- RubyMethod method = (RubyMethod)arg1;
- body = method;
-
- newMethod = new MethodMethod(this, method.unbind(null), visibility);
- } else {
- throw runtime.newTypeError("wrong argument type " + arg1.getType().getName() + " (expected Proc/Method)");
- }
-
- RuntimeHelpers.addInstanceMethod(this, name, newMethod, context.getPreviousVisibility(), context, runtime);
-
- return body;
- }
- @Deprecated
- public IRubyObject define_method(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 1:
- return define_method(context, args[0], block);
- case 2:
- return define_method(context, args[0], args[1], block);
- default:
- throw context.getRuntime().newArgumentError("wrong # of arguments(" + args.length + " for 2)");
- }
- }
-
- private DynamicMethod createProcMethod(String name, Visibility visibility, RubyProc proc) {
- Block block = proc.getBlock();
- block.getBinding().getFrame().setKlazz(this);
- block.getBinding().getFrame().setName(name);
-
- StaticScope scope = block.getBody().getStaticScope();
-
- // for zsupers in define_method (blech!) we tell the proc scope to act as the "argument" scope
- scope.setArgumentScope(true);
-
- Arity arity = block.arity();
- // just using required is broken...but no more broken than before zsuper refactoring
- scope.setRequiredArgs(arity.required());
-
- if(!arity.isFixed()) {
- scope.setRestArg(arity.required());
- }
-
- return new ProcMethod(this, proc, visibility);
- }
-
- @Deprecated
- public IRubyObject executeUnder(ThreadContext context, Callback method, IRubyObject[] args, Block block) {
- context.preExecuteUnder(this, block);
- try {
- return method.execute(this, args, block);
- } finally {
- context.postExecuteUnder();
- }
- }
-
- @JRubyMethod(name = "name")
- public RubyString name() {
- return getRuntime().newString(getBaseName() == null ? "" : getName());
- }
-
- protected IRubyObject cloneMethods(RubyModule clone) {
- RubyModule realType = this.getNonIncludedClass();
- for (Map.Entry<String, DynamicMethod> entry : getMethods().entrySet()) {
- DynamicMethod method = entry.getValue();
- // Do not clone cached methods
- // FIXME: MRI copies all methods here
- if (method.getImplementationClass() == realType || method instanceof UndefinedMethod) {
-
- // A cloned method now belongs to a new class. Set it.
- // TODO: Make DynamicMethod immutable
- DynamicMethod clonedMethod = method.dup();
- clonedMethod.setImplementationClass(clone);
- clone.putMethod(entry.getKey(), clonedMethod);
- }
- }
-
- return clone;
- }
-
- /** rb_mod_init_copy
- *
- */
- @JRubyMethod(name = "initialize_copy", required = 1)
- @Override
- public IRubyObject initialize_copy(IRubyObject original) {
- super.initialize_copy(original);
-
- RubyModule originalModule = (RubyModule)original;
-
- if (!getMetaClass().isSingleton()) setMetaClass(originalModule.getSingletonClassClone());
- setSuperClass(originalModule.getSuperClass());
-
- if (originalModule.hasVariables()){
- syncVariables(originalModule.getVariableList());
- }
-
- originalModule.cloneMethods(this);
-
- return this;
- }
-
- /** rb_mod_included_modules
- *
- */
- @JRubyMethod(name = "included_modules")
- public RubyArray included_modules(ThreadContext context) {
- RubyArray ary = context.getRuntime().newArray();
-
- for (RubyModule p = getSuperClass(); p != null; p = p.getSuperClass()) {
- if (p.isIncluded()) {
- ary.append(p.getNonIncludedClass());
- }
- }
-
- return ary;
- }
-
- /** rb_mod_ancestors
- *
- */
- @JRubyMethod(name = "ancestors")
- public RubyArray ancestors(ThreadContext context) {
- return context.getRuntime().newArray(getAncestorList());
- }
-
- @Deprecated
- public RubyArray ancestors() {
- return getRuntime().newArray(getAncestorList());
- }
-
- public List<IRubyObject> getAncestorList() {
- ArrayList<IRubyObject> list = new ArrayList<IRubyObject>();
-
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if(!p.isSingleton()) {
- list.add(p.getNonIncludedClass());
- }
- }
-
- return list;
- }
-
- public boolean hasModuleInHierarchy(RubyModule type) {
- // XXX: This check previously used callMethod("==") to check for equality between classes
- // when scanning the hierarchy. However the == check may be safe; we should only ever have
- // one instance bound to a given type/constant. If it's found to be unsafe, examine ways
- // to avoid the == call.
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if (p.getNonIncludedClass() == type) return true;
- }
-
- return false;
- }
-
- @Override
- public int hashCode() {
- return id;
- }
-
- @JRubyMethod(name = "hash")
- @Override
- public RubyFixnum hash() {
- return getRuntime().newFixnum(id);
- }
-
- /** rb_mod_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- @Override
- public IRubyObject to_s() {
- if(isSingleton()){
- IRubyObject attached = ((MetaClass)this).getAttached();
- StringBuilder buffer = new StringBuilder("#<Class:");
- if (attached != null) { // FIXME: figure out why we get null sometimes
- if(attached instanceof RubyClass || attached instanceof RubyModule){
- buffer.append(attached.inspect());
- }else{
- buffer.append(attached.anyToString());
- }
- }
- buffer.append(">");
- return getRuntime().newString(buffer.toString());
- }
- return getRuntime().newString(getName());
- }
-
- /** rb_mod_eqq
- *
- */
- @JRubyMethod(name = "===", required = 1)
- @Override
- public RubyBoolean op_eqq(ThreadContext context, IRubyObject obj) {
- return context.getRuntime().newBoolean(isInstance(obj));
- }
-
- @JRubyMethod(name = "==", required = 1)
- @Override
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- return super.op_equal(context, other);
- }
-
- /** rb_mod_freeze
- *
- */
- @JRubyMethod(name = "freeze")
- @Override
- public IRubyObject freeze(ThreadContext context) {
- to_s();
- return super.freeze(context);
- }
-
- /** rb_mod_le
- *
- */
- @JRubyMethod(name = "<=", required = 1)
- public IRubyObject op_le(IRubyObject obj) {
- if (!(obj instanceof RubyModule)) {
- throw getRuntime().newTypeError("compared with non class/module");
- }
-
- if (isKindOfModule((RubyModule) obj)) {
- return getRuntime().getTrue();
- } else if (((RubyModule) obj).isKindOfModule(this)) {
- return getRuntime().getFalse();
- }
-
- return getRuntime().getNil();
- }
-
- /** rb_mod_lt
- *
- */
- @JRubyMethod(name = "<", required = 1)
- public IRubyObject op_lt(IRubyObject obj) {
- return obj == this ? getRuntime().getFalse() : op_le(obj);
- }
-
- /** rb_mod_ge
- *
- */
- @JRubyMethod(name = ">=", required = 1)
- public IRubyObject op_ge(IRubyObject obj) {
- if (!(obj instanceof RubyModule)) {
- throw getRuntime().newTypeError("compared with non class/module");
- }
-
- return ((RubyModule) obj).op_le(this);
- }
-
- /** rb_mod_gt
- *
- */
- @JRubyMethod(name = ">", required = 1)
- public IRubyObject op_gt(IRubyObject obj) {
- return this == obj ? getRuntime().getFalse() : op_ge(obj);
- }
-
- /** rb_mod_cmp
- *
- */
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(IRubyObject obj) {
- if (this == obj) return getRuntime().newFixnum(0);
- if (!(obj instanceof RubyModule)) return getRuntime().getNil();
-
- RubyModule module = (RubyModule) obj;
-
- if (module.isKindOfModule(this)) {
- return getRuntime().newFixnum(1);
- } else if (this.isKindOfModule(module)) {
- return getRuntime().newFixnum(-1);
- }
-
- return getRuntime().getNil();
- }
-
- public boolean isKindOfModule(RubyModule type) {
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if (p.isSame(type)) {
- return true;
- }
- }
-
- return false;
- }
-
- protected boolean isSame(RubyModule module) {
- return this == module;
- }
-
- /** rb_mod_initialize
- *
- */
- @JRubyMethod(name = "initialize", frame = true, visibility = PRIVATE)
- public IRubyObject initialize(Block block) {
- if (block.isGiven()) {
- // class and module bodies default to public, so make the block's visibility public. JRUBY-1185.
- block.getBinding().setVisibility(PUBLIC);
- block.yield(getRuntime().getCurrentContext(), this, this, this, false);
- }
-
- return getRuntime().getNil();
- }
-
- public void addReadWriteAttribute(ThreadContext context, String name) {
- addAccessor(context, name.intern(), true, true);
- }
-
- public void addReadAttribute(ThreadContext context, String name) {
- addAccessor(context, name.intern(), true, false);
- }
-
- public void addWriteAttribute(ThreadContext context, String name) {
- addAccessor(context, name.intern(), false, true);
- }
-
- /** rb_mod_attr
- *
- */
- @JRubyMethod(name = "attr", required = 1, optional = 1, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject attr(ThreadContext context, IRubyObject[] args) {
- boolean writeable = args.length > 1 ? args[1].isTrue() : false;
-
- addAccessor(context, args[0].asJavaString().intern(), true, writeable);
-
- return getRuntime().getNil();
- }
-
- /**
- * @deprecated
- */
- public IRubyObject attr_reader(IRubyObject[] args) {
- return attr_reader(getRuntime().getCurrentContext(), args);
- }
-
- /** rb_mod_attr_reader
- *
- */
- @JRubyMethod(name = "attr_reader", rest = true, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject attr_reader(ThreadContext context, IRubyObject[] args) {
- for (int i = 0; i < args.length; i++) {
- addAccessor(context, args[i].asJavaString().intern(), true, false);
- }
-
- return context.getRuntime().getNil();
- }
-
- /** rb_mod_attr_writer
- *
- */
- @JRubyMethod(name = "attr_writer", rest = true, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject attr_writer(ThreadContext context, IRubyObject[] args) {
- for (int i = 0; i < args.length; i++) {
- addAccessor(context, args[i].asJavaString().intern(), false, true);
- }
-
- return context.getRuntime().getNil();
- }
-
- /**
- * @deprecated
- */
- public IRubyObject attr_accessor(IRubyObject[] args) {
- return attr_accessor(getRuntime().getCurrentContext(), args);
- }
-
- /** rb_mod_attr_accessor
- *
- */
- @JRubyMethod(name = "attr_accessor", rest = true, visibility = PRIVATE, reads = VISIBILITY)
- public IRubyObject attr_accessor(ThreadContext context, IRubyObject[] args) {
- for (int i = 0; i < args.length; i++) {
- // This is almost always already interned, since it will be called with a symbol in most cases
- // but when created from Java code, we might get an argument that needs to be interned.
- // addAccessor has as a precondition that the string MUST be interned
- addAccessor(context, args[i].asJavaString().intern(), true, true);
- }
-
- return context.getRuntime().getNil();
- }
-
- /**
- * Get a list of all instance methods names of the provided visibility unless not is true, then
- * get all methods which are not the provided
- *
- * @param args passed into one of the Ruby instance_method methods
- * @param visibility to find matching instance methods against
- * @param not if true only find methods not matching supplied visibility
- * @return a RubyArray of instance method names
- */
- private RubyArray instance_methods(IRubyObject[] args, final Visibility visibility, boolean not) {
- boolean includeSuper = args.length > 0 ? args[0].isTrue() : true;
- RubyArray ary = getRuntime().newArray();
- Set<String> seen = new HashSet<String>();
-
- for (RubyModule type = this; type != null; type = type.getSuperClass()) {
- RubyModule realType = type.getNonIncludedClass();
- for (Iterator iter = type.getMethods().entrySet().iterator(); iter.hasNext();) {
- Map.Entry entry = (Map.Entry) iter.next();
- DynamicMethod method = (DynamicMethod) entry.getValue();
- String methodName = (String) entry.getKey();
-
- if (! seen.contains(methodName)) {
- seen.add(methodName);
-
- if (method.getImplementationClass() == realType &&
- (!not && method.getVisibility() == visibility || (not && method.getVisibility() != visibility)) &&
- ! method.isUndefined()) {
-
- ary.append(getRuntime().newString(methodName));
- }
- }
- }
-
- if (!includeSuper) {
- break;
- }
- }
-
- return ary;
- }
-
- @JRubyMethod(name = "instance_methods", optional = 1)
- public RubyArray instance_methods(IRubyObject[] args) {
- return instance_methods(args, PRIVATE, true);
- }
-
- @JRubyMethod(name = "public_instance_methods", optional = 1)
- public RubyArray public_instance_methods(IRubyObject[] args) {
- return instance_methods(args, PUBLIC, false);
- }
-
- @JRubyMethod(name = "instance_method", required = 1)
- public IRubyObject instance_method(IRubyObject symbol) {
- return newMethod(null, symbol.asJavaString(), false);
- }
-
- /** rb_class_protected_instance_methods
- *
- */
- @JRubyMethod(name = "protected_instance_methods", optional = 1)
- public RubyArray protected_instance_methods(IRubyObject[] args) {
- return instance_methods(args, PROTECTED, false);
- }
-
- /** rb_class_private_instance_methods
- *
- */
- @JRubyMethod(name = "private_instance_methods", optional = 1)
- public RubyArray private_instance_methods(IRubyObject[] args) {
- return instance_methods(args, PRIVATE, false);
- }
-
- /** rb_mod_append_features
- *
- */
- @JRubyMethod(name = "append_features", required = 1, visibility = PRIVATE)
- public RubyModule append_features(IRubyObject module) {
- if (!(module instanceof RubyModule)) {
- // MRI error message says Class, even though Module is ok
- throw getRuntime().newTypeError(module,getRuntime().getClassClass());
- }
- ((RubyModule) module).includeModule(this);
- return this;
- }
-
- /** rb_mod_extend_object
- *
- */
- @JRubyMethod(name = "extend_object", required = 1, visibility = PRIVATE)
- public IRubyObject extend_object(IRubyObject obj) {
- obj.getSingletonClass().includeModule(this);
- return obj;
- }
-
- /** rb_mod_include
- *
- */
- @JRubyMethod(name = "include", required = 1, rest = true, visibility = PRIVATE)
- public RubyModule include(IRubyObject[] modules) {
- ThreadContext context = getRuntime().getCurrentContext();
- // MRI checks all types first:
- for (int i = modules.length; --i >= 0; ) {
- IRubyObject obj = modules[i];
- if (!obj.isModule()) throw context.getRuntime().newTypeError(obj, context.getRuntime().getModule());
- }
- for (int i = modules.length - 1; i >= 0; i--) {
- modules[i].callMethod(context, "append_features", this);
- modules[i].callMethod(context, "included", this);
- }
-
- return this;
- }
-
- @JRubyMethod(name = "included", required = 1)
- public IRubyObject included(ThreadContext context, IRubyObject other) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "extended", required = 1, frame = true)
- public IRubyObject extended(ThreadContext context, IRubyObject other, Block block) {
- return context.getRuntime().getNil();
- }
-
- private void setVisibility(ThreadContext context, IRubyObject[] args, Visibility visibility) {
- if (context.getRuntime().getSafeLevel() >= 4 && !isTaint()) {
- throw context.getRuntime().newSecurityError("Insecure: can't change method visibility");
- }
-
- if (args.length == 0) {
- // Note: we change current frames visibility here because the methods which call
- // this method are all "fast" (e.g. they do not created their own frame).
- context.setCurrentVisibility(visibility);
- } else {
- setMethodVisibility(args, visibility);
- }
- }
-
- /** rb_mod_public
- *
- */
- @JRubyMethod(name = "public", rest = true, visibility = PRIVATE, writes = VISIBILITY)
- public RubyModule rbPublic(ThreadContext context, IRubyObject[] args) {
- setVisibility(context, args, PUBLIC);
- return this;
- }
-
- /** rb_mod_protected
- *
- */
- @JRubyMethod(name = "protected", rest = true, visibility = PRIVATE, writes = VISIBILITY)
- public RubyModule rbProtected(ThreadContext context, IRubyObject[] args) {
- setVisibility(context, args, PROTECTED);
- return this;
- }
-
- /** rb_mod_private
- *
- */
- @JRubyMethod(name = "private", rest = true, visibility = PRIVATE, writes = VISIBILITY)
- public RubyModule rbPrivate(ThreadContext context, IRubyObject[] args) {
- setVisibility(context, args, PRIVATE);
- return this;
- }
-
- /** rb_mod_modfunc
- *
- */
- @JRubyMethod(name = "module_function", rest = true, visibility = PRIVATE, writes = VISIBILITY)
- public RubyModule module_function(ThreadContext context, IRubyObject[] args) {
- if (context.getRuntime().getSafeLevel() >= 4 && !isTaint()) {
- throw context.getRuntime().newSecurityError("Insecure: can't change method visibility");
- }
-
- if (args.length == 0) {
- context.setCurrentVisibility(MODULE_FUNCTION);
- } else {
- setMethodVisibility(args, PRIVATE);
-
- for (int i = 0; i < args.length; i++) {
- String name = args[i].asJavaString().intern();
- DynamicMethod method = searchMethod(name);
- assert !method.isUndefined() : "undefined method '" + name + "'";
- getSingletonClass().addMethod(name, new WrapperMethod(getSingletonClass(), method, PUBLIC));
- callMethod(context, "singleton_method_added", context.getRuntime().fastNewSymbol(name));
- }
- }
- return this;
- }
-
- @JRubyMethod(name = "method_added", required = 1, visibility = PRIVATE)
- public IRubyObject method_added(ThreadContext context, IRubyObject nothing) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "method_removed", required = 1, visibility = PRIVATE)
- public IRubyObject method_removed(ThreadContext context, IRubyObject nothing) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "method_undefined", required = 1, visibility = PRIVATE)
- public IRubyObject method_undefined(ThreadContext context, IRubyObject nothing) {
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "method_defined?", required = 1)
- public RubyBoolean method_defined_p(ThreadContext context, IRubyObject symbol) {
- return isMethodBound(symbol.asJavaString(), true) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "public_method_defined?", required = 1)
- public IRubyObject public_method_defined(ThreadContext context, IRubyObject symbol) {
- DynamicMethod method = searchMethod(symbol.asJavaString());
-
- return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PUBLIC);
- }
-
- @JRubyMethod(name = "protected_method_defined?", required = 1)
- public IRubyObject protected_method_defined(ThreadContext context, IRubyObject symbol) {
- DynamicMethod method = searchMethod(symbol.asJavaString());
-
- return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PROTECTED);
- }
-
- @JRubyMethod(name = "private_method_defined?", required = 1)
- public IRubyObject private_method_defined(ThreadContext context, IRubyObject symbol) {
- DynamicMethod method = searchMethod(symbol.asJavaString());
-
- return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PRIVATE);
- }
-
- @JRubyMethod(name = "public_class_method", rest = true)
- public RubyModule public_class_method(IRubyObject[] args) {
- getMetaClass().setMethodVisibility(args, PUBLIC);
- return this;
- }
-
- @JRubyMethod(name = "private_class_method", rest = true)
- public RubyModule private_class_method(IRubyObject[] args) {
- getMetaClass().setMethodVisibility(args, PRIVATE);
- return this;
- }
-
- @JRubyMethod(name = "alias_method", required = 2, visibility = PRIVATE)
- public RubyModule alias_method(ThreadContext context, IRubyObject newId, IRubyObject oldId) {
- String newName = newId.asJavaString();
- defineAlias(newName, oldId.asJavaString());
- RubySymbol newSym = newId instanceof RubySymbol ? (RubySymbol)newId :
- context.getRuntime().newSymbol(newName);
- if (isSingleton()) {
- ((MetaClass)this).getAttached().callMethod(context, "singleton_method_added", newSym);
- } else {
- callMethod(context, "method_added", newSym);
- }
- return this;
- }
-
- @JRubyMethod(name = "undef_method", required = 1, rest = true, visibility = PRIVATE)
- public RubyModule undef_method(ThreadContext context, IRubyObject[] args) {
- for (int i=0; i<args.length; i++) {
- undef(context, args[i].asJavaString());
- }
- return this;
- }
-
- @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
- public IRubyObject module_eval(ThreadContext context, Block block) {
- return specificEval(context, this, block);
- }
- @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
- public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, Block block) {
- return specificEval(context, this, arg0, block);
- }
- @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
- public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- return specificEval(context, this, arg0, arg1, block);
- }
- @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
- public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- return specificEval(context, this, arg0, arg1, arg2, block);
- }
- @Deprecated
- public IRubyObject module_eval(ThreadContext context, IRubyObject[] args, Block block) {
- return specificEval(context, this, args, block);
- }
-
- @JRubyMethod(name = "remove_method", required = 1, rest = true, visibility = PRIVATE)
- public RubyModule remove_method(ThreadContext context, IRubyObject[] args) {
- for(int i=0;i<args.length;i++) {
- removeMethod(context, args[i].asJavaString());
- }
- return this;
- }
-
- public static void marshalTo(RubyModule module, MarshalStream output) throws java.io.IOException {
- output.registerLinkTarget(module);
- output.writeString(MarshalStream.getPathFromClass(module));
- }
-
- public static RubyModule unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- String name = RubyString.byteListToString(input.unmarshalString());
- RubyModule result = UnmarshalStream.getModuleFromPath(input.getRuntime(), name);
- input.registerLinkTarget(result);
- return result;
- }
-
- /* Module class methods */
-
- /**
- * Return an array of nested modules or classes.
- */
- @JRubyMethod(name = "nesting", frame = true, meta = true)
- public static RubyArray nesting(ThreadContext context, IRubyObject recv, Block block) {
- Ruby runtime = context.getRuntime();
- RubyModule object = runtime.getObject();
- StaticScope scope = context.getCurrentScope().getStaticScope();
- RubyArray result = runtime.newArray();
-
- for (StaticScope current = scope; current.getModule() != object; current = current.getPreviousCRefScope()) {
- result.append(current.getModule());
- }
-
- return result;
- }
-
- private void doIncludeModule(RubyModule includedModule) {
- boolean skip = false;
-
- RubyModule currentModule = this;
- while (includedModule != null) {
-
- if (getNonIncludedClass() == includedModule.getNonIncludedClass()) {
- throw getRuntime().newArgumentError("cyclic include detected");
- }
-
- boolean superclassSeen = false;
-
- // scan class hierarchy for module
- for (RubyModule superClass = this.getSuperClass(); superClass != null; superClass = superClass.getSuperClass()) {
- if (superClass instanceof IncludedModuleWrapper) {
- if (superClass.getNonIncludedClass() == includedModule.getNonIncludedClass()) {
- if (!superclassSeen) {
- currentModule = superClass;
- }
- skip = true;
- break;
- }
- } else {
- superclassSeen = true;
- }
- }
-
- if (!skip) {
-
- // blow away caches for any methods that are redefined by module
- getRuntime().getCacheMap().moduleIncluded(currentModule, includedModule);
-
- // In the current logic, if we get here we know that module is not an
- // IncludedModuleWrapper, so there's no need to fish out the delegate. But just
- // in case the logic should change later, let's do it anyway:
- currentModule.setSuperClass(new IncludedModuleWrapper(getRuntime(), currentModule.getSuperClass(),
- includedModule.getNonIncludedClass()));
- currentModule = currentModule.getSuperClass();
- }
-
- includedModule = includedModule.getSuperClass();
- skip = false;
- }
- }
-
-
- //
- ////////////////// CLASS VARIABLE RUBY METHODS ////////////////
- //
-
- @JRubyMethod(name = "class_variable_defined?", required = 1)
- public IRubyObject class_variable_defined_p(ThreadContext context, IRubyObject var) {
- String internedName = validateClassVariable(var.asJavaString().intern());
- RubyModule module = this;
- do {
- if (module.fastHasClassVariable(internedName)) {
- return context.getRuntime().getTrue();
- }
- } while ((module = module.getSuperClass()) != null);
-
- return context.getRuntime().getFalse();
- }
-
- /** rb_mod_cvar_get
- *
- */
- @JRubyMethod(name = "class_variable_get", required = 1, visibility = PRIVATE)
- public IRubyObject class_variable_get(IRubyObject var) {
- return fastGetClassVar(validateClassVariable(var.asJavaString()).intern());
- }
-
- /** rb_mod_cvar_set
- *
- */
- @JRubyMethod(name = "class_variable_set", required = 2, visibility = PRIVATE)
- public IRubyObject class_variable_set(IRubyObject var, IRubyObject value) {
- return fastSetClassVar(validateClassVariable(var.asJavaString()).intern(), value);
- }
-
- /** rb_mod_remove_cvar
- *
- */
- @JRubyMethod(name = "remove_class_variable", required = 1, visibility = PRIVATE)
- public IRubyObject remove_class_variable(ThreadContext context, IRubyObject name) {
- String javaName = validateClassVariable(name.asJavaString());
- IRubyObject value;
-
- if ((value = deleteClassVariable(javaName)) != null) {
- return value;
- }
-
- if (fastIsClassVarDefined(javaName)) {
- throw cannotRemoveError(javaName);
- }
-
- throw context.getRuntime().newNameError("class variable " + javaName + " not defined for " + getName(), javaName);
- }
-
- /** rb_mod_class_variables
- *
- */
- @JRubyMethod(name = "class_variables")
- public RubyArray class_variables(ThreadContext context) {
- Set<String> names = new HashSet<String>();
-
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- for (String name : p.getClassVariableNameList()) {
- names.add(name);
- }
- }
-
- Ruby runtime = context.getRuntime();
- RubyArray ary = runtime.newArray();
-
- for (String name : names) {
- ary.append(runtime.newString(name));
- }
-
- return ary;
- }
-
-
- //
- ////////////////// CONSTANT RUBY METHODS ////////////////
- //
-
- /** rb_mod_const_defined
- *
- */
- @JRubyMethod(name = "const_defined?", required = 1)
- public RubyBoolean const_defined_p(ThreadContext context, IRubyObject symbol) {
- // Note: includes part of fix for JRUBY-1339
- return context.getRuntime().newBoolean(fastIsConstantDefined(validateConstant(symbol.asJavaString()).intern()));
- }
-
- /** rb_mod_const_get
- *
- */
- @JRubyMethod(name = "const_get", required = 1)
- public IRubyObject const_get(IRubyObject symbol) {
- return fastGetConstant(validateConstant(symbol.asJavaString()).intern());
- }
-
- /** rb_mod_const_set
- *
- */
- @JRubyMethod(name = "const_set", required = 2)
- public IRubyObject const_set(IRubyObject symbol, IRubyObject value) {
- return fastSetConstant(validateConstant(symbol.asJavaString()).intern(), value);
- }
-
- @JRubyMethod(name = "remove_const", required = 1, visibility = PRIVATE)
- public IRubyObject remove_const(ThreadContext context, IRubyObject name) {
- String id = validateConstant(name.asJavaString());
- IRubyObject value;
- if ((value = deleteConstant(id)) != null) {
- if (value != UNDEF) {
- return value;
- }
- context.getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + id);
- // FIXME: I'm not sure this is right, but the old code returned
- // the undef, which definitely isn't right...
- return context.getRuntime().getNil();
- }
-
- if (hasConstantInHierarchy(id)) {
- throw cannotRemoveError(id);
- }
-
- throw context.getRuntime().newNameError("constant " + id + " not defined for " + getName(), id);
- }
-
- private boolean hasConstantInHierarchy(final String name) {
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if (p.hasConstant(name)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Base implementation of Module#const_missing, throws NameError for specific missing constant.
- *
- * @param name The constant name which was found to be missing
- * @return Nothing! Absolutely nothing! (though subclasses might choose to return something)
- */
- @JRubyMethod(name = "const_missing", required = 1, frame = true)
- public IRubyObject const_missing(ThreadContext context, IRubyObject name, Block block) {
- /* Uninitialized constant */
- if (this != context.getRuntime().getObject()) {
- throw context.getRuntime().newNameError("uninitialized constant " + getName() + "::" + name.asJavaString(), "" + getName() + "::" + name.asJavaString());
- }
-
- throw context.getRuntime().newNameError("uninitialized constant " + name.asJavaString(), name.asJavaString());
- }
-
- /** rb_mod_constants
- *
- */
- @JRubyMethod(name = "constants")
- public RubyArray constants(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- RubyArray array = runtime.newArray();
- RubyModule objectClass = runtime.getObject();
-
- if (getRuntime().getModule() == this) {
-
- for (String name : objectClass.getStoredConstantNameList()) {
- array.append(runtime.newString(name));
- }
-
- } else if (objectClass == this) {
-
- for (String name : getStoredConstantNameList()) {
- array.append(runtime.newString(name));
- }
-
- } else {
- Set<String> names = new HashSet<String>();
- for (RubyModule p = this; p != null; p = p.getSuperClass()) {
- if (objectClass != p) {
- for (String name : p.getStoredConstantNameList()) {
- names.add(name);
- }
- }
- }
- for (String name : names) {
- array.append(runtime.newString(name));
- }
- }
-
- return array;
- }
-
-
- //
- ////////////////// CLASS VARIABLE API METHODS ////////////////
- //
-
- /**
- * Set the named class variable to the given value, provided taint and freeze allow setting it.
- *
- * Ruby C equivalent = "rb_cvar_set"
- *
- * @param name The variable name to set
- * @param value The value to set it to
- */
- public IRubyObject setClassVar(String name, IRubyObject value) {
- RubyModule module = this;
- do {
- if (module.hasClassVariable(name)) {
- return module.storeClassVariable(name, value);
- }
- } while ((module = module.getSuperClass()) != null);
-
- return storeClassVariable(name, value);
- }
-
- public IRubyObject fastSetClassVar(final String internedName, final IRubyObject value) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- RubyModule module = this;
- do {
- if (module.fastHasClassVariable(internedName)) {
- return module.fastStoreClassVariable(internedName, value);
- }
- } while ((module = module.getSuperClass()) != null);
-
- return fastStoreClassVariable(internedName, value);
- }
-
- /**
- * Retrieve the specified class variable, searching through this module, included modules, and supermodules.
- *
- * Ruby C equivalent = "rb_cvar_get"
- *
- * @param name The name of the variable to retrieve
- * @return The variable's value, or throws NameError if not found
- */
- public IRubyObject getClassVar(String name) {
- assert IdUtil.isClassVariable(name);
- IRubyObject value;
- RubyModule module = this;
-
- do {
- if ((value = module.variableTableFetch(name)) != null) return value;
- } while ((module = module.getSuperClass()) != null);
-
- throw getRuntime().newNameError("uninitialized class variable " + name + " in " + getName(), name);
- }
-
- public IRubyObject fastGetClassVar(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- assert IdUtil.isClassVariable(internedName);
- IRubyObject value;
- RubyModule module = this;
-
- do {
- if ((value = module.variableTableFastFetch(internedName)) != null) return value;
- } while ((module = module.getSuperClass()) != null);
-
- throw getRuntime().newNameError("uninitialized class variable " + internedName + " in " + getName(), internedName);
- }
-
- /**
- * Is class var defined?
- *
- * Ruby C equivalent = "rb_cvar_defined"
- *
- * @param name The class var to determine "is defined?"
- * @return true if true, false if false
- */
- public boolean isClassVarDefined(String name) {
- RubyModule module = this;
- do {
- if (module.hasClassVariable(name)) return true;
- } while ((module = module.getSuperClass()) != null);
-
- return false;
- }
-
- public boolean fastIsClassVarDefined(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- RubyModule module = this;
- do {
- if (module.fastHasClassVariable(internedName)) return true;
- } while ((module = module.getSuperClass()) != null);
-
- return false;
- }
-
-
- /** rb_mod_remove_cvar
- *
- * FIXME: any good reason to have two identical methods? (same as remove_class_variable)
- */
- public IRubyObject removeCvar(IRubyObject name) { // Wrong Parameter ?
- String internedName = validateClassVariable(name.asJavaString());
- IRubyObject value;
-
- if ((value = deleteClassVariable(internedName)) != null) {
- return value;
- }
-
- if (fastIsClassVarDefined(internedName)) {
- throw cannotRemoveError(internedName);
- }
-
- throw getRuntime().newNameError("class variable " + internedName + " not defined for " + getName(), internedName);
- }
-
-
- //
- ////////////////// CONSTANT API METHODS ////////////////
- //
-
- public IRubyObject getConstantAt(String name) {
- IRubyObject value;
- if ((value = fetchConstant(name)) != UNDEF) {
- return value;
- }
- deleteConstant(name);
- return getRuntime().getLoadService().autoload(getName() + "::" + name);
- }
-
- public IRubyObject fastGetConstantAt(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- IRubyObject value;
- if ((value = fastFetchConstant(internedName)) != UNDEF) {
- return value;
- }
- deleteConstant(internedName);
- return getRuntime().getLoadService().autoload(getName() + "::" + internedName);
- }
-
- /**
- * Retrieve the named constant, invoking 'const_missing' should that be appropriate.
- *
- * @param name The constant to retrieve
- * @return The value for the constant, or null if not found
- */
- public IRubyObject getConstant(String name) {
- assert IdUtil.isConstant(name);
- boolean retryForModule = false;
- IRubyObject value;
- RubyModule p = this;
-
- retry: while (true) {
- while (p != null) {
- if ((value = p.constantTableFetch(name)) != null) {
- if (value != UNDEF) {
- return value;
- }
- p.deleteConstant(name);
- if (getRuntime().getLoadService().autoload(
- p.getName() + "::" + name) == null) {
- break;
- }
- continue;
- }
- p = p.getSuperClass();
- }
-
- if (!retryForModule && !isClass()) {
- retryForModule = true;
- p = getRuntime().getObject();
- continue retry;
- }
-
- break;
- }
-
- return callMethod(getRuntime().getCurrentContext(),
- "const_missing", getRuntime().newSymbol(name));
- }
-
- public IRubyObject fastGetConstant(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- assert IdUtil.isConstant(internedName);
- boolean retryForModule = false;
- IRubyObject value;
- RubyModule p = this;
-
- retry: while (true) {
- while (p != null) {
- if ((value = p.constantTableFastFetch(internedName)) != null) {
- if (value != UNDEF) {
- return value;
- }
- p.deleteConstant(internedName);
- if (getRuntime().getLoadService().autoload(
- p.getName() + "::" + internedName) == null) {
- break;
- }
- continue;
- }
- p = p.getSuperClass();
- }
-
- if (!retryForModule && !isClass()) {
- retryForModule = true;
- p = getRuntime().getObject();
- continue retry;
- }
-
- break;
- }
-
- return callMethod(getRuntime().getCurrentContext(),
- "const_missing", getRuntime().fastNewSymbol(internedName));
- }
-
- // not actually called anywhere (all known uses call the fast version)
- public IRubyObject getConstantFrom(String name) {
- return fastGetConstantFrom(name.intern());
- }
-
- public IRubyObject fastGetConstantFrom(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- assert IdUtil.isConstant(internedName);
- RubyClass objectClass = getRuntime().getObject();
- IRubyObject value;
-
- RubyModule p = this;
-
- while (p != null) {
- if ((value = p.constantTableFastFetch(internedName)) != null) {
- if (value != UNDEF) {
- if (p == objectClass && this != objectClass) {
- String badCName = getName() + "::" + internedName;
- getRuntime().getWarnings().warn(ID.CONSTANT_BAD_REFERENCE, "toplevel constant " +
- internedName + " referenced by " + badCName, badCName);
- }
- return value;
- }
- p.deleteConstant(internedName);
- if (getRuntime().getLoadService().autoload(
- p.getName() + "::" + internedName) == null) {
- break;
- }
- continue;
- }
- p = p.getSuperClass();
- }
-
- return callMethod(getRuntime().getCurrentContext(),
- "const_missing", getRuntime().fastNewSymbol(internedName));
- }
- /**
- * Set the named constant on this module. Also, if the value provided is another Module and
- * that module has not yet been named, assign it the specified name.
- *
- * @param name The name to assign
- * @param value The value to assign to it; if an unnamed Module, also set its basename to name
- * @return The result of setting the variable.
- */
- public IRubyObject setConstant(String name, IRubyObject value) {
- IRubyObject oldValue;
- if ((oldValue = fetchConstant(name)) != null) {
- if (oldValue == UNDEF) {
- getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + name);
- } else {
- getRuntime().getWarnings().warn(ID.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + name, name);
- }
- }
-
- storeConstant(name, value);
-
- // if adding a module under a constant name, set that module's basename to the constant name
- if (value instanceof RubyModule) {
- RubyModule module = (RubyModule)value;
- if (module.getBaseName() == null) {
- module.setBaseName(name);
- module.setParent(this);
- }
- /*
- module.setParent(this);
- */
- }
- return value;
- }
-
- public IRubyObject fastSetConstant(String internedName, IRubyObject value) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- IRubyObject oldValue;
- if ((oldValue = fastFetchConstant(internedName)) != null) {
- if (oldValue == UNDEF) {
- getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + internedName);
- } else {
- getRuntime().getWarnings().warn(ID.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + internedName, internedName);
- }
- }
-
- fastStoreConstant(internedName, value);
-
- // if adding a module under a constant name, set that module's basename to the constant name
- if (value instanceof RubyModule) {
- RubyModule module = (RubyModule)value;
- if (module.getBaseName() == null) {
- module.setBaseName(internedName);
- module.setParent(this);
- }
- /*
- module.setParent(this);
- */
- }
- return value;
- }
-
- /** rb_define_const
- *
- */
- public void defineConstant(String name, IRubyObject value) {
- assert value != null;
-
- if (this == getRuntime().getClassClass()) {
- getRuntime().secure(4);
- }
-
- if (!IdUtil.isValidConstantName(name)) {
- throw getRuntime().newNameError("bad constant name " + name, name);
- }
-
- setConstant(name, value);
- }
-
- // Fix for JRUBY-1339 - search hierarchy for constant
- /** rb_const_defined_at
- *
- */
- public boolean isConstantDefined(String name) {
- assert IdUtil.isConstant(name);
- boolean isObject = this == getRuntime().getObject();
-
- RubyModule module = this;
-
- do {
- Object value;
- if ((value = module.constantTableFetch(name)) != null) {
- if (value != UNDEF) return true;
- return getRuntime().getLoadService().autoloadFor(
- module.getName() + "::" + name) != null;
- }
-
- } while (isObject && (module = module.getSuperClass()) != null );
-
- return false;
- }
-
- public boolean fastIsConstantDefined(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- assert IdUtil.isConstant(internedName);
- boolean isObject = this == getRuntime().getObject();
-
- RubyModule module = this;
-
- do {
- Object value;
- if ((value = module.constantTableFastFetch(internedName)) != null) {
- if (value != UNDEF) return true;
- return getRuntime().getLoadService().autoloadFor(
- module.getName() + "::" + internedName) != null;
- }
-
- } while (isObject && (module = module.getSuperClass()) != null );
-
- return false;
- }
-
- //
- ////////////////// COMMON CONSTANT / CVAR METHODS ////////////////
- //
-
- private RaiseException cannotRemoveError(String id) {
- return getRuntime().newNameError("cannot remove " + id + " for " + getName(), id);
- }
-
-
- //
- ////////////////// INTERNAL MODULE VARIABLE API METHODS ////////////////
- //
-
- /**
- * Behaves similarly to {@link #getClassVar(String)}. Searches this
- * class/module <em>and its ancestors</em> for the specified internal
- * variable.
- *
- * @param name the internal variable name
- * @return the value of the specified internal variable if found, else null
- * @see #setInternalModuleVariable(String, IRubyObject)
- */
- public boolean hasInternalModuleVariable(final String name) {
- RubyModule module = this;
- do {
- if (module.hasInternalVariable(name)) {
- return true;
- }
- } while ((module = module.getSuperClass()) != null);
-
- return false;
- }
- /**
- * Behaves similarly to {@link #getClassVar(String)}. Searches this
- * class/module <em>and its ancestors</em> for the specified internal
- * variable.
- *
- * @param name the internal variable name
- * @return the value of the specified internal variable if found, else null
- * @see #setInternalModuleVariable(String, IRubyObject)
- */
- public IRubyObject searchInternalModuleVariable(final String name) {
- RubyModule module = this;
- IRubyObject value;
- do {
- if ((value = module.getInternalVariable(name)) != null) {
- return value;
- }
- } while ((module = module.getSuperClass()) != null);
-
- return null;
- }
-
- /**
- * Behaves similarly to {@link #setClassVar(String, IRubyObject)}. If the
- * specified internal variable is found in this class/module <em>or an ancestor</em>,
- * it is set where found. Otherwise it is set in this module.
- *
- * @param name the internal variable name
- * @param value the internal variable value
- * @see #searchInternalModuleVariable(String)
- */
- public void setInternalModuleVariable(final String name, final IRubyObject value) {
- RubyModule module = this;
- do {
- if (module.hasInternalVariable(name)) {
- module.setInternalVariable(name, value);
- return;
- }
- } while ((module = module.getSuperClass()) != null);
-
- setInternalVariable(name, value);
- }
-
- //
- ////////////////// LOW-LEVEL CLASS VARIABLE INTERFACE ////////////////
- //
- // fetch/store/list class variables for this module
- //
-
- public boolean hasClassVariable(String name) {
- assert IdUtil.isClassVariable(name);
- return variableTableContains(name);
- }
-
- public boolean fastHasClassVariable(String internedName) {
- assert IdUtil.isClassVariable(internedName);
- return variableTableFastContains(internedName);
- }
-
- public IRubyObject fetchClassVariable(String name) {
- assert IdUtil.isClassVariable(name);
- return variableTableFetch(name);
- }
-
- public IRubyObject fastFetchClassVariable(String internedName) {
- assert IdUtil.isClassVariable(internedName);
- return variableTableFastFetch(internedName);
- }
-
- public IRubyObject storeClassVariable(String name, IRubyObject value) {
- assert IdUtil.isClassVariable(name) && value != null;
- ensureClassVariablesSettable();
- return variableTableStore(name, value);
- }
-
- public IRubyObject fastStoreClassVariable(String internedName, IRubyObject value) {
- assert IdUtil.isClassVariable(internedName) && value != null;
- ensureClassVariablesSettable();
- return variableTableFastStore(internedName, value);
- }
-
- public IRubyObject deleteClassVariable(String name) {
- assert IdUtil.isClassVariable(name);
- ensureClassVariablesSettable();
- return variableTableRemove(name);
- }
-
- public List<Variable<IRubyObject>> getClassVariableList() {
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- VariableTableEntry[] table = variableTableGetTable();
- IRubyObject readValue;
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if (IdUtil.isClassVariable(e.name)) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- list.add(new VariableEntry<IRubyObject>(e.name, readValue));
- }
- }
- }
- return list;
- }
-
- public List<String> getClassVariableNameList() {
- ArrayList<String> list = new ArrayList<String>();
- VariableTableEntry[] table = variableTableGetTable();
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if (IdUtil.isClassVariable(e.name)) {
- list.add(e.name);
- }
- }
- }
- return list;
- }
-
- protected static final String ERR_INSECURE_SET_CLASS_VAR = "Insecure: can't modify class variable";
- protected static final String ERR_FROZEN_CVAR_TYPE = "class/module ";
-
- protected final String validateClassVariable(String name) {
- if (IdUtil.isValidClassVariableName(name)) {
- return name;
- }
- throw getRuntime().newNameError("`" + name + "' is not allowed as a class variable name", name);
- }
-
- protected final void ensureClassVariablesSettable() {
- Ruby runtime = getRuntime();
-
- if (!isFrozen() && (runtime.getSafeLevel() < 4 || isTaint())) {
- return;
- }
-
- if (runtime.getSafeLevel() >= 4 && !isTaint()) {
- throw runtime.newSecurityError(ERR_INSECURE_SET_CONSTANT);
- }
- if (isFrozen()) {
- if (this instanceof RubyModule) {
- throw runtime.newFrozenError(ERR_FROZEN_CONST_TYPE);
- } else {
- throw runtime.newFrozenError("");
- }
- }
- }
-
- //
- ////////////////// LOW-LEVEL CONSTANT INTERFACE ////////////////
- //
- // fetch/store/list constants for this module
- //
-
- public boolean hasConstant(String name) {
- assert IdUtil.isConstant(name);
- return constantTableContains(name);
- }
-
- public boolean fastHasConstant(String internedName) {
- assert IdUtil.isConstant(internedName);
- return constantTableFastContains(internedName);
- }
-
- // returns the stored value without processing undefs (autoloads)
- public IRubyObject fetchConstant(String name) {
- assert IdUtil.isConstant(name);
- return constantTableFetch(name);
- }
-
- // returns the stored value without processing undefs (autoloads)
- public IRubyObject fastFetchConstant(String internedName) {
- assert IdUtil.isConstant(internedName);
- return constantTableFastFetch(internedName);
- }
-
- public IRubyObject storeConstant(String name, IRubyObject value) {
- assert IdUtil.isConstant(name) && value != null;
- ensureConstantsSettable();
- return constantTableStore(name, value);
- }
-
- public IRubyObject fastStoreConstant(String internedName, IRubyObject value) {
- assert IdUtil.isConstant(internedName) && value != null;
- ensureConstantsSettable();
- return constantTableFastStore(internedName, value);
- }
-
- // removes and returns the stored value without processing undefs (autoloads)
- public IRubyObject deleteConstant(String name) {
- assert IdUtil.isConstant(name);
- ensureConstantsSettable();
- return constantTableRemove(name);
- }
-
- public List<Variable<IRubyObject>> getStoredConstantList() {
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- ConstantTableEntry[] table = constantTableGetTable();
- for (int i = table.length; --i >= 0; ) {
- for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
- list.add(e);
- }
- }
- return list;
- }
-
- public List<String> getStoredConstantNameList() {
- ArrayList<String> list = new ArrayList<String>();
- ConstantTableEntry[] table = constantTableGetTable();
- for (int i = table.length; --i >= 0; ) {
- for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
- list.add(e.name);
- }
- }
- return list;
- }
-
- protected static final String ERR_INSECURE_SET_CONSTANT = "Insecure: can't modify constant";
- protected static final String ERR_FROZEN_CONST_TYPE = "class/module ";
-
- protected final String validateConstant(String name) {
- if (IdUtil.isValidConstantName(name)) {
- return name;
- }
- throw getRuntime().newNameError("wrong constant name " + name, name);
- }
-
- protected final void ensureConstantsSettable() {
- Ruby runtime = getRuntime();
-
- if (!isFrozen() && (runtime.getSafeLevel() < 4 || isTaint())) {
- return;
- }
-
- if (runtime.getSafeLevel() >= 4 && !isTaint()) {
- throw runtime.newSecurityError(ERR_INSECURE_SET_CONSTANT);
- }
- if (isFrozen()) {
- if (this instanceof RubyModule) {
- throw runtime.newFrozenError(ERR_FROZEN_CONST_TYPE);
- } else {
- throw runtime.newFrozenError("");
- }
- }
- }
-
-
- //
- ////////////////// VARIABLE TABLE METHODS ////////////////
- //
- // Overridden to use variableWriteLock in place of synchronization
- //
-
- @Override
- protected IRubyObject variableTableStore(String name, IRubyObject value) {
- int hash = name.hashCode();
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- VariableTableEntry[] table;
- VariableTableEntry e;
- if ((table = variableTable) == null) {
- table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- e = new VariableTableEntry(hash, name.intern(), value, null);
- table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTableSize = 1;
- variableTable = table;
- return value;
- }
- int potentialNewSize;
- if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
- table = variableTableRehash();
- }
- int index;
- for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- e.value = value;
- return value;
- }
- }
- // external volatile value initialization intended to obviate the need for
- // readValueUnderLock technique used in ConcurrentHashMap. may be a little
- // slower, but better to pay a price on first write rather than all reads.
- e = new VariableTableEntry(hash, name.intern(), value, table[index]);
- table[index] = e;
- variableTableSize = potentialNewSize;
- variableTable = table; // write-volatile
- } finally {
- lock.unlock();
- }
- return value;
- }
-
- @Override
- protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
- assert internedName == internedName.intern() : internedName + " not interned";
- int hash = internedName.hashCode();
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- VariableTableEntry[] table;
- VariableTableEntry e;
- if ((table = variableTable) == null) {
- table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- e = new VariableTableEntry(hash, internedName, value, null);
- table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTableSize = 1;
- variableTable = table;
- return value;
- }
- int potentialNewSize;
- if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
- table = variableTableRehash();
- }
- int index;
- for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- e.value = value;
- return value;
- }
- }
- // external volatile value initialization intended to obviate the need for
- // readValueUnderLock technique used in ConcurrentHashMap. may be a little
- // slower, but better to pay a price on first write rather than all reads.
- e = new VariableTableEntry(hash, internedName, value, table[index]);
- table[index] = e;
- variableTableSize = potentialNewSize;
- variableTable = table; // write-volatile
- } finally {
- lock.unlock();
- }
- return value;
- }
-
- @Override
- protected IRubyObject variableTableRemove(String name) {
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- VariableTableEntry[] table;
- if ((table = variableTable) != null) {
- int hash = name.hashCode();
- int index = hash & (table.length - 1);
- VariableTableEntry first = table[index];
- VariableTableEntry e;
- for (e = first; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- IRubyObject oldValue = e.value;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
- VariableTableEntry newFirst = e.next;
- for (VariableTableEntry p = first; p != e; p = p.next) {
- newFirst = new VariableTableEntry(p.hash, p.name, p.value, newFirst);
- }
- table[index] = newFirst;
- variableTableSize--;
- variableTable = table; // write-volatile
- return oldValue;
- }
- }
- }
- } finally {
- lock.unlock();
- }
- return null;
- }
-
- @Override
- protected IRubyObject variableTableReadLocked(VariableTableEntry entry) {
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- return entry.value;
- } finally {
- lock.unlock();
- }
- }
-
- @Override
- protected void variableTableSync(List<Variable<IRubyObject>> vars) {
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- variableTableSize = 0;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTable = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- for (Variable<IRubyObject> var : vars) {
- assert !var.isConstant() && var.getValue() != null;
- variableTableStore(var.getName(), var.getValue());
- }
- } finally {
- lock.unlock();
- }
- }
-
- @Override
- public void syncVariables(List<Variable<IRubyObject>> variables) {
- ArrayList<Variable<IRubyObject>> constants = new ArrayList<Variable<IRubyObject>>(variables.size());
- Variable<IRubyObject> var;
- for (Iterator<Variable<IRubyObject>> iter = variables.iterator(); iter.hasNext(); ) {
- if ((var = iter.next()).isConstant()) {
- constants.add(var);
- iter.remove();
- }
- }
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- variableTableSync(variables);
- constantTableSync(constants);
- } finally {
- lock.unlock();
- }
- }
-
- @Override
- @SuppressWarnings("unchecked")
- @Deprecated // born deprecated
- public Map getVariableMap() {
- Map map = variableTableGetMap();
- constantTableGetMap(map);
- return map;
- }
-
- @Override
- public boolean hasVariables() {
- return variableTableGetSize() > 0 || constantTableGetSize() > 0;
- }
-
- @Override
- public int getVariableCount() {
- return variableTableGetSize() + constantTableGetSize();
- }
-
- @Override
- public List<Variable<IRubyObject>> getVariableList() {
- VariableTableEntry[] vtable = variableTableGetTable();
- ConstantTableEntry[] ctable = constantTableGetTable();
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- IRubyObject readValue;
- for (int i = vtable.length; --i >= 0; ) {
- for (VariableTableEntry e = vtable[i]; e != null; e = e.next) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- list.add(new VariableEntry<IRubyObject>(e.name, readValue));
- }
- }
- for (int i = ctable.length; --i >= 0; ) {
- for (ConstantTableEntry e = ctable[i]; e != null; e = e.next) {
- list.add(e);
- }
- }
- return list;
- }
-
- @Override
- public List<String> getVariableNameList() {
- VariableTableEntry[] vtable = variableTableGetTable();
- ConstantTableEntry[] ctable = constantTableGetTable();
- ArrayList<String> list = new ArrayList<String>();
- for (int i = vtable.length; --i >= 0; ) {
- for (VariableTableEntry e = vtable[i]; e != null; e = e.next) {
- list.add(e.name);
- }
- }
- for (int i = ctable.length; --i >= 0; ) {
- for (ConstantTableEntry e = ctable[i]; e != null; e = e.next) {
- list.add(e.name);
- }
- }
- return list;
- }
-
-
- //
- ////////////////// CONSTANT TABLE METHODS, ETC. ////////////////
- //
-
- protected static final int CONSTANT_TABLE_DEFAULT_CAPACITY = 8; // MUST be power of 2!
- protected static final int CONSTANT_TABLE_MAXIMUM_CAPACITY = 1 << 30;
- protected static final float CONSTANT_TABLE_LOAD_FACTOR = 0.75f;
-
- protected static final class ConstantTableEntry implements Variable<IRubyObject> {
- final int hash;
- final String name;
- final IRubyObject value;
- final ConstantTableEntry next;
-
- // constant table entry values are final; if a constant is redefined, the
- // entry will be removed and replaced with a new entry.
- ConstantTableEntry(
- int hash,
- String name,
- IRubyObject value,
- ConstantTableEntry next) {
- this.hash = hash;
- this.name = name;
- this.value = value;
- this.next = next;
- }
-
- public String getName() {
- return name;
- }
-
- public IRubyObject getValue() {
- return value;
- }
- public final boolean isClassVariable() {
- return false;
- }
-
- public final boolean isConstant() {
- return true;
- }
-
- public final boolean isInstanceVariable() {
- return false;
- }
-
- public final boolean isRubyVariable() {
- return true;
- }
- }
-
- protected boolean constantTableContains(String name) {
- int hash = name.hashCode();
- ConstantTableEntry[] table;
- for (ConstantTableEntry e = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return true;
- }
- }
- return false;
- }
-
- protected boolean constantTableFastContains(String internedName) {
- ConstantTableEntry[] table;
- for (ConstantTableEntry e = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- return true;
- }
- }
- return false;
- }
-
- protected IRubyObject constantTableFetch(String name) {
- int hash = name.hashCode();
- ConstantTableEntry[] table;
- for (ConstantTableEntry e = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return e.value;
- }
- }
- return null;
- }
-
- protected IRubyObject constantTableFastFetch(String internedName) {
- ConstantTableEntry[] table;
- for (ConstantTableEntry e = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- return e.value;
- }
- }
- return null;
- }
-
- protected IRubyObject constantTableStore(String name, IRubyObject value) {
- int hash = name.hashCode();
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- ConstantTableEntry[] table;
- ConstantTableEntry e;
- ConstantTableEntry first;
- int potentialNewSize;
- if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
- table = constantTableRehash();
- } else {
- table = constantTable;
- }
- int index;
- for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- // if value is unchanged, do nothing
- if (value == e.value) {
- return value;
- }
- // create new entry, prepend to any trailing entries
- ConstantTableEntry newFirst = new ConstantTableEntry(e.hash, e.name, value, e.next);
- // all entries before this one must be cloned
- for (ConstantTableEntry n = first; n != e; n = n.next) {
- newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
- }
- table[index] = newFirst;
- constantTable = table; // write-volatile
- return value;
- }
- }
- table[index] = new ConstantTableEntry(hash, name.intern(), value, table[index]);
- constantTableSize = potentialNewSize;
- constantTable = table; // write-volatile
- } finally {
- lock.unlock();
- }
- return value;
- }
-
- protected IRubyObject constantTableFastStore(String internedName, IRubyObject value) {
- assert internedName == internedName.intern() : internedName + " not interned";
- int hash = internedName.hashCode();
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- ConstantTableEntry[] table;
- ConstantTableEntry e;
- ConstantTableEntry first;
- int potentialNewSize;
- if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
- table = constantTableRehash();
- } else {
- table = constantTable;
- }
- int index;
- for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- // if value is unchanged, do nothing
- if (value == e.value) {
- return value;
- }
- // create new entry, prepend to any trailing entries
- ConstantTableEntry newFirst = new ConstantTableEntry(e.hash, e.name, value, e.next);
- // all entries before this one must be cloned
- for (ConstantTableEntry n = first; n != e; n = n.next) {
- newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
- }
- table[index] = newFirst;
- constantTable = table; // write-volatile
- return value;
- }
- }
- table[index] = new ConstantTableEntry(hash, internedName, value, table[index]);
- constantTableSize = potentialNewSize;
- constantTable = table; // write-volatile
- } finally {
- lock.unlock();
- }
- return value;
- }
-
- protected IRubyObject constantTableRemove(String name) {
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- ConstantTableEntry[] table;
- if ((table = constantTable) != null) {
- int hash = name.hashCode();
- int index = hash & (table.length - 1);
- ConstantTableEntry first = table[index];
- ConstantTableEntry e;
- for (e = first; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- IRubyObject oldValue = e.value;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
- ConstantTableEntry newFirst = e.next;
- for (ConstantTableEntry p = first; p != e; p = p.next) {
- newFirst = new ConstantTableEntry(p.hash, p.name, p.value, newFirst);
- }
- table[index] = newFirst;
- constantTableSize--;
- constantTable = table; // write-volatile
- return oldValue;
- }
- }
- }
- } finally {
- lock.unlock();
- }
- return null;
- }
-
-
- protected ConstantTableEntry[] constantTableGetTable() {
- return constantTable;
- }
-
- protected int constantTableGetSize() {
- if (constantTable != null) {
- return constantTableSize;
- }
- return 0;
- }
-
- protected void constantTableSync(List<Variable<IRubyObject>> vars) {
- ReentrantLock lock;
- (lock = variableWriteLock).lock();
- try {
- constantTableSize = 0;
- constantTableThreshold = (int)(CONSTANT_TABLE_DEFAULT_CAPACITY * CONSTANT_TABLE_LOAD_FACTOR);
- constantTable = new ConstantTableEntry[CONSTANT_TABLE_DEFAULT_CAPACITY];
- for (Variable<IRubyObject> var : vars) {
- assert var.isConstant() && var.getValue() != null;
- constantTableStore(var.getName(), var.getValue());
- }
- } finally {
- lock.unlock();
- }
- }
-
- // MUST be called from synchronized/locked block!
- // should only be called by constantTableStore/constantTableFastStore
- private final ConstantTableEntry[] constantTableRehash() {
- ConstantTableEntry[] oldTable = constantTable;
- int oldCapacity;
- if ((oldCapacity = oldTable.length) >= CONSTANT_TABLE_MAXIMUM_CAPACITY) {
- return oldTable;
- }
-
- int newCapacity = oldCapacity << 1;
- ConstantTableEntry[] newTable = new ConstantTableEntry[newCapacity];
- constantTableThreshold = (int)(newCapacity * CONSTANT_TABLE_LOAD_FACTOR);
- int sizeMask = newCapacity - 1;
- ConstantTableEntry e;
- for (int i = oldCapacity; --i >= 0; ) {
- // We need to guarantee that any existing reads of old Map can
- // proceed. So we cannot yet null out each bin.
- e = oldTable[i];
-
- if (e != null) {
- ConstantTableEntry next = e.next;
- int idx = e.hash & sizeMask;
-
- // Single node on list
- if (next == null)
- newTable[idx] = e;
-
- else {
- // Reuse trailing consecutive sequence at same slot
- ConstantTableEntry lastRun = e;
- int lastIdx = idx;
- for (ConstantTableEntry last = next;
- last != null;
- last = last.next) {
- int k = last.hash & sizeMask;
- if (k != lastIdx) {
- lastIdx = k;
- lastRun = last;
- }
- }
- newTable[lastIdx] = lastRun;
-
- // Clone all remaining nodes
- for (ConstantTableEntry p = e; p != lastRun; p = p.next) {
- int k = p.hash & sizeMask;
- ConstantTableEntry m = new ConstantTableEntry(p.hash, p.name, p.value, newTable[k]);
- newTable[k] = m;
- }
- }
- }
- }
- constantTable = newTable;
- return newTable;
- }
-
-
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- protected Map constantTableGetMap() {
- HashMap map = new HashMap();
- ConstantTableEntry[] table;
- if ((table = constantTable) != null) {
- for (int i = table.length; --i >= 0; ) {
- for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
- map.put(e.name, e.value);
- }
- }
- }
- return map;
- }
-
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- protected Map constantTableGetMap(Map map) {
- ConstantTableEntry[] table;
- if ((table = constantTable) != null) {
- for (int i = table.length; --i >= 0; ) {
- for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
- map.put(e.name, e.value);
- }
- }
- }
- return 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) 2006 Anders Bengtsson <ndrsbngtssn@yahoo.se>
- *
- * 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.runtime.Visibility.PRIVATE;
-import static org.jruby.runtime.Visibility.PROTECTED;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.exceptions.JumpException;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallType;
-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.Sprintf;
-
-/**
- * @author Anders Bengtsson
- */
-@JRubyClass(name="NameError", parent="StandardError")
-public class RubyNameError extends RubyException {
- private IRubyObject name;
-
- /**
- * Nested class whose instances act as thunks reacting to to_str method
- * called from (Exception#to_str, Exception#message)
- * MRI equivalent: rb_cNameErrorMesg, class name: "message", construction method: "!",
- * to_str implementation: "name_err_mesg_to_str"
- *
- * TODO: this class should not be lookupable
- */
- @JRubyClass(name = "NameError::Message", parent = "Object")
- public static final class RubyNameErrorMessage extends RubyObject {
-
- static ObjectAllocator NAMEERRORMESSAGE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- IRubyObject dummy = new RubyObject(runtime, runtime.getObject());
- return new RubyNameErrorMessage(runtime, dummy, dummy, Visibility.PRIVATE, CallType.VARIABLE);
- }
- };
-
- private final IRubyObject object;
- private final IRubyObject method;
- private final Visibility visibility;
- private final CallType callType;
-
- RubyNameErrorMessage(Ruby runtime, IRubyObject object, IRubyObject method, Visibility visibility, CallType callType) {
- super(runtime, runtime.getNameErrorMessage(), false);
- this.object = object;
- this.method = method;
- this.visibility = visibility;
- this.callType = callType;
- }
-
- @JRubyMethod(name = "_load", meta = true)
- public static IRubyObject load(IRubyObject recv, IRubyObject arg) {
- return arg;
- }
-
- @JRubyMethod(name = "_dump")
- public IRubyObject dump(ThreadContext context, IRubyObject arg) {
- return to_str(context);
- }
-
- @JRubyMethod(name = "to_str")
- public IRubyObject to_str(ThreadContext context) {
- String format = null;
-
- if (visibility == PRIVATE) {
- format = "private method `%s' called for %s";
- } else if (visibility == PROTECTED) {
- format = "protected method `%s' called for %s";
- } else if (callType == CallType.VARIABLE) {
- format = "undefined local variable or method `%s' for %s";
- } else if (callType == CallType.SUPER) {
- format = "super: no superclass method `%s'";
- }
-
- if (format == null) format = "undefined method `%s' for %s";
-
- String description = null;
-
- if (object.isNil()) {
- description = "nil";
- } else if (object instanceof RubyBoolean && object.isTrue()) {
- description = "true";
- } else if (object instanceof RubyBoolean && !object.isTrue()) {
- description = "false";
- } else {
- try {
- description = RubyObject.inspect(context, object).toString();
- } catch(JumpException e) {}
-
- if (description == null || description.length() > 65) description = object.anyToString().toString();
- }
-
- if (description.length() == 0 || (description.length() > 0 && description.charAt(0) != '#')) {
- description = description + ":" + object.getMetaClass().getRealClass().getName();
- }
-
- Ruby runtime = getRuntime();
- RubyArray arr = runtime.newArray(method, runtime.newString(description));
- RubyString msg = runtime.newString(Sprintf.sprintf(runtime.newString(format), arr).toString());
- if (object.isTaint()) msg.setTaint(true);
- return msg;
- }
- }
-
- private static ObjectAllocator NAMEERROR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyNameError(runtime, klass);
- }
- };
-
- public static RubyClass createNameErrorClass(Ruby runtime, RubyClass standardErrorClass) {
- RubyClass nameErrorClass = runtime.defineClass("NameError", standardErrorClass, NAMEERROR_ALLOCATOR);
- nameErrorClass.defineAnnotatedMethods(RubyNameError.class);
- return nameErrorClass;
- }
-
- public static RubyClass createNameErrorMessageClass(Ruby runtime, RubyClass nameErrorClass) {
- RubyClass messageClass = nameErrorClass.defineClassUnder("Message", runtime.getObject(), RubyNameErrorMessage.NAMEERRORMESSAGE_ALLOCATOR);
- messageClass.defineAnnotatedMethods(RubyNameErrorMessage.class);
- return messageClass;
- }
-
- protected RubyNameError(Ruby runtime, RubyClass exceptionClass) {
- this(runtime, exceptionClass, exceptionClass.getName());
- }
-
- public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message) {
- this(runtime, exceptionClass, message, null);
- }
-
- public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message, String name) {
- super(runtime, exceptionClass, message);
- this.name = name == null ? runtime.getNil() : runtime.newString(name);
- }
-
- @JRubyMethod(name = "exception", rest = true, meta = true)
- public static RubyException newRubyNameError(IRubyObject recv, IRubyObject[] args) {
- RubyClass klass = (RubyClass)recv;
-
- RubyException newError = (RubyException) klass.allocate();
-
- newError.callInit(args, Block.NULL_BLOCK);
-
- return newError;
- }
-
- @JRubyMethod(name = "initialize", optional = 2, frame = true)
- @Override
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- if (args.length > 1) {
- name = args[args.length - 1];
- int newLength = args.length > 2 ? args.length - 2 : args.length - 1;
-
- IRubyObject []tmpArgs = new IRubyObject[newLength];
- System.arraycopy(args, 0, tmpArgs, 0, newLength);
- args = tmpArgs;
- } else {
- name = getRuntime().getNil();
- }
-
- super.initialize(args, block);
- return this;
- }
-
- @JRubyMethod(name = "to_s")
- @Override
- public IRubyObject to_s() {
- if (message.isNil()) return getRuntime().newString(message.getMetaClass().getName());
- RubyString str = message.convertToString();
- if (str != message) message = str;
- if (isTaint()) message.setTaint(true);
- return message;
- }
-
- @JRubyMethod(name = "name")
- public IRubyObject name() {
- return name;
- }
-}
-/***** 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 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 org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- *
- * @author jpetersen
- */
-@JRubyClass(name="NilClass")
-public class RubyNil extends RubyObject {
- public RubyNil(Ruby runtime) {
- super(runtime, runtime.getNilClass(), false);
- flags |= NIL_F | FALSE_F;
- }
-
- public static final ObjectAllocator NIL_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return runtime.getNil();
- }
- };
-
- public static RubyClass createNilClass(Ruby runtime) {
- RubyClass nilClass = runtime.defineClass("NilClass", runtime.getObject(), NIL_ALLOCATOR);
- runtime.setNilClass(nilClass);
- nilClass.index = ClassIndex.NIL;
-
- nilClass.defineAnnotatedMethods(RubyNil.class);
-
- nilClass.getMetaClass().undefineMethod("new");
-
- // FIXME: This is causing a verification error for some reason
- //nilClass.dispatcher = callbackFactory.createDispatcher(nilClass);
-
- return nilClass;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.NIL;
- }
-
- @Override
- public boolean isImmediate() {
- return true;
- }
-
- @Override
- public RubyClass getSingletonClass() {
- return metaClass;
- }
-
- @Override
- public Class<?> getJavaClass() {
- return void.class;
- }
-
- // Methods of the Nil Class (nil_*):
-
- /** nil_to_i
- *
- */
- @JRubyMethod(name = "to_i")
- public static RubyFixnum to_i(IRubyObject recv) {
- return RubyFixnum.zero(recv.getRuntime());
- }
-
- /**
- * nil_to_f
- *
- */
- @JRubyMethod(name = "to_f")
- public static RubyFloat to_f(IRubyObject recv) {
- return RubyFloat.newFloat(recv.getRuntime(), 0.0D);
- }
-
- /** nil_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- public static RubyString to_s(IRubyObject recv) {
- return RubyString.newEmptyString(recv.getRuntime());
- }
-
- /** nil_to_a
- *
- */
- @JRubyMethod(name = "to_a")
- public static RubyArray to_a(IRubyObject recv) {
- return recv.getRuntime().newEmptyArray();
- }
-
- /** nil_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- public static RubyString inspect(IRubyObject recv) {
- return recv.getRuntime().newString("nil");
- }
-
- /** nil_type
- *
- */
- @JRubyMethod(name = "type")
- public static RubyClass type(IRubyObject recv) {
- return recv.getRuntime().getNilClass();
- }
-
- /** nil_and
- *
- */
- @JRubyMethod(name = "&", required = 1)
- public static RubyBoolean op_and(IRubyObject recv, IRubyObject obj) {
- return recv.getRuntime().getFalse();
- }
-
- /** nil_or
- *
- */
- @JRubyMethod(name = "|", required = 1)
- public static RubyBoolean op_or(IRubyObject recv, IRubyObject obj) {
- return recv.getRuntime().newBoolean(obj.isTrue());
- }
-
- /** nil_xor
- *
- */
- @JRubyMethod(name = "^", required = 1)
- public static RubyBoolean op_xor(IRubyObject recv, IRubyObject obj) {
- return recv.getRuntime().newBoolean(obj.isTrue());
- }
-
- @JRubyMethod(name = "nil?")
- public IRubyObject nil_p() {
- return getRuntime().getTrue();
- }
-
- @Override
- public RubyFixnum id() {
- return getRuntime().newFixnum(4);
- }
-
- @Override
- public IRubyObject taint(ThreadContext context) {
- return this;
- }
-
- @Override
- public IRubyObject freeze(ThreadContext context) {
- return this;
- }
-
- /** nilclass_to_c
- *
- */
- @JRubyMethod(name = "to_c", compat = CompatVersion.RUBY1_9)
- public static IRubyObject to_c(ThreadContext context, IRubyObject recv) {
- return RubyComplex.newComplexCanonicalize(context, RubyFixnum.zero(context.getRuntime()));
- }
-
- /** nilclass_to_r
- *
- */
- @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
- public static IRubyObject to_r(ThreadContext context, IRubyObject recv) {
- return RubyRational.newRationalCanonicalize(context, RubyFixnum.zero(context.getRuntime()));
- }
-}
-/***** 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 Koichiro Ohba <koichiro@meadowy.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 java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.UnsupportedCharsetException;
-import java.util.HashMap;
-import java.util.Map;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.util.ByteList;
-import org.jruby.util.KCode;
-
-@JRubyModule(name="NKF")
-public class RubyNKF {
- public static final NKFCharset AUTO = new NKFCharset(0, "x-JISAutoDetect");
- public static final NKFCharset JIS = new NKFCharset(1, "iso-2022-jp");
- public static final NKFCharset EUC = new NKFCharset(2, "EUC-JP");
- public static final NKFCharset SJIS = new NKFCharset(3, "Windows-31J");
- public static final NKFCharset BINARY = new NKFCharset(4, null);
- public static final NKFCharset NOCONV = new NKFCharset(4, null);
- public static final NKFCharset UNKNOWN = new NKFCharset(0, null);
- public static final NKFCharset ASCII = new NKFCharset(5, "iso-8859-1");
- public static final NKFCharset UTF8 = new NKFCharset(6, "UTF-8");
- public static final NKFCharset UTF16 = new NKFCharset(8, "UTF-16");
- public static final NKFCharset UTF32 = new NKFCharset(12, "UTF-32");
- public static final NKFCharset OTHER = new NKFCharset(16, null);
-
- public static class NKFCharset {
- private final int value;
- private final String charset;
-
- public NKFCharset(int v, String c) {
- value = v;
- charset = c;
- }
-
- public int getValue() {
- return value;
- }
-
- public String getCharset() {
- return charset;
- }
- }
-
- public static void createNKF(Ruby runtime) {
- RubyModule nkfModule = runtime.defineModule("NKF");
-
- nkfModule.defineConstant("AUTO", RubyFixnum.newFixnum(runtime, AUTO.getValue()));
- nkfModule.defineConstant("JIS", RubyFixnum.newFixnum(runtime, JIS.getValue()));
- nkfModule.defineConstant("EUC", RubyFixnum.newFixnum(runtime, EUC.getValue()));
- nkfModule.defineConstant("SJIS", RubyFixnum.newFixnum(runtime, SJIS.getValue()));
- nkfModule.defineConstant("BINARY", RubyFixnum.newFixnum(runtime, BINARY.getValue()));
- nkfModule.defineConstant("NOCONV", RubyFixnum.newFixnum(runtime, NOCONV.getValue()));
- nkfModule.defineConstant("UNKNOWN", RubyFixnum.newFixnum(runtime, UNKNOWN.getValue()));
- nkfModule.defineConstant("ASCII", RubyFixnum.newFixnum(runtime, ASCII.getValue()));
- nkfModule.defineConstant("UTF8", RubyFixnum.newFixnum(runtime, UTF8.getValue()));
- nkfModule.defineConstant("UTF16", RubyFixnum.newFixnum(runtime, UTF16.getValue()));
- nkfModule.defineConstant("UTF32", RubyFixnum.newFixnum(runtime, UTF32.getValue()));
- nkfModule.defineConstant("OTHER", RubyFixnum.newFixnum(runtime, OTHER.getValue()));
-
- RubyString version = runtime.newString("2.0.7 (JRuby 2007-05-11)");
- RubyString nkfVersion = runtime.newString("2.0.7");
- RubyString nkfDate = runtime.newString("2007-05-11");
-
- ThreadContext context = runtime.getCurrentContext();
-
- version.freeze(context);
- nkfVersion.freeze(context);
- nkfDate.freeze(context);
-
- nkfModule.defineAnnotatedMethods(RubyNKF.class);
- }
-
- @JRubyMethod(name = "guess", required = 1, module = true)
- public static IRubyObject guess(ThreadContext context, IRubyObject recv, IRubyObject s) {
- Ruby runtime = context.getRuntime();
- if (!s.respondsTo("to_str")) {
- throw runtime.newTypeError("can't convert " + s.getMetaClass() + " into String");
- }
- ByteList bytes = s.convertToString().getByteList();
- ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- CharsetDecoder decoder = Charset.forName("x-JISAutoDetect").newDecoder();
- try {
- decoder.decode(buf);
- } catch (CharacterCodingException e) {
- return runtime.newFixnum(UNKNOWN.getValue());
- }
- if (!decoder.isCharsetDetected()) {
- return runtime.newFixnum(UNKNOWN.getValue());
- }
- Charset charset = decoder.detectedCharset();
- String name = charset.name();
-// System.out.println("detect: " + name + "\n");
- if ("Shift_JIS".equals(name))
- return runtime.newFixnum(SJIS.getValue());
- if ("windows-31j".equals(name))
- return runtime.newFixnum(SJIS.getValue());
- else if ("EUC-JP".equals(name))
- return runtime.newFixnum(EUC.getValue());
- else if ("ISO-2022-JP".equals(name))
- return runtime.newFixnum(JIS.getValue());
- else
- return runtime.newFixnum(UNKNOWN.getValue());
- }
-
- @JRubyMethod(name = "guess1", required = 1, module = true)
- public static IRubyObject guess1(ThreadContext context, IRubyObject recv, IRubyObject str) {
- return guess(context, recv, str);
- }
-
- @JRubyMethod(name = "guess2", required = 1, module = true)
- public static IRubyObject guess2(ThreadContext context, IRubyObject recv, IRubyObject str) {
- return guess(context, recv, str);
- }
-
- @JRubyMethod(name = "nkf", required = 2, module = true)
- public static IRubyObject nkf(ThreadContext context, IRubyObject recv, IRubyObject opt, IRubyObject str) {
- Ruby runtime = context.getRuntime();
-
- if (!opt.respondsTo("to_str")) {
- throw runtime.newTypeError("can't convert " + opt.getMetaClass() + " into String");
- }
-
- if (!str.respondsTo("to_str")) {
- throw runtime.newTypeError("can't convert " + str.getMetaClass() + " into String");
- }
-
- Map<String, NKFCharset> options = parseOpt(opt.convertToString().toString());
-
- NKFCharset nc = options.get("input");
- if (nc.getValue() == AUTO.getValue()) {
- KCode kcode = runtime.getKCode();
- if (kcode == KCode.SJIS) {
- nc = SJIS;
- } else if (kcode == KCode.EUC) {
- nc = EUC;
- } else if (kcode == KCode.UTF8) {
- nc = UTF8;
- }
- }
- String decodeCharset = nc.getCharset();
- String encodeCharset = options.get("output").getCharset();
-
- return convert(context, decodeCharset, encodeCharset, str);
- }
-
- private static IRubyObject convert(ThreadContext context, String decodeCharset,
- String encodeCharset, IRubyObject str) {
- Ruby runtime = context.getRuntime();
- CharsetDecoder decoder;
- CharsetEncoder encoder;
- try {
- decoder = Charset.forName(decodeCharset).newDecoder();
- encoder = Charset.forName(encodeCharset).newEncoder();
- } catch (UnsupportedCharsetException e) {
- throw runtime.newArgumentError("invalid encoding");
- }
-
- ByteList bytes = str.convertToString().getByteList();
- ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- try {
- CharBuffer cbuf = decoder.decode(buf);
- buf = encoder.encode(cbuf);
- } catch (CharacterCodingException e) {
- throw runtime.newArgumentError("invalid encoding");
- }
- byte[] arr = buf.array();
-
- return runtime.newString(new ByteList(arr, 0, buf.limit()));
-
- }
-
- private static int optionUTF(String s, int i) {
- int n = 8;
- if (i+1 < s.length() && Character.isDigit(s.charAt(i+1))) {
- n = Character.digit(s.charAt(i+1), 10);
- if (i+2 < s.length() && Character.isDigit(s.charAt(i+2))) {
- n *= 10;
- n += Character.digit(s.charAt(i+2), 10);
- }
- }
- return n;
- }
-
- private static Map<String, NKFCharset> parseOpt(String s) {
- Map<String, NKFCharset> options = new HashMap<String, NKFCharset>();
-
- // default options
- options.put("input", AUTO);
- options.put("output", JIS);
-
- for (int i = 0; i < s.length(); i++) {
- switch (s.charAt(i)) {
- case 'b':
- break;
- case 'u':
- break;
- case 'j': // iso-2022-jp
- options.put("output", JIS);
- break;
- case 's': // Shift_JIS
- options.put("output", SJIS);
- break;
- case 'e': // EUC-JP
- options.put("output", EUC);
- break;
- case 'w': // UTF-8
- {
- int n = optionUTF(s, i);
- if (n == 32)
- options.put("output", UTF32);
- else if (n == 16)
- options.put("output", UTF16);
- else
- options.put("output", UTF8);
- }
- break;
- case 'J': // iso-2022-jp
- options.put("input", JIS);
- break;
- case 'S': // Shift_JIS
- options.put("input", SJIS);
- break;
- case 'E': // EUC-JP
- options.put("input", EUC);
- break;
- case 'W': // UTF-8
- {
- int n = optionUTF(s, i);
- if (n == 32)
- options.put("input", UTF32);
- else if (n == 16)
- options.put("input", UTF16);
- else
- options.put("input", UTF8);
- }
- break;
- case 't':
- break;
- case 'r':
- break;
- case 'h':
- break;
- case 'm':
- break;
- case 'M':
- break;
- case 'l':
- break;
- case 'f':
- break;
- case 'F':
- break;
- case 'Z':
- break;
- case 'X':
- break;
- case 'x':
- break;
- case 'B':
- break;
- case 'T':
- break;
- case 'd':
- break;
- case 'c':
- break;
- case 'I':
- break;
- case 'L':
- break;
- case '-':
- if (s.charAt(i+1) == '-') {
- // long name option
- }
- default:
- }
- }
- return options;
- }
-}
-/***** 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 org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyClass(name="NoMethodError", parent="NameError")
-public class RubyNoMethodError extends RubyNameError {
- private IRubyObject args;
-
- private static final ObjectAllocator NOMETHODERROR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyNoMethodError(runtime, klass);
- }
- };
-
- public static RubyClass createNoMethodErrorClass(Ruby runtime, RubyClass nameErrorClass) {
- RubyClass noMethodErrorClass = runtime.defineClass("NoMethodError", nameErrorClass, NOMETHODERROR_ALLOCATOR);
-
- noMethodErrorClass.defineAnnotatedMethods(RubyNoMethodError.class);
-
- return noMethodErrorClass;
- }
-
- protected RubyNoMethodError(Ruby runtime, RubyClass exceptionClass) {
- super(runtime, exceptionClass, exceptionClass.getName());
- this.args = runtime.getNil();
- }
-
- public RubyNoMethodError(Ruby runtime, RubyClass exceptionClass, String message, String name, IRubyObject args) {
- super(runtime, exceptionClass, message, name);
- this.args = args;
- }
-
- @JRubyMethod(name = "initialize", optional = 3, frame = true)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- if (args.length > 2) {
- this.args = args[args.length - 1];
- IRubyObject []tmpArgs = new IRubyObject[args.length - 1];
- System.arraycopy(args, 0, tmpArgs, 0, tmpArgs.length);
- args = tmpArgs;
- } else {
- this.args = getRuntime().getNil();
- }
-
- super.initialize(args, block);
- return this;
- }
-
- @JRubyMethod(name = "args")
- public IRubyObject args() {
- return args;
- }
-
-}
-/***** 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 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2006 Antti Karanta <Antti.Karanta@napa.fi>
- *
- * 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_arg;
-import static org.jruby.util.Numeric.f_negative_p;
-
-import java.math.BigInteger;
-
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Block;
-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.util.ByteList;
-import org.jruby.util.Convert;
-
-/**
- * Base class for all numerical types in ruby.
- */
-// TODO: Numeric.new works in Ruby and it does here too. However trying to use
-// that instance in a numeric operation should generate an ArgumentError. Doing
-// this seems so pathological I do not see the need to fix this now.
-@JRubyClass(name="Numeric", include="Comparable")
-public class RubyNumeric extends RubyObject {
-
- public static RubyClass createNumericClass(Ruby runtime) {
- RubyClass numeric = runtime.defineClass("Numeric", runtime.getObject(), NUMERIC_ALLOCATOR);
- runtime.setNumeric(numeric);
-
- numeric.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyNumeric;
- }
- };
-
- numeric.includeModule(runtime.getComparable());
- numeric.defineAnnotatedMethods(RubyNumeric.class);
-
- return numeric;
- }
-
- protected static final ObjectAllocator NUMERIC_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyNumeric(runtime, klass);
- }
- };
-
- public static double DBL_EPSILON=2.2204460492503131e-16;
-
- private static IRubyObject convertToNum(double val, Ruby runtime) {
-
- if (val >= (double) RubyFixnum.MAX || val < (double) RubyFixnum.MIN) {
- return RubyBignum.newBignum(runtime, val);
- }
- return RubyFixnum.newFixnum(runtime, (long) val);
- }
-
- public RubyNumeric(Ruby runtime, RubyClass metaClass) {
- super(runtime, metaClass);
- }
-
- public RubyNumeric(Ruby runtime, RubyClass metaClass, boolean useObjectSpace) {
- super(runtime, metaClass, useObjectSpace);
- }
-
- // The implementations of these are all bonus (see TODO above) I was going
- // to throw an error from these, but it appears to be the wrong place to
- // do it.
- public double getDoubleValue() {
- return 0;
- }
-
- public long getLongValue() {
- return 0;
- }
-
- public static RubyNumeric newNumeric(Ruby runtime) {
- return new RubyNumeric(runtime, runtime.getNumeric());
- }
-
- /* ================
- * Utility Methods
- * ================
- */
-
- /** rb_num2int, NUM2INT
- *
- */
- public static int num2int(IRubyObject arg) {
- long num = num2long(arg);
-
- checkInt(arg, num);
- return (int)num;
- }
-
- /** check_int
- *
- */
- public static void checkInt(IRubyObject arg, long num){
- String s;
- if (num < Integer.MIN_VALUE) {
- s = "small";
- } else if (num > Integer.MAX_VALUE) {
- s = "big";
- } else {
- return;
- }
- throw arg.getRuntime().newRangeError("integer " + num + " too " + s + " to convert to `int'");
- }
-
- /**
- * NUM2CHR
- */
- public static byte num2chr(IRubyObject arg) {
- if (arg instanceof RubyString) {
- String value = ((RubyString) arg).toString();
-
- if (value != null && value.length() > 0) return (byte) value.charAt(0);
- }
-
- return (byte) num2int(arg);
- }
-
- /** rb_num2long and FIX2LONG (numeric.c)
- *
- */
- public static long num2long(IRubyObject arg) {
- if (arg instanceof RubyFixnum) {
- return ((RubyFixnum) arg).getLongValue();
- }
- if (arg.isNil()) {
- throw arg.getRuntime().newTypeError("no implicit conversion from nil to integer");
- }
-
- if (arg instanceof RubyFloat) {
- double aFloat = ((RubyFloat) arg).getDoubleValue();
- if (aFloat <= (double) Long.MAX_VALUE && aFloat >= (double) Long.MIN_VALUE) {
- return (long) aFloat;
- } else {
- // TODO: number formatting here, MRI uses "%-.10g", 1.4 API is a must?
- throw arg.getRuntime().newRangeError("float " + aFloat + " out of range of integer");
- }
- } else if (arg instanceof RubyBignum) {
- return RubyBignum.big2long((RubyBignum) arg);
- }
- return arg.convertToInteger().getLongValue();
- }
-
- /** rb_dbl2big + LONG2FIX at once (numeric.c)
- *
- */
- public static IRubyObject dbl2num(Ruby runtime, double val) {
- if (Double.isInfinite(val)) {
- throw runtime.newFloatDomainError(val < 0 ? "-Infinity" : "Infinity");
- }
- if (Double.isNaN(val)) {
- throw runtime.newFloatDomainError("NaN");
- }
- return convertToNum(val,runtime);
- }
-
- /** rb_num2dbl and NUM2DBL
- *
- */
- public static double num2dbl(IRubyObject arg) {
- if (arg instanceof RubyFloat) {
- return ((RubyFloat) arg).getDoubleValue();
- } else if (arg instanceof RubyString) {
- throw arg.getRuntime().newTypeError("no implicit conversion to float from string");
- } else if (arg == arg.getRuntime().getNil()) {
- throw arg.getRuntime().newTypeError("no implicit conversion to float from nil");
- }
- return arg.convertToFloat().getDoubleValue();
- }
-
- /** rb_dbl_cmp (numeric.c)
- *
- */
- public static IRubyObject dbl_cmp(Ruby runtime, double a, double b) {
- if (Double.isNaN(a) || Double.isNaN(b)) {
- return runtime.getNil();
- }
- if (a > b) {
- return RubyFixnum.one(runtime);
- }
- if (a < b) {
- return RubyFixnum.minus_one(runtime);
- }
- return RubyFixnum.zero(runtime);
- }
-
- public static long fix2long(IRubyObject arg) {
- return ((RubyFixnum) arg).getLongValue();
- }
-
- public static int fix2int(IRubyObject arg) {
- long num = arg instanceof RubyFixnum ? fix2long(arg) : num2long(arg);
-
- checkInt(arg, num);
- return (int) num;
- }
-
- public static RubyInteger str2inum(Ruby runtime, RubyString str, int base) {
- return str2inum(runtime,str,base,false);
- }
-
- public static RubyNumeric int2fix(Ruby runtime, long val) {
- return RubyFixnum.newFixnum(runtime,val);
- }
-
- /** rb_num2fix
- *
- */
- public static IRubyObject num2fix(IRubyObject val) {
- if (val instanceof RubyFixnum) {
- return val;
- }
- if (val instanceof RubyBignum) {
- // any BigInteger is bigger than Fixnum and we don't have FIXABLE
- throw val.getRuntime().newRangeError("integer " + val + " out of range of fixnum");
- }
- return RubyFixnum.newFixnum(val.getRuntime(), num2long(val));
- }
-
- /**
- * Converts a string representation of an integer to the integer value.
- * Parsing starts at the beginning of the string (after leading and
- * trailing whitespace have been removed), and stops at the end or at the
- * first character that can't be part of an integer. Leading signs are
- * allowed. If <code>base</code> is zero, strings that begin with '0[xX]',
- * '0[bB]', or '0' (optionally preceded by a sign) will be treated as hex,
- * binary, or octal numbers, respectively. If a non-zero base is given,
- * only the prefix (if any) that is appropriate to that base will be
- * parsed correctly. For example, if the base is zero or 16, the string
- * "0xff" will be converted to 256, but if the base is 10, it will come out
- * as zero, since 'x' is not a valid decimal digit. If the string fails
- * to parse as a number, zero is returned.
- *
- * @param runtime the ruby runtime
- * @param str the string to be converted
- * @param base the expected base of the number (for example, 2, 8, 10, 16),
- * or 0 if the method should determine the base automatically
- * (defaults to 10). Values 0 and 2-36 are permitted. Any other
- * value will result in an ArgumentError.
- * @param strict if true, enforce the strict criteria for String encoding of
- * numeric values, as required by Integer('n'), and raise an
- * exception when those criteria are not met. Otherwise, allow
- * lax expression of values, as permitted by String#to_i, and
- * return a value in almost all cases (excepting illegal radix).
- * TODO: describe the rules/criteria
- * @return a RubyFixnum or (if necessary) a RubyBignum representing
- * the result of the conversion, which will be zero if the
- * conversion failed.
- */
- public static RubyInteger str2inum(Ruby runtime, RubyString str, int base, boolean strict) {
- if (base != 0 && (base < 2 || base > 36)) {
- throw runtime.newArgumentError("illegal radix " + base);
- }
- ByteList bytes = str.getByteList();
- try {
- return runtime.newFixnum(Convert.byteListToLong(bytes, base, strict));
- } catch (InvalidIntegerException e) {
- return str2inumIIE(strict, runtime, str);
- } catch (NumberTooLargeException e) {
- return str2inumNTLE(strict, runtime, str, bytes, base);
- }
- }
-
- private static RubyInteger str2inumIIE(boolean strict, Ruby runtime, RubyString str) throws RaiseException {
- if (strict) {
- throw runtime.newArgumentError("invalid value for Integer: " + str.callMethod(runtime.getCurrentContext(), "inspect").toString());
- }
- return RubyFixnum.zero(runtime);
- }
-
- private static RubyInteger str2inumNTLE(boolean strict, Ruby runtime, RubyString str, ByteList bytes, int base) {
- try {
- BigInteger bi = Convert.byteListToBigInteger(bytes, base, strict);
- return new RubyBignum(runtime, bi);
- } catch (InvalidIntegerException e2) {
- return str2inumIIE(strict, runtime, str);
- }
- }
-
- public static RubyFloat str2fnum(Ruby runtime, RubyString arg) {
- return str2fnum(runtime,arg,false);
- }
-
- /**
- * Converts a string representation of a floating-point number to the
- * numeric value. Parsing starts at the beginning of the string (after
- * leading and trailing whitespace have been removed), and stops at the
- * end or at the first character that can't be part of a number. If
- * the string fails to parse as a number, 0.0 is returned.
- *
- * @param runtime the ruby runtime
- * @param arg the string to be converted
- * @param strict if true, enforce the strict criteria for String encoding of
- * numeric values, as required by Float('n'), and raise an
- * exception when those criteria are not met. Otherwise, allow
- * lax expression of values, as permitted by String#to_f, and
- * return a value in all cases.
- * TODO: describe the rules/criteria
- * @return a RubyFloat representing the result of the conversion, which
- * will be 0.0 if the conversion failed.
- */
- public static RubyFloat str2fnum(Ruby runtime, RubyString arg, boolean strict) {
- final double ZERO = 0.0;
-
- try {
- return new RubyFloat(runtime,Convert.byteListToDouble(arg.getByteList(),strict));
- } catch (NumberFormatException e) {
- if (strict) {
- throw runtime.newArgumentError("invalid value for Float(): "
- + arg.callMethod(runtime.getCurrentContext(), "inspect").toString());
- }
- return new RubyFloat(runtime,ZERO);
- }
- }
-
- /** Numeric methods. (num_*)
- *
- */
-
- protected IRubyObject[] getCoerced(ThreadContext context, IRubyObject other, boolean error) {
- IRubyObject result;
-
- try {
- result = other.callMethod(context, "coerce", this);
- } catch (RaiseException e) {
- if (error) {
- throw getRuntime().newTypeError(
- other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
- }
-
- return null;
- }
-
- if (!(result instanceof RubyArray) || ((RubyArray)result).getLength() != 2) {
- throw getRuntime().newTypeError("coerce must return [x, y]");
- }
-
- return ((RubyArray)result).toJavaArray();
- }
-
- protected IRubyObject callCoerced(ThreadContext context, String method, IRubyObject other, boolean err) {
- IRubyObject[] args = getCoerced(context, other, err);
- if(args == null) {
- return getRuntime().getNil();
- }
- return args[0].callMethod(context, method, args[1]);
- }
-
- protected IRubyObject callCoerced(ThreadContext context, String method, IRubyObject other) {
- IRubyObject[] args = getCoerced(context, other, false);
- if(args == null) {
- return getRuntime().getNil();
- }
- return args[0].callMethod(context, method, args[1]);
- }
-
- // beneath are rewritten coercions that reflect MRI logic, the aboves are used only by RubyBigDecimal
-
- /** coerce_body
- *
- */
- protected final IRubyObject coerceBody(ThreadContext context, IRubyObject other) {
- return other.callMethod(context, "coerce", this);
- }
-
- /** do_coerce
- *
- */
- protected final RubyArray doCoerce(ThreadContext context, IRubyObject other, boolean err) {
- IRubyObject result;
- try {
- result = coerceBody(context, other);
- } catch (RaiseException e) {
- if (err) {
- throw getRuntime().newTypeError(
- other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
- }
- return null;
- }
-
- if (!(result instanceof RubyArray) || ((RubyArray) result).getLength() != 2) {
- throw getRuntime().newTypeError("coerce must return [x, y]");
- }
- return (RubyArray) result;
- }
-
- /** rb_num_coerce_bin
- * coercion taking two arguments
- */
- protected final IRubyObject coerceBin(ThreadContext context, String method, IRubyObject other) {
- RubyArray ary = doCoerce(context, other, true);
- return (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
- }
-
- /** rb_num_coerce_cmp
- * coercion used for comparisons
- */
- protected final IRubyObject coerceCmp(ThreadContext context, String method, IRubyObject other) {
- RubyArray ary = doCoerce(context, other, false);
- if (ary == null) {
- return getRuntime().getNil(); // MRI does it!
- }
- return (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
- }
-
- /** rb_num_coerce_relop
- * coercion used for relative operators
- */
- protected final IRubyObject coerceRelOp(ThreadContext context, String method, IRubyObject other) {
- RubyArray ary = doCoerce(context, other, false);
- if (ary == null) {
- return RubyComparable.cmperr(this, other);
- }
-
- return unwrapCoerced(context, method, other, ary);
- }
-
- private final IRubyObject unwrapCoerced(ThreadContext context, String method, IRubyObject other, RubyArray ary) {
- IRubyObject result = (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
- if (result.isNil()) {
- return RubyComparable.cmperr(this, other);
- }
- return result;
- }
-
- public RubyNumeric asNumeric() {
- return this;
- }
-
- /* ================
- * Instance Methods
- * ================
- */
-
- /** num_sadded
- *
- */
- @JRubyMethod(name = "singleton_method_added")
- public IRubyObject sadded(IRubyObject name) {
- throw getRuntime().newTypeError("can't define singleton method " + name + " for " + getType().getName());
- }
-
- /** num_init_copy
- *
- */
- @Override
- @JRubyMethod(name = "initialize_copy", visibility = Visibility.PRIVATE)
- public IRubyObject initialize_copy(IRubyObject arg) {
- throw getRuntime().newTypeError("can't copy " + getType().getName());
- }
-
- /** num_coerce
- *
- */
- @JRubyMethod(name = "coerce")
- public IRubyObject coerce(IRubyObject other) {
- if (getClass() == other.getClass()) return getRuntime().newArray(other, this);
-
- IRubyObject cdr = RubyKernel.new_float(this, this);
- IRubyObject car = RubyKernel.new_float(this, other);
-
- return getRuntime().newArray(car, cdr);
- }
-
- /** num_uplus
- *
- */
- @JRubyMethod(name = "+@")
- public IRubyObject op_uplus() {
- return this;
- }
-
- /** num_uminus
- *
- */
- @JRubyMethod(name = "-@")
- public IRubyObject op_uminus(ThreadContext context) {
- RubyFixnum zero = RubyFixnum.zero(getRuntime());
- RubyArray ary = zero.doCoerce(context, this, true);
- return ary.eltInternal(0).callMethod(context, MethodIndex.OP_MINUS, "-", ary.eltInternal(1));
- }
-
- /** num_cmp
- *
- */
- @JRubyMethod(name = "<=>")
- public IRubyObject op_cmp(IRubyObject other) {
- if (this == other) { // won't hurt fixnums
- return RubyFixnum.zero(getRuntime());
- }
- return getRuntime().getNil();
- }
-
- /** num_eql
- *
- */
- @JRubyMethod(name = "eql?")
- public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
- if (getClass() != other.getClass()) return getRuntime().getFalse();
- return equalInternal(context, this, other) ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- /** num_quo
- *
- */
- @JRubyMethod(name = "quo")
- public IRubyObject quo(ThreadContext context, IRubyObject other) {
- return callMethod(context, "/", other);
- }
-
- /** num_quo
- *
- */
- @JRubyMethod(name = "quo", compat = CompatVersion.RUBY1_9)
- public IRubyObject quo_19(ThreadContext context, IRubyObject other) {
- return RubyRational.newRationalRaw(context.getRuntime(), this).callMethod(context, "/", other);
- }
-
- /** num_div
- *
- */
- @JRubyMethod(name = "div")
- public IRubyObject div(ThreadContext context, IRubyObject other) {
- return callMethod(context, "/", other).convertToFloat().floor();
- }
-
- /** num_divmod
- *
- */
- @JRubyMethod(name = "divmod")
- public IRubyObject divmod(ThreadContext context, IRubyObject other) {
- return RubyArray.newArray(getRuntime(), div(context, other), modulo(context, other));
- }
-
- /** num_fdiv (1.9) */
- @JRubyMethod(name = "fdiv", compat = CompatVersion.RUBY1_9)
- public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
- return RuntimeHelpers.invoke(context, this.convertToFloat(), "/", other);
- }
-
- /** num_modulo
- *
- */
- @JRubyMethod(name = "modulo")
- public IRubyObject modulo(ThreadContext context, IRubyObject other) {
- return callMethod(context, "%", other);
- }
-
- /** num_remainder
- *
- */
- @JRubyMethod(name = "remainder")
- public IRubyObject remainder(ThreadContext context, IRubyObject dividend) {
- IRubyObject z = callMethod(context, "%", dividend);
- IRubyObject x = this;
- RubyFixnum zero = RubyFixnum.zero(getRuntime());
-
- if (!equalInternal(context, z, zero) &&
- ((x.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue() &&
- dividend.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue()) ||
- (x.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue() &&
- dividend.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue()))) {
- return z.callMethod(context, MethodIndex.OP_MINUS, "-", dividend);
- } else {
- return z;
- }
- }
-
- /** num_abs
- *
- */
- @JRubyMethod(name = "abs")
- public IRubyObject abs(ThreadContext context) {
- if (callMethod(context, MethodIndex.OP_LT, "<", RubyFixnum.zero(getRuntime())).isTrue()) {
- return callMethod(context, "-@");
- }
- return this;
- }
-
- /** num_to_int
- *
- */
- @JRubyMethod(name = "to_int")
- public IRubyObject to_int(ThreadContext context) {
- return RuntimeHelpers.invoke(context, this, "to_i");
- }
-
- /** num_scalar_p
- *
- */
- @JRubyMethod(name = "scalar?", compat = CompatVersion.RUBY1_9)
- public IRubyObject scalar_p() {
- return getRuntime().getTrue();
- }
-
- /** num_int_p
- *
- */
- @JRubyMethod(name = "integer?")
- public IRubyObject integer_p() {
- return getRuntime().getFalse();
- }
-
- /** num_zero_p
- *
- */
- @JRubyMethod(name = "zero?")
- public IRubyObject zero_p(ThreadContext context) {
- return equalInternal(context, this, RubyFixnum.zero(getRuntime())) ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- /** num_nonzero_p
- *
- */
- @JRubyMethod(name = "nonzero?")
- public IRubyObject nonzero_p(ThreadContext context) {
- if (callMethod(context, "zero?").isTrue()) {
- return getRuntime().getNil();
- }
- return this;
- }
-
- /** num_floor
- *
- */
- @JRubyMethod(name = "floor")
- public IRubyObject floor() {
- return convertToFloat().floor();
- }
-
- /** num_ceil
- *
- */
- @JRubyMethod(name = "ceil")
- public IRubyObject ceil() {
- return convertToFloat().ceil();
- }
-
- /** num_round
- *
- */
- @JRubyMethod(name = "round")
- public IRubyObject round() {
- return convertToFloat().round();
- }
-
- /** num_truncate
- *
- */
- @JRubyMethod(name = "truncate")
- public IRubyObject truncate() {
- return convertToFloat().truncate();
- }
-
- @JRubyMethod(name = "step", required = 1, optional = 1, frame = true)
- public IRubyObject step(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 0: throw context.getRuntime().newArgumentError(0, 1);
- case 1: return step(context, args[0], block);
- case 2: return step(context, args[0], args[1], block);
- default: throw context.getRuntime().newArgumentError(args.length, 2);
- }
- }
-
- @JRubyMethod(name = "step", frame = true)
- public IRubyObject step(ThreadContext context, IRubyObject arg0, Block block) {
- return step(context, arg0, RubyFixnum.one(context.getRuntime()), block);
- }
-
- @JRubyMethod(name = "step", frame = true)
- public IRubyObject step(ThreadContext context, IRubyObject to, IRubyObject step, Block block) {
- if (this instanceof RubyFixnum && to instanceof RubyFixnum && step instanceof RubyFixnum) {
- long value = getLongValue();
- long end = ((RubyFixnum) to).getLongValue();
- long diff = ((RubyFixnum) step).getLongValue();
-
- if (diff == 0) {
- throw getRuntime().newArgumentError("step cannot be 0");
- }
- if (diff > 0) {
- for (long i = value; i <= end; i += diff) {
- block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
- }
- } else {
- for (long i = value; i >= end; i += diff) {
- block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
- }
- }
- } else if (this instanceof RubyFloat || to instanceof RubyFloat || step instanceof RubyFloat) {
- double beg = num2dbl(this);
- double end = num2dbl(to);
- double unit = num2dbl(step);
-
- if (unit == 0) {
- throw getRuntime().newArgumentError("step cannot be 0");
- }
-
- double n = (end - beg)/unit;
- double err = (Math.abs(beg) + Math.abs(end) + Math.abs(end - beg)) / Math.abs(unit) * DBL_EPSILON;
-
- if (err>0.5) {
- err=0.5;
- }
- n = Math.floor(n + err) + 1;
-
- for(double i = 0; i < n; i++){
- block.yield(context, RubyFloat.newFloat(getRuntime(), i * unit + beg));
- }
-
- } else {
- RubyNumeric i = this;
-
- int cmp;
- String cmpString;
- if (((RubyBoolean) step.callMethod(context, MethodIndex.OP_GT, ">", RubyFixnum.zero(getRuntime()))).isTrue()) {
- cmp = MethodIndex.OP_GT;
- } else {
- cmp = MethodIndex.OP_LT;
- }
- cmpString = MethodIndex.NAMES.get(cmp);
-
- while (true) {
- if (i.callMethod(context, cmp, cmpString, to).isTrue()) {
- break;
- }
- block.yield(context, i);
- i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", step);
- }
- }
- return this;
- }
-
- /** num_equal, doesn't override RubyObject.op_equal
- *
- */
- protected final IRubyObject op_num_equal(ThreadContext context, IRubyObject other) {
- // it won't hurt fixnums
- if (this == other) return getRuntime().getTrue();
-
- return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this);
- }
-
- /** num_numerator
- *
- */
- @JRubyMethod(name = "numerator", compat = CompatVersion.RUBY1_9)
- public IRubyObject numerator(ThreadContext context) {
- return RubyRational.newRationalConvert(context, this).callMethod(context, "numerator");
- }
-
- /** num_denominator
- *
- */
- @JRubyMethod(name = "denominator", compat = CompatVersion.RUBY1_9)
- public IRubyObject denominator(ThreadContext context) {
- return RubyRational.newRationalConvert(context, this).callMethod(context, "denominator");
- }
-
- /** numeric_to_c
- *
- */
- @JRubyMethod(name = "to_c", compat = CompatVersion.RUBY1_9)
- public IRubyObject to_c(ThreadContext context) {
- return RubyComplex.newComplexCanonicalize(context, this);
- }
-
- /** numeric_re
- *
- */
- @JRubyMethod(name = "re", compat = CompatVersion.RUBY1_9)
- public IRubyObject re(ThreadContext context) {
- return RubyComplex.newComplexConvert(context, this);
- }
-
- /** numeric_im
- *
- */
- @JRubyMethod(name = "im", compat = CompatVersion.RUBY1_9)
- public IRubyObject im(ThreadContext context) {
- return RubyComplex.newComplexConvert(context, RubyFixnum.zero(context.getRuntime()), this);
- }
-
- /** numeric_real
- *
- */
- @JRubyMethod(name = "real", compat = CompatVersion.RUBY1_9)
- public IRubyObject real(ThreadContext context) {
- return this;
- }
-
- /** numeric_image
- *
- */
- @JRubyMethod(name = {"image", "imag"}, compat = CompatVersion.RUBY1_9)
- public IRubyObject image(ThreadContext context) {
- return RubyFixnum.zero(context.getRuntime());
- }
-
- /** numeric_arg
- *
- */
- @JRubyMethod(name = "arg", compat = CompatVersion.RUBY1_9)
- public IRubyObject arg(ThreadContext context) {
- if (!f_negative_p(context, this)) return RubyFixnum.zero(context.getRuntime());
- return context.getRuntime().getMath().fastFetchConstant("PI");
- }
-
- /** numeric_polar
- *
- */
- @JRubyMethod(name = "polar", compat = CompatVersion.RUBY1_9)
- public IRubyObject polar(ThreadContext context) {
- return context.getRuntime().newArray(f_abs(context, this), f_arg(context, this));
- }
-
- /** numeric_real
- *
- */
- @JRubyMethod(name = "conjugate", compat = CompatVersion.RUBY1_9)
- public IRubyObject conjugate(ThreadContext context) {
- return this;
- }
-
- public static class InvalidIntegerException extends NumberFormatException {
- private static final long serialVersionUID = 55019452543252148L;
-
- public InvalidIntegerException() {
- super();
- }
- public InvalidIntegerException(String message) {
- super(message);
- }
- public Throwable fillInStackTrace() {
- return this;
- }
- }
-
- public static class NumberTooLargeException extends NumberFormatException {
- private static final long serialVersionUID = -1835120694982699449L;
- public NumberTooLargeException() {
- super();
- }
- public NumberTooLargeException(String message) {
- super(message);
- }
- public Throwable fillInStackTrace() {
- 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 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-2006 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 Miguel Covarrubias <mlcovarrubias@gmail.com>
- * Copyright (C) 2007 MenTaLguY <mental@rydia.net>
- * 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.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.evaluator.ASTInterpreter;
-import org.jruby.exceptions.JumpException;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallType;
-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.builtin.Variable;
-import org.jruby.runtime.component.VariableEntry;
-import org.jruby.util.IdUtil;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.javasupport.JavaObject;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.builtin.InstanceVariables;
-import org.jruby.runtime.builtin.InternalVariables;
-import org.jruby.runtime.marshal.CoreObjectType;
-import org.jruby.util.TypeConverter;
-
-/**
- * RubyObject is the only implementation of the
- * {@link org.jruby.runtime.builtin.IRubyObject}. Every Ruby object in JRuby
- * is represented by something that is an instance of RubyObject. In
- * some of the core class implementations, this means doing a subclass
- * that extends RubyObject, in other cases it means using a simple
- * RubyObject instance and the data field to store specific
- * information about the Ruby object.
- *
- * Some care has been taken to make the implementation be as
- * monomorphic as possible, so that the Java Hotspot engine can
- * improve performance of it. That is the reason for several patterns
- * that might seem odd in this class.
- *
- * The IRubyObject interface used to have lots of methods for
- * different things, but these have now mostly been refactored into
- * several interfaces that gives access to that specific part of the
- * object. This gives us the possibility to switch out that subsystem
- * without changing interfaces again. For example, instance variable
- * and internal variables are handled this way, but the implementation
- * in RubyObject only returns "this" in {@link #getInstanceVariables()} and
- * {@link #getInternalVariables()}.
- *
- * @author jpetersen
- */
-@JRubyClass(name="Object", include="Kernel")
-public class RubyObject implements Cloneable, IRubyObject, Serializable, CoreObjectType, InstanceVariables, InternalVariables {
-
- /**
- * It's not valid to create a totally empty RubyObject. Since the
- * RubyObject is always defined in relation to a runtime, that
- * means that creating RubyObjects from outside the class might
- * cause problems.
- */
- private RubyObject(){};
-
- /**
- * A value that is used as a null sentinel in among other places
- * the RubyArray implementation. It will cause large problems to
- * call any methods on this object.
- */
- public static final IRubyObject NEVER = new RubyObject();
-
- /**
- * A value that specifies an undefined value. This value is used
- * as a sentinel for undefined constant values, and other places
- * where neither null nor NEVER makes sense.
- */
- public static final IRubyObject UNDEF = new RubyObject();
-
- // The class of this object
- protected transient RubyClass metaClass;
-
- /**
- * The variableTable contains variables for an object, defined as:
- * <ul>
- * <li> instance variables
- * <li> class variables (for classes/modules)
- * <li> internal variables (such as those used when marshaling RubyRange and RubyException)
- * </ul>
- *
- * Constants are stored separately, see {@link RubyModule}.
- *
- */
- protected transient volatile VariableTableEntry[] variableTable;
- protected transient int variableTableSize;
- protected transient int variableTableThreshold;
-
- // The dataStruct is a place where custom information can be
- // contained for core implementations that doesn't necessarily
- // want to go to the trouble of creating a subclass of
- // RubyObject. The OpenSSL implementation uses this heavily to
- // save holder objects containing Java cryptography objects.
- // Java integration uses this to store the Java object ref.
- protected transient Object dataStruct;
-
- protected int flags; // zeroed by jvm
- public static final int ALL_F = -1;
- public static final int FALSE_F = 1 << 0;
-
- /**
- * This flag is a bit funny. It's used to denote that this value
- * is nil. It's a bit counterintuitive for a Java programmer to
- * not use subclassing to handle this case, since we have a
- * RubyNil subclass anyway. Well, the reason for it being a flag
- * is that the {@link #isNil()} method is called extremely often. So often
- * that it gives a good speed boost to make it monomorphic and
- * final. It turns out using a flag for this actually gives us
- * better performance than having a polymorphic {@link #isNil()} method.
- */
- public static final int NIL_F = 1 << 1;
-
- public static final int FROZEN_F = 1 << 2;
- public static final int TAINTED_F = 1 << 3;
-
- public static final int FL_USHIFT = 4;
-
- public static final int USER0_F = (1<<(FL_USHIFT+0));
- public static final int USER1_F = (1<<(FL_USHIFT+1));
- public static final int USER2_F = (1<<(FL_USHIFT+2));
- public static final int USER3_F = (1<<(FL_USHIFT+3));
- public static final int USER4_F = (1<<(FL_USHIFT+4));
- public static final int USER5_F = (1<<(FL_USHIFT+5));
- public static final int USER6_F = (1<<(FL_USHIFT+6));
- public static final int USER7_F = (1<<(FL_USHIFT+7));
-
- /**
- * Sets or unsets a flag on this object. The only flags that are
- * guaranteed to be valid to use as the first argument is:
- *
- * <ul>
- * <li>{@link #FALSE_F}</li>
- * <li>{@link NIL_F}</li>
- * <li>{@link FROZEN_F}</li>
- * <li>{@link TAINTED_F}</li>
- * <li>{@link USER0_F}</li>
- * <li>{@link USER1_F}</li>
- * <li>{@link USER2_F}</li>
- * <li>{@link USER3_F}</li>
- * <li>{@link USER4_F}</li>
- * <li>{@link USER5_F}</li>
- * <li>{@link USER6_F}</li>
- * <li>{@link USER7_F}</li>
- * </ul>
- *
- * @param flag the actual flag to set or unset.
- * @param set if true, the flag will be set, if false, the flag will be unset.
- */
- public final void setFlag(int flag, boolean set) {
- if (set) {
- flags |= flag;
- } else {
- flags &= ~flag;
- }
- }
-
- /**
- * Get the value of a custom flag on this object. The only
- * guaranteed flags that can be sent in to this method is:
- *
- * <ul>
- * <li>{@link #FALSE_F}</li>
- * <li>{@link NIL_F}</li>
- * <li>{@link FROZEN_F}</li>
- * <li>{@link TAINTED_F}</li>
- * <li>{@link USER0_F}</li>
- * <li>{@link USER1_F}</li>
- * <li>{@link USER2_F}</li>
- * <li>{@link USER3_F}</li>
- * <li>{@link USER4_F}</li>
- * <li>{@link USER5_F}</li>
- * <li>{@link USER6_F}</li>
- * <li>{@link USER7_F}</li>
- * </ul>
- *
- * @param flag the flag to get
- * @return true if the flag is set, false otherwise
- */
- public final boolean getFlag(int flag) {
- return (flags & flag) != 0;
- }
-
- private transient Finalizer finalizer;
-
- /**
- * Class that keeps track of the finalizers for the object under
- * operation.
- */
- public class Finalizer implements Finalizable {
- private long id;
- private List<IRubyObject> finalizers;
- private AtomicBoolean finalized;
-
- public Finalizer(long id) {
- this.id = id;
- this.finalized = new AtomicBoolean(false);
- }
-
- public void addFinalizer(IRubyObject finalizer) {
- if (finalizers == null) {
- finalizers = new ArrayList<IRubyObject>();
- }
- finalizers.add(finalizer);
- }
-
- public void removeFinalizers() {
- finalizers = null;
- }
-
- @Override
- public void finalize() {
- if (finalized.compareAndSet(false, true)) {
- if (finalizers != null) {
- for (int i = 0; i < finalizers.size(); i++) {
- IRubyObject finalizer = finalizers.get(i);
- RuntimeHelpers.invoke(
- finalizer.getRuntime().getCurrentContext(),
- finalizer, "call", RubyObject.this.id());
- }
- }
- }
- }
- }
-
- /**
- * Standard path for object creation. Objects are entered into ObjectSpace
- * only if ObjectSpace is enabled.
- */
- public RubyObject(Ruby runtime, RubyClass metaClass) {
- this.metaClass = metaClass;
-
- if (runtime.isObjectSpaceEnabled()) addToObjectSpace(runtime);
- if (runtime.getSafeLevel() >= 3) taint(runtime);
- }
-
- /**
- * Path for objects who want to decide whether they don't want to be in
- * ObjectSpace even when it is on. (notably used by objects being
- * considered immediate, they'll always pass false here)
- */
- protected RubyObject(Ruby runtime, RubyClass metaClass, boolean useObjectSpace) {
- this.metaClass = metaClass;
-
- if (useObjectSpace) addToObjectSpace(runtime);
- if (runtime.getSafeLevel() >= 3) taint(runtime);
- }
-
- private void addToObjectSpace(Ruby runtime) {
- assert runtime.isObjectSpaceEnabled();
- runtime.getObjectSpace().add(this);
- }
-
- /**
- * Will create the Ruby class Object in the runtime
- * specified. This method needs to take the actual class as an
- * argument because of the Object class' central part in runtime
- * initialization.
- */
- public static RubyClass createObjectClass(Ruby runtime, RubyClass objectClass) {
- objectClass.index = ClassIndex.OBJECT;
-
- objectClass.defineAnnotatedMethods(ObjectMethods.class);
-
- return objectClass;
- }
-
- /**
- * Interestingly, the Object class doesn't really have that many
- * methods for itself. Instead almost all of the Object methods
- * are really defined on the Kernel module. This class is a holder
- * for all Object methods.
- *
- * @see RubyKernel
- */
- public static class ObjectMethods {
- @JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
- public static IRubyObject intialize(IRubyObject self) {
- return self.getRuntime().getNil();
- }
- }
-
- /**
- * Default allocator instance for all Ruby objects. The only
- * reason to not use this allocator is if you actually need to
- * have all instances of something be a subclass of RubyObject.
- *
- * @see org.jruby.runtime.ObjectAllocator
- */
- public static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyObject(runtime, klass);
- }
- };
-
- /**
- * Will make sure that this object is added to the current object
- * space.
- *
- * @see org.jruby.runtime.ObjectSpace
- */
- public void attachToObjectSpace() {
- getRuntime().getObjectSpace().add(this);
- }
-
- /**
- * This is overridden in the other concrete Java builtins to provide a fast way
- * to determine what type they are.
- *
- * Will generally return a value from org.jruby.runtime.ClassIndex
- *
- * @see org.jruby.runtime.ClassInde
- */
- public int getNativeTypeIndex() {
- return ClassIndex.OBJECT;
- }
-
- /**
- * Specifically polymorphic method that are meant to be overridden
- * by modules to specify that they are modules in an easy way.
- */
- public boolean isModule() {
- return false;
- }
-
- /**
- * Specifically polymorphic method that are meant to be overridden
- * by classes to specify that they are classes in an easy way.
- */
- public boolean isClass() {
- return false;
- }
-
- /**
- * Is object immediate (def: Fixnum, Symbol, true, false, nil?).
- */
- public boolean isImmediate() {
- return false;
- }
-
- /** rb_make_metaclass
- *
- * Will create a new meta class, insert this in the chain of
- * classes for this specific object, and return the generated meta
- * class.
- */
- public RubyClass makeMetaClass(RubyClass superClass) {
- MetaClass klass = new MetaClass(getRuntime(), superClass); // rb_class_boot
- setMetaClass(klass);
-
- klass.setAttached(this);
- klass.setMetaClass(superClass.getRealClass().getMetaClass());
-
- return klass;
- }
-
- /**
- * Will return the Java interface that most closely can represent
- * this object, when working through JAva integration
- * translations.
- */
- public Class getJavaClass() {
- if (dataGetStruct() instanceof JavaObject) {
- return ((JavaObject)dataGetStruct()).getValue().getClass();
- }
- return getClass();
- }
-
- /**
- * Simple helper to print any objects.
- */
- public static void puts(Object obj) {
- System.out.println(obj.toString());
- }
-
- /**
- * This method is just a wrapper around the Ruby "==" method,
- * provided so that RubyObjects can be used as keys in the Java
- * HashMap object underlying RubyHash.
- */
- @Override
- public boolean equals(Object other) {
- return other == this ||
- other instanceof IRubyObject &&
- callMethod(getRuntime().getCurrentContext(), MethodIndex.EQUALEQUAL, "==", (IRubyObject) other).isTrue();
- }
-
- /**
- * The default toString method is just a wrapper that calls the
- * Ruby "to_s" method.
- */
- @Override
- public String toString() {
- return RuntimeHelpers.invoke(getRuntime().getCurrentContext(), this, "to_s").toString();
- }
-
- /**
- * Will return the runtime that this object is associated with.
- *
- * @return current runtime
- */
- public final Ruby getRuntime() {
- return getMetaClass().getClassRuntime();
- }
-
- /**
- * if exist return the meta-class else return the type of the object.
- *
- */
- public final RubyClass getMetaClass() {
- return metaClass;
- }
-
- /**
- * Makes it possible to change the metaclass of an object. In
- * practice, this is a simple version of Smalltalks Become, except
- * that it doesn't work when we're dealing with subclasses. In
- * practice it's used to change the singleton/meta class used,
- * without changing the "real" inheritance chain.
- */
- public void setMetaClass(RubyClass metaClass) {
- this.metaClass = metaClass;
- }
-
- /**
- * Is this value frozen or not? Shortcut for doing
- * getFlag(FROZEN_F).
- *
- * @return true if this object is frozen, false otherwise
- */
- public boolean isFrozen() {
- return (flags & FROZEN_F) != 0;
- }
-
- /**
- * Sets whether this object is frozen or not. Shortcut for doing
- * setFlag(FROZEN_F, frozen).
- *
- * @param frozen should this object be frozen?
- */
- public void setFrozen(boolean frozen) {
- if (frozen) {
- flags |= FROZEN_F;
- } else {
- flags &= ~FROZEN_F;
- }
- }
-
- /** rb_frozen_class_p
- *
- * Helper to test whether this object is frozen, and if it is will
- * throw an exception based on the message.
- */
- protected final void testFrozen(String message) {
- if (isFrozen()) {
- throw getRuntime().newFrozenError(message + " " + getMetaClass().getName());
- }
- }
-
- /**
- * The actual method that checks frozen with the default frozen message from MRI.
- * If possible, call this instead of {@link #testFrozen}.
- */
- protected void checkFrozen() {
- testFrozen("can't modify frozen ");
- }
-
- /**
- * Gets the taint. Shortcut for getFlag(TAINTED_F).
- *
- * @return true if this object is tainted
- */
- public boolean isTaint() {
- return (flags & TAINTED_F) != 0;
- }
-
- /**
- * Sets the taint flag. Shortcut for setFlag(TAINTED_F, taint)
- *
- * @param taint should this object be tainted or not?
- */
- public void setTaint(boolean taint) {
- if (taint) {
- flags |= TAINTED_F;
- } else {
- flags &= ~TAINTED_F;
- }
- }
-
- /**
- * Does this object represent nil? See the docs for the {@link
- * #NIL_F} flag for more information.
- */
- public final boolean isNil() {
- return (flags & NIL_F) != 0;
- }
-
- /**
- * Is this value a true value or not? Based on the {@link #FALSE_F} flag.
- */
- public final boolean isTrue() {
- return (flags & FALSE_F) == 0;
- }
-
- /**
- * Is this value a false value or not? Based on the {@link #FALSE_F} flag.
- */
- public final boolean isFalse() {
- return (flags & FALSE_F) != 0;
- }
-
- /**
- * Does this object respond to the specified message? Uses a
- * shortcut if it can be proved that respond_to? haven't been
- * overridden.
- */
- public final boolean respondsTo(String name) {
- if(getMetaClass().searchMethod("respond_to?") == getRuntime().getRespondToMethod()) {
- return getMetaClass().isMethodBound(name, false);
- } else {
- return callMethod(getRuntime().getCurrentContext(),"respond_to?",getRuntime().newSymbol(name)).isTrue();
- }
- }
-
- /** rb_singleton_class
- *
- * Note: this method is specialized for RubyFixnum, RubySymbol,
- * RubyNil and RubyBoolean
- *
- * Will either return the existing singleton class for this
- * object, or create a new one and return that.
- */
- public RubyClass getSingletonClass() {
- RubyClass klass;
-
- if (getMetaClass().isSingleton() && ((MetaClass)getMetaClass()).getAttached() == this) {
- klass = getMetaClass();
- } else {
- klass = makeMetaClass(getMetaClass());
- }
-
- klass.setTaint(isTaint());
- if (isFrozen()) klass.setFrozen(true);
-
- return klass;
- }
-
- /** rb_singleton_class_clone
- *
- * Will make sure that if the current objects class is a
- * singleton, it will get cloned.
- *
- * @return either a real class, or a clone of the current singleton class
- */
- protected RubyClass getSingletonClassClone() {
- RubyClass klass = getMetaClass();
-
- if (!klass.isSingleton()) return klass;
-
- MetaClass clone = new MetaClass(getRuntime());
- clone.flags = flags;
-
- if (this instanceof RubyClass) {
- clone.setMetaClass(clone);
- } else {
- clone.setMetaClass(klass.getSingletonClassClone());
- }
-
- clone.setSuperClass(klass.getSuperClass());
-
- if (klass.hasVariables()) {
- clone.syncVariables(klass.getVariableList());
- }
-
- klass.cloneMethods(clone);
-
- ((MetaClass)clone.getMetaClass()).setAttached(clone);
-
- ((MetaClass)clone).setAttached(((MetaClass)klass).getAttached());
-
- return clone;
- }
-
- /** init_copy
- *
- * Initializes a copy with variable and special instance variable
- * information, and then call the initialize_copy Ruby method.
- */
- private static void initCopy(IRubyObject clone, RubyObject original) {
- assert !clone.isFrozen() : "frozen object (" + clone.getMetaClass().getName() + ") allocated";
-
- original.copySpecialInstanceVariables(clone);
-
- if (original.hasVariables()) {
- clone.syncVariables(original.getVariableList());
- }
-
- /* FIXME: finalizer should be dupped here */
- clone.callMethod(clone.getRuntime().getCurrentContext(), "initialize_copy", original);
- }
-
- /** OBJ_INFECT
- *
- * Infects this object with traits from the argument obj. In real
- * terms this currently means that if obj is tainted, this object
- * will get tainted too. It's possible to hijack this method to do
- * other infections if that would be interesting.
- */
- public IRubyObject infectBy(IRubyObject obj) {
- if (obj.isTaint()) setTaint(true);
- return this;
- }
-
- /**
- * The protocol for super method invocation is a bit complicated
- * in Ruby. In real terms it involves first finding the real
- * implementation class (the super class), getting the name of the
- * method to call from the frame, and then invoke that on the
- * super class with the current self as the actual object
- * invoking.
- */
- public IRubyObject callSuper(ThreadContext context, IRubyObject[] args, Block block) {
- RubyModule klazz = context.getFrameKlazz();
-
- RubyClass superClass = RuntimeHelpers.findImplementerIfNecessary(getMetaClass(), klazz).getSuperClass();
-
- if (superClass == null) {
- String name = context.getFrameName();
- return RuntimeHelpers.callMethodMissing(context, this, klazz.searchMethod(name), name, args, this, CallType.SUPER, block);
- }
- return RuntimeHelpers.invokeAs(context, superClass, this, context.getFrameName(), args, CallType.SUPER, block);
- }
-
- /**
- * Will invoke a named method with no arguments and no block.
- */
- public final IRubyObject callMethod(ThreadContext context, String name) {
- return RuntimeHelpers.invoke(context, this, name);
- }
-
- /**
- * Will invoke a named method with one argument and no block with
- * functional invocation.
- */
- public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject arg) {
- return RuntimeHelpers.invoke(context, this, name, arg);
- }
-
- /**
- * Will invoke a named method with the supplied arguments and no
- * block with functional invocation.
- */
- public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args) {
- return RuntimeHelpers.invoke(context, this, name, args);
- }
-
- /**
- * Will invoke a named method with the supplied arguments and
- * supplied block with functional invocation.
- */
- public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args, Block block) {
- return RuntimeHelpers.invoke(context, this, name, args, block);
- }
-
- /**
- * Will invoke an indexed method with the no arguments and no
- * block.
- */
- public final IRubyObject callMethod(ThreadContext context, int methodIndex, String name) {
- return RuntimeHelpers.invoke(context, this, name);
- }
-
- /**
- * Will invoke an indexed method with the one argument and no
- * block with a functional invocation.
- */
- public final IRubyObject callMethod(ThreadContext context, int methodIndex, String name, IRubyObject arg) {
- return RuntimeHelpers.invoke(context, this, name, arg, Block.NULL_BLOCK);
- }
-
- /**
- * Call the Ruby initialize method with the supplied arguments and block.
- */
- public final void callInit(IRubyObject[] args, Block block) {
- callMethod(getRuntime().getCurrentContext(), "initialize", args, block);
- }
-
- /** rb_to_id
- *
- * Will try to convert this object to a String using the Ruby
- * "to_str" if the object isn't already a String. If this still
- * doesn't work, will throw a Ruby TypeError.
- *
- */
- public String asJavaString() {
- IRubyObject asString = checkStringType();
- if(!asString.isNil()) return ((RubyString)asString).asJavaString();
- throw getRuntime().newTypeError(inspect().toString() + " is not a symbol");
- }
-
- /**
- * Tries to convert this object to a Ruby Array using the "to_ary"
- * method.
- */
- public RubyArray convertToArray() {
- return (RubyArray) TypeConverter.convertToType(this, getRuntime().getArray(), MethodIndex.TO_ARY, "to_ary");
- }
-
- /**
- * Tries to convert this object to a Ruby Hash using the "to_hash"
- * method.
- */
- public RubyHash convertToHash() {
- return (RubyHash)TypeConverter.convertToType(this, getRuntime().getHash(), MethodIndex.TO_HASH, "to_hash");
- }
-
- /**
- * Tries to convert this object to a Ruby Float using the "to_f"
- * method.
- */
- public RubyFloat convertToFloat() {
- return (RubyFloat) TypeConverter.convertToType(this, getRuntime().getFloat(), MethodIndex.TO_F, "to_f");
- }
-
- /**
- * Tries to convert this object to a Ruby Integer using the "to_int"
- * method.
- */
- public RubyInteger convertToInteger() {
- return convertToInteger(MethodIndex.TO_INT, "to_int");
- }
-
- /**
- * Tries to convert this object to a Ruby Integer using the
- * supplied conversion method.
- */
- public RubyInteger convertToInteger(int convertMethodIndex, String convertMethod) {
- IRubyObject val = TypeConverter.convertToType(this, getRuntime().getInteger(), convertMethodIndex, convertMethod, true);
- if (!(val instanceof RubyInteger)) throw getRuntime().newTypeError(getMetaClass().getName() + "#" + convertMethod + " should return Integer");
- return (RubyInteger)val;
- }
-
- /**
- * Tries to convert this object to a Ruby String using the
- * "to_str" method.
- */
- public RubyString convertToString() {
- return (RubyString) TypeConverter.convertToType(this, getRuntime().getString(), MethodIndex.TO_STR, "to_str");
- }
-
- /**
- * Tries to convert this object to the specified Ruby type, using
- * a specific conversion method.
- */
- public final IRubyObject convertToType(RubyClass target, int convertMethodIndex) {
- return TypeConverter.convertToType(this, target, convertMethodIndex, (String)MethodIndex.NAMES.get(convertMethodIndex));
- }
-
- /** rb_obj_as_string
- *
- * First converts this object into a String using the "to_s"
- * method, infects it with the current taint and returns it. If
- * to_s doesn't return a Ruby String, {@link #anyToString} is used
- * instead.
- */
- public RubyString asString() {
- IRubyObject str = RuntimeHelpers.invoke(getRuntime().getCurrentContext(), this, "to_s");
-
- if (!(str instanceof RubyString)) return (RubyString)anyToString();
- if (isTaint()) str.setTaint(true);
- return (RubyString) str;
- }
-
- /** rb_check_string_type
- *
- * Tries to return a coerced string representation of this object,
- * using "to_str". If that returns something other than a String
- * or nil, an empty String will be returned.
- *
- */
- public IRubyObject checkStringType() {
- IRubyObject str = TypeConverter.convertToTypeWithCheck(this, getRuntime().getString(), MethodIndex.TO_STR, "to_str");
- if(!str.isNil() && !(str instanceof RubyString)) {
- str = RubyString.newEmptyString(getRuntime());
- }
- return str;
- }
-
- /** rb_check_array_type
- *
- * Returns the result of trying to convert this object to an Array
- * with "to_ary".
- */
- public IRubyObject checkArrayType() {
- return TypeConverter.convertToTypeWithCheck(this, getRuntime().getArray(), MethodIndex.TO_ARY, "to_ary");
- }
-
- /** specific_eval
- *
- * Evaluates the block or string inside of the context of this
- * object, using the supplied arguments. If a block is given, this
- * will be yielded in the specific context of this object. If no
- * block is given then a String-like object needs to be the first
- * argument, and this string will be evaluated. Second and third
- * arguments in the args-array is optional, but can contain the
- * filename and line of the string under evaluation.
- */
- @Deprecated
- public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject[] args, Block block) {
- if (block.isGiven()) {
- if (args.length > 0) throw getRuntime().newArgumentError(args.length, 0);
-
- return yieldUnder(context, mod, block);
- }
-
- if (args.length == 0) {
- throw getRuntime().newArgumentError("block not supplied");
- } else if (args.length > 3) {
- String lastFuncName = context.getFrameName();
- throw getRuntime().newArgumentError(
- "wrong # of arguments: " + lastFuncName + "(src) or " + lastFuncName + "{..}");
- }
-
- // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
- RubyString evalStr;
- if (args[0] instanceof RubyString) {
- evalStr = (RubyString)args[0];
- } else {
- evalStr = args[0].convertToString();
- }
-
- String file;
- int line;
- if (args.length > 1) {
- file = args[1].convertToString().asJavaString();
- if (args.length > 2) {
- line = (int)(args[2].convertToInteger().getLongValue() - 1);
- } else {
- line = 0;
- }
- } else {
- file = "(eval)";
- line = 0;
- }
-
- return evalUnder(context, mod, evalStr, file, line);
- }
-
- /** specific_eval
- *
- * Evaluates the block or string inside of the context of this
- * object, using the supplied arguments. If a block is given, this
- * will be yielded in the specific context of this object. If no
- * block is given then a String-like object needs to be the first
- * argument, and this string will be evaluated. Second and third
- * arguments in the args-array is optional, but can contain the
- * filename and line of the string under evaluation.
- */
- public IRubyObject specificEval(ThreadContext context, RubyModule mod, Block block) {
- if (block.isGiven()) {
- return yieldUnder(context, mod, block);
- } else {
- throw context.getRuntime().newArgumentError("block not supplied");
- }
- }
-
- /** specific_eval
- *
- * Evaluates the block or string inside of the context of this
- * object, using the supplied arguments. If a block is given, this
- * will be yielded in the specific context of this object. If no
- * block is given then a String-like object needs to be the first
- * argument, and this string will be evaluated. Second and third
- * arguments in the args-array is optional, but can contain the
- * filename and line of the string under evaluation.
- */
- public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg, Block block) {
- if (block.isGiven()) throw context.getRuntime().newArgumentError(1, 0);
-
- // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
- RubyString evalStr;
- if (arg instanceof RubyString) {
- evalStr = (RubyString)arg;
- } else {
- evalStr = arg.convertToString();
- }
-
- String file = "(eval)";
- int line = 0;
-
- return evalUnder(context, mod, evalStr, file, line);
- }
-
- /** specific_eval
- *
- * Evaluates the block or string inside of the context of this
- * object, using the supplied arguments. If a block is given, this
- * will be yielded in the specific context of this object. If no
- * block is given then a String-like object needs to be the first
- * argument, and this string will be evaluated. Second and third
- * arguments in the args-array is optional, but can contain the
- * filename and line of the string under evaluation.
- */
- public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg0, IRubyObject arg1, Block block) {
- if (block.isGiven()) throw context.getRuntime().newArgumentError(2, 0);
-
- // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
- RubyString evalStr;
- if (arg0 instanceof RubyString) {
- evalStr = (RubyString)arg0;
- } else {
- evalStr = arg0.convertToString();
- }
-
- String file = arg1.convertToString().asJavaString();
- int line = 0;
-
- return evalUnder(context, mod, evalStr, file, line);
- }
-
- /** specific_eval
- *
- * Evaluates the block or string inside of the context of this
- * object, using the supplied arguments. If a block is given, this
- * will be yielded in the specific context of this object. If no
- * block is given then a String-like object needs to be the first
- * argument, and this string will be evaluated. Second and third
- * arguments in the args-array is optional, but can contain the
- * filename and line of the string under evaluation.
- */
- public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- if (block.isGiven()) throw context.getRuntime().newArgumentError(2, 0);
-
- // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
- RubyString evalStr;
- if (arg0 instanceof RubyString) {
- evalStr = (RubyString)arg0;
- } else {
- evalStr = arg0.convertToString();
- }
-
- String file = arg1.convertToString().asJavaString();
- int line = (int)(arg2.convertToInteger().getLongValue() - 1);
-
- return evalUnder(context, mod, evalStr, file, line);
- }
-
- /**
- * Evaluates the string src with self set to the current object,
- * using the module under as the context.
- * @deprecated Call with an int line number and String file
- */
- public IRubyObject evalUnder(final ThreadContext context, RubyModule under, IRubyObject src, IRubyObject file, IRubyObject line) {
- return evalUnder(context, under, src.convertToString(), file.convertToString().toString(), (int) (line.convertToInteger().getLongValue() - 1));
- }
-
- /**
- * Evaluates the string src with self set to the current object,
- * using the module under as the context.
- */
- public IRubyObject evalUnder(final ThreadContext context, RubyModule under, RubyString src, String file, int line) {
- Visibility savedVisibility = context.getCurrentVisibility();
- context.setCurrentVisibility(Visibility.PUBLIC);
- context.preExecuteUnder(under, Block.NULL_BLOCK);
- try {
- return ASTInterpreter.evalSimple(context, this, src,
- file, line);
- } finally {
- context.postExecuteUnder();
- context.setCurrentVisibility(savedVisibility);
- }
- }
-
- /**
- * Will yield to the specific block changing the self to be the
- * current object instead of the self that is part of the frame
- * saved in the block frame. This method is the basis for the Ruby
- * instance_eval and module_eval methods. The arguments sent in to
- * it in the args array will be yielded to the block. This makes
- * it possible to emulate both instance_eval and instance_exec
- * with this implementation.
- */
- private IRubyObject yieldUnder(final ThreadContext context, RubyModule under, IRubyObject[] args, Block block) {
- context.preExecuteUnder(under, block);
-
- Visibility savedVisibility = block.getBinding().getVisibility();
- block.getBinding().setVisibility(Visibility.PUBLIC);
-
- try {
- IRubyObject valueInYield;
- boolean aValue;
- if (args.length == 1) {
- valueInYield = args[0];
- aValue = false;
- } else {
- valueInYield = RubyArray.newArrayNoCopy(context.getRuntime(), args);
- aValue = true;
- }
-
- // FIXME: This is an ugly hack to resolve JRUBY-1381; I'm not proud of it
- block = block.cloneBlock();
- block.getBinding().setSelf(RubyObject.this);
- block.getBinding().getFrame().setSelf(RubyObject.this);
- // end hack
-
- return block.yield(context, valueInYield, RubyObject.this, context.getRubyClass(), aValue);
- //TODO: Should next and return also catch here?
- } catch (JumpException.BreakJump bj) {
- return (IRubyObject) bj.getValue();
- } finally {
- block.getBinding().setVisibility(savedVisibility);
-
- context.postExecuteUnder();
- }
- }
-
- /**
- * Will yield to the specific block changing the self to be the
- * current object instead of the self that is part of the frame
- * saved in the block frame. This method is the basis for the Ruby
- * instance_eval and module_eval methods. The arguments sent in to
- * it in the args array will be yielded to the block. This makes
- * it possible to emulate both instance_eval and instance_exec
- * with this implementation.
- */
- private IRubyObject yieldUnder(final ThreadContext context, RubyModule under, Block block) {
- context.preExecuteUnder(under, block);
-
- Visibility savedVisibility = block.getBinding().getVisibility();
- block.getBinding().setVisibility(Visibility.PUBLIC);
-
- try {
- // FIXME: This is an ugly hack to resolve JRUBY-1381; I'm not proud of it
- block = block.cloneBlock();
- block.getBinding().setSelf(RubyObject.this);
- block.getBinding().getFrame().setSelf(RubyObject.this);
- // end hack
-
- return block.yield(context, this, this, context.getRubyClass(), false);
- //TODO: Should next and return also catch here?
- } catch (JumpException.BreakJump bj) {
- return (IRubyObject) bj.getValue();
- } finally {
- block.getBinding().setVisibility(savedVisibility);
-
- context.postExecuteUnder();
- }
- }
-
- // Methods of the Object class (rb_obj_*):
-
- /** rb_obj_equal
- *
- * Will by default use identity equality to compare objects. This
- * follows the Ruby semantics.
- */
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
- return this == obj ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- /** rb_obj_equal
- *
- * Will use Java identity equality.
- */
- @JRubyMethod(name = "equal?", required = 1)
- public IRubyObject equal_p(ThreadContext context, IRubyObject obj) {
- return this == obj ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- /** method used for Hash key comparison (specialized for String, Symbol and Fixnum)
- *
- * Will by default just call the Ruby method "eql?"
- */
- public boolean eql(IRubyObject other) {
- return callMethod(getRuntime().getCurrentContext(), MethodIndex.EQL_P, "eql?", other).isTrue();
- }
-
- /** rb_obj_equal
- *
- * Just like "==" and "equal?", "eql?" will use identity equality for Object.
- */
- @JRubyMethod(name = "eql?", required = 1)
- public IRubyObject eql_p(IRubyObject obj) {
- return this == obj ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- /** rb_equal
- *
- * The Ruby "===" method is used by default in case/when
- * statements. The Object implementation first checks Java identity
- * equality and then calls the "==" method too.
- */
- @JRubyMethod(name = "===", required = 1)
- public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
- return context.getRuntime().newBoolean(equalInternal(context, this, other));
- }
-
- /**
- * Helper method for checking equality, first using Java identity
- * equality, and then calling the "==" method.
- */
- protected static boolean equalInternal(final ThreadContext context, final IRubyObject that, final IRubyObject other){
- return that == other || that.callMethod(context, MethodIndex.EQUALEQUAL, "==", other).isTrue();
- }
-
- /**
- * Helper method for checking equality, first using Java identity
- * equality, and then calling the "eql?" method.
- */
- protected static boolean eqlInternal(final ThreadContext context, final IRubyObject that, final IRubyObject other){
- return that == other || that.callMethod(context, MethodIndex.EQL_P, "eql?", other).isTrue();
- }
-
- /** rb_obj_init_copy
- *
- * Initializes this object as a copy of the original, that is the
- * parameter to this object. Will make sure that the argument
- * actually has the same real class as this object. It shouldn't
- * be possible to initialize an object with something totally
- * different.
- */
- @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
- public IRubyObject initialize_copy(IRubyObject original) {
- if (this == original) return this;
- checkFrozen();
-
- if (getMetaClass().getRealClass() != original.getMetaClass().getRealClass()) {
- throw getRuntime().newTypeError("initialize_copy should take same class object");
- }
-
- return this;
- }
-
- /** obj_respond_to
- *
- * respond_to?( aSymbol, includePriv=false ) -> true or false
- *
- * Returns true if this object responds to the given method. Private
- * methods are included in the search only if the optional second
- * parameter evaluates to true.
- *
- * @return true if this responds to the given method
- *
- * !!! For some reason MRI shows the arity of respond_to? as -1, when it should be -2; that's why this is rest instead of required, optional = 1
- *
- * Going back to splitting according to method arity. MRI is wrong
- * about most of these anyway, and since we have arity splitting
- * in both the compiler and the interpreter, the performance
- * benefit is important for this method.
- */
- @JRubyMethod(name = "respond_to?")
- public RubyBoolean respond_to_p(IRubyObject mname) {
- String name = mname.asJavaString();
- return getRuntime().newBoolean(getMetaClass().isMethodBound(name, true));
- }
-
- /** obj_respond_to
- *
- * respond_to?( aSymbol, includePriv=false ) -> true or false
- *
- * Returns true if this object responds to the given method. Private
- * methods are included in the search only if the optional second
- * parameter evaluates to true.
- *
- * @return true if this responds to the given method
- *
- * !!! For some reason MRI shows the arity of respond_to? as -1, when it should be -2; that's why this is rest instead of required, optional = 1
- *
- * Going back to splitting according to method arity. MRI is wrong
- * about most of these anyway, and since we have arity splitting
- * in both the compiler and the interpreter, the performance
- * benefit is important for this method.
- */
- @JRubyMethod(name = "respond_to?")
- public RubyBoolean respond_to_p(IRubyObject mname, IRubyObject includePrivate) {
- String name = mname.asJavaString();
- return getRuntime().newBoolean(getMetaClass().isMethodBound(name, !includePrivate.isTrue()));
- }
-
- /** rb_obj_id
- *
- * Return the internal id of an object.
- *
- * FIXME: Should this be renamed to match its ruby name?
- */
- @JRubyMethod(name = {"object_id", "__id__"})
- public synchronized IRubyObject id() {
- return getRuntime().newFixnum(getRuntime().getObjectSpace().idOf(this));
- }
-
- /** rb_obj_id_obsolete
- *
- * Old id version. This one is bound to the "id" name and will emit a deprecation warning.
- */
- @JRubyMethod(name = "id")
- public synchronized IRubyObject id_deprecated() {
- getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Object#id will be deprecated; use Object#object_id", "Object#id", "Object#object_id");
- return id();
- }
-
- /** rb_obj_id
- *
- * Will return the hash code of this object. In comparison to MRI,
- * this method will use the Java identity hash code instead of
- * using rb_obj_id, since the usage of id in JRuby will incur the
- * cost of some. ObjectSpace maintenance.
- */
- @JRubyMethod(name = "hash")
- public RubyFixnum hash() {
- return getRuntime().newFixnum(super.hashCode());
- }
-
- /**
- * Override the Object#hashCode method to make sure that the Ruby
- * hash is actually used as the hashcode for Ruby objects. If the
- * Ruby "hash" method doesn't return a number, the Object#hashCode
- * implementation will be used instead.
- */
- @Override
- public int hashCode() {
- IRubyObject hashValue = callMethod(getRuntime().getCurrentContext(), MethodIndex.HASH, "hash");
-
- if (hashValue instanceof RubyFixnum) return (int) RubyNumeric.fix2long(hashValue);
-
- return super.hashCode();
- }
-
- /** rb_obj_class
- *
- * Returns the real class of this object, excluding any
- * singleton/meta class in the inheritance chain.
- */
- @JRubyMethod(name = "class")
- public RubyClass type() {
- return getMetaClass().getRealClass();
- }
-
- /** rb_obj_type
- *
- * The deprecated version of type, that emits a deprecation
- * warning.
- */
- @JRubyMethod(name = "type")
- public RubyClass type_deprecated() {
- getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Object#type is deprecated; use Object#class", "Object#type", "Object#class");
- return type();
- }
-
- /** rb_obj_clone
- *
- * This method should be overridden only by: Proc, Method,
- * UnboundedMethod, Binding. It will use the defined allocated of
- * the object, then clone the singleton class, taint the object,
- * call initCopy and then copy frozen state.
- */
- @JRubyMethod(name = "clone", frame = true)
- public IRubyObject rbClone() {
- if (isImmediate()) throw getRuntime().newTypeError("can't clone " + getMetaClass().getName());
-
- // We're cloning ourselves, so we know the result should be a RubyObject
- RubyObject clone = (RubyObject)getMetaClass().getRealClass().allocate();
- clone.setMetaClass(getSingletonClassClone());
- if (isTaint()) clone.setTaint(true);
-
- initCopy(clone, this);
-
- if (isFrozen()) clone.setFrozen(true);
- return clone;
- }
-
- /** rb_obj_dup
- *
- * This method should be overridden only by: Proc
- *
- * Will allocate a new instance of the real class of this object,
- * and then initialize that copy. It's different from {@link
- * #rbClone} in that it doesn't copy the singleton class.
- */
- @JRubyMethod(name = "dup")
- public IRubyObject dup() {
- if (isImmediate()) throw getRuntime().newTypeError("can't dup " + getMetaClass().getName());
-
- IRubyObject dup = getMetaClass().getRealClass().allocate();
- if (isTaint()) dup.setTaint(true);
-
- initCopy(dup, this);
-
- return dup;
- }
-
- /**
- * Lots of MRI objects keep their state in non-lookupable ivars
- * (e:g. Range, Struct, etc). This method is responsible for
- * dupping our java field equivalents
- */
- protected void copySpecialInstanceVariables(IRubyObject clone) {
- }
-
- /** rb_obj_display
- *
- * call-seq:
- * obj.display(port=$>) => nil
- *
- * Prints <i>obj</i> on the given port (default <code>$></code>).
- * Equivalent to:
- *
- * def display(port=$>)
- * port.write self
- * end
- *
- * For example:
- *
- * 1.display
- * "cat".display
- * [ 4, 5, 6 ].display
- * puts
- *
- * <em>produces:</em>
- *
- * 1cat456
- *
- */
- @JRubyMethod(name = "display", optional = 1)
- public IRubyObject display(ThreadContext context, IRubyObject[] args) {
- IRubyObject port = args.length == 0 ? context.getRuntime().getGlobalVariables().get("$>") : args[0];
-
- port.callMethod(context, "write", this);
-
- return context.getRuntime().getNil();
- }
-
- /** rb_obj_tainted
- *
- * call-seq:
- * obj.tainted? => true or false
- *
- * Returns <code>true</code> if the object is tainted.
- *
- */
- @JRubyMethod(name = "tainted?")
- public RubyBoolean tainted_p(ThreadContext context) {
- return context.getRuntime().newBoolean(isTaint());
- }
-
- /** rb_obj_taint
- *
- * call-seq:
- * obj.taint -> obj
- *
- * Marks <i>obj</i> as tainted---if the <code>$SAFE</code> level is
- * set appropriately, many method calls which might alter the running
- * programs environment will refuse to accept tainted strings.
- */
- @JRubyMethod(name = "taint")
- public IRubyObject taint(ThreadContext context) {
- taint(context.getRuntime());
- return this;
- }
-
- private void taint(Ruby runtime) {
- runtime.secure(4);
- if (!isTaint()) {
- testFrozen("object");
- setTaint(true);
- }
- }
-
- /** rb_obj_untaint
- *
- * call-seq:
- * obj.untaint => obj
- *
- * Removes the taint from <i>obj</i>.
- *
- * Only callable in if more secure than 3.
- */
- @JRubyMethod(name = "untaint")
- public IRubyObject untaint(ThreadContext context) {
- context.getRuntime().secure(3);
-
- if (isTaint()) {
- testFrozen("object");
- setTaint(false);
- }
-
- return this;
- }
-
- /** rb_obj_freeze
- *
- * call-seq:
- * obj.freeze => obj
- *
- * Prevents further modifications to <i>obj</i>. A
- * <code>TypeError</code> will be raised if modification is attempted.
- * There is no way to unfreeze a frozen object. See also
- * <code>Object#frozen?</code>.
- *
- * a = [ "a", "b", "c" ]
- * a.freeze
- * a << "z"
- *
- * <em>produces:</em>
- *
- * prog.rb:3:in `<<': can't modify frozen array (TypeError)
- * from prog.rb:3
- */
- @JRubyMethod(name = "freeze")
- public IRubyObject freeze(ThreadContext context) {
- if ((flags & FROZEN_F) == 0) {
- if (context.getRuntime().getSafeLevel() >= 4 && isTaint()) {
- throw context.getRuntime().newSecurityError("Insecure: can't freeze object");
- }
- flags |= FROZEN_F;
- }
- return this;
- }
-
- /** rb_obj_frozen_p
- *
- * call-seq:
- * obj.frozen? => true or false
- *
- * Returns the freeze status of <i>obj</i>.
- *
- * a = [ "a", "b", "c" ]
- * a.freeze #=> ["a", "b", "c"]
- * a.frozen? #=> true
- */
- @JRubyMethod(name = "frozen?")
- public RubyBoolean frozen_p(ThreadContext context) {
- return context.getRuntime().newBoolean(isFrozen());
- }
-
- /** inspect_obj
- *
- * The internal helper method that takes care of the part of the
- * inspection that inspects instance variables.
- */
- private StringBuilder inspectObj(StringBuilder part) {
- ThreadContext context = getRuntime().getCurrentContext();
- String sep = "";
-
- for (Variable<IRubyObject> ivar : getInstanceVariableList()) {
- part.append(sep).append(" ").append(ivar.getName()).append("=");
- part.append(ivar.getValue().callMethod(context, "inspect"));
- sep = ",";
- }
- part.append(">");
- return part;
- }
-
- /** rb_inspect
- *
- * The internal helper that ensures a RubyString instance is returned
- * so dangerous casting can be omitted
- * Prefered over callMethod(context, "inspect")
- */
- static RubyString inspect(ThreadContext context, IRubyObject object) {
- return RubyString.objAsString(context, object.callMethod(context, "inspect"));
- }
-
- /** rb_obj_inspect
- *
- * call-seq:
- * obj.inspect => string
- *
- * Returns a string containing a human-readable representation of
- * <i>obj</i>. If not overridden, uses the <code>to_s</code> method to
- * generate the string.
- *
- * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
- * Time.new.inspect #=> "Wed Apr 09 08:54:39 CDT 2003"
- */
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect() {
- Ruby runtime = getRuntime();
- if ((!isImmediate()) &&
- // TYPE(obj) == T_OBJECT
- !(this instanceof RubyClass) &&
- this != runtime.getObject() &&
- this != runtime.getModule() &&
- !(this instanceof RubyModule) &&
- // TODO: should have #hasInstanceVariables method, though
- // this will work here:
- hasVariables()) {
-
- StringBuilder part = new StringBuilder();
- String cname = getMetaClass().getRealClass().getName();
- part.append("#<").append(cname).append(":0x");
- part.append(Integer.toHexString(System.identityHashCode(this)));
-
- if (runtime.isInspecting(this)) {
- /* 6:tags 16:addr 1:eos */
- part.append(" ...>");
- return runtime.newString(part.toString());
- }
- try {
- runtime.registerInspecting(this);
- return runtime.newString(inspectObj(part).toString());
- } finally {
- runtime.unregisterInspecting(this);
- }
- }
-
- if (isNil()) return RubyNil.inspect(this);
- return RuntimeHelpers.invoke(runtime.getCurrentContext(), this, "to_s");
- }
-
- /** rb_obj_is_instance_of
- *
- * call-seq:
- * obj.instance_of?(class) => true or false
- *
- * Returns <code>true</code> if <i>obj</i> is an instance of the given
- * class. See also <code>Object#kind_of?</code>.
- */
- @JRubyMethod(name = "instance_of?", required = 1)
- public RubyBoolean instance_of_p(ThreadContext context, IRubyObject type) {
- if (type() == type) {
- return context.getRuntime().getTrue();
- } else if (!(type instanceof RubyModule)) {
- throw context.getRuntime().newTypeError("class or module required");
- } else {
- return context.getRuntime().getFalse();
- }
- }
-
-
- /** rb_obj_is_kind_of
- *
- * call-seq:
- * obj.is_a?(class) => true or false
- * obj.kind_of?(class) => true or false
- *
- * Returns <code>true</code> if <i>class</i> is the class of
- * <i>obj</i>, or if <i>class</i> is one of the superclasses of
- * <i>obj</i> or modules included in <i>obj</i>.
- *
- * module M; end
- * class A
- * include M
- * end
- * class B < A; end
- * class C < B; end
- * b = B.new
- * b.instance_of? A #=> false
- * b.instance_of? B #=> true
- * b.instance_of? C #=> false
- * b.instance_of? M #=> false
- * b.kind_of? A #=> true
- * b.kind_of? B #=> true
- * b.kind_of? C #=> false
- * b.kind_of? M #=> true
- */
- @JRubyMethod(name = {"kind_of?", "is_a?"}, required = 1)
- public RubyBoolean kind_of_p(ThreadContext context, IRubyObject type) {
- // TODO: Generalize this type-checking code into IRubyObject helper.
- if (!(type instanceof RubyModule)) {
- // TODO: newTypeError does not offer enough for ruby error string...
- throw context.getRuntime().newTypeError("class or module required");
- }
-
- return context.getRuntime().newBoolean(((RubyModule)type).isInstance(this));
- }
-
- /** rb_obj_methods
- *
- * call-seq:
- * obj.methods => array
- *
- * Returns a list of the names of methods publicly accessible in
- * <i>obj</i>. This will include all the methods accessible in
- * <i>obj</i>'s ancestors.
- *
- * class Klass
- * def kMethod()
- * end
- * end
- * k = Klass.new
- * k.methods[0..9] #=> ["kMethod", "freeze", "nil?", "is_a?",
- * "class", "instance_variable_set",
- * "methods", "extend", "__send__", "instance_eval"]
- * k.methods.length #=> 42
- */
- @JRubyMethod(name = "methods", optional = 1)
- public IRubyObject methods(ThreadContext context, IRubyObject[] args) {
- boolean all = true;
- if (args.length == 1) {
- all = args[0].isTrue();
- }
-
- RubyArray singletonMethods = null;
- if (getMetaClass().isSingleton()) {
- singletonMethods =
- getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getFalse()});
- if (all) {
- singletonMethods.concat(getMetaClass().getSuperClass().instance_methods(new IRubyObject[] {context.getRuntime().getTrue()}));
- }
- } else {
- if (all) {
- singletonMethods = getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getTrue()});
- } else {
- singletonMethods = context.getRuntime().newEmptyArray();
- }
- }
-
- return singletonMethods;
- }
-
- /** rb_obj_public_methods
- *
- * call-seq:
- * obj.public_methods(all=true) => array
- *
- * Returns the list of public methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- */
- @JRubyMethod(name = "public_methods", optional = 1)
- public IRubyObject public_methods(ThreadContext context, IRubyObject[] args) {
- if (args.length == 0) {
- args = new IRubyObject[] { context.getRuntime().getTrue() };
- }
-
- return getMetaClass().public_instance_methods(args);
- }
-
- /** rb_obj_protected_methods
- *
- * call-seq:
- * obj.protected_methods(all=true) => array
- *
- * Returns the list of protected methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- *
- * Internally this implementation uses the
- * {@link RubyModule#protected_instance_methods} method.
- */
- @JRubyMethod(name = "protected_methods", optional = 1)
- public IRubyObject protected_methods(ThreadContext context, IRubyObject[] args) {
- if (args.length == 0) {
- args = new IRubyObject[] { context.getRuntime().getTrue() };
- }
-
- return getMetaClass().protected_instance_methods(args);
- }
-
- /** rb_obj_private_methods
- *
- * call-seq:
- * obj.private_methods(all=true) => array
- *
- * Returns the list of private methods accessible to <i>obj</i>. If
- * the <i>all</i> parameter is set to <code>false</code>, only those methods
- * in the receiver will be listed.
- *
- * Internally this implementation uses the
- * {@link RubyModule#private_instance_methods} method.
- */
- @JRubyMethod(name = "private_methods", optional = 1)
- public IRubyObject private_methods(ThreadContext context, IRubyObject[] args) {
- if (args.length == 0) {
- args = new IRubyObject[] { context.getRuntime().getTrue() };
- }
-
- return getMetaClass().private_instance_methods(args);
- }
-
- /** rb_obj_singleton_methods
- *
- * call-seq:
- * obj.singleton_methods(all=true) => array
- *
- * Returns an array of the names of singleton methods for <i>obj</i>.
- * If the optional <i>all</i> parameter is true, the list will include
- * methods in modules included in <i>obj</i>.
- *
- * module Other
- * def three() end
- * end
- *
- * class Single
- * def Single.four() end
- * end
- *
- * a = Single.new
- *
- * def a.one()
- * end
- *
- * class << a
- * include Other
- * def two()
- * end
- * end
- *
- * Single.singleton_methods #=> ["four"]
- * a.singleton_methods(false) #=> ["two", "one"]
- * a.singleton_methods #=> ["two", "one", "three"]
- */
- // TODO: This is almost RubyModule#instance_methods on the metaClass. Perhaps refactor.
- @JRubyMethod(name = "singleton_methods", optional = 1)
- public RubyArray singleton_methods(ThreadContext context, IRubyObject[] args) {
- boolean all = true;
- if(args.length == 1) {
- all = args[0].isTrue();
- }
-
- RubyArray singletonMethods;
- if (getMetaClass().isSingleton()) {
- singletonMethods = getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getFalse()});
- if (all) {
- RubyClass superClass = getMetaClass().getSuperClass();
- while (superClass.isIncluded()) {
- singletonMethods.concat(superClass.instance_methods(new IRubyObject[] {context.getRuntime().getFalse()}));
- superClass = superClass.getSuperClass();
- }
- }
- } else {
- singletonMethods = context.getRuntime().newEmptyArray();
- }
-
- return singletonMethods;
- }
-
- /** rb_obj_method
- *
- * call-seq:
- * obj.method(sym) => method
- *
- * Looks up the named method as a receiver in <i>obj</i>, returning a
- * <code>Method</code> object (or raising <code>NameError</code>). The
- * <code>Method</code> object acts as a closure in <i>obj</i>'s object
- * instance, so instance variables and the value of <code>self</code>
- * remain available.
- *
- * class Demo
- * def initialize(n)
- * @iv = n
- * end
- * def hello()
- * "Hello, @iv = #{@iv}"
- * end
- * end
- *
- * k = Demo.new(99)
- * m = k.method(:hello)
- * m.call #=> "Hello, @iv = 99"
- *
- * l = Demo.new('Fred')
- * m = l.method("hello")
- * m.call #=> "Hello, @iv = Fred"
- */
- @JRubyMethod(name = "method", required = 1)
- public IRubyObject method(IRubyObject symbol) {
- return getMetaClass().newMethod(this, symbol.asJavaString(), true);
- }
-
- /**
- * Internal method that helps to convert any object into the
- * format of a class name and a hex string inside of #<>.
- */
- public IRubyObject anyToString() {
- String cname = getMetaClass().getRealClass().getName();
- /* 6:tags 16:addr 1:eos */
- RubyString str = getRuntime().newString("#<" + cname + ":0x" + Integer.toHexString(System.identityHashCode(this)) + ">");
- str.setTaint(isTaint());
- return str;
- }
-
- /** rb_any_to_s
- *
- * call-seq:
- * obj.to_s => string
- *
- * Returns a string representing <i>obj</i>. The default
- * <code>to_s</code> prints the object's class and an encoding of the
- * object id. As a special case, the top-level object that is the
- * initial execution context of Ruby programs returns ``main.''
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- return anyToString();
- }
-
- /** rb_any_to_a
- *
- * call-seq:
- * obj.to_a -> anArray
- *
- * Returns an array representation of <i>obj</i>. For objects of class
- * <code>Object</code> and others that don't explicitly override the
- * method, the return value is an array containing <code>self</code>.
- * However, this latter behavior will soon be obsolete.
- *
- * self.to_a #=> -:1: warning: default `to_a' will be obsolete
- * "hello".to_a #=> ["hello"]
- * Time.new.to_a #=> [39, 54, 8, 9, 4, 2003, 3, 99, true, "CDT"]
- *
- * The default to_a method is deprecated.
- */
- @JRubyMethod(name = "to_a", visibility = Visibility.PUBLIC)
- public RubyArray to_a() {
- getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "default 'to_a' will be obsolete", "to_a");
- return getRuntime().newArray(this);
- }
-
- /** rb_obj_instance_eval
- *
- * call-seq:
- * obj.instance_eval(string [, filename [, lineno]] ) => obj
- * obj.instance_eval {| | block } => obj
- *
- * Evaluates a string containing Ruby source code, or the given block,
- * within the context of the receiver (_obj_). In order to set the
- * context, the variable +self+ is set to _obj_ while
- * the code is executing, giving the code access to _obj_'s
- * instance variables. In the version of <code>instance_eval</code>
- * that takes a +String+, the optional second and third
- * parameters supply a filename and starting line number that are used
- * when reporting compilation errors.
- *
- * class Klass
- * def initialize
- * @secret = 99
- * end
- * end
- * k = Klass.new
- * k.instance_eval { @secret } #=> 99
- */
- @JRubyMethod(name = "instance_eval", frame = true)
- public IRubyObject instance_eval(ThreadContext context, Block block) {
- return specificEval(context, getInstanceEvalClass(), block);
- }
- @JRubyMethod(name = "instance_eval", frame = true)
- public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, Block block) {
- return specificEval(context, getInstanceEvalClass(), arg0, block);
- }
- @JRubyMethod(name = "instance_eval", frame = true)
- public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- return specificEval(context, getInstanceEvalClass(), arg0, arg1, block);
- }
- @JRubyMethod(name = "instance_eval", frame = true)
- public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- return specificEval(context, getInstanceEvalClass(), arg0, arg1, arg2, block);
- }
- @Deprecated
- public IRubyObject instance_eval(ThreadContext context, IRubyObject[] args, Block block) {
- RubyModule klazz;
-
- if (isImmediate()) {
- // Ruby uses Qnil here, we use "dummy" because we need a class
- klazz = context.getRuntime().getDummy();
- } else {
- klazz = getSingletonClass();
- }
-
- return specificEval(context, klazz, args, block);
- }
-
- private RubyModule getInstanceEvalClass() {
- if (isImmediate()) {
- // Ruby uses Qnil here, we use "dummy" because we need a class
- return getRuntime().getDummy();
- } else {
- return getSingletonClass();
- }
- }
-
- /** rb_obj_instance_exec
- *
- * call-seq:
- * obj.instance_exec(arg...) {|var...| block } => obj
- *
- * Executes the given block within the context of the receiver
- * (_obj_). In order to set the context, the variable +self+ is set
- * to _obj_ while the code is executing, giving the code access to
- * _obj_'s instance variables. Arguments are passed as block parameters.
- *
- * class Klass
- * def initialize
- * @secret = 99
- * end
- * end
- * k = Klass.new
- * k.instance_exec(5) {|x| @secret+x } #=> 104
- */
- @JRubyMethod(name = "instance_exec", optional = 3, frame = true)
- public IRubyObject instance_exec(ThreadContext context, IRubyObject[] args, Block block) {
- if (!block.isGiven()) throw context.getRuntime().newArgumentError("block not supplied");
-
- RubyModule klazz;
- if (isImmediate()) {
- // Ruby uses Qnil here, we use "dummy" because we need a class
- klazz = context.getRuntime().getDummy();
- } else {
- klazz = getSingletonClass();
- }
-
- return yieldUnder(context, klazz, args, block);
- }
-
- /** rb_obj_extend
- *
- * call-seq:
- * obj.extend(module, ...) => obj
- *
- * Adds to _obj_ the instance methods from each module given as a
- * parameter.
- *
- * module Mod
- * def hello
- * "Hello from Mod.\n"
- * end
- * end
- *
- * class Klass
- * def hello
- * "Hello from Klass.\n"
- * end
- * end
- *
- * k = Klass.new
- * k.hello #=> "Hello from Klass.\n"
- * k.extend(Mod) #=> #<Klass:0x401b3bc8>
- * k.hello #=> "Hello from Mod.\n"
- */
- @JRubyMethod(name = "extend", required = 1, rest = true)
- public IRubyObject extend(IRubyObject[] args) {
- Ruby runtime = getRuntime();
-
- // Make sure all arguments are modules before calling the callbacks
- for (int i = 0; i < args.length; i++) {
- if (!args[i].isModule()) throw runtime.newTypeError(args[i], runtime.getModule());
- }
-
- ThreadContext context = runtime.getCurrentContext();
-
- // MRI extends in order from last to first
- for (int i = args.length - 1; i >= 0; i--) {
- args[i].callMethod(context, "extend_object", this);
- args[i].callMethod(context, "extended", this);
- }
- return this;
- }
-
- /** rb_obj_dummy
- *
- * Default initialize method. This one gets defined in some other
- * place as a Ruby method.
- */
- public IRubyObject initialize() {
- return getRuntime().getNil();
- }
-
- /** rb_f_send
- *
- * send( aSymbol [, args ]* ) -> anObject
- *
- * Invokes the method identified by aSymbol, passing it any arguments
- * specified. You can use __send__ if the name send clashes with an
- * existing method in this object.
- *
- * <pre>
- * class Klass
- * def hello(*args)
- * "Hello " + args.join(' ')
- * end
- * end
- *
- * k = Klass.new
- * k.send :hello, "gentle", "readers"
- * </pre>
- *
- * @return the result of invoking the method identified by aSymbol.
- */
- @JRubyMethod(name = {"send", "__send__"})
- public IRubyObject send(ThreadContext context, Block block) {
- throw context.getRuntime().newArgumentError(0, 1);
- }
- @JRubyMethod(name = {"send", "__send__"})
- public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) {
- String name = arg0.asJavaString();
-
- return getMetaClass().finvoke(context, this, name, block);
- }
- @JRubyMethod(name = {"send", "__send__"})
- public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- String name = arg0.asJavaString();
-
- return getMetaClass().finvoke(context, this, name, arg1, block);
- }
- @JRubyMethod(name = {"send", "__send__"})
- public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
- String name = arg0.asJavaString();
-
- return getMetaClass().finvoke(context, this, name, arg1, arg2, block);
- }
- @JRubyMethod(name = {"send", "__send__"}, rest = true)
- public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) {
- String name = args[0].asJavaString();
- int newArgsLength = args.length - 1;
-
- IRubyObject[] newArgs;
- if (newArgsLength == 0) {
- newArgs = IRubyObject.NULL_ARRAY;
- } else {
- newArgs = new IRubyObject[newArgsLength];
- System.arraycopy(args, 1, newArgs, 0, newArgs.length);
- }
-
- return getMetaClass().finvoke(context, this, name, newArgs, block);
- }
-
- /** rb_false
- *
- * call_seq:
- * nil.nil? => true
- * <anything_else>.nil? => false
- *
- * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
- */
- @JRubyMethod(name = "nil?")
- public IRubyObject nil_p(ThreadContext context) {
- return context.getRuntime().getFalse();
- }
-
- /** rb_obj_pattern_match
- *
- * call-seq:
- * obj =~ other => false
- *
- * Pattern Match---Overridden by descendents (notably
- * <code>Regexp</code> and <code>String</code>) to provide meaningful
- * pattern-match semantics.
- */
- @JRubyMethod(name = "=~", required = 1)
- public IRubyObject op_match(ThreadContext context, IRubyObject arg) {
- return context.getRuntime().getFalse();
- }
-
- public IRubyObject to_java() {
- throw getRuntime().newTypeError(getMetaClass().getBaseName() + " cannot coerce to a Java type.");
- }
-
- public IRubyObject as(Class javaClass) {
- throw getRuntime().newTypeError(getMetaClass().getBaseName() + " cannot coerce to a Java type.");
- }
-
- /**
- * @see org.jruby.runtime.builtin.IRubyObject#getType()
- */
- public RubyClass getType() {
- return type();
- }
-
- /**
- * @see org.jruby.runtime.builtin.IRubyObject#dataWrapStruct()
- */
- public synchronized void dataWrapStruct(Object obj) {
- this.dataStruct = obj;
- }
-
- /**
- * @see org.jruby.runtime.builtin.IRubyObject#dataGetStruct()
- */
- public synchronized Object dataGetStruct() {
- return dataStruct;
- }
-
- /**
- * Adds the specified object as a finalizer for this object.
- */
- public void addFinalizer(IRubyObject finalizer) {
- if (this.finalizer == null) {
- this.finalizer = new Finalizer(getRuntime().getObjectSpace().idOf(this));
- getRuntime().addFinalizer(this.finalizer);
- }
- this.finalizer.addFinalizer(finalizer);
- }
-
- /**
- * Remove all the finalizers for this object.
- */
- public void removeFinalizers() {
- if (finalizer != null) {
- finalizer.removeFinalizers();
- finalizer = null;
- getRuntime().removeFinalizer(this.finalizer);
- }
- }
-
-
- //
- // INSTANCE VARIABLE RUBY METHODS
- //
-
- /** rb_obj_ivar_defined
- *
- * call-seq:
- * obj.instance_variable_defined?(symbol) => true or false
- *
- * Returns <code>true</code> if the given instance variable is
- * defined in <i>obj</i>.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_defined?(:@a) #=> true
- * fred.instance_variable_defined?("@b") #=> true
- * fred.instance_variable_defined?("@c") #=> false
- */
- @JRubyMethod(name = "instance_variable_defined?", required = 1)
- public IRubyObject instance_variable_defined_p(ThreadContext context, IRubyObject name) {
- if (variableTableContains(validateInstanceVariable(name.asJavaString()))) {
- return context.getRuntime().getTrue();
- }
- return context.getRuntime().getFalse();
- }
-
- /** rb_obj_ivar_get
- *
- * call-seq:
- * obj.instance_variable_get(symbol) => obj
- *
- * Returns the value of the given instance variable, or nil if the
- * instance variable is not set. The <code>@</code> part of the
- * variable name should be included for regular instance
- * variables. Throws a <code>NameError</code> exception if the
- * supplied symbol is not valid as an instance variable name.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_get(:@a) #=> "cat"
- * fred.instance_variable_get("@b") #=> 99
- */
- @JRubyMethod(name = "instance_variable_get", required = 1)
- public IRubyObject instance_variable_get(ThreadContext context, IRubyObject name) {
- IRubyObject value;
- if ((value = variableTableFetch(validateInstanceVariable(name.asJavaString()))) != null) {
- return value;
- }
- return context.getRuntime().getNil();
- }
-
- /** rb_obj_ivar_set
- *
- * call-seq:
- * obj.instance_variable_set(symbol, obj) => obj
- *
- * Sets the instance variable names by <i>symbol</i> to
- * <i>object</i>, thereby frustrating the efforts of the class's
- * author to attempt to provide proper encapsulation. The variable
- * did not have to exist prior to this call.
- *
- * class Fred
- * def initialize(p1, p2)
- * @a, @b = p1, p2
- * end
- * end
- * fred = Fred.new('cat', 99)
- * fred.instance_variable_set(:@a, 'dog') #=> "dog"
- * fred.instance_variable_set(:@c, 'cat') #=> "cat"
- * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
- */
- @JRubyMethod(name = "instance_variable_set", required = 2)
- public IRubyObject instance_variable_set(IRubyObject name, IRubyObject value) {
- ensureInstanceVariablesSettable();
- return variableTableStore(validateInstanceVariable(name.asJavaString()), value);
- }
-
- /** rb_obj_remove_instance_variable
- *
- * call-seq:
- * obj.remove_instance_variable(symbol) => obj
- *
- * Removes the named instance variable from <i>obj</i>, returning that
- * variable's value.
- *
- * class Dummy
- * attr_reader :var
- * def initialize
- * @var = 99
- * end
- * def remove
- * remove_instance_variable(:@var)
- * end
- * end
- * d = Dummy.new
- * d.var #=> 99
- * d.remove #=> 99
- * d.var #=> nil
- */
- @JRubyMethod(name = "remove_instance_variable", required = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject remove_instance_variable(ThreadContext context, IRubyObject name, Block block) {
- ensureInstanceVariablesSettable();
- IRubyObject value;
- if ((value = variableTableRemove(validateInstanceVariable(name.asJavaString()))) != null) {
- return value;
- }
- throw context.getRuntime().newNameError("instance variable " + name.asJavaString() + " not defined", name.asJavaString());
- }
-
- /** rb_obj_instance_variables
- *
- * call-seq:
- * obj.instance_variables => array
- *
- * Returns an array of instance variable names for the receiver. Note
- * that simply defining an accessor does not create the corresponding
- * instance variable.
- *
- * class Fred
- * attr_accessor :a1
- * def initialize
- * @iv = 3
- * end
- * end
- * Fred.new.instance_variables #=> ["@iv"]
- */
- @JRubyMethod(name = "instance_variables")
- public RubyArray instance_variables(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- List<String> nameList = getInstanceVariableNameList();
-
- RubyArray array = runtime.newArray(nameList.size());
-
- for (String name : nameList) {
- array.append(runtime.newString(name));
- }
-
- return array;
- }
-
- //
- // INSTANCE VARIABLE API METHODS
- //
-
- /**
- * Dummy method to avoid a cast, and to avoid polluting the
- * IRubyObject interface with all the instance variable management
- * methods.
- */
- public InstanceVariables getInstanceVariables() {
- return this;
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#hasInstanceVariable
- */
- public boolean hasInstanceVariable(String name) {
- assert IdUtil.isInstanceVariable(name);
- return variableTableContains(name);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#fastHasInstanceVariable
- */
- public boolean fastHasInstanceVariable(String internedName) {
- assert IdUtil.isInstanceVariable(internedName);
- return variableTableFastContains(internedName);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariable
- */
- public IRubyObject getInstanceVariable(String name) {
- assert IdUtil.isInstanceVariable(name);
- return variableTableFetch(name);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#fastGetInstanceVariable
- */
- public IRubyObject fastGetInstanceVariable(String internedName) {
- assert IdUtil.isInstanceVariable(internedName);
- return variableTableFastFetch(internedName);
- }
-
- /** rb_iv_set / rb_ivar_set
- *
- * @see org.jruby.runtime.builtin.InstanceVariables#setInstanceVariable
- */
- public IRubyObject setInstanceVariable(String name, IRubyObject value) {
- assert IdUtil.isInstanceVariable(name) && value != null;
- ensureInstanceVariablesSettable();
- return variableTableStore(name, value);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#fastSetInstanceVariable
- */
- public IRubyObject fastSetInstanceVariable(String internedName, IRubyObject value) {
- assert IdUtil.isInstanceVariable(internedName) && value != null;
- ensureInstanceVariablesSettable();
- return variableTableFastStore(internedName, value);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#removeInstanceVariable
- */
- public IRubyObject removeInstanceVariable(String name) {
- assert IdUtil.isInstanceVariable(name);
- ensureInstanceVariablesSettable();
- return variableTableRemove(name);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariableList
- */
- public List<Variable<IRubyObject>> getInstanceVariableList() {
- VariableTableEntry[] table = variableTableGetTable();
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- IRubyObject readValue;
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if (IdUtil.isInstanceVariable(e.name)) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- list.add(new VariableEntry<IRubyObject>(e.name, readValue));
- }
- }
- }
- return list;
- }
-
- /**
- * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariableNameList
- */
- public List<String> getInstanceVariableNameList() {
- VariableTableEntry[] table = variableTableGetTable();
- ArrayList<String> list = new ArrayList<String>();
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if (IdUtil.isInstanceVariable(e.name)) {
- list.add(e.name);
- }
- }
- }
- return list;
- }
-
- /**
- * The error message used when some one tries to modify an
- * instance variable in a high security setting.
- */
- protected static final String ERR_INSECURE_SET_INST_VAR = "Insecure: can't modify instance variable";
-
- /**
- * Checks if the name parameter represents a legal instance variable name, and otherwise throws a Ruby NameError
- */
- protected String validateInstanceVariable(String name) {
- if (IdUtil.isValidInstanceVariableName(name)) return name;
-
- throw getRuntime().newNameError("`" + name + "' is not allowable as an instance variable name", name);
- }
-
- /**
- * Makes sure that instance variables can be set on this object,
- * including information about whether this object is frozen, or
- * tainted. Will throw a suitable exception in that case.
- */
- protected void ensureInstanceVariablesSettable() {
- if (!isFrozen() && (getRuntime().getSafeLevel() < 4 || isTaint())) {
- return;
- }
-
- if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
- throw getRuntime().newSecurityError(ERR_INSECURE_SET_INST_VAR);
- }
- if (isFrozen()) {
- if (this instanceof RubyModule) {
- throw getRuntime().newFrozenError("class/module ");
- } else {
- throw getRuntime().newFrozenError("");
- }
- }
- }
-
- //
- // INTERNAL VARIABLE METHODS
- //
-
- /**
- * Dummy method to avoid a cast, and to avoid polluting the
- * IRubyObject interface with all the instance variable management
- * methods.
- */
- public InternalVariables getInternalVariables() {
- return this;
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#hasInternalVariable
- */
- public boolean hasInternalVariable(String name) {
- assert !isRubyVariable(name);
- return variableTableContains(name);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#fastHasInternalVariable
- */
- public boolean fastHasInternalVariable(String internedName) {
- assert !isRubyVariable(internedName);
- return variableTableFastContains(internedName);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#getInternalVariable
- */
- public IRubyObject getInternalVariable(String name) {
- assert !isRubyVariable(name);
- return variableTableFetch(name);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#fastGetInternalVariable
- */
- public IRubyObject fastGetInternalVariable(String internedName) {
- assert !isRubyVariable(internedName);
- return variableTableFastFetch(internedName);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#setInternalVariable
- */
- public void setInternalVariable(String name, IRubyObject value) {
- assert !isRubyVariable(name);
- variableTableStore(name, value);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#fastSetInternalVariable
- */
- public void fastSetInternalVariable(String internedName, IRubyObject value) {
- assert !isRubyVariable(internedName);
- variableTableFastStore(internedName, value);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#removeInternalVariable
- */
- public IRubyObject removeInternalVariable(String name) {
- assert !isRubyVariable(name);
- return variableTableRemove(name);
- }
-
- /**
- * Sync one variable table with another - this is used to make
- * rbClone work correctly.
- */
- public void syncVariables(List<Variable<IRubyObject>> variables) {
- variableTableSync(variables);
- }
-
- /**
- * @see org.jruby.runtime.builtin.InternalVariables#getInternalVariableList
- */
- public List<Variable<IRubyObject>> getInternalVariableList() {
- VariableTableEntry[] table = variableTableGetTable();
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- IRubyObject readValue;
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if (!isRubyVariable(e.name)) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- list.add(new VariableEntry<IRubyObject>(e.name, readValue));
- }
- }
- }
- return list;
- }
-
-
- //
- // COMMON VARIABLE METHODS
- //
-
- /**
- * Returns true if object has any variables, defined as:
- * <ul>
- * <li> instance variables
- * <li> class variables
- * <li> constants
- * <li> internal variables, such as those used when marshaling Ranges and Exceptions
- * </ul>
- * @return true if object has any variables, else false
- */
- public boolean hasVariables() {
- return variableTableGetSize() > 0;
- }
-
- /**
- * Returns the amount of instance variables, class variables,
- * constants and internal variables this object has.
- */
- public int getVariableCount() {
- return variableTableGetSize();
- }
-
- /**
- * Gets a list of all variables in this object.
- */
- // TODO: must override in RubyModule to pick up constants
- public List<Variable<IRubyObject>> getVariableList() {
- VariableTableEntry[] table = variableTableGetTable();
- ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
- IRubyObject readValue;
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- list.add(new VariableEntry<IRubyObject>(e.name, readValue));
- }
- }
- return list;
- }
-
- /**
- * Gets a name list of all variables in this object.
- */
- // TODO: must override in RubyModule to pick up constants
- public List<String> getVariableNameList() {
- VariableTableEntry[] table = variableTableGetTable();
- ArrayList<String> list = new ArrayList<String>();
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- list.add(e.name);
- }
- }
- return list;
- }
-
- /**
- * Gets internal access to the getmap for variables.
- */
- @SuppressWarnings("unchecked")
- @Deprecated // born deprecated
- public Map getVariableMap() {
- return variableTableGetMap();
- }
-
- /**
- * Check the syntax of a Ruby variable, including that it's longer
- * than zero characters, and starts with either an @ or a capital
- * letter.
- */
- // FIXME: this should go somewhere more generic -- maybe IdUtil
- protected static final boolean isRubyVariable(String name) {
- char c;
- return name.length() > 0 && ((c = name.charAt(0)) == '@' || (c <= 'Z' && c >= 'A'));
- }
-
- //
- // VARIABLE TABLE METHODS, ETC.
- //
-
- protected static final int VARIABLE_TABLE_DEFAULT_CAPACITY = 8; // MUST be power of 2!
- protected static final int VARIABLE_TABLE_MAXIMUM_CAPACITY = 1 << 30;
- protected static final float VARIABLE_TABLE_LOAD_FACTOR = 0.75f;
- protected static final VariableTableEntry[] VARIABLE_TABLE_EMPTY_TABLE = new VariableTableEntry[0];
-
- /**
- * Every entry in the variable map is represented by an instance
- * of this class.
- */
- protected static final class VariableTableEntry {
- final int hash;
- final String name;
- volatile IRubyObject value;
- final VariableTableEntry next;
-
- VariableTableEntry(int hash, String name, IRubyObject value, VariableTableEntry next) {
- assert name == name.intern() : name + " is not interned";
- this.hash = hash;
- this.name = name;
- this.value = value;
- this.next = next;
- }
- }
-
- /**
- * Reads the value of the specified entry, locked on the current
- * object.
- */
- protected synchronized IRubyObject variableTableReadLocked(VariableTableEntry entry) {
- return entry.value;
- }
-
- /**
- * Checks if the variable table contains a variable of the
- * specified name.
- */
- protected boolean variableTableContains(String name) {
- VariableTableEntry[] table;
- if ((table = variableTable) != null) {
- int hash = name.hashCode();
- for (VariableTableEntry e = table[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Checks if the variable table contains the the variable of the
- * specified name, where the precondition is that the name must be
- * an interned Java String.
- */
- protected boolean variableTableFastContains(String internedName) {
- assert internedName == internedName.intern() : internedName + " not interned";
- VariableTableEntry[] table;
- if ((table = variableTable) != null) {
- for (VariableTableEntry e = table[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Fetch an object from the variable table based on the name.
- *
- * @return the object or null if not found
- */
- protected IRubyObject variableTableFetch(String name) {
- VariableTableEntry[] table;
- IRubyObject readValue;
- if ((table = variableTable) != null) {
- int hash = name.hashCode();
- for (VariableTableEntry e = table[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- if ((readValue = e.value) != null) return readValue;
- return variableTableReadLocked(e);
- }
- }
- }
- return null;
- }
-
- /**
- * Fetch an object from the variable table based on the name,
- * where the name must be an interned Java String.
- *
- * @return the object or null if not found
- */
- protected IRubyObject variableTableFastFetch(String internedName) {
- assert internedName == internedName.intern() : internedName + " not interned";
- VariableTableEntry[] table;
- IRubyObject readValue;
- if ((table = variableTable) != null) {
- for (VariableTableEntry e = table[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- if ((readValue = e.value) != null) return readValue;
- return variableTableReadLocked(e);
- }
- }
- }
- return null;
- }
-
- /**
- * Store a value in the variable store under the specific name.
- */
- protected IRubyObject variableTableStore(String name, IRubyObject value) {
- int hash = name.hashCode();
- synchronized(this) {
- VariableTableEntry[] table;
- VariableTableEntry e;
- if ((table = variableTable) == null) {
- table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- e = new VariableTableEntry(hash, name.intern(), value, null);
- table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTableSize = 1;
- variableTable = table;
- return value;
- }
- int potentialNewSize;
- if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
- table = variableTableRehash();
- }
- int index;
- for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- e.value = value;
- return value;
- }
- }
- e = new VariableTableEntry(hash, name.intern(), value, table[index]);
- table[index] = e;
- variableTableSize = potentialNewSize;
- variableTable = table; // write-volatile
- }
- return value;
- }
-
- /**
- * Will store the value under the specified name, where the name
- * needs to be an interned Java String.
- */
- protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
- assert internedName == internedName.intern() : internedName + " not interned";
- int hash = internedName.hashCode();
- synchronized(this) {
- VariableTableEntry[] table;
- VariableTableEntry e;
- if ((table = variableTable) == null) {
- table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- e = new VariableTableEntry(hash, internedName, value, null);
- table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTableSize = 1;
- variableTable = table;
- return value;
- }
- int potentialNewSize;
- if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
- table = variableTableRehash();
- }
- int index;
- for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- e.value = value;
- return value;
- }
- }
- e = new VariableTableEntry(hash, internedName, value, table[index]);
- table[index] = e;
- variableTableSize = potentialNewSize;
- variableTable = table; // write-volatile
- }
- return value;
- }
-
- /**
- * Removes the entry with the specified name from the variable
- * table, and returning the removed value.
- */
- protected IRubyObject variableTableRemove(String name) {
- synchronized(this) {
- VariableTableEntry[] table;
- if ((table = variableTable) != null) {
- int hash = name.hashCode();
- int index = hash & (table.length - 1);
- VariableTableEntry first = table[index];
- VariableTableEntry e;
- for (e = first; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- IRubyObject oldValue = e.value;
- // All entries following removed node can stay
- // in list, but all preceding ones need to be
- // cloned.
- VariableTableEntry newFirst = e.next;
- for (VariableTableEntry p = first; p != e; p = p.next) {
- newFirst = new VariableTableEntry(p.hash, p.name, p.value, newFirst);
- }
- table[index] = newFirst;
- variableTableSize--;
- variableTable = table; // write-volatile
- return oldValue;
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Get the actual table used to save variable entries.
- */
- protected VariableTableEntry[] variableTableGetTable() {
- VariableTableEntry[] table;
- if ((table = variableTable) != null) {
- return table;
- }
- return VARIABLE_TABLE_EMPTY_TABLE;
- }
-
- /**
- * Get the size of the variable table.
- */
- protected int variableTableGetSize() {
- if (variableTable != null) {
- return variableTableSize;
- }
- return 0;
- }
-
- /**
- * Synchronize the variable table with the argument. In real terms
- * this means copy all entries into a newly allocated table.
- */
- protected void variableTableSync(List<Variable<IRubyObject>> vars) {
- synchronized(this) {
- variableTableSize = 0;
- variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
- variableTable = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
- for (Variable<IRubyObject> var : vars) {
- variableTableStore(var.getName(), var.getValue());
- }
- }
- }
-
- /**
- * Rehashes the variable table. Must be called from a synchronized
- * block.
- */
- // MUST be called from synchronized/locked block!
- // should only be called by variableTableStore/variableTableFastStore
- protected final VariableTableEntry[] variableTableRehash() {
- VariableTableEntry[] oldTable = variableTable;
- int oldCapacity;
- if ((oldCapacity = oldTable.length) >= VARIABLE_TABLE_MAXIMUM_CAPACITY) {
- return oldTable;
- }
-
- int newCapacity = oldCapacity << 1;
- VariableTableEntry[] newTable = new VariableTableEntry[newCapacity];
- variableTableThreshold = (int)(newCapacity * VARIABLE_TABLE_LOAD_FACTOR);
- int sizeMask = newCapacity - 1;
- VariableTableEntry e;
- for (int i = oldCapacity; --i >= 0; ) {
- // We need to guarantee that any existing reads of old Map can
- // proceed. So we cannot yet null out each bin.
- e = oldTable[i];
-
- if (e != null) {
- VariableTableEntry next = e.next;
- int idx = e.hash & sizeMask;
-
- // Single node on list
- if (next == null)
- newTable[idx] = e;
-
- else {
- // Reuse trailing consecutive sequence at same slot
- VariableTableEntry lastRun = e;
- int lastIdx = idx;
- for (VariableTableEntry last = next;
- last != null;
- last = last.next) {
- int k = last.hash & sizeMask;
- if (k != lastIdx) {
- lastIdx = k;
- lastRun = last;
- }
- }
- newTable[lastIdx] = lastRun;
-
- // Clone all remaining nodes
- for (VariableTableEntry p = e; p != lastRun; p = p.next) {
- int k = p.hash & sizeMask;
- VariableTableEntry m = new VariableTableEntry(p.hash, p.name, p.value, newTable[k]);
- newTable[k] = m;
- }
- }
- }
- }
- variableTable = newTable;
- return newTable;
- }
-
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- protected Map variableTableGetMap() {
- HashMap map = new HashMap();
- VariableTableEntry[] table;
- IRubyObject readValue;
- if ((table = variableTable) != null) {
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- map.put(e.name, readValue);
- }
- }
- }
- return map;
- }
-
- /**
- * Method to help ease transition to new variables implementation.
- * Will likely be deprecated in the near future.
- */
- @SuppressWarnings("unchecked")
- protected Map variableTableGetMap(Map map) {
- VariableTableEntry[] table;
- IRubyObject readValue;
- if ((table = variableTable) != null) {
- for (int i = table.length; --i >= 0; ) {
- for (VariableTableEntry e = table[i]; e != null; e = e.next) {
- if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
- map.put(e.name, readValue);
- }
- }
- }
- return map;
- }
-
- /**
- * Tries to support Java serialization of Ruby objects. This is
- * still experimental and might not work.
- */
- // NOTE: Serialization is primarily supported for testing purposes, and there is no general
- // guarantee that serialization will work correctly. Specifically, instance variables pointing
- // at symbols, threads, modules, classes, and other unserializable types are not detected.
- private void writeObject(ObjectOutputStream out) throws IOException {
- out.defaultWriteObject();
- // write out ivar count followed by name/value pairs
- List<String> names = getInstanceVariableNameList();
- out.writeInt(names.size());
- for (String name : names) {
- out.writeObject(name);
- out.writeObject(getInstanceVariables().getInstanceVariable(name));
- }
- }
-
- /**
- * Tries to support Java unserialization of Ruby objects. This is
- * still experimental and might not work.
- */
- private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
- in.defaultReadObject();
- // rest in ivar count followed by name/value pairs
- int ivarCount = in.readInt();
- for (int i = 0; i < ivarCount; i++) {
- setInstanceVariable((String)in.readObject(), (IRubyObject)in.readObject());
- }
- }
-
-}
-
-package org.jruby;
-
-import org.jruby.runtime.Block;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- *
- * @author nicksieger
- */
-public interface RubyObjectAdapter {
-
- boolean isKindOf(IRubyObject value, RubyModule rubyModule);
-
- IRubyObject setInstanceVariable(IRubyObject obj, String variableName, IRubyObject value);
-
- IRubyObject[] convertToJavaArray(IRubyObject array);
-
- RubyInteger convertToRubyInteger(IRubyObject obj);
-
- IRubyObject getInstanceVariable(IRubyObject obj, String variableName);
-
- RubyString convertToRubyString(IRubyObject obj);
-
- // These call* assume ThreadContext = receiver.getRuntime().getCurrentContext()
- IRubyObject callMethod(IRubyObject receiver, String methodName);
-
- IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject singleArg);
-
- IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject[] args);
-
- IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject[] args, Block block);
-
- IRubyObject callSuper(IRubyObject receiver, IRubyObject[] args);
-
- IRubyObject callSuper(IRubyObject receiver, IRubyObject[] args, Block block);
-}
-/***** 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 java.util.Iterator;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyModule(name="ObjectSpace")
-public class RubyObjectSpace {
-
- /** Create the ObjectSpace module and add it to the Ruby runtime.
- *
- */
- public static RubyModule createObjectSpaceModule(Ruby runtime) {
- RubyModule objectSpaceModule = runtime.defineModule("ObjectSpace");
- runtime.setObjectSpaceModule(objectSpaceModule);
-
- objectSpaceModule.defineAnnotatedMethods(RubyObjectSpace.class);
-
- return objectSpaceModule;
- }
-
- @JRubyMethod(name = "define_finalizer", required = 1, optional = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject define_finalizer(IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = recv.getRuntime();
- IRubyObject finalizer = null;
- if (args.length == 2) {
- finalizer = args[1];
- if (!finalizer.respondsTo("call")) {
- throw runtime.newArgumentError("wrong type argument "
- + finalizer.getType() + " (should be callable)");
- }
- } else {
- finalizer = runtime.newProc(Block.Type.PROC, block);
- }
- IRubyObject obj = args[0];
- runtime.getObjectSpace().addFinalizer(obj, finalizer);
- return runtime.newArray(runtime.newFixnum(runtime.getSafeLevel()), finalizer);
- }
-
- @JRubyMethod(name = "undefine_finalizer", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject undefine_finalizer(IRubyObject recv, IRubyObject arg1, Block block) {
- recv.getRuntime().getObjectSpace().removeFinalizers(RubyNumeric.fix2long(arg1.id()));
- return recv;
- }
-
- @JRubyMethod(name = "_id2ref", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject id2ref(IRubyObject recv, IRubyObject id) {
- Ruby runtime = id.getRuntime();
- if (!(id instanceof RubyFixnum)) {
- throw recv.getRuntime().newTypeError(id, recv.getRuntime().getFixnum());
- }
- RubyFixnum idFixnum = (RubyFixnum) id;
- long longId = idFixnum.getLongValue();
- if (longId == 0) {
- return runtime.getFalse();
- } else if (longId == 2) {
- return runtime.getTrue();
- } else if (longId == 4) {
- return runtime.getNil();
- } else if (longId % 2 != 0) {
- // odd
- return runtime.newFixnum((longId - 1) / 2);
- } else {
- IRubyObject object = runtime.getObjectSpace().id2ref(longId);
- if (object == null) {
- return runtime.getNil();
- }
- return object;
- }
- }
-
- @JRubyMethod(name = "each_object", optional = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject each_object(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyModule rubyClass;
- if (args.length == 0) {
- rubyClass = recv.getRuntime().getObject();
- } else {
- if (!(args[0] instanceof RubyModule)) throw recv.getRuntime().newTypeError("class or module required");
- rubyClass = (RubyModule) args[0];
- }
- Ruby runtime = recv.getRuntime();
- int count = 0;
- if (rubyClass != runtime.getClassClass()) {
- if (!runtime.isObjectSpaceEnabled()) {
- throw runtime.newRuntimeError("ObjectSpace is disabled; each_object will only work with Class, pass +O to enable");
- }
- Iterator iter = recv.getRuntime().getObjectSpace().iterator(rubyClass);
-
- IRubyObject obj = null;
- while ((obj = (IRubyObject)iter.next()) != null) {
- count++;
- block.yield(context, obj);
- }
- } else {
- Iterator iter = runtime.getObject().subclasses(true).iterator();
-
- while (iter.hasNext()) {
- count++;
- block.yield(context, (IRubyObject)iter.next());
- }
- }
- return recv.getRuntime().newFixnum(count);
- }
-
- @JRubyMethod(name = "garbage_collect", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject garbage_collect(IRubyObject recv) {
- return RubyGC.start(recv);
- }
-}
-/***** 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>
- *
- * 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.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.callback.Callback;
-
-/**
- *
- * @author jpetersen
- */
-@JRubyModule(name="Precision")
-public class RubyPrecision {
-
- public static RubyModule createPrecisionModule(Ruby runtime) {
- RubyModule precisionModule = runtime.defineModule("Precision");
- runtime.setPrecision(precisionModule);
-
- precisionModule.defineAnnotatedMethods(RubyPrecision.class);
-
- return precisionModule;
- }
-
- public static IRubyObject induced_from(IRubyObject receiver, IRubyObject source, Block block) {
- throw receiver.getRuntime().newTypeError("Undefined conversion from " + source.getMetaClass().getName() + " into " + ((RubyClass)receiver).getName());
- }
-
- @JRubyMethod(name = "append_features", required = 1, frame = true, module = true)
- public static IRubyObject append_features(IRubyObject receiver, IRubyObject include, Block block) {
- if (include instanceof RubyModule) {
- ((RubyModule) include).includeModule(receiver);
- include.getSingletonClass().defineMethod("induced_from", new Callback() {
-
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- Arity.checkArgumentCount(recv.getRuntime(), args, 1, 1);
-
- return RubyPrecision.induced_from(recv, args[0], block);
- }
-
- public Arity getArity() {
- return Arity.ONE_ARGUMENT;
- }
- });
- }
- return receiver;
- }
-
-
- @JRubyMethod(name = "prec", required = 1, frame = true)
- public static IRubyObject prec(ThreadContext context, IRubyObject receiver, IRubyObject type, Block block) {
- return type.callMethod(context, "induced_from", receiver);
- }
-
- @JRubyMethod(name = "prec_i", frame = true)
- public static IRubyObject prec_i(ThreadContext context, IRubyObject receiver, Block block) {
- return receiver.getRuntime().getInteger().callMethod(context, "induced_from", receiver);
- }
-
- @JRubyMethod(name = "prec_f", frame = true)
- public static IRubyObject prec_f(ThreadContext context, IRubyObject receiver, Block block) {
- return receiver.getRuntime().getFloat().callMethod(context, "induced_from", receiver);
- }
-}
-/***** 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>
- * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@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.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.exceptions.JumpException;
-import org.jruby.internal.runtime.JumpTarget;
-import org.jruby.java.MiniJava;
-import org.jruby.runtime.Block;
-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="Proc")
-public class RubyProc extends RubyObject implements JumpTarget {
- private Block block = Block.NULL_BLOCK;
- private Block.Type type;
- private String file;
- private int line;
-
- public RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type) {
- super(runtime, rubyClass);
-
- this.type = type;
- }
-
- private static ObjectAllocator PROC_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyProc instance = RubyProc.newProc(runtime, Block.Type.PROC);
-
- instance.setMetaClass(klass);
-
- return instance;
- }
- };
-
- public static RubyClass createProcClass(Ruby runtime) {
- RubyClass procClass = runtime.defineClass("Proc", runtime.getObject(), PROC_ALLOCATOR);
- runtime.setProc(procClass);
-
- procClass.defineAnnotatedMethods(RubyProc.class);
-
- return procClass;
- }
-
- public Block getBlock() {
- return block;
- }
-
- // Proc class
-
- public static RubyProc newProc(Ruby runtime, Block.Type type) {
- return new RubyProc(runtime, runtime.getProc(), type);
- }
- public static RubyProc newProc(Ruby runtime, Block block, Block.Type type) {
- RubyProc proc = new RubyProc(runtime, runtime.getProc(), type);
- proc.callInit(NULL_ARRAY, block);
-
- return proc;
- }
-
- /**
- * Create a new instance of a Proc object. We override this method (from RubyClass)
- * since we need to deal with special case of Proc.new with no arguments or block arg. In
- * this case, we need to check previous frame for a block to consume.
- */
- @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- // No passed in block, lets check next outer frame for one ('Proc.new')
- if (!block.isGiven()) {
- block = context.getPreviousFrame().getBlock();
- }
-
- if (block.isGiven() && block.getProcObject() != null) {
- return block.getProcObject();
- }
-
- IRubyObject obj = ((RubyClass) recv).allocate();
-
- obj.callMethod(context, "initialize", args, block);
- return obj;
- }
-
- @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(ThreadContext context, Block procBlock) {
- if (!procBlock.isGiven()) {
- throw getRuntime().newArgumentError("tried to create Proc object without a block");
- }
-
- if (type == Block.Type.LAMBDA && procBlock == null) {
- // TODO: warn "tried to create Proc object without a block"
- }
-
- block = procBlock.cloneBlock();
- block.type = type;
- block.setProcObject(this);
-
- file = context.getFile();
- line = context.getLine();
- return this;
- }
-
- @JRubyMethod(name = "clone")
- public IRubyObject rbClone() {
- RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
- newProc.block = getBlock();
- newProc.file = file;
- newProc.line = line;
- // TODO: CLONE_SETUP here
- return newProc;
- }
-
- @JRubyMethod(name = "dup")
- public IRubyObject dup() {
- RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
- newProc.block = getBlock();
- newProc.file = file;
- newProc.line = line;
- return newProc;
- }
-
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(IRubyObject other) {
- if (!(other instanceof RubyProc)) return getRuntime().getFalse();
-
- if (this == other || this.block == ((RubyProc)other).block) {
- return getRuntime().getTrue();
- }
-
- return getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s() {
- return RubyString.newString(getRuntime(),
- "#<Proc:0x" + Integer.toString(block.hashCode(), 16) + "@" +
- file + ":" + (line + 1) + ">");
- }
-
- @JRubyMethod(name = "binding")
- public IRubyObject binding() {
- return getRuntime().newBinding(block.getBinding());
- }
-
- @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
- public IRubyObject call(ThreadContext context, IRubyObject[] args) {
- return call(context, args, null);
- }
-
- public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self) {
- assert args != null;
-
- Ruby runtime = getRuntime();
- Block newBlock = block.cloneBlock();
- JumpTarget jumpTarget = newBlock.getBinding().getFrame().getJumpTarget();
-
- try {
- if (self != null) newBlock.getBinding().setSelf(self);
-
- return newBlock.call(context, args);
- } catch (JumpException.BreakJump bj) {
- switch(block.type) {
- case LAMBDA: if (bj.getTarget() == jumpTarget) {
- return (IRubyObject) bj.getValue();
- } else {
- throw runtime.newLocalJumpError("break", (IRubyObject)bj.getValue(), "unexpected break");
- }
- case PROC:
- if (newBlock.isEscaped()) {
- throw runtime.newLocalJumpError("break", (IRubyObject)bj.getValue(), "break from proc-closure");
- } else {
- throw bj;
- }
- default: throw bj;
- }
- } catch (JumpException.ReturnJump rj) {
- Object target = rj.getTarget();
-
- if (target == jumpTarget && block.type == Block.Type.LAMBDA) return (IRubyObject) rj.getValue();
-
- if (type == Block.Type.THREAD) {
- throw runtime.newThreadError("return can't jump across threads");
- }
- throw rj;
- } catch (JumpException.RetryJump rj) {
- throw runtime.newLocalJumpError("retry", (IRubyObject)rj.getValue(), "retry not supported outside rescue");
- }
- }
-
- @JRubyMethod(name = "arity")
- public RubyFixnum arity() {
- return getRuntime().newFixnum(block.arity().getValue());
- }
-
- @JRubyMethod(name = "to_proc")
- public RubyProc to_proc() {
- return this;
- }
-
- public IRubyObject as(Class asClass) {
- final Ruby ruby = getRuntime();
- if (!asClass.isInterface()) {
- throw ruby.newTypeError(asClass.getCanonicalName() + " is not an interface");
- }
-
- return MiniJava.javaToRuby(ruby, Proxy.newProxyInstance(Ruby.getClassLoader(), new Class[] {asClass}, new InvocationHandler() {
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- IRubyObject[] rubyArgs = new IRubyObject[args.length + 1];
- rubyArgs[0] = RubySymbol.newSymbol(ruby, method.getName());
- for (int i = 1; i < rubyArgs.length; i++) {
- rubyArgs[i] = MiniJava.javaToRuby(ruby, args[i - 1]);
- }
- return MiniJava.rubyToJava(call(ruby.getCurrentContext(), rubyArgs));
- }
- }));
- }
-}
-/*
- **** 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) 2004 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 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;
-
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyModule;
-import org.jruby.ext.posix.POSIX;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.BlockCallback;
-import org.jruby.runtime.CallBlock;
-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;
-
-
-/**
- */
-
-@JRubyModule(name="Process")
-public class RubyProcess {
-
- public static RubyModule createProcessModule(Ruby runtime) {
- RubyModule process = runtime.defineModule("Process");
- runtime.setProcess(process);
-
- // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
- RubyClass process_status = process.defineClassUnder("Status", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setProcStatus(process_status);
-
- RubyModule process_uid = process.defineModuleUnder("UID");
- runtime.setProcUID(process_uid);
-
- RubyModule process_gid = process.defineModuleUnder("GID");
- runtime.setProcGID(process_gid);
-
- RubyModule process_sys = process.defineModuleUnder("Sys");
- runtime.setProcSys(process_sys);
-
- process.defineAnnotatedMethods(RubyProcess.class);
- process_status.defineAnnotatedMethods(RubyStatus.class);
- process_uid.defineAnnotatedMethods(UserID.class);
- process_gid.defineAnnotatedMethods(GroupID.class);
- process_sys.defineAnnotatedMethods(Sys.class);
-
- process.defineConstant("PRIO_PROCESS", runtime.newFixnum(0));
- process.defineConstant("PRIO_PGRP", runtime.newFixnum(1));
- process.defineConstant("PRIO_USER", runtime.newFixnum(2));
-
- process.defineConstant("WNOHANG", runtime.newFixnum(1));
-
- return process;
- }
-
- @JRubyClass(name="Process::Status")
- public static class RubyStatus extends RubyObject {
- private long status = 0L;
-
- private static final long EXIT_SUCCESS = 0L;
- public RubyStatus(Ruby runtime, RubyClass metaClass, long status) {
- super(runtime, metaClass);
- this.status = status;
- }
-
- public static RubyStatus newProcessStatus(Ruby runtime, long status) {
- return new RubyStatus(runtime, runtime.getProcStatus(), status);
- }
-
- // Bunch of methods still not implemented
- @JRubyMethod(name = {"to_int", "pid", "stopped?", "stopsig", "signaled?", "termsig?", "exited?", "coredump?"})
- public IRubyObject not_implemented() {
- String error = "Process::Status#" + getRuntime().getCurrentContext().getFrameName() + " not implemented";
- throw getRuntime().newNotImplementedError(error);
- }
-
- @JRubyMethod(name = {"&"})
- public IRubyObject not_implemented1(IRubyObject arg) {
- String error = "Process::Status#" + getRuntime().getCurrentContext().getFrameName() + " not implemented";
- throw getRuntime().newNotImplementedError(error);
- }
-
- @JRubyMethod
- public IRubyObject exitstatus() {
- return getRuntime().newFixnum(status);
- }
-
- @JRubyMethod(name = ">>")
- public IRubyObject op_rshift(IRubyObject other) {
- long shiftValue = other.convertToInteger().getLongValue();
- return getRuntime().newFixnum(status >> shiftValue);
- }
-
- @JRubyMethod(name = "==")
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this.to_i());
- }
-
- @JRubyMethod
- public IRubyObject to_i() {
- return getRuntime().newFixnum(shiftedValue());
- }
-
- @JRubyMethod
- public IRubyObject to_s() {
- return getRuntime().newString(String.valueOf(shiftedValue()));
- }
-
- @JRubyMethod
- public IRubyObject inspect() {
- return getRuntime().newString("#<Process::Status: pid=????,exited(" + String.valueOf(status) + ")>");
- }
-
- @JRubyMethod(name = "success?")
- public IRubyObject success_p() {
- return getRuntime().newBoolean(status == EXIT_SUCCESS);
- }
-
- private long shiftedValue() {
- return status << 8;
- }
- }
-
- @JRubyModule(name="Process::UID")
- public static class UserID {
- @JRubyMethod(name = "change_privilege", module = true)
- public static IRubyObject change_privilege(IRubyObject self, IRubyObject arg) {
- throw self.getRuntime().newNotImplementedError("Process::UID::change_privilege not implemented yet");
- }
-
- @JRubyMethod(name = "eid", module = true)
- public static IRubyObject eid(IRubyObject self) {
- return euid(self);
- }
-
- @JRubyMethod(name = "eid=", module = true)
- public static IRubyObject eid(IRubyObject self, IRubyObject arg) {
- return euid_set(self, arg);
- }
-
- @JRubyMethod(name = "grant_privilege", module = true)
- public static IRubyObject grant_privilege(IRubyObject self, IRubyObject arg) {
- throw self.getRuntime().newNotImplementedError("Process::UID::grant_privilege not implemented yet");
- }
-
- @JRubyMethod(name = "re_exchange", module = true)
- public static IRubyObject re_exchange(ThreadContext context, IRubyObject self) {
- return switch_rb(context, self, Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "re_exchangeable?", module = true)
- public static IRubyObject re_exchangeable_p(IRubyObject self) {
- throw self.getRuntime().newNotImplementedError("Process::UID::re_exchangeable? not implemented yet");
- }
-
- @JRubyMethod(name = "rid", module = true)
- public static IRubyObject rid(IRubyObject self) {
- return uid(self);
- }
-
- @JRubyMethod(name = "sid_available?", module = true)
- public static IRubyObject sid_available_p(IRubyObject self) {
- throw self.getRuntime().newNotImplementedError("Process::UID::sid_available not implemented yet");
- }
-
- @JRubyMethod(name = "switch", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject switch_rb(ThreadContext context, IRubyObject self, Block block) {
- Ruby runtime = self.getRuntime();
- int uid = runtime.getPosix().getuid();
- int euid = runtime.getPosix().geteuid();
-
- if (block.isGiven()) {
- try {
- runtime.getPosix().seteuid(uid);
- runtime.getPosix().setuid(euid);
-
- return block.yield(context, runtime.getNil());
- } finally {
- runtime.getPosix().seteuid(euid);
- runtime.getPosix().setuid(uid);
- }
- } else {
- runtime.getPosix().seteuid(uid);
- runtime.getPosix().setuid(euid);
-
- return RubyFixnum.zero(runtime);
- }
- }
- }
-
- @JRubyModule(name="Process::GID")
- public static class GroupID {
- @JRubyMethod(name = "change_privilege", module = true)
- public static IRubyObject change_privilege(IRubyObject self, IRubyObject arg) {
- throw self.getRuntime().newNotImplementedError("Process::GID::change_privilege not implemented yet");
- }
-
- @JRubyMethod(name = "eid", module = true)
- public static IRubyObject eid(IRubyObject self) {
- return egid(self);
- }
-
- @JRubyMethod(name = "eid=", module = true)
- public static IRubyObject eid(IRubyObject self, IRubyObject arg) {
- return RubyProcess.egid_set(self, arg);
- }
-
- @JRubyMethod(name = "grant_privilege", module = true)
- public static IRubyObject grant_privilege(IRubyObject self, IRubyObject arg) {
- throw self.getRuntime().newNotImplementedError("Process::GID::grant_privilege not implemented yet");
- }
-
- @JRubyMethod(name = "re_exchange", module = true)
- public static IRubyObject re_exchange(ThreadContext context, IRubyObject self) {
- return switch_rb(context, self, Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "re_exchangeable?", module = true)
- public static IRubyObject re_exchangeable_p(IRubyObject self) {
- throw self.getRuntime().newNotImplementedError("Process::GID::re_exchangeable? not implemented yet");
- }
-
- @JRubyMethod(name = "rid", module = true)
- public static IRubyObject rid(IRubyObject self) {
- return gid(self);
- }
-
- @JRubyMethod(name = "sid_available?", module = true)
- public static IRubyObject sid_available_p(IRubyObject self) {
- throw self.getRuntime().newNotImplementedError("Process::GID::sid_available not implemented yet");
- }
-
- @JRubyMethod(name = "switch", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject switch_rb(ThreadContext context, IRubyObject self, Block block) {
- Ruby runtime = self.getRuntime();
- int gid = runtime.getPosix().getgid();
- int egid = runtime.getPosix().getegid();
-
- if (block.isGiven()) {
- try {
- runtime.getPosix().setegid(gid);
- runtime.getPosix().setgid(egid);
-
- return block.yield(context, runtime.getNil());
- } finally {
- runtime.getPosix().setegid(egid);
- runtime.getPosix().setgid(gid);
- }
- } else {
- runtime.getPosix().setegid(gid);
- runtime.getPosix().setgid(egid);
-
- return RubyFixnum.zero(runtime);
- }
- }
- }
-
- @JRubyModule(name="Process::Sys")
- public static class Sys {
- @JRubyMethod(name = "getegid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getegid(IRubyObject self) {
- return egid(self);
- }
-
- @JRubyMethod(name = "geteuid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject geteuid(IRubyObject self) {
- return euid(self);
- }
-
- @JRubyMethod(name = "getgid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getgid(IRubyObject self) {
- return gid(self);
- }
-
- @JRubyMethod(name = "getuid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getuid(IRubyObject self) {
- return uid(self);
- }
-
- @JRubyMethod(name = "setegid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setegid(IRubyObject recv, IRubyObject arg) {
- return egid_set(recv, arg);
- }
-
- @JRubyMethod(name = "seteuid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject seteuid(IRubyObject recv, IRubyObject arg) {
- return euid_set(recv, arg);
- }
-
- @JRubyMethod(name = "setgid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setgid(IRubyObject recv, IRubyObject arg) {
- return gid_set(recv, arg);
- }
-
- @JRubyMethod(name = "setuid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setuid(IRubyObject recv, IRubyObject arg) {
- return uid_set(recv, arg);
- }
- }
-
- @JRubyMethod(name = "abort", optional = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject abort(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- return RubyKernel.abort(context, recv, args);
- }
-
- @JRubyMethod(name = "exit!", optional = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject exit_bang(IRubyObject recv, IRubyObject[] args) {
- return RubyKernel.exit_bang(recv, args);
- }
-
- @JRubyMethod(name = "groups", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject groups(IRubyObject recv) {
- throw recv.getRuntime().newNotImplementedError("Process#groups not yet implemented");
- }
-
- @JRubyMethod(name = "setrlimit", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setrlimit(IRubyObject recv, IRubyObject[] args) {
- throw recv.getRuntime().newNotImplementedError("Process#setrlimit not yet implemented");
- }
-
- @JRubyMethod(name = "getpgrp", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getpgrp(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpgrp());
- }
-
- @JRubyMethod(name = "groups=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject groups_set(IRubyObject recv, IRubyObject arg) {
- throw recv.getRuntime().newNotImplementedError("Process#groups not yet implemented");
- }
-
- @JRubyMethod(name = "waitpid", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject waitpid(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- int pid = -1;
- int flags = 0;
- if (args.length > 0) {
- pid = (int)args[0].convertToInteger().getLongValue();
- }
- if (args.length > 1) {
- flags = (int)args[1].convertToInteger().getLongValue();
- }
-
- int[] status = new int[1];
- pid = runtime.getPosix().waitpid(pid, status, flags);
-
- if (pid == -1) {
- throw runtime.newErrnoECHILDError();
- }
-
- runtime.getGlobalVariables().set(
- "$?",
- RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
- return runtime.newFixnum(pid);
- }
-
- @JRubyMethod(name = "wait", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject wait(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
-
- if (args.length > 0) {
- return waitpid(recv, args);
- }
-
- int[] status = new int[1];
- int pid = runtime.getPosix().wait(status);
-
- if (pid == -1) {
- throw runtime.newErrnoECHILDError();
- }
-
- runtime.getGlobalVariables().set(
- "$?",
- RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
- return runtime.newFixnum(pid);
- }
-
- @JRubyMethod(name = "waitall", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject waitall(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- POSIX posix = runtime.getPosix();
- RubyArray results = recv.getRuntime().newArray();
-
- int[] status = new int[1];
- int result = posix.wait(status);
- while (result != -1) {
- results.append(runtime.newArray(runtime.newFixnum(result), RubyProcess.RubyStatus.newProcessStatus(runtime, status[0])));
- result = posix.wait(status);
- }
-
- return results;
- }
-
- @JRubyMethod(name = "setsid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setsid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setsid());
- }
-
- @JRubyMethod(name = "setpgrp", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setpgrp(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setpgid(0, 0));
- }
-
- @JRubyMethod(name = "egid=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject egid_set(IRubyObject recv, IRubyObject arg) {
- recv.getRuntime().getPosix().setegid((int)arg.convertToInteger().getLongValue());
- return RubyFixnum.zero(recv.getRuntime());
- }
-
- @JRubyMethod(name = "euid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject euid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().geteuid());
- }
-
- @JRubyMethod(name = "uid=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject uid_set(IRubyObject recv, IRubyObject arg) {
- recv.getRuntime().getPosix().setuid((int)arg.convertToInteger().getLongValue());
- return RubyFixnum.zero(recv.getRuntime());
- }
-
- @JRubyMethod(name = "gid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject gid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getgid());
- }
-
- @JRubyMethod(name = "maxgroups", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject maxgroups(IRubyObject recv) {
- throw recv.getRuntime().newNotImplementedError("Process#maxgroups not yet implemented");
- }
-
- @JRubyMethod(name = "getpriority", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getpriority(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
- int which = (int)arg1.convertToInteger().getLongValue();
- int who = (int)arg2.convertToInteger().getLongValue();
- int result = recv.getRuntime().getPosix().getpriority(which, who);
-
- return recv.getRuntime().newFixnum(result);
- }
-
- @JRubyMethod(name = "uid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject uid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getuid());
- }
-
- @JRubyMethod(name = "waitpid2", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject waitpid2(IRubyObject recv, IRubyObject[] args) {
- Ruby runtime = recv.getRuntime();
- int pid = -1;
- int flags = 0;
- if (args.length > 0) {
- pid = (int)args[0].convertToInteger().getLongValue();
- }
- if (args.length > 1) {
- flags = (int)args[1].convertToInteger().getLongValue();
- }
-
- int[] status = new int[1];
- pid = runtime.getPosix().waitpid(pid, status, flags);
-
- if (pid == -1) {
- throw runtime.newErrnoECHILDError();
- }
-
- return runtime.newArray(runtime.newFixnum(pid), RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
- }
-
- @JRubyMethod(name = "initgroups", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject initgroups(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
- throw recv.getRuntime().newNotImplementedError("Process#initgroups not yet implemented");
- }
-
- @JRubyMethod(name = "maxgroups=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject maxgroups_set(IRubyObject recv, IRubyObject arg) {
- throw recv.getRuntime().newNotImplementedError("Process#maxgroups_set not yet implemented");
- }
-
- @JRubyMethod(name = "ppid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject ppid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getppid());
- }
-
- @JRubyMethod(name = "gid=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject gid_set(IRubyObject recv, IRubyObject arg) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setgid((int)arg.convertToInteger().getLongValue()));
- }
-
- @JRubyMethod(name = "wait2", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject wait2(IRubyObject recv, IRubyObject[] args) {
- return waitpid2(recv, args);
- }
-
- @JRubyMethod(name = "euid=", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject euid_set(IRubyObject recv, IRubyObject arg) {
- recv.getRuntime().getPosix().seteuid((int)arg.convertToInteger().getLongValue());
- return RubyFixnum.zero(recv.getRuntime());
- }
-
- @JRubyMethod(name = "setpriority", required = 3, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setpriority(IRubyObject recv, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
- int which = (int)arg1.convertToInteger().getLongValue();
- int who = (int)arg2.convertToInteger().getLongValue();
- int prio = (int)arg3.convertToInteger().getLongValue();
- int result = recv.getRuntime().getPosix().setpriority(which, who, prio);
-
- return recv.getRuntime().newFixnum(result);
- }
-
- @JRubyMethod(name = "setpgid", required = 2, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject setpgid(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
- int pid = (int)arg1.convertToInteger().getLongValue();
- int gid = (int)arg2.convertToInteger().getLongValue();
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setpgid(pid, gid));
- }
-
- @JRubyMethod(name = "getpgid", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getpgid(IRubyObject recv, IRubyObject arg) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpgid((int)arg.convertToInteger().getLongValue()));
- }
-
- @JRubyMethod(name = "getrlimit", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject getrlimit(IRubyObject recv, IRubyObject arg) {
- throw recv.getRuntime().newNotImplementedError("Process#getrlimit not yet implemented");
- }
-
- @JRubyMethod(name = "egid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject egid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getegid());
- }
-
- private static String[] signals = new String[] {"EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP",
- "ABRT", "POLL", "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP",
- "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "XCPU", "XFSZ", "VTALRM", "PROF", "USR1", "USR2"};
-
- private static int parseSignalString(Ruby runtime, String value) {
- int startIndex = 0;
- boolean negative = value.startsWith("-");
-
- if (negative) startIndex++;
-
- boolean signalString = value.startsWith("SIG", startIndex);
-
- if (signalString) startIndex += 3;
-
- String signalName = value.substring(startIndex);
-
- // FIXME: This table will get moved into POSIX library so we can get all actual supported
- // signals. This is a quick fix to support basic signals until that happens.
- for (int i = 0; i < signals.length; i++) {
- if (signals[i].equals(signalName)) return negative ? -i : i;
- }
-
- throw runtime.newArgumentError("unsupported name `SIG" + signalName + "'");
- }
-
- @JRubyMethod(name = "kill", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject kill(IRubyObject recv, IRubyObject[] args) {
- if (args.length < 2) {
- throw recv.getRuntime().newArgumentError("wrong number of arguments -- kill(sig, pid...)");
- }
-
- Ruby runtime = recv.getRuntime();
- int signal;
- if (args[0] instanceof RubyFixnum) {
- signal = (int) ((RubyFixnum) args[0]).getLongValue();
- } else if (args[0] instanceof RubySymbol) {
- signal = parseSignalString(runtime, args[0].toString());
- } else if (args[0] instanceof RubyString) {
- signal = parseSignalString(runtime, args[0].toString());
- } else {
- signal = parseSignalString(runtime, args[0].checkStringType().toString());
- }
-
- boolean processGroupKill = signal < 0;
-
- if (processGroupKill) signal = -signal;
-
- POSIX posix = runtime.getPosix();
- for (int i = 1; i < args.length; i++) {
- int pid = RubyNumeric.num2int(args[i]);
-
- // FIXME: It may be possible to killpg on systems which support it. POSIX library
- // needs to tell whether a particular method works or not
- if (pid == 0) pid = runtime.getPosix().getpid();
- posix.kill(processGroupKill ? -pid : pid, signal);
- }
-
- return runtime.newFixnum(args.length - 1);
-
- }
-
- @JRubyMethod(name = "detach", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject detach(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- final int pid = (int)arg.convertToInteger().getLongValue();
- Ruby runtime = recv.getRuntime();
-
- BlockCallback callback = new BlockCallback() {
- public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
- int[] status = new int[1];
- int result = context.getRuntime().getPosix().waitpid(pid, status, 0);
-
- return context.getRuntime().newFixnum(result);
- }
- };
-
- return RubyThread.newInstance(
- runtime.getThread(),
- IRubyObject.NULL_ARRAY,
- CallBlock.newCallClosure(recv, (RubyModule)recv, Arity.NO_ARGUMENTS, callback, context));
- }
-
- @JRubyMethod(name = "times", frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject times(IRubyObject recv, Block unusedBlock) {
- Ruby runtime = recv.getRuntime();
- double currentTime = System.currentTimeMillis() / 1000.0;
- double startTime = runtime.getStartTime() / 1000.0;
- RubyFloat zero = runtime.newFloat(0.0);
- return RubyStruct.newStruct(runtime.getTmsStruct(),
- new IRubyObject[] { runtime.newFloat(currentTime - startTime), zero, zero, zero },
- Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "pid", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject pid(IRubyObject recv) {
- return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpid());
- }
-
- @JRubyMethod(name = "fork", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject fork(ThreadContext context, IRubyObject recv, Block block) {
- return RubyKernel.fork(context, recv, block);
- }
-
- @JRubyMethod(name = "exit", optional = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject exit(IRubyObject recv, IRubyObject[] args) {
- return RubyKernel.exit(recv, args);
- }
-}
-/***** 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 Ed Sinjiashvili <slorcim@users.sourceforge.net>
- * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
- * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2002-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>
- *
- * 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.List;
-
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.BlockCallback;
-import org.jruby.runtime.CallBlock;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ObjectMarshal;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.builtin.Variable;
-import org.jruby.runtime.component.VariableEntry;
-import org.jruby.runtime.marshal.MarshalStream;
-import org.jruby.runtime.marshal.UnmarshalStream;
-
-/**
- * @author jpetersen
- */
-@JRubyClass(name="Range", include="Enumerable")
-public class RubyRange extends RubyObject {
- private IRubyObject begin;
- private IRubyObject end;
- private boolean isExclusive;
-
- public static RubyClass createRangeClass(Ruby runtime) {
- RubyClass result = runtime.defineClass("Range", runtime.getObject(), RANGE_ALLOCATOR);
- runtime.setRange(result);
- result.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyRange;
- }
- };
-
- result.setMarshal(RANGE_MARSHAL);
- result.includeModule(runtime.getEnumerable());
-
- // We override Enumerable#member? since ranges in 1.8.1 are continuous.
- // result.defineMethod("member?", callbackFactory.getMethod("include_p", RubyKernel.IRUBY_OBJECT));
- // result.defineMethod("===", callbackFactory.getMethod("include_p", RubyKernel.IRUBY_OBJECT));
-
- result.defineAnnotatedMethods(RubyRange.class);
- return result;
- }
-
- private static final ObjectAllocator RANGE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyRange(runtime, klass);
- }
- };
-
- private RubyRange(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- begin = end = runtime.getNil();
- }
-
- public static RubyRange newRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end, boolean isExclusive) {
- RubyRange range = new RubyRange(runtime, runtime.getRange());
- range.init(context, begin, end, isExclusive);
- return range;
- }
-
- public static RubyRange newExclusiveRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end) {
- RubyRange range = new RubyRange(runtime, runtime.getRange());
- range.init(context, begin, end, true);
- return range;
- }
-
- public static RubyRange newInclusiveRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end) {
- RubyRange range = new RubyRange(runtime, runtime.getRange());
- range.init(context, begin, end, false);
- return range;
- }
-
- protected void copySpecialInstanceVariables(IRubyObject clone) {
- RubyRange range = (RubyRange)clone;
- range.begin = begin;
- range.end = end;
- range.isExclusive = isExclusive;
- }
-
- final long[] begLen(long len, int err){
- long beg = RubyNumeric.num2long(this.begin);
- long end = RubyNumeric.num2long(this.end);
-
- if (beg < 0) {
- beg += len;
- if (beg < 0) {
- if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
- return null;
- }
- }
-
- if (err == 0 || err == 2) {
- if (beg > len) {
- if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
- return null;
- }
- if (end > len) end = len;
- }
-
- if (end < 0) end += len;
- if (!isExclusive) end++;
- len = end - beg;
- if (len < 0) len = 0;
-
- return new long[]{beg, len};
- }
-
- private void init(ThreadContext context, IRubyObject begin, IRubyObject end, boolean isExclusive) {
- if (!(begin instanceof RubyFixnum && end instanceof RubyFixnum)) {
- try {
- IRubyObject result = begin.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", end);
- if (result.isNil()) throw getRuntime().newArgumentError("bad value for range");
- } catch (RaiseException re) {
- throw getRuntime().newArgumentError("bad value for range");
- }
- }
-
- this.begin = begin;
- this.end = end;
- this.isExclusive = isExclusive;
- }
-
- @JRubyMethod(name = "initialize", required = 2, optional = 1, frame = true)
- public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block unusedBlock) {
- init(context, args[0], args[1], args.length > 2 && args[2].isTrue());
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = {"first", "begin"})
- public IRubyObject first() {
- return begin;
- }
-
- @JRubyMethod(name = {"last", "end"})
- public IRubyObject last() {
- return end;
- }
-
- @JRubyMethod(name = "hash")
- public RubyFixnum hash(ThreadContext context) {
- long hash = isExclusive ? 1 : 0;
- long h = hash;
-
- long v = begin.callMethod(context, MethodIndex.HASH, "hash").convertToInteger().getLongValue();
- hash ^= v << 1;
- v = end.callMethod(context, MethodIndex.HASH, "hash").convertToInteger().getLongValue();
- hash ^= v << 9;
- hash ^= h << 24;
- return getRuntime().newFixnum(hash);
- }
-
- private static byte[] DOTDOTDOT = "...".getBytes();
- private static byte[] DOTDOT = "..".getBytes();
-
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect(ThreadContext context) {
- RubyString str = inspect(context, begin).strDup(context.getRuntime());
- RubyString str2 = inspect(context, end);
-
- str.cat(isExclusive ? DOTDOTDOT : DOTDOT);
- str.concat(str2);
- str.infectBy(str2);
- return str;
- }
-
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s(ThreadContext context) {
- RubyString str = RubyString.objAsString(context, begin).strDup(context.getRuntime());
- RubyString str2 = RubyString.objAsString(context, end);
-
- str.cat(isExclusive ? DOTDOTDOT : DOTDOT);
- str.concat(str2);
- str.infectBy(str2);
- return str;
-
- }
-
- @JRubyMethod(name = "exclude_end?")
- public RubyBoolean exclude_end_p() {
- return getRuntime().newBoolean(isExclusive);
- }
-
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if (this == other) return getRuntime().getTrue();
- if (!(other instanceof RubyRange)) return getRuntime().getFalse();
- RubyRange otherRange = (RubyRange) other;
-
- if (equalInternal(context, begin, otherRange.begin) &&
- equalInternal(context, end, otherRange.end) &&
- isExclusive == otherRange.isExclusive) return getRuntime().getTrue();
-
- return getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "eql?", required = 1)
- public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
- if (this == other) return getRuntime().getTrue();
- if (!(other instanceof RubyRange)) return getRuntime().getFalse();
- RubyRange otherRange = (RubyRange)other;
-
- if (eqlInternal(context, begin, otherRange.begin) &&
- eqlInternal(context, end, otherRange.end) &&
- isExclusive == otherRange.isExclusive) return getRuntime().getTrue();
-
- return getRuntime().getFalse();
- }
-
- private static abstract class RangeCallBack {
- abstract void call(ThreadContext context, IRubyObject arg);
- }
-
- private static final class StepBlockCallBack extends RangeCallBack implements BlockCallback {
- final Block block;
- IRubyObject iter;
- final IRubyObject step;
-
- StepBlockCallBack(Block block, IRubyObject iter, IRubyObject step) {
- this.block = block;
- this.iter = iter;
- this.step = step;
- }
-
- public IRubyObject call(ThreadContext context, IRubyObject[] args, Block originalBlock) {
- call(context, args[0]);
- return context.getRuntime().getNil();
- }
-
- void call(ThreadContext context, IRubyObject arg) {
- if (iter instanceof RubyFixnum) {
- iter = RubyFixnum.newFixnum(context.getRuntime(), ((RubyFixnum)iter).getLongValue() - 1);
- } else {
- iter = iter.callMethod(context, MethodIndex.OP_MINUS, "-", RubyFixnum.one(context.getRuntime()));
- }
- if (iter == RubyFixnum.zero(context.getRuntime())) {
- block.yield(context, arg);
- iter = step;
- }
- }
- }
-
- private IRubyObject rangeLt(ThreadContext context, IRubyObject a, IRubyObject b) {
- IRubyObject result = a.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", b);
- if (result.isNil()) return null;
- return RubyComparable.cmpint(context, result, a, b) < 0 ? getRuntime().getTrue() : null;
- }
-
- private IRubyObject rangeLe(ThreadContext context, IRubyObject a, IRubyObject b) {
- IRubyObject result = a.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", b);
- if (result.isNil()) return null;
- int c = RubyComparable.cmpint(context, result, a, b);
- if (c == 0) return RubyFixnum.zero(getRuntime());
- return c < 0 ? getRuntime().getTrue() : null;
- }
-
- private void rangeEach(ThreadContext context, RangeCallBack callback) {
- IRubyObject v = begin;
- if (isExclusive) {
- while (rangeLt(context, v, end) != null) {
- callback.call(context, v);
- v = v.callMethod(context, "succ");
- }
- } else {
- IRubyObject c;
- while ((c = rangeLe(context, v, end)) != null && c.isTrue()) {
- callback.call(context, v);
- if (c == RubyFixnum.zero(getRuntime())) break;
- v = v.callMethod(context, "succ");
- }
- }
- }
-
- @JRubyMethod(name = "each", frame = true)
- public IRubyObject each(ThreadContext context, final Block block) {
- final Ruby runtime = context.getRuntime();
-
- if (begin instanceof RubyFixnum && end instanceof RubyFixnum) {
- long lim = ((RubyFixnum) end).getLongValue();
- if (!isExclusive) lim++;
-
- for (long i = ((RubyFixnum) begin).getLongValue(); i < lim; i++) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- } else if (begin instanceof RubyString) {
- ((RubyString) begin).upto(context, end, isExclusive, block);
- } else {
- if (!begin.respondsTo("succ")) throw getRuntime().newTypeError(
- "can't iterate from " + begin.getMetaClass().getName());
- rangeEach(context, new RangeCallBack() {
- @Override
- void call(ThreadContext context, IRubyObject arg) {
- block.yield(context, arg);
- }
- });
- }
- return this;
- }
-
- @JRubyMethod(name = "step", optional = 1, frame = true)
- public IRubyObject step(ThreadContext context, IRubyObject[] args, Block block) {
- final Ruby runtime = context.getRuntime();
- final IRubyObject step;
- if (args.length == 0) {
- step = RubyFixnum.one(runtime);
- } else {
- step = args[0];
- }
-
- long unit = RubyNumeric.num2long(step);
- if (unit < 0) throw runtime.newArgumentError("step can't be negative");
-
- if (begin instanceof RubyFixnum && end instanceof RubyFixnum) {
- if (unit == 0) throw runtime.newArgumentError("step can't be 0");
-
- long e = ((RubyFixnum)end).getLongValue();
- if (!isExclusive) e++;
-
- for (long i = ((RubyFixnum)begin).getLongValue(); i < e; i += unit) {
- block.yield(context, RubyFixnum.newFixnum(runtime, i));
- }
- } else {
- IRubyObject tmp = begin.checkStringType();
- if (!tmp.isNil()) {
- if (unit == 0) throw runtime.newArgumentError("step can't be 0");
- // rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
- StepBlockCallBack callback = new StepBlockCallBack(block, RubyFixnum.one(runtime), step);
- Block blockCallback = CallBlock.newCallClosure(this, runtime.getRange(), Arity.singleArgument(), callback, context);
- ((RubyString)tmp).upto(context, end, isExclusive, blockCallback);
- } else if (begin instanceof RubyNumeric) {
- if (equalInternal(context, step, RubyFixnum.zero(runtime))) {
- throw runtime.newArgumentError("step can't be 0");
- }
- final String method;
- final int methodIndex;
- if (isExclusive) {
- method = "<";
- methodIndex = MethodIndex.OP_LT;
- } else {
- method = "<=";
- methodIndex = MethodIndex.OP_LE;
- }
- IRubyObject beg = begin;
- while (beg.callMethod(context, methodIndex, method, end).isTrue()) {
- block.yield(context, beg);
- beg = beg.callMethod(context, MethodIndex.OP_PLUS, "+", step);
- }
- } else {
- if (unit == 0) throw runtime.newArgumentError("step can't be 0");
- if (!begin.respondsTo("succ")) throw runtime.newTypeError(
- "can't iterate from " + begin.getMetaClass().getName());
- // range_each_func(range, step_i, b, e, args);
- rangeEach(context, new StepBlockCallBack(block, RubyFixnum.one(runtime), step));
- }
- }
- return this;
- }
-
- @JRubyMethod(name = {"include?", "member?", "==="}, required = 1)
- public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
- if (rangeLe(context, begin, obj) != null) {
- if (isExclusive) {
- if (rangeLt(context, obj, end) != null) return context.getRuntime().getTrue();
- } else {
- if (rangeLe(context, obj, end) != null) return context.getRuntime().getTrue();
- }
- }
- return context.getRuntime().getFalse();
- }
-
- private static final ObjectMarshal RANGE_MARSHAL = new ObjectMarshal() {
- public void marshalTo(Ruby runtime, Object obj, RubyClass type,
- MarshalStream marshalStream) throws IOException {
- RubyRange range = (RubyRange)obj;
-
- marshalStream.registerLinkTarget(range);
- List<Variable<IRubyObject>> attrs = range.getVariableList();
-
- attrs.add(new VariableEntry<IRubyObject>("begin", range.begin));
- attrs.add(new VariableEntry<IRubyObject>("end", range.end));
- attrs.add(new VariableEntry<IRubyObject>("excl", range.isExclusive ? runtime.getTrue() : runtime.getFalse()));
-
- marshalStream.dumpVariables(attrs);
- }
-
- public Object unmarshalFrom(Ruby runtime, RubyClass type,
- UnmarshalStream unmarshalStream) throws IOException {
- RubyRange range = (RubyRange)type.allocate();
-
- unmarshalStream.registerLinkTarget(range);
-
- unmarshalStream.defaultVariablesUnmarshal(range);
-
- range.begin = range.removeInternalVariable("begin");
- range.end = range.removeInternalVariable("end");
- range.isExclusive = range.removeInternalVariable("excl").isTrue();
-
- return range;
- }
- };
-}
-/***** 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_add;
-import static org.jruby.util.Numeric.f_cmp;
-import static org.jruby.util.Numeric.f_div;
-import static org.jruby.util.Numeric.f_equal_p;
-import static org.jruby.util.Numeric.f_expt;
-import static org.jruby.util.Numeric.f_floor;
-import static org.jruby.util.Numeric.f_gcd;
-import static org.jruby.util.Numeric.f_idiv;
-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_one_p;
-import static org.jruby.util.Numeric.f_rshift;
-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_truncate;
-import static org.jruby.util.Numeric.f_xor;
-import static org.jruby.util.Numeric.f_zero_p;
-import static org.jruby.util.Numeric.i_gcd;
-import static org.jruby.util.Numeric.i_ilog2;
-import static org.jruby.util.Numeric.ldexp;
-
-import org.joni.encoding.specific.ASCIIEncoding;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.common.IRubyWarnings;
-import org.jruby.javasupport.util.RuntimeHelpers;
-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 rational.c as of revision: 18876
- */
-
-@JRubyClass(name = "Rational", parent = "Numeric", include = "Precision")
-public class RubyRational extends RubyNumeric {
-
- public static RubyClass createRationalClass(Ruby runtime) {
- RubyClass rationalc = runtime.defineClass("Rational", runtime.getNumeric(), RATIONAL_ALLOCATOR); // because one can Complex.send(:allocate)
- runtime.setRational(rationalc);
-
- rationalc.index = ClassIndex.RATIONAL;
- rationalc.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyRational;
- }
- };
-
- ThreadContext context = runtime.getCurrentContext();
- rationalc.callMethod(context, "private_class_method", runtime.newSymbol("allocate"));
-
- rationalc.defineAnnotatedMethods(RubyRational.class);
-
- return rationalc;
- }
-
- private static ObjectAllocator RATIONAL_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyRational(runtime, klass, RubyFixnum.zero(runtime), RubyFixnum.one(runtime));
- }
- };
-
- /** internal
- *
- */
- private RubyRational(Ruby runtime, IRubyObject clazz, IRubyObject num, IRubyObject den) {
- super(runtime, (RubyClass)clazz);
- this.num = num;
- this.den = den;
- }
-
- /** rb_rational_raw
- *
- */
- static RubyRational newRationalRaw(Ruby runtime, IRubyObject x, RubyObject y) {
- return new RubyRational(runtime, runtime.getRational(), x, y);
- }
-
- /** rb_rational_raw1
- *
- */
- static RubyRational newRationalRaw(Ruby runtime, IRubyObject x) {
- return new RubyRational(runtime, runtime.getRational(), x, RubyFixnum.one(runtime));
- }
-
- /** rb_rational_new1
- *
- */
- static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x) {
- return newRationalCanonicalize(context, x, RubyFixnum.one(context.getRuntime()));
- }
-
- /** rb_rational_new
- *
- */
- private static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
- return canonicalizeInternal(context, context.getRuntime().getRational(), x, y);
- }
-
- /** f_rational_new2
- *
- */
- private static IRubyObject newRational(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert x instanceof RubyRational && y instanceof RubyRational;
- return canonicalizeInternal(context, clazz, x, y);
- }
-
- /** f_rational_new1
- *
- */
- private static IRubyObject newRational(ThreadContext context, IRubyObject clazz, IRubyObject x) {
- assert x instanceof RubyRational;
- return canonicalizeInternal(context, clazz, x, RubyFixnum.one(context.getRuntime()));
- }
-
- /** f_rational_new_no_reduce2
- *
- */
- private static IRubyObject newRationalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert x instanceof RubyRational && y instanceof RubyRational;
- return canonicalizeInternalNoReduce(context, clazz, x, y);
- }
-
- /** f_rational_new_no_reduce1
- *
- */
- private static IRubyObject newRationalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject x) {
- assert x instanceof RubyRational;
- return canonicalizeInternalNoReduce(context, clazz, x, RubyFixnum.one(context.getRuntime()));
- }
-
- /** f_rational_new_bang2
- *
- */
- private static RubyRational newRationalBang(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
- assert !f_negative_p(context, y) && !(f_zero_p(context, y));
- return new RubyRational(context.getRuntime(), clazz, x, y);
- }
-
- /** f_rational_new_bang1
- *
- */
- private static RubyRational newRationalBang(ThreadContext context, IRubyObject clazz, IRubyObject x) {
- return newRationalBang(context, clazz, x, RubyFixnum.one(context.getRuntime()));
- }
-
- private IRubyObject num;
- private IRubyObject den;
-
- /** nurat_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 num) {
- return newInstanceBang(context, recv, num, RubyFixnum.one(context.getRuntime()));
- }
-
- @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
- public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject num, IRubyObject den) {
- if (!(num instanceof RubyInteger)) num = f_to_i(context, num);
- if (!(den instanceof RubyInteger)) den = f_to_i(context, den);
-
- Ruby runtime = context.getRuntime();
- IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
- if (res == RubyFixnum.minus_one(runtime)) {
- num = f_negate(context, num);
- den = f_negate(context, den);
- } else if (res == RubyFixnum.zero(runtime)) {
- throw runtime.newZeroDivisionError();
- }
-
- return new RubyRational(runtime, recv, num, den);
- }
-
- /** nurat_int_check
- *
- */
- private static void intCheck(IRubyObject num) {
- if (!(num instanceof RubyFixnum ) && !(num instanceof RubyBignum)) throw num.getRuntime().newArgumentError("not an integer");
- }
-
- /** nurat_s_canonicalize_internal
- *
- */
- private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
- Ruby runtime = context.getRuntime();
- IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
- if (res == RubyFixnum.minus_one(runtime)) {
- num = f_negate(context, num);
- den = f_negate(context, den);
- } else if (res == RubyFixnum.zero(runtime)) {
- throw runtime.newZeroDivisionError();
- }
-
- IRubyObject gcd = f_gcd(context, num, den);
- num = f_idiv(context, num, gcd);
- den = f_idiv(context, den, gcd);
-
- if (f_one_p(context, den) && ((RubyModule)clazz).fastHasConstant("Unify")) return num;
- return new RubyRational(context.getRuntime(), clazz, num, den);
- }
-
- /** nurat_s_canonicalize_internal_no_reduce
- *
- */
- private static IRubyObject canonicalizeInternalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
- Ruby runtime = context.getRuntime();
- IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
- if (res == RubyFixnum.minus_one(runtime)) {
- num = f_negate(context, num);
- den = f_negate(context, den);
- } else if (res == RubyFixnum.zero(runtime)) {
- throw runtime.newZeroDivisionError();
- }
-
- if (f_equal_p(context, den, RubyFixnum.one(runtime)) && ((RubyModule)clazz).fastHasConstant("Unify")) return num;
- return new RubyRational(context.getRuntime(), clazz, num, den);
- }
-
- /** nurat_s_new
- *
- */
- @Deprecated
- public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject[]args) {
- switch (args.length) {
- case 1: return newInstance(context, clazz, args[0]);
- case 2: return newInstance(context, clazz, args[0], args[1]);
- }
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
- return null;
- }
-
- @JRubyMethod(name = "new", meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject num) {
- intCheck(num);
- return canonicalizeInternal(context, clazz, num, RubyFixnum.one(context.getRuntime()));
- }
-
- @JRubyMethod(name = "new", meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
- intCheck(num);
- intCheck(den);
- return canonicalizeInternal(context, clazz, num, den);
- }
-
- /** rb_Rational1
- *
- */
- public static IRubyObject newRationalConvert(ThreadContext context, IRubyObject x) {
- return newRationalConvert(context, x, RubyFixnum.one(context.getRuntime()));
- }
-
- /** rb_Rational/rb_Rational2
- *
- */
- public static IRubyObject newRationalConvert(ThreadContext context, IRubyObject x, IRubyObject y) {
- return convert(context, context.getRuntime().getRational(), 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;
- }
-
- /** nurat_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);
- }
-
- /** nurat_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());
- }
-
- /** nurat_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) {
- if (a1 instanceof RubyComplex) {
- RubyComplex a1Complex = (RubyComplex)a1;
- if (a1Complex.getImage() instanceof RubyFloat || !f_zero_p(context, a1Complex.getImage())) {
- IRubyObject s = f_to_s(context, a1);
- throw context.getRuntime().newArgumentError("can't accept " + s.convertToString());
- }
- a1 = a1Complex.getReal();
- }
- if (a2 instanceof RubyComplex) {
- RubyComplex a2Complex = (RubyComplex)a2;
- if (a2Complex.getImage() instanceof RubyFloat || !f_zero_p(context, a2Complex.getImage())) {
- IRubyObject s = f_to_s(context, a2);
- throw context.getRuntime().newArgumentError("can't accept " + s.convertToString());
- }
- a2 = a2Complex.getReal();
- }
-
- Frame frame = context.getCurrentFrame();
- IRubyObject backref = frame.getBackRef();
- if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
-
- if (a1 instanceof RubyFloat) {
- a1 = f_to_r(context, a1);
- } else if (a1 instanceof RubyString) {
- a1 = str_to_r_strict(context, a1);
- }
-
- if (a2 instanceof RubyFloat) {
- a2 = f_to_r(context, a2);
- } else if (a2 instanceof RubyString) {
- a2 = str_to_r_strict(context, a2);
- }
-
- frame.setBackRef(backref);
-
- if (a1 instanceof RubyRational) {
- if (a2.isNil() || f_zero_p(context, a2)) return a1;
- return f_div(context, a1, a2);
- }
-
- if (a2 instanceof RubyRational) {
- return f_div(context, a1, a2);
- }
-
- return a2.isNil() ? newInstance(context, recv, a1) : newInstance(context, recv, a1, a2);
- }
-
- /** nurat_s_induced_from
- *
- */
- @JRubyMethod(name = "induced_from", meta = true)
- public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- return f_to_r(context, arg);
- }
-
- /** nurat_numerator
- *
- */
- @JRubyMethod(name = "numerator")
- public IRubyObject numerator(ThreadContext context) {
- return num;
- }
-
- /** nurat_denominator
- *
- */
- @JRubyMethod(name = "denominator")
- public IRubyObject denominator(ThreadContext context) {
- return den;
- }
-
- /** f_imul
- *
- */
- private static IRubyObject f_imul(ThreadContext context, long a, long b) {
- Ruby runtime = context.getRuntime();
- if (a == 0 || b == 0) {
- return RubyFixnum.zero(runtime);
- } else if (a == 1) {
- return RubyFixnum.newFixnum(runtime, b);
- } else if (b == 1) {
- return RubyFixnum.newFixnum(runtime, a);
- }
-
- long c = a * b;
- if(c / a != b) {
- return RubyBignum.newBignum(runtime, a).op_mul(context, RubyBignum.newBignum(runtime, b));
- }
- return RubyFixnum.newFixnum(runtime, c);
- }
-
- /** f_addsub
- *
- */
- private IRubyObject f_addsub(ThreadContext context, IRubyObject anum, IRubyObject aden, IRubyObject bnum, IRubyObject bden, boolean plus) {
- Ruby runtime = context.getRuntime();
- IRubyObject num, den, g, a, b;
- if (anum instanceof RubyFixnum && aden instanceof RubyFixnum &&
- bnum instanceof RubyFixnum && bden instanceof RubyFixnum) {
- long an = ((RubyFixnum)anum).getLongValue();
- long ad = ((RubyFixnum)aden).getLongValue();
- long bn = ((RubyFixnum)bnum).getLongValue();
- long bd = ((RubyFixnum)bden).getLongValue();
- long ig = i_gcd(ad, bd);
-
- g = RubyFixnum.newFixnum(runtime, ig);
- a = f_imul(context, an, bd / ig);
- b = f_imul(context, bn, ad / ig);
- } else {
- g = f_gcd(context, aden, bden);
- a = f_mul(context, anum, f_idiv(context, bden, g));
- b = f_mul(context, bnum, f_idiv(context, aden, g));
- }
-
- IRubyObject c = plus ? f_add(context, a, b) : f_sub(context, a, b);
-
- b = f_idiv(context, aden, g);
- g = f_gcd(context, c, g);
- num = f_idiv(context, c, g);
- a = f_idiv(context, bden, g);
- den = f_mul(context, a, b);
-
- return RubyRational.newRationalNoReduce(context, getMetaClass(), num, den);
- }
-
- /** nurat_add
- *
- */
- @JRubyMethod(name = "+")
- public IRubyObject op_add(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- return f_addsub(context, num, den, other, RubyFixnum.one(context.getRuntime()), true);
- case ClassIndex.FLOAT:
- return f_add(context, f_to_f(context, this), other);
- case ClassIndex.RATIONAL:
- RubyRational otherRational = (RubyRational)other;
- return f_addsub(context, num, den, otherRational.num, otherRational.den, true);
- }
- return coerceBin(context, "+", other);
- }
-
- /** nurat_sub
- *
- */
- @JRubyMethod(name = "-")
- public IRubyObject op_sub(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- return f_addsub(context, num, den, other, RubyFixnum.one(context.getRuntime()), false);
- case ClassIndex.FLOAT:
- return f_sub(context, f_to_f(context, this), other);
- case ClassIndex.RATIONAL:
- RubyRational otherRational = (RubyRational)other;
- return f_addsub(context, num, den, otherRational.num, otherRational.den, false);
- }
- return coerceBin(context, "-", other);
- }
-
- /** f_muldiv
- *
- */
- private IRubyObject f_muldiv(ThreadContext context, IRubyObject anum, IRubyObject aden, IRubyObject bnum, IRubyObject bden, boolean mult) {
- if (!mult) {
- if (f_negative_p(context, bnum)) {
- anum = f_negate(context, anum);
- bnum = f_negate(context, bnum);
- }
- IRubyObject tmp = bnum;
- bnum = bden;
- bden = tmp;
- }
-
- final IRubyObject num, den;
- if (anum instanceof RubyFixnum && aden instanceof RubyFixnum &&
- bnum instanceof RubyFixnum && bden instanceof RubyFixnum) {
- long an = ((RubyFixnum)anum).getLongValue();
- long ad = ((RubyFixnum)aden).getLongValue();
- long bn = ((RubyFixnum)bnum).getLongValue();
- long bd = ((RubyFixnum)bden).getLongValue();
- long g1 = i_gcd(an, bd);
- long g2 = i_gcd(ad, bn);
-
- num = f_imul(context, an / g1, bn / g2);
- den = f_imul(context, ad / g2, bd / g1);
- } else {
- IRubyObject g1 = f_gcd(context, anum, bden);
- IRubyObject g2 = f_gcd(context, aden, bnum);
-
- num = f_mul(context, f_idiv(context, anum, g1), f_idiv(context, bnum, g2));
- den = f_mul(context, f_idiv(context, aden, g2), f_idiv(context, bden, g1));
- }
- return RubyRational.newRationalNoReduce(context, getMetaClass(), num, den);
-
- }
-
- /** nurat_mul
- *
- */
- @JRubyMethod(name = "*")
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- return f_muldiv(context, num, den, other, RubyFixnum.one(context.getRuntime()), true);
- case ClassIndex.FLOAT:
- return f_mul(context, f_to_f(context, this), other);
- case ClassIndex.RATIONAL:
- RubyRational otherRational = (RubyRational)other;
- return f_muldiv(context, num, den, otherRational.num, otherRational.den, true);
- }
- return coerceBin(context, "*", other);
- }
-
- /** nurat_div
- *
- */
- @JRubyMethod(name = "/")
- public IRubyObject op_div(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- if (f_zero_p(context, other)) throw context.getRuntime().newZeroDivisionError();
- return f_muldiv(context, num, den, other, RubyFixnum.one(context.getRuntime()), false);
- case ClassIndex.FLOAT:
- return f_to_f(context, this).callMethod(context, "/", other);
- case ClassIndex.RATIONAL:
- if (f_zero_p(context, other)) throw context.getRuntime().newZeroDivisionError();
- RubyRational otherRational = (RubyRational)other;
- return f_muldiv(context, num, den, otherRational.num, otherRational.den, false);
- }
- return coerceBin(context, "/", other);
- }
-
- /** nurat_fdiv
- *
- */
- @JRubyMethod(name = "fdiv")
- public IRubyObject op_fdiv(ThreadContext context, IRubyObject other) {
- return f_div(context, f_to_f(context, this), other);
- }
-
- /** nurat_expt
- *
- */
- @JRubyMethod(name = "**")
- public IRubyObject op_expt(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
-
- if (f_zero_p(context, other)) {
- return RubyRational.newRationalBang(context, getMetaClass(), RubyFixnum.one(runtime));
- }
-
- if (other instanceof RubyRational) {
- RubyRational otherRational = (RubyRational)other;
- if (f_one_p(context, otherRational.den)) other = otherRational.num;
- }
-
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- final IRubyObject tnum, tden;
- IRubyObject res = f_cmp(context, other, RubyFixnum.zero(runtime));
- if (res == RubyFixnum.one(runtime)) {
- tnum = f_expt(context, num, other);
- tden = f_expt(context, den, other);
- } else if (res == RubyFixnum.minus_one(runtime)){
- tnum = f_expt(context, den, f_negate(context, other));
- tden = f_expt(context, num, f_negate(context, other));
- } else {
- tnum = tden = RubyFixnum.one(runtime);
- }
- return RubyRational.newRational(context, getMetaClass(), tnum, tden);
- case ClassIndex.FLOAT:
- case ClassIndex.RATIONAL:
- return f_expt(context, f_to_f(context, this), other);
- }
- return coerceBin(context, "**", other);
- }
-
-
- /** nurat_cmp
- *
- */
- @JRubyMethod(name = "<=>")
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- if (den instanceof RubyFixnum && ((RubyFixnum)den).getLongValue() == 1) return f_cmp(context, num, other);
- return f_cmp(context, this, RubyRational.newRationalBang(context, getMetaClass(), other));
-
- case ClassIndex.FLOAT:
- return f_cmp(context, f_to_f(context, this), other);
-
- case ClassIndex.RATIONAL:
- RubyRational otherRational = (RubyRational)other;
- final IRubyObject num1, num2;
- if (num instanceof RubyFixnum && den instanceof RubyFixnum &&
- otherRational.num instanceof RubyFixnum && otherRational.den instanceof RubyFixnum) {
- num1 = f_imul(context, ((RubyFixnum)num).getLongValue(), ((RubyFixnum)otherRational.den).getLongValue());
- num2 = f_imul(context, ((RubyFixnum)otherRational.num).getLongValue(), ((RubyFixnum)den).getLongValue());
- } else {
- num1 = f_mul(context, num, otherRational.den);
- num2 = f_mul(context, otherRational.num, den);
- }
- return f_cmp(context, f_sub(context, num1, num2), RubyFixnum.zero(context.getRuntime()));
- }
- return coerceBin(context, "<=>", other);
- }
-
- /** nurat_equal_p
- *
- */
- @JRubyMethod(name = "==")
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- if (f_zero_p(context, num) && f_zero_p(context, den)) return runtime.getTrue();
- if (!(den instanceof RubyFixnum) || ((RubyFixnum)den).getLongValue() != 1) return runtime.getFalse();
- if (f_equal_p(context, num, other)) return runtime.getTrue();
- return runtime.getFalse();
-
- case ClassIndex.FLOAT:
- return f_equal_p(context, f_to_f(context, this), other) ? runtime.getTrue() : runtime.getFalse();
-
- case ClassIndex.RATIONAL:
- RubyRational otherRational = (RubyRational)other;
- if (f_zero_p(context, num) && f_zero_p(context, otherRational.num)) return runtime.getTrue();
- if (f_equal_p(context, num, otherRational.num) && f_equal_p(context, den, otherRational.den)) return runtime.getTrue();
- return runtime.getFalse();
- }
- return f_equal_p(context, other, this) ? runtime.getTrue() : runtime.getFalse();
- }
-
- /** nurat_coerce
- *
- */
- @JRubyMethod(name = "coerce")
- public IRubyObject op_coerce(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
- switch (other.getMetaClass().index) {
- case ClassIndex.FIXNUM:
- case ClassIndex.BIGNUM:
- return runtime.newArray(RubyRational.newRationalBang(context, getMetaClass(), other), this);
- case ClassIndex.FLOAT:
- return runtime.newArray(other, f_to_f(context, this));
- }
- throw runtime.newTypeError(other.getMetaClass() + " can't be coerced into " + getMetaClass());
- }
-
- /** nurat_idiv
- *
- */
- @JRubyMethod(name = "div")
- public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
- return f_floor(context, f_div(context, this, other));
- }
-
- /** nurat_mod
- *
- */
- @JRubyMethod(name = {"modulo", "%"})
- public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
- IRubyObject val = f_floor(context, f_div(context, this, other));
- return f_sub(context, this, f_mul(context, other, val));
- }
-
- /** nurat_divmod
- *
- */
- @JRubyMethod(name = "divmod")
- public IRubyObject op_divmod(ThreadContext context, IRubyObject other) {
- IRubyObject val = f_floor(context, f_div(context, this, other));
- return context.getRuntime().newArray(val, f_sub(context, this, f_mul(context, other, val)));
- }
-
- /** nurat_rem
- *
- */
- @JRubyMethod(name = "remainder")
- public IRubyObject op_rem(ThreadContext context, IRubyObject other) {
- IRubyObject val = f_truncate(context, f_div(context, this, other));
- return f_sub(context, this, f_mul(context, other, val));
- }
-
- /** nurat_abs
- *
- */
- @JRubyMethod(name = "abs")
- public IRubyObject op_abs(ThreadContext context) {
- if (!f_negative_p(context, this)) return this;
- return f_negate(context, this);
- }
-
- /** nurat_floor
- *
- */
- @JRubyMethod(name = "floor")
- public IRubyObject op_floor(ThreadContext context) {
- return f_idiv(context, num, den);
- }
-
- /** nurat_ceil
- *
- */
- @JRubyMethod(name = "ceil")
- public IRubyObject op_ceil(ThreadContext context) {
- return f_negate(context, f_idiv(context, f_negate(context, num), den));
- }
-
- /** nurat_truncate
- *
- */
- @JRubyMethod(name = {"truncate", "to_i"})
- public IRubyObject op_truncate(ThreadContext context) {
- if (f_negative_p(context, num)) {
- return f_negate(context, f_idiv(context, f_negate(context, num), den));
- }
- return f_idiv(context, num, den);
- }
-
- /** nurat_round
- *
- */
- @JRubyMethod(name = "round")
- public IRubyObject op_round(ThreadContext context) {
- IRubyObject two = RubyFixnum.two(context.getRuntime());
- if (f_negative_p(context, num)) {
- IRubyObject tnum = f_negate(context, num);
- tnum = f_add(context, f_mul(context, tnum, two), den);
- IRubyObject tden = f_mul(context, den, two);
- return f_negate(context, f_idiv(context, tnum, tden));
- } else {
- IRubyObject tnum = f_add(context, f_mul(context, num, two), den);
- IRubyObject tden = f_mul(context, den, two);
- return f_idiv(context, tnum, tden);
- }
- }
-
- /** nurat_to_f
- *
- */
- private static long ML = (long)(Math.log(Double.MAX_VALUE) / Math.log(2.0) - 1);
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- if (f_zero_p(context, num)) return runtime.newFloat(0);
-
- IRubyObject num = this.num;
- IRubyObject den = this.den;
-
- boolean minus = false;
- if (f_negative_p(context, num)) {
- num = f_negate(context, num);
- minus = true;
- }
-
- long nl = i_ilog2(context, num);
- long dl = i_ilog2(context, den);
-
- long ne = 0;
- if (nl > ML) {
- ne = nl - ML;
- num = f_rshift(context, num, RubyFixnum.newFixnum(runtime, ne));
- }
-
- long de = 0;
- if (dl > ML) {
- de = dl - ML;
- den = f_rshift(context, den, RubyFixnum.newFixnum(runtime, de));
- }
-
- long e = ne - de;
-
- if (e > 1023 || e < -1022) {
- runtime.getWarnings().warn(IRubyWarnings.ID.FLOAT_OUT_OF_RANGE, "out of Float range", getMetaClass());
- return runtime.newFloat(e > 0 ? Double.MAX_VALUE : 0);
- }
-
- double f = RubyNumeric.num2dbl(num) / RubyNumeric.num2dbl(den);
-
- if (minus) {
- f = -f;
- f = ldexp(f, e);
- }
-
- if (Double.isInfinite(f) || Double.isNaN(f)) {
- runtime.getWarnings().warn(IRubyWarnings.ID.FLOAT_OUT_OF_RANGE, "out of Float range", getMetaClass());
- }
-
- return runtime.newFloat(f);
- }
-
- /** nurat_to_r
- *
- */
- @JRubyMethod(name = "to_r")
- public IRubyObject to_r(ThreadContext context) {
- return this;
- }
-
- /** nurat_to_r
- *
- */
- @JRubyMethod(name = "hash")
- public IRubyObject hash(ThreadContext context) {
- return f_xor(context, num, den);
- }
-
- /** nurat_to_s
- *
- */
- @JRubyMethod(name = "to_s")
- public IRubyObject to_s(ThreadContext context) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getKernel(), "format", context.getRuntime().newString("%d/%d"), num, den);
- }
-
- /** nurat_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- public IRubyObject inspect(ThreadContext context) {
- return RuntimeHelpers.invoke(context, context.getRuntime().getKernel(), "format", context.getRuntime().newString("(%d/%d)"), num, den);
- }
-
- /** nurat_marshal_dump
- *
- */
- @JRubyMethod(name = "marshal_dump")
- public IRubyObject marshal_dump(ThreadContext context) {
- return context.getRuntime().newArray(num, den);
- }
-
- /** nurat_marshal_load
- *
- */
- @JRubyMethod(name = "marshal_load")
- public IRubyObject marshal_load(ThreadContext context, IRubyObject arg) {
- RubyArray a = arg.convertToArray();
- num = a.size() > 0 ? a.eltInternal(0) : context.getRuntime().getNil();
- den = a.size() > 1 ? a.eltInternal(1) : context.getRuntime().getNil();
-
- if (f_zero_p(context, den)) throw context.getRuntime().newZeroDivisionError();
- return this;
- }
-
- /** rb_gcd
- *
- */
- @JRubyMethod(name = "gcd")
- public IRubyObject gcd(ThreadContext context, IRubyObject other) {
- intCheck(other);
- return f_gcd(context, this, other);
- }
-
- /** rb_lcm
- *
- */
- @JRubyMethod(name = "lcm")
- public IRubyObject lcm(ThreadContext context, IRubyObject other) {
- intCheck(other);
- return Numeric.f_lcm(context, this, other);
- }
-
- /** rb_gcdlcm
- *
- */
- @JRubyMethod(name = "gcdlcm")
- public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
- intCheck(other);
- return context.getRuntime().newArray(f_gcd(context, this, other), Numeric.f_lcm(context, this, other));
- }
-
-
- static RubyArray str_to_r_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 m = RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.rat_pat).callMethod(context, "match", s);
-
- if (!m.isNil()) {
- IRubyObject si = m.callMethod(context, "[]", RubyFixnum.one(runtime));
- IRubyObject nu = m.callMethod(context, "[]", RubyFixnum.two(runtime));
- IRubyObject de = m.callMethod(context, "[]", RubyFixnum.three(runtime));
- IRubyObject re = m.callMethod(context, "post_match");
-
- RubyArray a = nu.callMethod(context, "split", RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.an_e_pat)).convertToArray();
- IRubyObject ifp = a.eltInternal(0);
- IRubyObject exp = a.size() != 2 ? runtime.getNil() : a.eltInternal(1);
-
- a = ifp.callMethod(context, "split", RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.a_dot_pat)).convertToArray();
- IRubyObject ip = a.eltInternal(0);
- IRubyObject fp = a.size() != 2 ? runtime.getNil() : a.eltInternal(1);
-
- IRubyObject v = RubyRational.newRationalCanonicalize(context, f_to_i(context, ip));
-
- if (!fp.isNil()) {
- bytes = fp.convertToString().getByteList();
- int count = 0;
- byte[]buf = bytes.bytes;
- int i = bytes.begin;
- int end = i + bytes.realSize;
- while (i < end) if (ASCIIEncoding.INSTANCE.isDigit(buf[i++])) count++;
- IRubyObject l = f_expt(context, RubyFixnum.newFixnum(runtime, 10), RubyFixnum.newFixnum(runtime, count));
- v = f_mul(context, v, l);
- v = f_add(context, v, f_to_i(context, fp));
- v = f_div(context, v, l);
- }
- if (!exp.isNil()) {
- v = f_mul(context, v, f_expt(context, RubyFixnum.newFixnum(runtime, 10), f_to_i(context, exp)));
- }
- if (!si.isNil()) {
- bytes = si.convertToString().getByteList();
- if (bytes.length() > 0 && bytes.get(0) == '-') v = f_negate(context, v);
- }
- if (!de.isNil()) {
- v = f_div(context, v, f_to_i(context, de));
- }
- return runtime.newArray(v, re);
- }
- return runtime.newArray(runtime.getNil(), recv);
- }
-
- private static IRubyObject str_to_r_strict(ThreadContext context, IRubyObject recv) {
- RubyArray a = str_to_r_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 Rational: " + 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) 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-2005 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
- * Copyright (C) 2006 Nick Sieger <nicksieger@gmail.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@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.lang.ref.SoftReference;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.joni.Matcher;
-import org.joni.NameEntry;
-import org.joni.Option;
-import org.joni.Regex;
-import org.joni.Region;
-import org.joni.Syntax;
-import org.joni.WarnCallback;
-import org.joni.encoding.Encoding;
-import org.joni.exception.JOniException;
-
-import static org.jruby.anno.FrameField.*;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.parser.ReOptions;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-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.runtime.marshal.MarshalStream;
-import org.jruby.runtime.marshal.UnmarshalStream;
-import org.jruby.util.ByteList;
-import org.jruby.util.KCode;
-import org.jruby.util.TypeConverter;
-
-/**
- *
- */
-@JRubyClass(name="Regexp")
-public class RubyRegexp extends RubyObject implements ReOptions, WarnCallback {
- private KCode kcode;
- private Regex pattern;
- private ByteList str;
-
- private static final int REGEXP_LITERAL_F = 1 << 11;
- private static final int REGEXP_KCODE_DEFAULT = 1 << 12;
-
- public void setLiteral() {
- flags |= REGEXP_LITERAL_F;
- }
-
- public void clearLiteral() {
- flags &= ~REGEXP_LITERAL_F;
- }
-
- public boolean isLiteral() {
- return (flags & REGEXP_LITERAL_F) != 0;
- }
-
- public void setKCodeDefault() {
- flags |= REGEXP_KCODE_DEFAULT;
- }
-
- public void clearKCodeDefault() {
- flags &= ~REGEXP_KCODE_DEFAULT;
- }
-
- public boolean isKCodeDefault() {
- return (flags & REGEXP_KCODE_DEFAULT) != 0;
- }
-
- public KCode getKCode() {
- return kcode;
- }
-
- private static Map<ByteList, Regex> getPatternCache() {
- Map<ByteList, Regex> cache = patternCache.get();
- if (cache == null) {
- cache = new ConcurrentHashMap<ByteList, Regex>(5);
- patternCache = new SoftReference<Map<ByteList, Regex>>(cache);
- }
- return cache;
- }
-
- static volatile SoftReference<Map<ByteList, Regex>> patternCache = new SoftReference<Map<ByteList, Regex>>(null);
-
- public static RubyClass createRegexpClass(Ruby runtime) {
- RubyClass regexpClass = runtime.defineClass("Regexp", runtime.getObject(), REGEXP_ALLOCATOR);
- runtime.setRegexp(regexpClass);
- regexpClass.index = ClassIndex.REGEXP;
- regexpClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyRegexp;
- }
- };
-
- regexpClass.defineConstant("IGNORECASE", runtime.newFixnum(RE_OPTION_IGNORECASE));
- regexpClass.defineConstant("EXTENDED", runtime.newFixnum(RE_OPTION_EXTENDED));
- regexpClass.defineConstant("MULTILINE", runtime.newFixnum(RE_OPTION_MULTILINE));
-
- regexpClass.defineAnnotatedMethods(RubyRegexp.class);
-
- return regexpClass;
- }
-
- private static ObjectAllocator REGEXP_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyRegexp instance = new RubyRegexp(runtime, klass);
- return instance;
- }
- };
-
- /** used by allocator
- *
- */
- private RubyRegexp(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- }
-
- /** default constructor
- *
- */
- private RubyRegexp(Ruby runtime) {
- super(runtime, runtime.getRegexp());
- }
-
- // used only by the compiler/interpreter (will set the literal flag)
- public static RubyRegexp newRegexp(Ruby runtime, String pattern, int options) {
- return newRegexp(runtime, ByteList.create(pattern), options);
- }
-
- // used only by the compiler/interpreter (will set the literal flag)
- public static RubyRegexp newRegexp(Ruby runtime, ByteList pattern, int options) {
- RubyRegexp regexp = newRegexp(runtime, pattern, options, false);
- regexp.setLiteral();
- return regexp;
- }
-
- public static RubyRegexp newRegexp(Ruby runtime, ByteList pattern, int options, boolean quote) {
- RubyRegexp regexp = new RubyRegexp(runtime);
- regexp.initialize(pattern, options, quote);
- return regexp;
- }
-
- // internal usage
- static RubyRegexp newRegexp(Ruby runtime, Regex regex) {
- RubyRegexp regexp = new RubyRegexp(runtime);
- regexp.pattern = regex;
- regexp.str = ByteList.EMPTY_BYTELIST;
- return regexp;
- }
-
- public void warn(String message) {
- getRuntime().getWarnings().warn(ID.MISCELLANEOUS, message);
- }
-
- @JRubyMethod(name = "kcode")
- public IRubyObject kcode(ThreadContext context) {
- return (!isKCodeDefault() && kcode != null) ?
- context.getRuntime().newString(kcode.name()) : context.getRuntime().getNil();
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.REGEXP;
- }
-
- public Regex getPattern() {
- return pattern;
- }
-
- private void check() {
- if (pattern == null || str == null) throw getRuntime().newTypeError("uninitialized Regexp");
- }
-
- @JRubyMethod(name = "hash")
- @Override
- public RubyFixnum hash() {
- check();
- int hashval = (int)pattern.getOptions();
- int len = this.str.realSize;
- int p = this.str.begin;
- while (len-->0) {
- hashval = hashval * 33 + str.bytes[p++];
- }
- hashval = hashval + (hashval>>5);
- return getRuntime().newFixnum(hashval);
- }
-
- @JRubyMethod(name = {"==", "eql?"}, required = 1)
- @Override
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if(this == other) return context.getRuntime().getTrue();
- if(!(other instanceof RubyRegexp)) return context.getRuntime().getFalse();
- RubyRegexp otherRegex = (RubyRegexp)other;
-
- check();
- otherRegex.check();
-
- return context.getRuntime().newBoolean(str.equal(otherRegex.str) &&
- kcode == otherRegex.kcode && pattern.getOptions() == otherRegex.pattern.getOptions());
- }
-
- @JRubyMethod(name = "~", reads = {LASTLINE, BACKREF}, writes = BACKREF)
- public IRubyObject op_match2(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- IRubyObject line = context.getCurrentFrame().getLastLine();
- if(!(line instanceof RubyString)) {
- context.getCurrentFrame().setBackRef(runtime.getNil());
- return runtime.getNil();
- }
- int start = search(context, (RubyString)line, 0, false);
- if(start < 0) {
- return runtime.getNil();
- } else {
- return runtime.newFixnum(start);
- }
- }
-
- /** rb_reg_eqq
- *
- */
- @JRubyMethod(name = "===", required = 1, writes = BACKREF)
- public IRubyObject eqq(ThreadContext context, IRubyObject str) {
- Ruby runtime = context.getRuntime();
- if(!(str instanceof RubyString)) str = str.checkStringType();
-
- if (str.isNil()) {
- context.getCurrentFrame().setBackRef(runtime.getNil());
- return runtime.getFalse();
- }
- int start = search(context, (RubyString)str, 0, false);
- return (start < 0) ? runtime.getFalse() : runtime.getTrue();
- }
-
- private static final int REGEX_QUOTED = 1;
- private void initialize(ByteList bytes, int options, boolean quote) {
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: can't modify regexp");
- checkFrozen();
- if (isLiteral()) throw getRuntime().newSecurityError("can't modify literal regexp");
-
- setKCode(options);
-
- Map<ByteList, Regex> cache = getPatternCache();
- Regex pat = cache.get(bytes);
-
- if (pat != null &&
- pat.getEncoding() == kcode.getEncoding() &&
- pat.getOptions() == (options & 0xf) &&
- ((pat.getUserOptions() & REGEX_QUOTED) != 0) == quote) { // cache hit
- pattern = pat;
- } else {
- if (quote) bytes = quote(bytes, getRuntime().getKCode());
- makeRegexp(bytes, bytes.begin, bytes.realSize, options & 0xf, kcode.getEncoding());
- if (quote) pattern.setUserOptions(REGEX_QUOTED);
- cache.put(bytes, pattern);
- }
-
- str = bytes;
- }
-
- private void makeRegexp(ByteList bytes, int start, int len, int flags, Encoding enc) {
- try {
- pattern = new Regex(bytes.bytes, start, start + len, flags, enc, Syntax.DEFAULT, this);
- } catch(Exception e) {
- rb_reg_raise(bytes.bytes, start, len, e.getMessage(), flags);
- }
- }
-
- private final void rb_reg_raise(byte[] s, int start, int len, String err,int flags) {
- throw getRuntime().newRegexpError(err + ": " + rb_reg_desc(s,start, len,flags));
- }
-
- private final StringBuilder rb_reg_desc(byte[] s, int start, int len, int flags) {
- StringBuilder sb = new StringBuilder("/");
- rb_reg_expr_str(sb, s, start, len);
- sb.append("/");
-
- if((flags & ReOptions.RE_OPTION_MULTILINE) != 0) sb.append("m");
- if((flags & ReOptions.RE_OPTION_IGNORECASE) != 0) sb.append("i");
- if((flags & ReOptions.RE_OPTION_EXTENDED) != 0) sb.append("x");
-
- if (kcode != null && !isKCodeDefault()) {
- sb.append(kcode.name().charAt(0));
- }
- return sb;
- }
-
- private final void rb_reg_expr_str(StringBuilder sb, byte[] s, int start, int len) {
- int p,pend;
- boolean need_escape = false;
- p = start;
- pend = start+len;
- Encoding enc = kcode.getEncoding();
- while(p<pend) {
- if(s[p] == '/' || (!(' ' == s[p] || (!Character.isWhitespace(s[p]) &&
- !Character.isISOControl(s[p]))) &&
- enc.length(s[p])==1)) {
- need_escape = true;
- break;
- }
- p += enc.length(s[p]);
- }
- if(!need_escape) {
- sb.append(new ByteList(s,start,len,false).toString());
- } else {
- p = 0;
- while(p < pend) {
- if(s[p] == '\\') {
- int n = enc.length(s[p+1]) + 1;
- sb.append(new ByteList(s,p,n,false).toString());
- p += n;
- continue;
- } else if(s[p] == '/') {
- sb.append("\\/");
- } else if(enc.length(s[p])!=1) {
- sb.append(new ByteList(s,p,enc.length(s[p]),false).toString());
- p += enc.length(s[p]);
- continue;
- } else if((' ' == s[p] || (!Character.isWhitespace(s[p]) &&
- !Character.isISOControl(s[p])))) {
- sb.append((char)(s[p]&0xFF));
- } else if(!Character.isWhitespace((char)(s[p]&0xFF))) {
- sb.append('\\');
- sb.append(Integer.toString((int)(s[p]&0377),8));
- } else {
- sb.append((char)(s[p]&0xFF));
- }
- p++;
- }
- }
- }
-
- /** rb_reg_init_copy
- */
- @JRubyMethod(name = "initialize_copy", required = 1)
- @Override
- public IRubyObject initialize_copy(IRubyObject re) {
- if(this == re) return this;
-
- checkFrozen();
-
- if (getMetaClass().getRealClass() != re.getMetaClass().getRealClass()) {
- throw getRuntime().newTypeError("wrong argument type");
- }
-
- RubyRegexp regexp = (RubyRegexp)re;
- regexp.check();
-
- initialize(regexp.str, regexp.getOptions(), false);
-
- return this;
- }
-
- /** rb_set_kcode
- */
- private int getKcode() {
- if(kcode == KCode.NONE) {
- return 16;
- } else if(kcode == KCode.EUC) {
- return 32;
- } else if(kcode == KCode.SJIS) {
- return 48;
- } else if(kcode == KCode.UTF8) {
- return 64;
- }
- return 0;
- }
-
- /**
- */
- private void setKCode(int options) {
- clearKCodeDefault();
- switch(options & ~0xf) {
- case 0:
- default:
- setKCodeDefault();
- kcode = getRuntime().getKCode();
- break;
- case 16:
- kcode = KCode.NONE;
- break;
- case 32:
- kcode = KCode.EUC;
- break;
- case 48:
- kcode = KCode.SJIS;
- break;
- case 64:
- kcode = KCode.UTF8;
- break;
- }
- }
-
- /** rb_reg_options
- */
- private int getOptions() {
- check();
- int options = (int)(pattern.getOptions() & (RE_OPTION_IGNORECASE|RE_OPTION_MULTILINE|RE_OPTION_EXTENDED));
- if(!isKCodeDefault()) {
- options |= getKcode();
- }
- return options;
- }
-
- /** rb_reg_initialize_m
- */
- @JRubyMethod(name = "initialize", optional = 3, visibility = Visibility.PRIVATE)
- public IRubyObject initialize_m(IRubyObject[] args) {
- ByteList bytes;
- int regexFlags = 0;
-
- if(args[0] instanceof RubyRegexp) {
- if(args.length > 1) {
- getRuntime().getWarnings().warn(ID.REGEXP_IGNORED_FLAGS, "flags" + (args.length == 3 ? " and encoding" : "") + " ignored");
- }
- RubyRegexp regexp = (RubyRegexp)args[0];
- regexp.check();
-
- regexFlags = (int)regexp.pattern.getOptions() & 0xF;
- if (!regexp.isKCodeDefault() && regexp.kcode != null && regexp.kcode != KCode.NIL) {
- if (regexp.kcode == KCode.NONE) {
- regexFlags |= 16;
- } else if (regexp.kcode == KCode.EUC) {
- regexFlags |= 32;
- } else if (regexp.kcode == KCode.SJIS) {
- regexFlags |= 48;
- } else if (regexp.kcode == KCode.UTF8) {
- regexFlags |= 64;
- }
- }
- bytes = regexp.str;
- } else {
- if (args.length >= 2) {
- if (args[1] instanceof RubyFixnum) {
- regexFlags = RubyNumeric.fix2int(args[1]);
- } else if (args[1].isTrue()) {
- regexFlags = RE_OPTION_IGNORECASE;
- }
- }
- if (args.length == 3 && !args[2].isNil()) {
- ByteList kcodeBytes = args[2].convertToString().getByteList();
- char first = kcodeBytes.length() > 0 ? kcodeBytes.charAt(0) : 0;
- regexFlags &= ~0x70;
- switch (first) {
- case 'n': case 'N':
- regexFlags |= 16;
- break;
- case 'e': case 'E':
- regexFlags |= 32;
- break;
- case 's': case 'S':
- regexFlags |= 48;
- break;
- case 'u': case 'U':
- regexFlags |= 64;
- break;
- default:
- break;
- }
- }
- bytes = args[0].convertToString().getByteList();
- }
- initialize(bytes, regexFlags, false);
-
- return this;
- }
-
- @JRubyMethod(name = {"new", "compile"}, required = 1, optional = 2, meta = true)
- public static RubyRegexp newInstance(IRubyObject recv, IRubyObject[] args) {
- RubyClass klass = (RubyClass)recv;
-
- RubyRegexp re = (RubyRegexp) klass.allocate();
- re.callInit(args, Block.NULL_BLOCK);
-
- return re;
- }
-
- @JRubyMethod(name = "options")
- public IRubyObject options() {
- return getRuntime().newFixnum(getOptions());
- }
-
- /** rb_reg_search
- */
- public int search(ThreadContext context, RubyString str, int pos, boolean reverse) {
- Ruby runtime = context.getRuntime();
- Frame frame = context.getCurrentRubyFrame();
-
- ByteList value = str.getByteList();
- if (pos > value.realSize || pos < 0) {
- frame.setBackRef(runtime.getNil());
- return -1;
- }
-
- return performSearch(reverse, pos, value, frame, runtime, context, str);
- }
-
- private int performSearch(boolean reverse, int pos, ByteList value, Frame frame, Ruby runtime, ThreadContext context, RubyString str) {
- check();
-
- int realSize = value.realSize;
- int begin = value.begin;
- int range = reverse ? -pos : realSize - pos;
-
- Matcher matcher = pattern.matcher(value.bytes, begin, begin + realSize);
-
- int result = matcher.search(begin + pos, begin + pos + range, Option.NONE);
-
- if (result < 0) {
- frame.setBackRef(runtime.getNil());
- return result;
- }
-
- updateBackRef(context, str, frame, matcher);
-
- return result;
- }
-
- final RubyMatchData updateBackRef(ThreadContext context, RubyString str, Frame frame, Matcher matcher) {
- Ruby runtime = context.getRuntime();
- IRubyObject backref = frame.getBackRef();
- final RubyMatchData match;
- if (backref == null || backref.isNil() || ((RubyMatchData)backref).used()) {
- match = new RubyMatchData(runtime);
- } else {
- match = (RubyMatchData)backref;
- match.setTaint(runtime.getSafeLevel() >= 3);
- }
-
- match.regs = matcher.getRegion(); // lazy, null when no groups defined
- match.begin = matcher.getBegin();
- match.end = matcher.getEnd();
-
- match.str = (RubyString)str.strDup(runtime).freeze(context);
- match.pattern = pattern;
-
- frame.setBackRef(match);
-
- match.infectBy(this);
- match.infectBy(str);
- return match;
- }
-
- /** rb_reg_match
- *
- */
- @JRubyMethod(name = "=~", required = 1, reads = BACKREF, writes = BACKREF)
- @Override
- public IRubyObject op_match(ThreadContext context, IRubyObject str) {
- int start;
- if(str.isNil()) {
- context.getCurrentFrame().setBackRef(context.getRuntime().getNil());
- return str;
- }
-
- start = search(context, str.convertToString(), 0, false);
-
- if (start < 0) return context.getRuntime().getNil();
-
- return RubyFixnum.newFixnum(context.getRuntime(), start);
- }
-
- /** rb_reg_match_m
- *
- */
- @JRubyMethod(name = "match", required = 1, reads = BACKREF)
- public IRubyObject match_m(ThreadContext context, IRubyObject str) {
- if (op_match(context, str).isNil()) return context.getRuntime().getNil();
-
- IRubyObject result = context.getCurrentFrame().getBackRef();
- if (result instanceof RubyMatchData) {
- ((RubyMatchData)result).use();
- }
- return result;
- }
-
-
- public RubyString regsub(RubyString str, RubyString src, Matcher matcher) {
- Region regs = matcher.getRegion();
- int mbeg = matcher.getBegin();
- int mend = matcher.getEnd();
-
- int p,s,e;
- p = s = 0;
- int no = -1;
- ByteList bs = str.getByteList();
- ByteList srcbs = src.getByteList();
- e = bs.length();
- RubyString val = null;
- Encoding enc = kcode.getEncoding();
-
- int beg, end;
- while(s < e) {
- int ss = s;
- char c = bs.charAt(s++);
- if(enc.length((byte)c) != 1) {
- s += enc.length((byte)c) - 1;
- continue;
- }
- if (c != '\\' || s == e) continue;
- if (val == null) val = RubyString.newString(getRuntime(), new ByteList(ss - p));
-
- val.cat(bs.bytes, bs.begin + p, ss - p);
- c = bs.charAt(s++);
- p = s;
-
- switch(c) {
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- no = c - '0';
- break;
- case '&':
- no = 0;
- break;
- case '`':
- beg = regs == null ? mbeg : regs.beg[0];
- val.cat(srcbs.bytes, srcbs.begin, beg);
- continue;
-
- case '\'':
- end = regs == null ? mend : regs.end[0];
- val.cat(srcbs.bytes, srcbs.begin + end, src.getByteList().realSize - end);
- continue;
-
- case '+':
- if (regs == null) {
- if (mbeg == -1) {
- no = 0;
- continue;
- }
- } else {
- no = regs.numRegs-1;
- while(regs.beg[no] == -1 && no > 0) no--;
- if (no == 0) continue;
- }
- break;
- case '\\':
- val.cat(bs.bytes, s - 1, 1);
- continue;
- default:
- val.cat(bs.bytes, s - 2, 2);
- continue;
- }
-
- if (regs != null) {
- if (no >= 0) {
- if (no >= regs.numRegs || regs.beg[no] == -1) continue;
- val.cat(srcbs.bytes, srcbs.begin + regs.beg[no], regs.end[no] - regs.beg[no]);
- }
- } else {
- if (no != 0 || mbeg == -1) continue;
- val.cat(srcbs.bytes, srcbs.begin + mbeg, mend - mbeg);
- }
- }
-
- if(p < e) {
- if(val == null) {
- val = RubyString.newString(getRuntime(), bs.makeShared(p, e-p));
- } else {
- val.cat(bs.bytes, bs.begin + p, e - p);
- }
- }
- if (val == null) return str;
-
- return val;
- }
-
- final int adjustStartPos(RubyString str, int pos, boolean reverse) {
- check();
- ByteList value = str.getByteList();
- return pattern.adjustStartPosition(value.bytes, value.begin, value.realSize, pos, reverse);
- }
-
- @JRubyMethod(name = "casefold?")
- public IRubyObject casefold_p(ThreadContext context) {
- check();
-
- return context.getRuntime().newBoolean((pattern.getOptions() & RE_OPTION_IGNORECASE) != 0);
- }
-
- /** rb_reg_source
- *
- */
- @JRubyMethod(name = "source")
- public IRubyObject source() {
- Ruby runtime = getRuntime();
- check();
- RubyString str = RubyString.newStringShared(runtime, this.str);
- if(isTaint()) {
- str.taint(runtime.getCurrentContext());
- }
- return str;
- }
-
- final int length() {
- return str.realSize;
- }
-
- /** rb_reg_inspect
- *
- */
- @JRubyMethod(name = "inspect")
- @Override
- public IRubyObject inspect() {
- check();
- return getRuntime().newString(ByteList.create(rb_reg_desc(str.bytes, str.begin, str.realSize, pattern.getOptions()).toString()));
- }
-
- private final static int EMBEDDABLE = RE_OPTION_MULTILINE|RE_OPTION_IGNORECASE|RE_OPTION_EXTENDED;
-
- @JRubyMethod(name = "to_s")
- @Override
- public IRubyObject to_s() {
- RubyString ss = getRuntime().newString("(?");
- check();
-
- int options = pattern.getOptions();
- int ptr = str.begin;
- int len = str.realSize;
- byte[] bytes = str.bytes;
- again: do {
- if (len >= 4 && bytes[ptr] == '(' && bytes[ptr + 1] == '?') {
- boolean err = true;
- ptr += 2;
- if ((len -= 2) > 0) {
- do {
- if (bytes[ptr] == 'm') {
- options |= RE_OPTION_MULTILINE;
- } else if (bytes[ptr] == 'i') {
- options |= RE_OPTION_IGNORECASE;
- } else if (bytes[ptr] == 'x') {
- options |= RE_OPTION_EXTENDED;
- } else {
- break;
- }
- ptr++;
- } while(--len > 0);
- }
- if (len > 1 && bytes[ptr] == '-') {
- ++ptr;
- --len;
- do {
- if (bytes[ptr] == 'm') {
- options &= ~RE_OPTION_MULTILINE;
- } else if (bytes[ptr] == 'i') {
- options &= ~RE_OPTION_IGNORECASE;
- } else if (bytes[ptr] == 'x') {
- options &= ~RE_OPTION_EXTENDED;
- } else {
- break;
- }
- ptr++;
- } while(--len > 0);
- }
-
- if (bytes[ptr] == ')') {
- --len;
- ++ptr;
- continue again;
- }
-
- if (bytes[ptr] == ':' && bytes[ptr + len - 1] == ')') {
- try {
- new Regex(bytes, ++ptr, ptr + (len-=2) ,Option.DEFAULT, kcode.getEncoding(), Syntax.DEFAULT);
- err = false;
- } catch (JOniException e) {
- err = true;
- }
- }
-
- if (err) {
- options = (int)pattern.getOptions();
- ptr = str.begin;
- len = str.realSize;
- }
- }
- if ((options & RE_OPTION_MULTILINE) !=0 ) ss.cat((byte)'m');
- if ((options & RE_OPTION_IGNORECASE) !=0 ) ss.cat((byte)'i');
- if ((options & RE_OPTION_EXTENDED) !=0 ) ss.cat((byte)'x');
-
- if ((options & EMBEDDABLE) != EMBEDDABLE) {
- ss.cat((byte)'-');
- if ((options & RE_OPTION_MULTILINE) == 0) ss.cat((byte)'m');
- if ((options & RE_OPTION_IGNORECASE) == 0) ss.cat((byte)'i');
- if ((options & RE_OPTION_EXTENDED) == 0) ss.cat((byte)'x');
- }
- ss.cat((byte)':');
- rb_reg_expr_str(ss, ptr, len);
- ss.cat((byte)')');
- ss.infectBy(this);
- return ss;
- } while(true);
- }
-
- private final void rb_reg_expr_str(RubyString ss, int s, int len) {
- int p = s;
- int pend = p + len;
- boolean need_escape = false;
- Encoding enc = kcode.getEncoding();
- while (p < pend) {
- if (str.bytes[p] == '/' || (!enc.isPrint(str.bytes[p] & 0xff) && enc.length(str.bytes[p]) == 1)) {
- need_escape = true;
- break;
- }
- p += enc.length(str.bytes[p]);
- }
- if (!need_escape) {
- ss.cat(str.bytes, s, len);
- } else {
- p = s;
- while (p<pend) {
- if (str.bytes[p] == '\\') {
- int n = enc.length(str.bytes[p+1]) + 1;
- ss.cat(str.bytes, p, n);
- p += n;
- continue;
- } else if (str.bytes[p] == '/') {
- ss.cat((byte)'\\');
- ss.cat(str.bytes, p, 1);
- } else if (enc.length(str.bytes[p]) != 1) {
- ss.cat(str.bytes, p, enc.length(str.bytes[p]));
- p += enc.length(str.bytes[p]);
- continue;
- } else if (enc.isPrint(str.bytes[p] & 0xff)) {
- ss.cat(str.bytes,p,1);
- } else if (!enc.isSpace(str.bytes[p] & 0xff)) {
- ss.cat(ByteList.create(Integer.toString(str.bytes[p] & 0377, 8)));
- } else {
- ss.cat(str.bytes, p, 1);
- }
- p++;
- }
- }
- }
-
- /** rb_reg_s_quote
- *
- */
- @JRubyMethod(name = {"quote", "escape"}, required = 1, optional = 1, meta = true)
- public static RubyString quote(IRubyObject recv, IRubyObject[] args) {
- IRubyObject kcode = args.length == 2 ? args[1] : null;
- IRubyObject str = args[0];
- KCode code = recv.getRuntime().getKCode();
-
- if (kcode != null && !kcode.isNil()) {
- code = KCode.create(recv.getRuntime(), kcode.toString());
- }
-
- RubyString src = str.convertToString();
- RubyString dst = RubyString.newString(recv.getRuntime(), quote(src.getByteList(), code));
- dst.infectBy(src);
- return dst;
- }
-
- /** rb_reg_quote
- *
- */
- public static ByteList quote(ByteList str, KCode kcode) {
- ByteList bs = str;
- int tix = 0;
- int s = bs.begin;
- char c;
- int send = s+bs.length();
- Encoding enc = kcode.getEncoding();
- meta_found: do {
- for(; s<send; s++) {
- c = (char)(bs.bytes[s]&0xFF);
- if(enc.length((byte)c) != 1) {
- int n = enc.length((byte)c);
- while(n-- > 0 && s < send) {
- s++;
- }
- s--;
- continue;
- }
- switch (c) {
- case '[': case ']': case '{': case '}':
- case '(': case ')': case '|': case '-':
- case '*': case '.': case '\\':
- case '?': case '+': case '^': case '$':
- case ' ': case '#':
- case '\t': case '\f': case '\n': case '\r':
- break meta_found;
- }
- }
- return bs;
- } while(false);
- ByteList b1 = new ByteList(send*2);
- System.arraycopy(bs.bytes,bs.begin,b1.bytes,b1.begin,s-bs.begin);
- tix += (s-bs.begin);
-
- for(; s<send; s++) {
- c = (char)(bs.bytes[s]&0xFF);
- if(enc.length((byte)c) != 1) {
- int n = enc.length((byte)c);
- while(n-- > 0 && s < send) {
- b1.bytes[tix++] = bs.bytes[s++];
- }
- s--;
- continue;
- }
-
- switch(c) {
- case '[': case ']': case '{': case '}':
- case '(': case ')': case '|': case '-':
- case '*': case '.': case '\\':
- case '?': case '+': case '^': case '$':
- case '#':
- b1.bytes[tix++] = '\\';
- break;
- case ' ':
- b1.bytes[tix++] = '\\';
- b1.bytes[tix++] = ' ';
- continue;
- case '\t':
- b1.bytes[tix++] = '\\';
- b1.bytes[tix++] = 't';
- continue;
- case '\n':
- b1.bytes[tix++] = '\\';
- b1.bytes[tix++] = 'n';
- continue;
- case '\r':
- b1.bytes[tix++] = '\\';
- b1.bytes[tix++] = 'r';
- continue;
- case '\f':
- b1.bytes[tix++] = '\\';
- b1.bytes[tix++] = 'f';
- continue;
- }
- b1.bytes[tix++] = (byte)c;
- }
- b1.realSize = tix;
- return b1;
- }
-
-
- /** rb_reg_nth_match
- *
- */
- public static IRubyObject nth_match(int nth, IRubyObject match) {
- if (match.isNil()) return match;
- RubyMatchData m = (RubyMatchData)match;
-
- int start, end;
-
- if (m.regs == null) {
- if (nth >= 1) return match.getRuntime().getNil();
- if (nth < 0 && ++nth <= 0) return match.getRuntime().getNil();
- start = m.begin;
- end = m.end;
- } else {
- if (nth >= m.regs.numRegs) return match.getRuntime().getNil();
- if (nth < 0 && (nth+=m.regs.numRegs) <= 0) return match.getRuntime().getNil();
- start = m.regs.beg[nth];
- end = m.regs.end[nth];
- }
-
- if (start == -1) return match.getRuntime().getNil();
-
- RubyString str = m.str.makeShared(match.getRuntime(), start, end - start);
- str.infectBy(match);
- return str;
- }
-
- /** rb_reg_last_match
- *
- */
- public static IRubyObject last_match(IRubyObject match) {
- return nth_match(0, match);
- }
-
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return last_match_s(context, recv);
- case 1:
- return last_match_s(context, recv, args[0]);
- default:
- Arity.raiseArgumentError(recv.getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
-
- /** rb_reg_s_last_match / match_getter
- *
- */
- @JRubyMethod(name = "last_match", meta = true, reads = BACKREF)
- public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv) {
- IRubyObject match = context.getCurrentFrame().getBackRef();
- if (match instanceof RubyMatchData) ((RubyMatchData)match).use();
- return match;
- }
-
- /** rb_reg_s_last_match
- *
- */
- @JRubyMethod(name = "last_match", meta = true, reads = BACKREF)
- public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv, IRubyObject nth) {
- IRubyObject match = context.getCurrentFrame().getBackRef();
- if (match.isNil()) return match;
- return nth_match(((RubyMatchData)match).backrefNumber(nth), match);
- }
-
- /** rb_reg_match_pre
- *
- */
- public static IRubyObject match_pre(IRubyObject match) {
- if (match.isNil()) return match;
- RubyMatchData m = (RubyMatchData)match;
-
- int beg = m.regs == null ? m.begin : m.regs.beg[0];
-
- if(beg == -1) match.getRuntime().getNil();
-
- RubyString str = m.str.makeShared(match.getRuntime(), 0, beg);
- str.infectBy(match);
- return str;
- }
-
- /** rb_reg_match_post
- *
- */
- public static IRubyObject match_post(IRubyObject match) {
- if (match.isNil()) return match;
- RubyMatchData m = (RubyMatchData)match;
-
- int end;
- if (m.regs == null) {
- if (m.begin == -1) return match.getRuntime().getNil();
- end = m.end;
- } else {
- if (m.regs.beg[0] == -1) return match.getRuntime().getNil();
- end = m.regs.end[0];
- }
-
- RubyString str = m.str.makeShared(match.getRuntime(), end, m.str.getByteList().realSize - end);
- str.infectBy(match);
- return str;
- }
-
- /** rb_reg_match_last
- *
- */
- public static IRubyObject match_last(IRubyObject match) {
- if (match.isNil()) return match;
- RubyMatchData m = (RubyMatchData)match;
-
- if (m.regs == null || m.regs.beg[0] == -1) return match.getRuntime().getNil();
-
- int i;
- for (i=m.regs.numRegs-1; m.regs.beg[i]==-1 && i>0; i--);
- if (i == 0) return match.getRuntime().getNil();
-
- return nth_match(i,match);
- }
-
- /** rb_reg_s_union
- *
- */
- @JRubyMethod(name = "union", rest = true, meta = true)
- public static IRubyObject union(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
- if (args.length == 0) {
- return newRegexp(recv.getRuntime(), ByteList.create("(?!)"), 0, false);
- } else if (args.length == 1) {
- IRubyObject v = TypeConverter.convertToTypeWithCheck(args[0], recv.getRuntime().getRegexp(), 0, "to_regexp");
- if(!v.isNil()) {
- return v;
- } else {
- // newInstance here
- return newRegexp(recv.getRuntime(), quote(recv,args).getByteList(), 0, false);
- }
- } else {
- KCode kcode = null;
- IRubyObject kcode_re = recv.getRuntime().getNil();
- RubyString source = recv.getRuntime().newString();
- IRubyObject[] _args = new IRubyObject[3];
-
- for (int i = 0; i < args.length; i++) {
- if (0 < i) {
- source.cat((byte)'|');
- }
- IRubyObject v = TypeConverter.convertToTypeWithCheck(args[i], recv.getRuntime().getRegexp(), 0, "to_regexp");
- if (!v.isNil()) {
- if (!((RubyRegexp)v).isKCodeDefault()) {
- if (kcode == null) {
- kcode_re = v;
- kcode = ((RubyRegexp)v).kcode;
- } else if (((RubyRegexp)v).kcode != kcode) {
- IRubyObject str1 = kcode_re.inspect();
- IRubyObject str2 = v.inspect();
- throw recv.getRuntime().newArgumentError("mixed kcode " + str1 + " and " + str2);
- }
- }
- v = ((RubyRegexp)v).to_s();
- } else {
- v = quote(recv, new IRubyObject[]{args[i]});
- }
- source.append(v);
- }
-
- _args[0] = source;
- _args[1] = recv.getRuntime().getNil();
- if (kcode == null) {
- _args[2] = recv.getRuntime().getNil();
- } else if (kcode == KCode.NONE) {
- _args[2] = recv.getRuntime().newString("n");
- } else if (kcode == KCode.EUC) {
- _args[2] = recv.getRuntime().newString("e");
- } else if (kcode == KCode.SJIS) {
- _args[2] = recv.getRuntime().newString("s");
- } else if (kcode == KCode.UTF8) {
- _args[2] = recv.getRuntime().newString("u");
- }
- return recv.callMethod(context, "new", _args);
- }
- }
-
- /** rb_reg_names
- *
- */
- @JRubyMethod(name = "names", compat = CompatVersion.RUBY1_9)
- public IRubyObject names() {
- if (pattern.numberOfNames() == 0) return getRuntime().newEmptyArray();
-
- RubyArray ary = getRuntime().newArray(pattern.numberOfNames());
- for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
- NameEntry e = i.next();
- ary.append(RubyString.newStringShared(getRuntime(), e.name, e.nameP, e.nameEnd - e.nameP));
- }
- return ary;
- }
-
- /** rb_reg_named_captures
- *
- */
- @JRubyMethod(name = "named_captures", compat = CompatVersion.RUBY1_9)
- public IRubyObject named_captures(ThreadContext context) {
- RubyHash hash = RubyHash.newHash(getRuntime());
- if (pattern.numberOfNames() == 0) return hash;
-
- for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
- NameEntry e = i.next();
- int[]backrefs = e.getBackRefs();
- RubyArray ary = getRuntime().newArray(backrefs.length);
-
- for (int backref : backrefs) ary.append(RubyFixnum.newFixnum(getRuntime(), backref));
- hash.fastASet(RubyString.newStringShared(getRuntime(), e.name, e.nameP, e.nameEnd - e.nameP).freeze(context), ary);
- }
- return hash;
- }
-
- public static RubyRegexp unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- RubyRegexp result = newRegexp(input.getRuntime(), input.unmarshalString(), input.unmarshalInt(), false);
- input.registerLinkTarget(result);
- return result;
- }
-
- public static void marshalTo(RubyRegexp regexp, MarshalStream output) throws java.io.IOException {
- output.registerLinkTarget(regexp);
- output.writeString(new String(regexp.str.bytes,regexp.str.begin,regexp.str.realSize));
- output.writeInt(regexp.pattern.getOptions() & EMBEDDABLE);
- }
-}
-
-package org.jruby;
-
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- *
- * @author nicksieger
- */
-public interface RubyRuntimeAdapter {
- IRubyObject eval(Ruby runtime, String script);
-}
-/***** 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.JRubyModule;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.CallType;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-import org.jruby.util.SignalFacade;
-import org.jruby.util.NoFunctionalitySignalFacade;
-
-@JRubyModule(name="Signal")
-public class RubySignal {
- private final static SignalFacade SIGNALS = getSignalFacade();
-
- private final static SignalFacade getSignalFacade() {
- try {
- Class realFacadeClass = Class.forName("org.jruby.util.SunSignalFacade");
- return (SignalFacade)realFacadeClass.newInstance();
- } catch(Throwable e) {
- return new NoFunctionalitySignalFacade();
- }
- }
-
- // NOTE: The indicies here match exactly the signal values; do not reorder
- public static final String[] NAMES = {
- "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT",
- "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG",
- "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU",
- "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2"};
-
- public static void createSignal(Ruby runtime) {
- RubyModule mSignal = runtime.defineModule("Signal");
-
- mSignal.defineAnnotatedMethods(RubySignal.class);
- }
-
- @JRubyMethod(name = "trap", required = 1, optional = 1, frame = true, meta = true)
- public static IRubyObject trap(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = recv.getRuntime();
- runtime.getLoadService().require("jsignal");
- return RuntimeHelpers.invoke(context, runtime.getKernel(), "__jtrap", args, block);
- }
-
- @JRubyMethod(name = "list", meta = true)
- public static IRubyObject list(ThreadContext context, IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- RubyHash names = RubyHash.newHash(runtime);
- for (int i = 0; i < NAMES.length; i++) {
- names.op_aset(context, runtime.newString(NAMES[i]), runtime.newFixnum(i));
- }
- // IOT is also 6
- names.op_aset(context, runtime.newString("IOT"), runtime.newFixnum(6));
- // CLD is also 20
- names.op_aset(context, runtime.newString("CLD"), runtime.newFixnum(20));
- return names;
- }
-
- @JRubyMethod(name = "__jtrap_kernel", required = 3,meta = true)
- public static IRubyObject __jtrap_kernel(final IRubyObject recv, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
- return SIGNALS.trap(recv, arg1, arg2, arg3);
- }
-}// RubySignal
-/*
- **** 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) 2002-2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
- * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.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 static org.jruby.anno.FrameField.BACKREF;
-import static org.jruby.anno.FrameField.LASTLINE;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Locale;
-
-import org.joni.Matcher;
-import org.joni.Option;
-import org.joni.Regex;
-import org.joni.Region;
-import org.joni.encoding.Encoding;
-import org.joni.encoding.specific.ASCIIEncoding;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.java.MiniJava;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.Frame;
-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.UnmarshalStream;
-import org.jruby.util.ByteList;
-import org.jruby.util.Numeric;
-import org.jruby.util.Pack;
-import org.jruby.util.Sprintf;
-import org.jruby.util.string.JavaCrypt;
-
-/**
- * Implementation of Ruby String class
- *
- * Concurrency: no synchronization is required among readers, but
- * all users must synchronize externally with writers.
- *
- */
-@JRubyClass(name="String", include={"Enumerable", "Comparable"})
-public class RubyString extends RubyObject {
- private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
-
- // string doesn't share any resources
- private static final int SHARE_LEVEL_NONE = 0;
- // string has it's own ByteList, but it's pointing to a shared buffer (byte[])
- private static final int SHARE_LEVEL_BUFFER = 1;
- // string doesn't have it's own ByteList (values)
- private static final int SHARE_LEVEL_BYTELIST = 2;
-
- private volatile int shareLevel = SHARE_LEVEL_NONE;
-
- private ByteList value;
-
- private static ObjectAllocator STRING_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return RubyString.newEmptyString(runtime, klass);
- }
- };
-
- public static RubyClass createStringClass(Ruby runtime) {
- RubyClass stringClass = runtime.defineClass("String", runtime.getObject(), STRING_ALLOCATOR);
- runtime.setString(stringClass);
- stringClass.index = ClassIndex.STRING;
- stringClass.kindOf = new RubyModule.KindOf() {
- @Override
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubyString;
- }
- };
-
- stringClass.includeModule(runtime.getComparable());
- stringClass.includeModule(runtime.getEnumerable());
- stringClass.defineAnnotatedMethods(RubyString.class);
-
- return stringClass;
- }
-
- /** short circuit for String key comparison
- *
- */
- @Override
- public final boolean eql(IRubyObject other) {
- if (other.getMetaClass() == getRuntime().getString()) return value.equal(((RubyString)other).value);
- return super.eql(other);
- }
-
- private RubyString(Ruby runtime, RubyClass rubyClass, CharSequence value) {
- super(runtime, rubyClass);
- assert value != null;
- this.value = new ByteList(ByteList.plain(value), false);
- }
-
- private RubyString(Ruby runtime, RubyClass rubyClass, byte[] value) {
- super(runtime, rubyClass);
- assert value != null;
- this.value = new ByteList(value);
- }
-
- private RubyString(Ruby runtime, RubyClass rubyClass, ByteList value) {
- super(runtime, rubyClass);
- assert value != null;
- this.value = value;
- }
-
- private RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, boolean objectSpace) {
- super(runtime, rubyClass, objectSpace);
- assert value != null;
- this.value = value;
- }
-
-
- /** Create a new String which uses the same Ruby runtime and the same
- * class like this String.
- *
- * This method should be used to satisfy RCR #38.
- * @deprecated
- */
- public RubyString newString(CharSequence s) {
- return new RubyString(getRuntime(), getType(), s);
- }
-
- /** Create a new String which uses the same Ruby runtime and the same
- * class like this String.
- *
- * This method should be used to satisfy RCR #38.
- * @deprecated
- */
- public RubyString newString(ByteList s) {
- return new RubyString(getRuntime(), getMetaClass(), s);
- }
-
- // Methods of the String class (rb_str_*):
-
- /** rb_str_new2
- *
- */
- public static RubyString newString(Ruby runtime, CharSequence str) {
- return new RubyString(runtime, runtime.getString(), str);
- }
-
- public static RubyString newEmptyString(Ruby runtime) {
- return newEmptyString(runtime, runtime.getString());
- }
-
- public static RubyString newEmptyString(Ruby runtime, RubyClass metaClass) {
- RubyString empty = new RubyString(runtime, metaClass, ByteList.EMPTY_BYTELIST);
- empty.shareLevel = SHARE_LEVEL_BYTELIST;
- return empty;
- }
-
- public static RubyString newUnicodeString(Ruby runtime, String str) {
- try {
- return new RubyString(runtime, runtime.getString(), new ByteList(str.getBytes("UTF8"), false));
- } catch (UnsupportedEncodingException uee) {
- return new RubyString(runtime, runtime.getString(), str);
- }
- }
-
- @Deprecated
- public static RubyString newString(Ruby runtime, RubyClass clazz, CharSequence str) {
- return new RubyString(runtime, clazz, str);
- }
-
- public static RubyString newString(Ruby runtime, byte[] bytes) {
- return new RubyString(runtime, runtime.getString(), bytes);
- }
-
- public static RubyString newString(Ruby runtime, byte[] bytes, int start, int length) {
- byte[] copy = new byte[length];
- System.arraycopy(bytes, start, copy, 0, length);
- return new RubyString(runtime, runtime.getString(), new ByteList(copy, false));
- }
-
- public static RubyString newString(Ruby runtime, ByteList bytes) {
- return new RubyString(runtime, runtime.getString(), bytes);
- }
-
- public static RubyString newStringLight(Ruby runtime, ByteList bytes) {
- return new RubyString(runtime, runtime.getString(), bytes, false);
- }
-
- public static RubyString newStringShared(Ruby runtime, RubyString orig) {
- orig.shareLevel = SHARE_LEVEL_BYTELIST;
- RubyString str = new RubyString(runtime, runtime.getString(), orig.value);
- str.shareLevel = SHARE_LEVEL_BYTELIST;
- return str;
- }
-
- public static RubyString newStringShared(Ruby runtime, ByteList bytes) {
- return newStringShared(runtime, runtime.getString(), bytes);
- }
-
- public static RubyString newStringShared(Ruby runtime, RubyClass clazz, ByteList bytes) {
- RubyString str = new RubyString(runtime, clazz, bytes);
- str.shareLevel = SHARE_LEVEL_BYTELIST;
- return str;
- }
-
- public static RubyString newStringShared(Ruby runtime, byte[] bytes, int start, int length) {
- RubyString str = new RubyString(runtime, runtime.getString(), new ByteList(bytes, start, length, false));
- str.shareLevel = SHARE_LEVEL_BUFFER;
- return str;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.STRING;
- }
-
- @Override
- public Class getJavaClass() {
- return String.class;
- }
-
- @Override
- public RubyString convertToString() {
- return this;
- }
-
- @Override
- public String toString() {
- return value.toString();
- }
-
- /** rb_str_dup
- *
- */
- @Deprecated
- public final RubyString strDup() {
- return strDup(getRuntime(), getMetaClass());
- }
-
- public final RubyString strDup(Ruby runtime) {
- return strDup(runtime, getMetaClass());
- }
-
- @Deprecated
- final RubyString strDup(RubyClass clazz) {
- return strDup(getRuntime(), getMetaClass());
- }
-
- final RubyString strDup(Ruby runtime, RubyClass clazz) {
- shareLevel = SHARE_LEVEL_BYTELIST;
- RubyString dup = new RubyString(runtime, clazz, value);
- dup.shareLevel = SHARE_LEVEL_BYTELIST;
-
- dup.infectBy(this);
- return dup;
- }
-
- public final RubyString makeShared(Ruby runtime, int index, int len) {
- if (len == 0) {
- RubyString s = newEmptyString(runtime, getMetaClass());
- s.infectBy(this);
- return s;
- }
-
- if (shareLevel == SHARE_LEVEL_NONE) shareLevel = SHARE_LEVEL_BUFFER;
- RubyString shared = new RubyString(runtime, getMetaClass(), value.makeShared(index, len));
- shared.shareLevel = SHARE_LEVEL_BUFFER;
-
- shared.infectBy(this);
- return shared;
- }
-
- final void modifyCheck() {
- if ((flags & FROZEN_F) != 0) throw getRuntime().newFrozenError("string");
-
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
- throw getRuntime().newSecurityError("Insecure: can't modify string");
- }
- }
-
- private final void modifyCheck(byte[] b, int len) {
- if (value.bytes != b || value.realSize != len) throw getRuntime().newRuntimeError("string modified");
- }
-
- private final void frozenCheck() {
- if (isFrozen()) throw getRuntime().newRuntimeError("string frozen");
- }
-
- /** rb_str_modify
- *
- */
- public final void modify() {
- modifyCheck();
-
- if (shareLevel != SHARE_LEVEL_NONE) {
- if (shareLevel == SHARE_LEVEL_BYTELIST) {
- value = value.dup();
- } else {
- value.unshare();
- }
- shareLevel = SHARE_LEVEL_NONE;
- }
-
- value.invalidate();
- }
-
- /** rb_str_modify (with length bytes ensured)
- *
- */
- public final void modify(int length) {
- modifyCheck();
-
- if (shareLevel != SHARE_LEVEL_NONE) {
- if (shareLevel == SHARE_LEVEL_BYTELIST) {
- value = value.dup(length);
- } else {
- value.unshare(length);
- }
- shareLevel = SHARE_LEVEL_NONE;
- } else {
- value.ensure(length);
- }
-
- value.invalidate();
- }
-
- private final void view(ByteList bytes) {
- modifyCheck();
-
- value = bytes;
- shareLevel = SHARE_LEVEL_NONE;
- }
-
- private final void view(byte[]bytes) {
- modifyCheck();
-
- value.replace(bytes);
- shareLevel = SHARE_LEVEL_NONE;
-
- value.invalidate();
- }
-
- private final void view(int index, int len) {
- modifyCheck();
-
- if (shareLevel != SHARE_LEVEL_NONE) {
- if (shareLevel == SHARE_LEVEL_BYTELIST) {
- // if len == 0 then shared empty
- value = value.makeShared(index, len);
- shareLevel = SHARE_LEVEL_BUFFER;
- } else {
- value.view(index, len);
- }
- } else {
- value.view(index, len);
- // FIXME this below is temporary, but its much safer for COW (it prevents not shared Strings with begin != 0)
- // this allows now e.g.: ByteList#set not to be begin aware
- shareLevel = SHARE_LEVEL_BUFFER;
- }
-
- value.invalidate();
- }
-
- public static String bytesToString(byte[] bytes, int beg, int len) {
- return new String(ByteList.plain(bytes, beg, len));
- }
-
- public static String byteListToString(ByteList bytes) {
- return bytesToString(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- }
-
- public static String bytesToString(byte[] bytes) {
- return bytesToString(bytes, 0, bytes.length);
- }
-
- public static byte[] stringToBytes(String string) {
- return ByteList.plain(string);
- }
-
- public static boolean isDigit(int c) {
- return c >= '0' && c <= '9';
- }
-
- public static boolean isUpper(int c) {
- return c >= 'A' && c <= 'Z';
- }
-
- public static boolean isLower(int c) {
- return c >= 'a' && c <= 'z';
- }
-
- public static boolean isLetter(int c) {
- return isUpper(c) || isLower(c);
- }
-
- public static boolean isAlnum(int c) {
- return isUpper(c) || isLower(c) || isDigit(c);
- }
-
- public static boolean isPrint(int c) {
- return c >= 0x20 && c <= 0x7E;
- }
-
- @Override
- public RubyString asString() {
- return this;
- }
-
- @Override
- public IRubyObject checkStringType() {
- return this;
- }
-
- @JRubyMethod(name = {"to_s", "to_str"})
- @Override
- public IRubyObject to_s() {
- Ruby runtime = getRuntime();
- if (getMetaClass().getRealClass() != runtime.getString()) {
- return strDup(runtime, runtime.getString());
- }
- return this;
- }
-
- /* rb_str_cmp_m */
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyString) {
- return context.getRuntime().newFixnum(op_cmp((RubyString)other));
- }
-
- // deal with case when "other" is not a string
- if (other.respondsTo("to_str") && other.respondsTo("<=>")) {
- IRubyObject result = other.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", this);
-
- if (result instanceof RubyNumeric) {
- return ((RubyNumeric) result).op_uminus(context);
- }
- }
-
- return context.getRuntime().getNil();
- }
-
- /**
- *
- */
- @JRubyMethod(name = "==", required = 1)
- @Override
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- Ruby runtime = context.getRuntime();
-
- if (this == other) return runtime.getTrue();
-
- if (!(other instanceof RubyString)) {
- if (!other.respondsTo("to_str")) return runtime.getFalse();
-
- return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this).isTrue() ? runtime.getTrue() : runtime.getFalse();
- }
- return value.equal(((RubyString)other).value) ? runtime.getTrue() : runtime.getFalse();
- }
-
- @JRubyMethod(name = "+", required = 1)
- public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
- RubyString str = other.convertToString();
-
- ByteList result = new ByteList(value.realSize + str.value.realSize);
- result.realSize = value.realSize + str.value.realSize;
- System.arraycopy(value.bytes, value.begin, result.bytes, 0, value.realSize);
- System.arraycopy(str.value.bytes, str.value.begin, result.bytes, value.realSize, str.value.realSize);
-
- RubyString resultStr = newString(context.getRuntime(), result);
- if (isTaint() || str.isTaint()) resultStr.setTaint(true);
- return resultStr;
- }
-
- @JRubyMethod(name = "*", required = 1)
- public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
- RubyInteger otherInteger = (RubyInteger) other.convertToInteger();
- long len = otherInteger.getLongValue();
-
- if (len < 0) throw context.getRuntime().newArgumentError("negative argument");
-
- // we limit to int because ByteBuffer can only allocate int sizes
- if (len > 0 && Integer.MAX_VALUE / len < value.length()) {
- throw context.getRuntime().newArgumentError("argument too big");
- }
- ByteList newBytes = new ByteList(value.length() * (int)len);
-
- for (int i = 0; i < len; i++) {
- newBytes.append(value);
- }
-
- RubyString newString = new RubyString(context.getRuntime(), getMetaClass(), newBytes);
- newString.setTaint(isTaint());
- return newString;
- }
-
- @JRubyMethod(name = "%", required = 1)
- public IRubyObject op_format(ThreadContext context, IRubyObject arg) {
- final RubyString s;
-
- IRubyObject tmp = arg.checkArrayType();
- if (tmp.isNil()) {
- tmp = arg;
- }
-
- // FIXME: Should we make this work with platform's locale,
- // or continue hardcoding US?
- s = Sprintf.sprintf(context.getRuntime(), Locale.US, value, tmp);
-
- s.infectBy(this);
- return s;
- }
-
- @JRubyMethod(name = "hash")
- @Override
- public RubyFixnum hash() {
- return getRuntime().newFixnum(value.hashCode());
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
- @Override
- public boolean equals(Object other) {
- if (this == other) return true;
-
- if (other instanceof RubyString) {
- RubyString string = (RubyString) other;
-
- if (string.value.equal(value)) return true;
- }
-
- return false;
- }
-
- /** rb_obj_as_string
- *
- */
- public static RubyString objAsString(ThreadContext context, IRubyObject obj) {
- if (obj instanceof RubyString) return (RubyString) obj;
-
- IRubyObject str = obj.callMethod(context, MethodIndex.TO_S, "to_s");
-
- if (!(str instanceof RubyString)) return (RubyString) obj.anyToString();
-
- if (obj.isTaint()) str.setTaint(true);
-
- return (RubyString) str;
- }
-
- /** rb_str_cmp
- *
- */
- public int op_cmp(RubyString other) {
- return value.cmp(other.value);
- }
-
- /** rb_to_id
- *
- */
- @Override
- public String asJavaString() {
- // TODO: This used to intern; but it didn't appear to change anything
- // turning that off, and it's unclear if it was needed. Plus, we intern
- //
- return toString();
- }
-
- public IRubyObject doClone(){
- return newString(getRuntime(), value.dup());
- }
-
- public RubyString cat(byte[] str) {
- modify(value.realSize + str.length);
- System.arraycopy(str, 0, value.bytes, value.begin + value.realSize, str.length);
- value.realSize += str.length;
- return this;
- }
-
- public RubyString cat(byte[] str, int beg, int len) {
- modify(value.realSize + len);
- System.arraycopy(str, beg, value.bytes, value.begin + value.realSize, len);
- value.realSize += len;
- return this;
- }
-
- public RubyString cat(ByteList str) {
- modify(value.realSize + str.realSize);
- System.arraycopy(str.bytes, str.begin, value.bytes, value.begin + value.realSize, str.realSize);
- value.realSize += str.realSize;
- return this;
- }
-
- public RubyString cat(byte ch) {
- modify(value.realSize + 1);
- value.bytes[value.begin + value.realSize] = ch;
- value.realSize++;
- return this;
- }
-
- /** rb_str_replace_m
- *
- */
- @JRubyMethod(name = {"replace", "initialize_copy"}, required = 1)
- public RubyString replace(IRubyObject other) {
- if (this == other) return this;
-
- modifyCheck();
-
- RubyString otherStr = stringValue(other);
-
- otherStr.shareLevel = shareLevel = SHARE_LEVEL_BYTELIST;
-
- value = otherStr.value;
-
- infectBy(other);
- return this;
- }
-
- @JRubyMethod(name = "reverse")
- public RubyString reverse(ThreadContext context) {
- if (value.length() <= 1) return strDup(context.getRuntime());
-
- ByteList buf = new ByteList(value.length()+2);
- buf.realSize = value.length();
- int src = value.length() - 1;
- int dst = 0;
-
- while (src >= 0) buf.set(dst++, value.get(src--));
-
- RubyString rev = new RubyString(context.getRuntime(), getMetaClass(), buf);
- rev.infectBy(this);
- return rev;
- }
-
- @JRubyMethod(name = "reverse!")
- public RubyString reverse_bang() {
- if (value.length() > 1) {
- modify();
- for (int i = 0; i < (value.length() / 2); i++) {
- byte b = (byte) value.get(i);
-
- value.set(i, value.get(value.length() - i - 1));
- value.set(value.length() - i - 1, b);
- }
- }
-
- return this;
- }
-
- /** rb_str_s_new
- *
- */
- public static RubyString newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
- RubyString newString = newStringShared(recv.getRuntime(), ByteList.EMPTY_BYTELIST);
- newString.setMetaClass((RubyClass) recv);
- newString.callInit(args, block);
- return newString;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with zero or one arguments
- */
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- switch (args.length) {
- case 0:
- return this;
- case 1:
- return initialize(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
-
- @JRubyMethod(frame = true, visibility = Visibility.PRIVATE)
- @Override
- public IRubyObject initialize() {
- return this;
- }
-
- @JRubyMethod(frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject arg0) {
- replace(arg0);
-
- return this;
- }
-
- @JRubyMethod
- public IRubyObject casecmp(IRubyObject other) {
- int compare = value.caseInsensitiveCmp(stringValue(other).value);
- return RubyFixnum.newFixnum(getRuntime(), compare);
- }
-
- /** rb_str_match
- *
- */
- @JRubyMethod(name = "=~")
- @Override
- public IRubyObject op_match(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyRegexp) return ((RubyRegexp) other).op_match(context, this);
- if (other instanceof RubyString) {
- throw context.getRuntime().newTypeError("type mismatch: String given");
- }
- return other.callMethod(context, "=~", this);
- }
-
- /** rb_str_match2
- *
- */
- @JRubyMethod(name = "~", reads = {LASTLINE, BACKREF}, writes = BACKREF)
- public IRubyObject op_match2(ThreadContext context) {
- return RubyRegexp.newRegexp(context.getRuntime(), value, 0, false).op_match2(context);
- }
-
- /**
- * String#match(pattern)
- *
- * rb_str_match_m
- *
- * @param pattern Regexp or String
- */
- @JRubyMethod
- public IRubyObject match(ThreadContext context, IRubyObject pattern) {
- return getPattern(pattern, false).callMethod(context, "match", this);
- }
-
- /** rb_str_capitalize
- *
- */
- @JRubyMethod
- public IRubyObject capitalize(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.capitalize_bang(context);
- return str;
- }
-
- /** rb_str_capitalize_bang
- *
- */
- @JRubyMethod(name = "capitalize!")
- public IRubyObject capitalize_bang(ThreadContext context) {
- if (value.realSize == 0) {
- modifyCheck();
- return context.getRuntime().getNil();
- }
-
- modify();
-
- int s = value.begin;
- int send = s + value.realSize;
- byte[]buf = value.bytes;
-
-
-
- boolean modify = false;
-
- int c = buf[s] & 0xff;
- if (ASCII.isLower(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
- modify = true;
- }
-
- while (++s < send) {
- c = (char)(buf[s] & 0xff);
- if (ASCII.isUpper(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
- modify = true;
- }
- }
-
- if (modify) return this;
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = ">=")
- public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyString) {
- return context.getRuntime().newBoolean(op_cmp((RubyString) other) >= 0);
- }
-
- return RubyComparable.op_ge(context, this, other);
- }
-
- @JRubyMethod(name = ">")
- public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyString) {
- return context.getRuntime().newBoolean(op_cmp((RubyString) other) > 0);
- }
-
- return RubyComparable.op_gt(context, this, other);
- }
-
- @JRubyMethod(name = "<=")
- public IRubyObject op_le(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyString) {
- return context.getRuntime().newBoolean(op_cmp((RubyString) other) <= 0);
- }
-
- return RubyComparable.op_le(context, this, other);
- }
-
- @JRubyMethod(name = "<")
- public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyString) {
- return context.getRuntime().newBoolean(op_cmp((RubyString) other) < 0);
- }
-
- return RubyComparable.op_lt(context, this, other);
- }
-
- @JRubyMethod(name = "eql?")
- public IRubyObject str_eql_p(ThreadContext context, IRubyObject other) {
- if (!(other instanceof RubyString)) return context.getRuntime().getFalse();
- RubyString otherString = (RubyString)other;
- return value.equal(otherString.value) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- /** rb_str_upcase
- *
- */
- @JRubyMethod
- public RubyString upcase(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.upcase_bang(context);
- return str;
- }
-
- /** rb_str_upcase_bang
- *
- */
- @JRubyMethod(name = "upcase!")
- public IRubyObject upcase_bang(ThreadContext context) {
- if (value.realSize == 0) {
- modifyCheck();
- return context.getRuntime().getNil();
- }
-
- modify();
-
- int s = value.begin;
- int send = s + value.realSize;
- byte []buf = value.bytes;
-
- boolean modify = false;
- while (s < send) {
- int c = buf[s] & 0xff;
- if (ASCII.isLower(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
- modify = true;
- }
- s++;
- }
-
- if (modify) return this;
- return context.getRuntime().getNil();
- }
-
- /** rb_str_downcase
- *
- */
- @JRubyMethod
- public RubyString downcase(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.downcase_bang(context);
- return str;
- }
-
- /** rb_str_downcase_bang
- *
- */
- @JRubyMethod(name = "downcase!")
- public IRubyObject downcase_bang(ThreadContext context) {
- if (value.realSize == 0) {
- modifyCheck();
- return context.getRuntime().getNil();
- }
-
- modify();
-
- int s = value.begin;
- int send = s + value.realSize;
- byte []buf = value.bytes;
-
- boolean modify = false;
- while (s < send) {
- int c = buf[s] & 0xff;
- if (ASCII.isUpper(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
- modify = true;
- }
- s++;
- }
-
- if (modify) return this;
- return context.getRuntime().getNil();
- }
-
- /** rb_str_swapcase
- *
- */
- @JRubyMethod
- public RubyString swapcase(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.swapcase_bang(context);
- return str;
- }
-
- /** rb_str_swapcase_bang
- *
- */
- @JRubyMethod(name = "swapcase!")
- public IRubyObject swapcase_bang(ThreadContext context) {
- if (value.realSize == 0) {
- modifyCheck();
- return context.getRuntime().getNil();
- }
-
- modify();
-
- int s = value.begin;
- int send = s + value.realSize;
- byte[]buf = value.bytes;
-
- boolean modify = false;
- while (s < send) {
- int c = buf[s] & 0xff;
- if (ASCII.isUpper(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
- modify = true;
- } else if (ASCII.isLower(c)) {
- buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
- modify = true;
- }
- s++;
- }
-
- if (modify) return this;
- return context.getRuntime().getNil();
- }
-
- /** rb_str_dump
- *
- */
- @JRubyMethod
- public IRubyObject dump() {
- RubyString s = new RubyString(getRuntime(), getMetaClass(), inspectIntoByteList(true));
- s.infectBy(this);
- return s;
- }
-
- @JRubyMethod
- public IRubyObject insert(ThreadContext context, IRubyObject indexArg, IRubyObject stringArg) {
- // MRI behavior: first check for ability to convert to String...
- RubyString s = (RubyString)stringArg.convertToString();
- ByteList insert = s.value;
-
- // ... and then the index
- int index = (int) indexArg.convertToInteger().getLongValue();
- if (index < 0) index += value.length() + 1;
-
- if (index < 0 || index > value.length()) {
- throw context.getRuntime().newIndexError("index " + index + " out of range");
- }
-
- modify();
-
- value.unsafeReplace(index, 0, insert);
- this.infectBy(s);
- return this;
- }
-
- /** rb_str_inspect
- *
- */
- @JRubyMethod
- @Override
- public IRubyObject inspect() {
- RubyString s = getRuntime().newString(inspectIntoByteList(false));
- s.infectBy(this);
- return s;
- }
-
- private ByteList inspectIntoByteList(boolean ignoreKCode) {
- Ruby runtime = getRuntime();
- Encoding enc = runtime.getKCode().getEncoding();
- final int length = value.length();
- ByteList sb = new ByteList(length + 2 + length / 100);
-
- sb.append('\"');
-
- for (int i = 0; i < length; i++) {
- int c = value.get(i) & 0xFF;
-
- if (!ignoreKCode) {
- int seqLength = enc.length((byte)c);
-
- if (seqLength > 1 && (i + seqLength -1 < length)) {
- // don't escape multi-byte characters, leave them as bytes
- sb.append(value, i, seqLength);
- i += seqLength - 1;
- continue;
- }
- }
-
- if (isAlnum(c)) {
- sb.append((char)c);
- } else if (c == '\"' || c == '\\') {
- sb.append('\\').append((char)c);
- } else if (c == '#' && isEVStr(i, length)) {
- sb.append('\\').append((char)c);
- } else if (isPrint(c)) {
- sb.append((char)c);
- } else if (c == '\n') {
- sb.append('\\').append('n');
- } else if (c == '\r') {
- sb.append('\\').append('r');
- } else if (c == '\t') {
- sb.append('\\').append('t');
- } else if (c == '\f') {
- sb.append('\\').append('f');
- } else if (c == '\u000B') {
- sb.append('\\').append('v');
- } else if (c == '\u0007') {
- sb.append('\\').append('a');
- } else if (c == '\u0008') {
- sb.append('\\').append('b');
- } else if (c == '\u001B') {
- sb.append('\\').append('e');
- } else {
- sb.append(ByteList.plain(Sprintf.sprintf(runtime,"\\%03o",c)));
- }
- }
-
- sb.append('\"');
- return sb;
- }
-
- private boolean isEVStr(int i, int length) {
- if (i+1 >= length) return false;
- int c = value.get(i+1) & 0xFF;
-
- return c == '$' || c == '@' || c == '{';
- }
-
- /** rb_str_length
- *
- */
- @JRubyMethod(name = {"length", "size"})
- public RubyFixnum length() {
- return getRuntime().newFixnum(value.length());
- }
-
- /** rb_str_empty
- *
- */
- @JRubyMethod(name = "empty?")
- public RubyBoolean empty_p(ThreadContext context) {
- return isEmpty() ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- public boolean isEmpty() {
- return value.length() == 0;
- }
-
- /** rb_str_append
- *
- */
- public RubyString append(IRubyObject other) {
- infectBy(other);
- return cat(stringValue(other).value);
- }
-
- /** rb_str_concat
- *
- */
- @JRubyMethod(name = {"concat", "<<"})
- public RubyString concat(IRubyObject other) {
- if (other instanceof RubyFixnum) {
- long value = ((RubyFixnum) other).getLongValue();
- if (value >= 0 && value < 256) return cat((byte) value);
- }
- return append(other);
- }
-
- /** rb_str_crypt
- *
- */
- @JRubyMethod(name = "crypt")
- public RubyString crypt(ThreadContext context, IRubyObject other) {
- ByteList salt = stringValue(other).getByteList();
- if (salt.realSize < 2) {
- throw context.getRuntime().newArgumentError("salt too short(need >=2 bytes)");
- }
-
- salt = salt.makeShared(0, 2);
- RubyString s = RubyString.newStringShared(context.getRuntime(), JavaCrypt.crypt(salt, this.getByteList()));
- s.infectBy(this);
- s.infectBy(other);
- return s;
- }
-
- /* RubyString aka rb_string_value */
- public static RubyString stringValue(IRubyObject object) {
- return (RubyString) (object instanceof RubyString ? object :
- object.convertToString());
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two args.
- */
- public IRubyObject sub(ThreadContext context, IRubyObject[] args, Block block) {
- RubyString str = strDup(context.getRuntime());
- str.sub_bang(context, args, block);
- return str;
- }
-
- /** rb_str_sub
- *
- */
- @JRubyMethod(name = "sub", frame = true)
- public IRubyObject sub(ThreadContext context, IRubyObject arg0, Block block) {
- RubyString str = strDup(context.getRuntime());
- str.sub_bang(context, arg0, block);
- return str;
- }
-
- /** rb_str_sub
- *
- */
- @JRubyMethod(name = "sub", frame = true)
- public IRubyObject sub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- RubyString str = strDup(context.getRuntime());
- str.sub_bang(context, arg0, arg1, block);
- return str;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two arguments.
- */
- public IRubyObject sub_bang(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 1:
- return sub_bang(context, args[0], block);
- case 2:
- return sub_bang(context, args[0], args[1], block);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_sub_bang
- *
- */
- @JRubyMethod(name = "sub!", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, Block block) {
- if (block.isGiven()) {
- RubyRegexp rubyRegex = getPattern(arg0, true);
- Regex regex = rubyRegex.getPattern();
- return subBangCommon(regex, context, true, rubyRegex, block, null, false);
- } else {
- throw context.getRuntime().newArgumentError("wrong number of arguments (1 for 2)");
- }
- }
-
- /** rb_str_sub_bang
- *
- */
- @JRubyMethod(name = "sub!", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- RubyString repl = arg1.convertToString();
- RubyRegexp rubyRegex = getPattern(arg0, true);
- Regex regex = rubyRegex.getPattern();
- return subBangCommon(regex, context, false, rubyRegex, block, repl, repl.isTaint());
- }
-
- private IRubyObject subBangCommon(Regex regex, ThreadContext context, final boolean iter, RubyRegexp rubyRegex, Block block, RubyString repl, boolean tainted) {
-
- int range = value.begin + value.realSize;
- Matcher matcher = regex.matcher(value.bytes, value.begin, range);
-
- Frame frame = context.getPreviousFrame();
- if (matcher.search(value.begin, range, Option.NONE) >= 0) {
- if (iter) {
- byte[] bytes = value.bytes;
- int size = value.realSize;
- RubyMatchData match = rubyRegex.updateBackRef(context, this, frame, matcher);
- match.use();
- if (regex.numberOfCaptures() == 0) {
- repl = objAsString(context, block.yield(context, substr(matcher.getBegin(), matcher.getEnd() - matcher.getBegin())));
- } else {
- Region region = matcher.getRegion();
- repl = objAsString(context, block.yield(context, substr(region.beg[0], region.end[0] - region.beg[0])));
- }
- modifyCheck(bytes, size);
- frozenCheck();
- frame.setBackRef(match);
- } else {
- repl = rubyRegex.regsub(repl, this, matcher);
- rubyRegex.updateBackRef(context, this, frame, matcher);
- }
-
- final int beg;
- final int plen;
- if (regex.numberOfCaptures() == 0) {
- beg = matcher.getBegin();
- plen = matcher.getEnd() - beg;
- } else {
- Region region = matcher.getRegion();
- beg = region.beg[0];
- plen = region.end[0] - beg;
- }
-
- ByteList replValue = repl.value;
- if (replValue.realSize > plen) {
- modify(value.realSize + replValue.realSize - plen);
- } else {
- modify();
- }
- if (repl.isTaint()) {
- tainted = true;
- }
- if (replValue.realSize != plen) {
- int src = value.begin + beg + plen;
- int dst = value.begin + beg + replValue.realSize;
- int length = value.realSize - beg - plen;
- System.arraycopy(value.bytes, src, value.bytes, dst, length);
- }
- System.arraycopy(replValue.bytes, replValue.begin, value.bytes, value.begin + beg, replValue.realSize);
- value.realSize += replValue.realSize - plen;
- if (tainted) {
- setTaint(true);
- }
- return this;
- } else {
- frame.setBackRef(context.getRuntime().getNil());
- return context.getRuntime().getNil();
- }
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two arguments.
- */
- public IRubyObject gsub(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 1:
- return gsub(context, args[0], block);
- case 2:
- return gsub(context, args[0], args[1], block);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_gsub
- *
- */
- @JRubyMethod(name = "gsub", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject gsub(ThreadContext context, IRubyObject arg0, Block block) {
- return gsub(context, arg0, block, false);
- }
-
- /** rb_str_gsub
- *
- */
- @JRubyMethod(name = "gsub", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject gsub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- return gsub(context, arg0, arg1, block, false);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two arguments.
- */
- public IRubyObject gsub_bang(ThreadContext context, IRubyObject[] args, Block block) {
- switch (args.length) {
- case 1:
- return gsub_bang(context, args[0], block);
- case 2:
- return gsub_bang(context, args[0], args[1], block);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_gsub_bang
- *
- */
- @JRubyMethod(name = "gsub!", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, Block block) {
- return gsub(context, arg0, block, true);
- }
-
- /** rb_str_gsub_bang
- *
- */
- @JRubyMethod(name = "gsub!", frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
- return gsub(context, arg0, arg1, block, true);
- }
-
- private final IRubyObject gsub(ThreadContext context, IRubyObject arg0, Block block, final boolean bang) {
- if (block.isGiven()) {
- RubyRegexp rubyRegex = getPattern(arg0, true);
- Regex regex = rubyRegex.getPattern();
- return gsubCommon(regex, context, bang, true, rubyRegex, block, null, false);
- } else {
- throw context.getRuntime().newArgumentError("wrong number of arguments (1 for 2)");
- }
- }
-
- private final IRubyObject gsub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block, final boolean bang) {
- IRubyObject repl = arg1.convertToString();
- RubyRegexp rubyRegex = getPattern(arg0, true);
- Regex regex = rubyRegex.getPattern();
- return gsubCommon(regex, context, bang, false, rubyRegex, block, repl, repl.isTaint());
- }
-
- private IRubyObject gsubCommon(Regex regex, ThreadContext context, final boolean bang, final boolean iter, RubyRegexp rubyRegex, Block block, IRubyObject repl, boolean tainted) {
-
- int begin = value.begin;
- int range = begin + value.realSize;
- Matcher matcher = regex.matcher(value.bytes, begin, range);
-
- int beg = matcher.search(begin, range, Option.NONE);
-
- Frame frame = context.getPreviousFrame();
-
- if (beg < 0) {
- frame.setBackRef(context.getRuntime().getNil());
- return bang ? context.getRuntime().getNil() : strDup(context.getRuntime()); /* bang: true, no match, no substitution */
- }
-
- int blen = value.realSize + 30; /* len + margin */
- ByteList dest = new ByteList(blen);
- dest.realSize = blen;
- int buf = 0;
- int bp = 0;
- int cp = value.begin;
-
- int offset = 0;
- RubyString val;
-
- RubyMatchData match = null;
- while (beg >= 0) {
- final int begz;
- final int endz;
- if (iter) {
- byte[] bytes = value.bytes;
- int size = value.realSize;
- match = rubyRegex.updateBackRef(context, this, frame, matcher);
- match.use();
- if (regex.numberOfCaptures() == 0) {
- begz = matcher.getBegin();
- endz = matcher.getEnd();
- val = objAsString(context, block.yield(context, substr(context.getRuntime(), begz, endz - begz)));
- } else {
- Region region = matcher.getRegion();
- begz = region.beg[0];
- endz = region.end[0];
- val = objAsString(context, block.yield(context, substr(context.getRuntime(), begz, endz - begz)));
- }
- modifyCheck(bytes, size);
- if (bang) {
- frozenCheck();
- }
- } else {
- val = rubyRegex.regsub((RubyString) repl, this, matcher);
- if (regex.numberOfCaptures() == 0) {
- begz = matcher.getBegin();
- endz = matcher.getEnd();
- } else {
- Region region = matcher.getRegion();
- begz = region.beg[0];
- endz = region.end[0];
- }
- }
-
- if (val.isTaint()) {
- tainted = true;
- }
- ByteList vbuf = val.value;
- int len = (bp - buf) + (beg - offset) + vbuf.realSize + 3;
- if (blen < len) {
- while (blen < len) {
- blen <<= 1;
- }
- len = bp - buf;
- dest.realloc(blen);
- dest.realSize = blen;
- bp = buf + len;
- }
- len = beg - offset; /* copy pre-match substr */
- System.arraycopy(value.bytes, cp, dest.bytes, bp, len);
- bp += len;
- System.arraycopy(vbuf.bytes, vbuf.begin, dest.bytes, bp, vbuf.realSize);
- bp += vbuf.realSize;
- offset = endz;
-
- if (begz == endz) {
- if (value.realSize <= endz) {
- break;
- }
- len = regex.getEncoding().length(value.bytes[begin + endz]);
- System.arraycopy(value.bytes, begin + endz, dest.bytes, bp, len);
- bp += len;
- offset = endz + len;
- }
- cp = begin + offset;
- if (offset > value.realSize) {
- break;
- }
- beg = matcher.search(cp, range, Option.NONE);
- }
-
- if (value.realSize > offset) {
- int len = bp - buf;
- if (blen - len < value.realSize - offset) {
- blen = len + value.realSize - offset;
- dest.realloc(blen);
- bp = buf + len;
- }
- System.arraycopy(value.bytes, cp, dest.bytes, bp, value.realSize - offset);
- bp += value.realSize - offset;
- }
-
- if (match != null) {
- frame.setBackRef(match);
- } else {
- rubyRegex.updateBackRef(context, this, frame, matcher);
- }
-
- dest.realSize = bp - buf;
- if (bang) {
- view(dest);
- if (tainted) {
- setTaint(true);
- }
- return this;
- } else {
- RubyString destStr = new RubyString(context.getRuntime(), getMetaClass(), dest);
- destStr.infectBy(this);
- if (tainted) {
- destStr.setTaint(true);
- }
- return destStr;
- }
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two args.
- */
- public IRubyObject index(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return index(context, args[0]);
- case 2:
- return index(context, args[0], args[1]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_index_m
- *
- */
- @JRubyMethod(reads = BACKREF, writes = BACKREF)
- public IRubyObject index(ThreadContext context, IRubyObject arg0) {
- return indexCommon(0, arg0, context);
- }
-
- /** rb_str_index_m
- *
- */
- @JRubyMethod(reads = BACKREF, writes = BACKREF)
- public IRubyObject index(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- int pos = RubyNumeric.num2int(arg1);
-
- if (pos < 0) {
- pos += value.realSize;
- if (pos < 0) {
- if (arg0 instanceof RubyRegexp) {
- context.getPreviousFrame().setBackRef(context.getRuntime().getNil());
- }
- return context.getRuntime().getNil();
- }
- }
-
- return indexCommon(pos, arg0, context);
- }
-
- private IRubyObject indexCommon(int pos, IRubyObject sub, ThreadContext context) throws RaiseException {
- if (sub instanceof RubyRegexp) {
- RubyRegexp regSub = (RubyRegexp) sub;
-
- pos = regSub.adjustStartPos(this, pos, false);
- pos = regSub.search(context, this, pos, false);
- } else if (sub instanceof RubyFixnum) {
- int c_int = RubyNumeric.fix2int(sub);
- if (c_int < 0x00 || c_int > 0xFF) {
- // out of byte range
- // there will be no match for sure
- return context.getRuntime().getNil();
- }
- byte c = (byte) c_int;
- byte[] bytes = value.bytes;
- int end = value.begin + value.realSize;
-
- pos += value.begin;
- for (; pos < end; pos++) {
- if (bytes[pos] == c) {
- return RubyFixnum.newFixnum(context.getRuntime(), pos - value.begin);
- }
- }
- return context.getRuntime().getNil();
- } else if (sub instanceof RubyString) {
- pos = strIndex((RubyString) sub, pos);
- } else {
- IRubyObject tmp = sub.checkStringType();
-
- if (tmp.isNil()) {
- throw context.getRuntime().newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
- }
- pos = strIndex((RubyString) tmp, pos);
- }
-
- return pos == -1 ? context.getRuntime().getNil() : RubyFixnum.newFixnum(context.getRuntime(), pos);
- }
-
- private int strIndex(RubyString sub, int offset) {
- if (offset < 0) {
- offset += value.realSize;
- if (offset < 0) return -1;
- }
-
- if (value.realSize - offset < sub.value.realSize) return -1;
- if (sub.value.realSize == 0) return offset;
- return value.indexOf(sub.value, offset);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two arguments.
- */
- public IRubyObject rindex(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return rindex(context, args[0]);
- case 2:
- return rindex(context, args[0], args[1]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_rindex_m
- *
- */
- @JRubyMethod(reads = BACKREF, writes = BACKREF)
- public IRubyObject rindex(ThreadContext context, IRubyObject arg0) {
- return rindexCommon(arg0, value.realSize, context);
- }
-
- /** rb_str_rindex_m
- *
- */
- @JRubyMethod(reads = BACKREF, writes = BACKREF)
- public IRubyObject rindex(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- int pos = RubyNumeric.num2int(arg1);
-
- if (pos < 0) {
- pos += value.realSize;
- if (pos < 0) {
- if (arg0 instanceof RubyRegexp) {
- context.getPreviousFrame().setBackRef(context.getRuntime().getNil());
- }
- return context.getRuntime().getNil();
- }
- }
- if (pos > value.realSize) pos = value.realSize;
-
- return rindexCommon(arg0, pos, context);
- }
-
- private IRubyObject rindexCommon(final IRubyObject sub, int pos, ThreadContext context) throws RaiseException {
-
- if (sub instanceof RubyRegexp) {
- RubyRegexp regSub = (RubyRegexp) sub;
- if (regSub.length() > 0) {
- pos = regSub.adjustStartPos(this, pos, true);
- pos = regSub.search(context, this, pos, true);
- }
- if (pos >= 0) {
- return RubyFixnum.newFixnum(context.getRuntime(), pos);
- }
- } else if (sub instanceof RubyString) {
- pos = strRindex((RubyString) sub, pos);
- if (pos >= 0) return RubyFixnum.newFixnum(context.getRuntime(), pos);
- } else if (sub instanceof RubyFixnum) {
- int c_int = RubyNumeric.fix2int(sub);
- if (c_int < 0x00 || c_int > 0xFF) {
- // out of byte range
- // there will be no match for sure
- return context.getRuntime().getNil();
- }
- byte c = (byte) c_int;
-
- byte[] bytes = value.bytes;
- int pbeg = value.begin;
- int p = pbeg + pos;
-
- if (pos == value.realSize) {
- if (pos == 0) {
- return context.getRuntime().getNil();
- }
- --p;
- }
- while (pbeg <= p) {
- if (bytes[p] == c) {
- return RubyFixnum.newFixnum(context.getRuntime(), p - value.begin);
- }
- p--;
- }
- return context.getRuntime().getNil();
- } else {
- IRubyObject tmp = sub.checkStringType();
- if (tmp.isNil()) throw context.getRuntime().newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
- pos = strRindex((RubyString) tmp, pos);
- if (pos >= 0) return RubyFixnum.newFixnum(context.getRuntime(), pos);
- }
-
- return context.getRuntime().getNil();
- }
-
- private int strRindex(RubyString sub, int pos) {
- int subLength = sub.value.realSize;
-
- /* substring longer than string */
- if (value.realSize < subLength) return -1;
- if (value.realSize - pos < subLength) pos = value.realSize - subLength;
-
- return value.lastIndexOf(sub.value, pos);
- }
-
- /* rb_str_substr */
- public IRubyObject substr(int beg, int len) {
- return substr(getRuntime(), beg, len);
- }
-
- public IRubyObject substr(Ruby runtime, int beg, int len) {
- int length = value.length();
- if (len < 0 || beg > length) return getRuntime().getNil();
-
- if (beg < 0) {
- beg += length;
- if (beg < 0) return getRuntime().getNil();
- }
-
- int end = Math.min(length, beg + len);
- return makeShared(getRuntime(), beg, end - beg);
- }
-
-
-
- /* rb_str_replace */
- public IRubyObject replace(int beg, int len, RubyString replaceWith) {
- if (beg + len >= value.length()) len = value.length() - beg;
-
- modify();
- value.unsafeReplace(beg,len,replaceWith.value);
-
- return infectBy(replaceWith);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the versions with one or two args
- */
- public IRubyObject op_aref(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return op_aref(context, args[0]);
- case 2:
- return op_aref(context, args[0], args[1]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_aref, rb_str_aref_m
- *
- */
- @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
- public IRubyObject op_aref(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
- if (arg1 instanceof RubyRegexp) {
- if(((RubyRegexp)arg1).search(context, this, 0, false) >= 0) {
- return RubyRegexp.nth_match(RubyNumeric.fix2int(arg2), context.getCurrentFrame().getBackRef());
- }
- return context.getRuntime().getNil();
- }
- return substr(context.getRuntime(), RubyNumeric.fix2int(arg1), RubyNumeric.fix2int(arg2));
- }
-
- /** rb_str_aref, rb_str_aref_m
- *
- */
- @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
- public IRubyObject op_aref(ThreadContext context, IRubyObject arg) {
- if (arg instanceof RubyRegexp) {
- if(((RubyRegexp)arg).search(context, this, 0, false) >= 0) {
- return RubyRegexp.nth_match(0, context.getCurrentFrame().getBackRef());
- }
- return context.getRuntime().getNil();
- } else if (arg instanceof RubyString) {
- return value.indexOf(stringValue(arg).value) != -1 ?
- arg : context.getRuntime().getNil();
- } else if (arg instanceof RubyRange) {
- long[] begLen = ((RubyRange) arg).begLen(value.length(), 0);
- return begLen == null ? context.getRuntime().getNil() :
- substr(context.getRuntime(), (int) begLen[0], (int) begLen[1]);
- }
- int idx = (int) arg.convertToInteger().getLongValue();
-
- if (idx < 0) idx += value.length();
- if (idx < 0 || idx >= value.length()) return context.getRuntime().getNil();
-
- return context.getRuntime().newFixnum(value.get(idx) & 0xFF);
- }
-
- /**
- * rb_str_subpat_set
- *
- */
- private void subpatSet(ThreadContext context, RubyRegexp regexp, int nth, IRubyObject repl) {
- RubyMatchData match;
- int start, end, len;
- if (regexp.search(context, this, 0, false) < 0) throw context.getRuntime().newIndexError("regexp not matched");
-
- match = (RubyMatchData)context.getCurrentFrame().getBackRef();
-
- if (match.regs == null) {
- if (nth >= 1) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
- if (nth < 0) {
- if(-nth >= 1) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
- nth += 1;
- }
- start = match.begin;
- if(start == -1) throw context.getRuntime().newIndexError("regexp group " + nth + " not matched");
- end = match.end;
- } else {
- if(nth >= match.regs.numRegs) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
- if(nth < 0) {
- if(-nth >= match.regs.numRegs) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
- nth += match.regs.numRegs;
- }
- start = match.regs.beg[nth];
- if(start == -1) throw context.getRuntime().newIndexError("regexp group " + nth + " not matched");
- end = match.regs.end[nth];
- }
-
- len = end - start;
- replace(start, len, stringValue(repl));
- }
-
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with two or three args.
- */
- public IRubyObject op_aset(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 2:
- return op_aset(context, args[0], args[1]);
- case 3:
- return op_aset(context, args[0], args[1], args[2]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 2, 3);
- return null; // not reached
- }
- }
-
- /** rb_str_aset, rb_str_aset_m
- *
- */
- @JRubyMethod(name = "[]=", reads = BACKREF)
- public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- if (arg0 instanceof RubyFixnum || arg0.respondsTo("to_int")) { // FIXME: RubyNumeric or RubyInteger instead?
- int idx = RubyNumeric.fix2int(arg0);
-
- if (idx < 0) idx += value.length();
-
- if (idx < 0 || idx >= value.length()) {
- throw context.getRuntime().newIndexError("string index out of bounds");
- }
- if (arg1 instanceof RubyFixnum) {
- modify();
- value.set(idx, (byte) RubyNumeric.fix2int(arg1));
- } else {
- replace(idx, 1, stringValue(arg1));
- }
- return arg1;
- }
- if (arg0 instanceof RubyRegexp) {
- RubyString repl = stringValue(arg1);
- subpatSet(context, (RubyRegexp) arg0, 0, repl);
- return repl;
- }
- if (arg0 instanceof RubyString) {
- RubyString orig = (RubyString)arg0;
- int beg = value.indexOf(orig.value);
- if (beg < 0) throw context.getRuntime().newIndexError("string not matched");
- replace(beg, orig.value.length(), stringValue(arg1));
- return arg1;
- }
- if (arg0 instanceof RubyRange) {
- long[] begLen = ((RubyRange) arg0).begLen(value.realSize, 2);
- replace((int) begLen[0], (int) begLen[1], stringValue(arg1));
- return arg1;
- }
- throw context.getRuntime().newTypeError("wrong argument type");
- }
-
- /** rb_str_aset, rb_str_aset_m
- *
- */
- @JRubyMethod(name = "[]=", reads = BACKREF)
- public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
- if (arg0 instanceof RubyRegexp) {
- RubyString repl = stringValue(arg2);
- int nth = RubyNumeric.fix2int(arg1);
- subpatSet(context, (RubyRegexp) arg0, nth, repl);
- return repl;
- }
- RubyString repl = stringValue(arg2);
- int beg = RubyNumeric.fix2int(arg0);
- int len = RubyNumeric.fix2int(arg1);
- if (len < 0) throw context.getRuntime().newIndexError("negative length");
- int strLen = value.length();
- if (beg < 0) beg += strLen;
-
- if (beg < 0 || (beg > 0 && beg > strLen)) {
- throw context.getRuntime().newIndexError("string index out of bounds");
- }
- if (beg + len > strLen) len = strLen - beg;
-
- replace(beg, len, repl);
- return repl;
- }
-
- /**
- * Variable arity version for compatibility. Not bound as a Ruby method.
- * @deprecated Use the versions with one or two args.
- */
- public IRubyObject slice_bang(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 1:
- return slice_bang(context, args[0]);
- case 2:
- return slice_bang(context, args[0], args[1]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_slice_bang
- *
- */
- @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
- public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0) {
- IRubyObject result = op_aref(context, arg0);
- if (result.isNil()) return result;
-
- op_aset(context, arg0, RubyString.newEmptyString(context.getRuntime()));
- return result;
- }
-
- /** rb_str_slice_bang
- *
- */
- @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
- public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- IRubyObject result = op_aref(context, arg0, arg1);
- if (result.isNil()) return result;
-
- op_aset(context, arg0, arg1, RubyString.newEmptyString(context.getRuntime()));
- return result;
- }
-
- @JRubyMethod(name = {"succ", "next"})
- public IRubyObject succ(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.succ_bang();
- return str;
- }
-
- @JRubyMethod(name = {"succ!", "next!"})
- public IRubyObject succ_bang() {
- if (value.length() == 0) {
- modifyCheck();
- return this;
- }
-
- modify();
-
- boolean alnumSeen = false;
- int pos = -1;
- int c = 0;
- int n = 0;
- for (int i = value.length() - 1; i >= 0; i--) {
- c = value.get(i) & 0xFF;
- if (isAlnum(c)) {
- alnumSeen = true;
- if ((isDigit(c) && c < '9') || (isLower(c) && c < 'z') || (isUpper(c) && c < 'Z')) {
- value.set(i, (byte)(c + 1));
- pos = -1;
- break;
- }
- pos = i;
- n = isDigit(c) ? '1' : (isLower(c) ? 'a' : 'A');
- value.set(i, (byte)(isDigit(c) ? '0' : (isLower(c) ? 'a' : 'A')));
- }
- }
- if (!alnumSeen) {
- for (int i = value.length() - 1; i >= 0; i--) {
- c = value.get(i) & 0xFF;
- if (c < 0xff) {
- value.set(i, (byte)(c + 1));
- pos = -1;
- break;
- }
- pos = i;
- n = '\u0001';
- value.set(i, 0);
- }
- }
- if (pos > -1) {
- // This represents left most digit in a set of incremented
- // values? Therefore leftmost numeric must be '1' and not '0'
- // 999 -> 1000, not 999 -> 0000. whereas chars should be
- // zzz -> aaaa and non-alnum byte values should be "\377" -> "\001\000"
- value.insert(pos, (byte) n);
- }
- return this;
- }
-
- /** rb_str_upto_m
- *
- */
- @JRubyMethod(name = "upto", required = 1, frame = true)
- public IRubyObject upto(ThreadContext context, IRubyObject str, Block block) {
- return upto(context, str, false, block);
- }
-
- /* rb_str_upto */
- public IRubyObject upto(ThreadContext context, IRubyObject str, boolean excl, Block block) {
- RubyString end = str.convertToString();
-
- int n = value.cmp(end.value);
- if (n > 0 || (excl && n == 0)) return this;
-
- IRubyObject afterEnd = end.callMethod(context, "succ");
- RubyString current = this;
-
- while (!current.op_equal(context, afterEnd).isTrue()) {
- block.yield(context, current);
- if (!excl && current.op_equal(context, end).isTrue()) break;
- current = current.callMethod(context, "succ").convertToString();
- if (excl && current.op_equal(context, end).isTrue()) break;
- if (current.value.realSize > end.value.realSize || current.value.realSize == 0) break;
- }
-
- return this;
- }
-
- /** rb_str_include
- *
- */
- @JRubyMethod(name = "include?", required = 1)
- public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
- if (obj instanceof RubyFixnum) {
- int c = RubyNumeric.fix2int(obj);
- for (int i = 0; i < value.length(); i++) {
- if (value.get(i) == (byte)c) {
- return context.getRuntime().getTrue();
- }
- }
- return context.getRuntime().getFalse();
- }
- ByteList str = stringValue(obj).value;
- return context.getRuntime().newBoolean(value.indexOf(str) != -1);
- }
-
- /**
- * Variable-arity version for compatibility. Not bound as a Ruby method.
- * @deprecated Use the versions with zero or one args.
- */
- public IRubyObject to_i(IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return to_i();
- case 1:
- return to_i(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
-
- /** rb_str_to_i
- *
- */
- @JRubyMethod(name = "to_i")
- public IRubyObject to_i() {
- return RubyNumeric.str2inum(getRuntime(), this, 10);
- }
-
- /** rb_str_to_i
- *
- */
- @JRubyMethod(name = "to_i")
- public IRubyObject to_i(IRubyObject arg0) {
- long base = arg0.convertToInteger().getLongValue();
- return RubyNumeric.str2inum(getRuntime(), this, (int) base);
- }
-
- /** rb_str_oct
- *
- */
- @JRubyMethod(name = "oct")
- public IRubyObject oct(ThreadContext context) {
- if (isEmpty()) return context.getRuntime().newFixnum(0);
-
- int base = 8;
-
- int ix = value.begin;
-
- while(ix < value.begin+value.realSize && ASCII.isSpace(value.bytes[ix] & 0xff)) {
- ix++;
- }
-
- int pos = (value.bytes[ix] == '-' || value.bytes[ix] == '+') ? ix+1 : ix;
- if((pos+1) < value.begin+value.realSize && value.bytes[pos] == '0') {
- if(value.bytes[pos+1] == 'x' || value.bytes[pos+1] == 'X') {
- base = 16;
- } else if(value.bytes[pos+1] == 'b' || value.bytes[pos+1] == 'B') {
- base = 2;
- } else if(value.bytes[pos+1] == 'd' || value.bytes[pos+1] == 'D') {
- base = 10;
- }
- }
- return RubyNumeric.str2inum(context.getRuntime(), this, base);
- }
-
- /** rb_str_hex
- *
- */
- @JRubyMethod(name = "hex")
- public IRubyObject hex(ThreadContext context) {
- return RubyNumeric.str2inum(context.getRuntime(), this, 16);
- }
-
- /** rb_str_to_f
- *
- */
- @JRubyMethod(name = "to_f")
- public IRubyObject to_f() {
- return RubyNumeric.str2fnum(getRuntime(), this);
- }
-
- /**
- * Variable arity version for compatibility. Not bound to a Ruby method.
- * @deprecated Use the versions with zero, one, or two args.
- */
- public RubyArray split(ThreadContext context, IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return split(context);
- case 1:
- return split(context, args[0]);
- case 2:
- return split(context, args[0], args[1]);
- default:
- Arity.raiseArgumentError(context.getRuntime(), args.length, 0, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_split_m
- *
- */
- @JRubyMethod(writes = BACKREF)
- public RubyArray split(ThreadContext context) {
- return split(context, context.getRuntime().getNil());
- }
-
- /** rb_str_split_m
- *
- */
- @JRubyMethod(writes = BACKREF)
- public RubyArray split(ThreadContext context, IRubyObject arg0) {
- return splitCommon(arg0, false, 0, 0, context);
- }
-
- /** rb_str_split_m
- *
- */
- @JRubyMethod(writes = BACKREF)
- public RubyArray split(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
- final int lim = RubyNumeric.fix2int(arg1);
- if (lim <= 0) {
- return splitCommon(arg0, false, lim, 1, context);
- } else {
- if (lim == 1) return value.realSize == 0 ? context.getRuntime().newArray() : context.getRuntime().newArray(this);
- return splitCommon(arg0, true, lim, 1, context);
- }
- }
-
- private RubyArray splitCommon(IRubyObject spat, final boolean limit, final int lim, final int i, ThreadContext context) {
- final RubyArray result;
- if (spat.isNil() && (spat = context.getRuntime().getGlobalVariables().get("$;")).isNil()) {
- result = awkSplit(limit, lim, i);
- } else {
- if (spat instanceof RubyString && ((RubyString) spat).value.realSize == 1) {
- RubyString strSpat = (RubyString) spat;
- if (strSpat.value.bytes[strSpat.value.begin] == (byte) ' ') {
- result = awkSplit(limit, lim, i);
- } else {
- result = split(context, spat, limit, lim, i);
- }
- } else {
- result = split(context, spat, limit, lim, i);
- }
- }
-
- if (!limit && lim == 0) {
- while (result.size() > 0 && ((RubyString) result.eltInternal(result.size() - 1)).value.realSize == 0) {
- result.pop();
- }
- }
-
- return result;
- }
-
- private RubyArray split(ThreadContext context, IRubyObject pat, boolean limit, int lim, int i) {
- Ruby runtime = context.getRuntime();
-
- final Regex regex = getPattern(pat, true).getPattern();
- int beg, end, start;
-
- int begin = value.begin;
- start = begin;
- beg = 0;
-
- int range = value.begin + value.realSize;
- final Matcher matcher = regex.matcher(value.bytes, value.begin, range);
-
- boolean lastNull = false;
- RubyArray result = runtime.newArray();
- if (regex.numberOfCaptures() == 0) { // shorter path, no captures defined, no region will be returned
- while ((end = matcher.search(start, range, Option.NONE)) >= 0) {
- if (start == end + begin && matcher.getBegin() == matcher.getEnd()) {
- if (value.realSize == 0) {
- result.append(newEmptyString(runtime, getMetaClass()));
- break;
- } else if (lastNull) {
- result.append(substr(runtime, beg, regex.getEncoding().length(value.bytes[begin + beg])));
- beg = start - begin;
- } else {
- if (start == range) {
- start++;
- } else {
- start += regex.getEncoding().length(value.bytes[start]);
- }
- lastNull = true;
- continue;
- }
- } else {
- result.append(substr(beg, end - beg));
- beg = matcher.getEnd();
- start = begin + matcher.getEnd();
- }
- lastNull = false;
- if (limit && lim <= ++i) break;
- }
- } else {
- while ((end = matcher.search(start, range, Option.NONE)) >= 0) {
- final Region region = matcher.getRegion();
- if (start == end + begin && region.beg[0] == region.end[0]) {
- if (value.realSize == 0) {
- result.append(newEmptyString(runtime, getMetaClass()));
- break;
- } else if (lastNull) {
- result.append(substr(beg, regex.getEncoding().length(value.bytes[begin + beg])));
- beg = start - begin;
- } else {
- if (start == range) {
- start++;
- } else {
- start += regex.getEncoding().length(value.bytes[start]);
- }
- lastNull = true;
- continue;
- }
- } else {
- result.append(substr(beg, end - beg));
- beg = start = region.end[0];
- start += begin;
- }
- lastNull = false;
-
- for (int idx=1; idx<region.numRegs; idx++) {
- if (region.beg[idx] == -1) continue;
- if (region.beg[idx] == region.end[idx]) {
- result.append(newEmptyString(runtime, getMetaClass()));
- } else {
- result.append(substr(region.beg[idx], region.end[idx] - region.beg[idx]));
- }
- }
- if (limit && lim <= ++i) break;
- }
- }
-
- // only this case affects backrefs
- context.getCurrentFrame().setBackRef(runtime.getNil());
-
- if (value.realSize > 0 && (limit || value.realSize > beg || lim < 0)) {
- if (value.realSize == beg) {
- result.append(newEmptyString(runtime, getMetaClass()));
- } else {
- result.append(substr(beg, value.realSize - beg));
- }
- }
-
- return result;
- }
-
- private RubyArray awkSplit(boolean limit, int lim, int i) {
- Ruby runtime = getRuntime();
- RubyArray result = runtime.newArray();
-
- byte[]bytes = value.bytes;
- int p = value.begin;
- int endp = p + value.realSize;
-
- boolean skip = true;
-
- int end, beg = 0;
- for (end = beg = 0; p < endp; p++) {
- if (skip) {
- if (ASCII.isSpace(bytes[p] & 0xff)) {
- beg++;
- } else {
- end = beg + 1;
- skip = false;
- if (limit && lim <= i) break;
- }
- } else {
- if (ASCII.isSpace(bytes[p] & 0xff)) {
- result.append(makeShared(runtime, beg, end - beg));
- skip = true;
- beg = end + 1;
- if (limit) i++;
- } else {
- end++;
- }
- }
- }
-
- if (value.realSize > 0 && (limit || value.realSize > beg || lim < 0)) {
- if (value.realSize == beg) {
- result.append(newEmptyString(runtime, getMetaClass()));
- } else {
- result.append(makeShared(runtime, beg, value.realSize - beg));
- }
- }
- return result;
- }
-
- /** get_pat
- *
- */
- private final RubyRegexp getPattern(IRubyObject obj, boolean quote) {
- if (obj instanceof RubyRegexp) {
- return (RubyRegexp)obj;
- } else if (!(obj instanceof RubyString)) {
- IRubyObject val = obj.checkStringType();
- if (val.isNil()) throw getRuntime().newTypeError("wrong argument type " + obj.getMetaClass() + " (expected Regexp)");
- obj = val;
- }
-
- return RubyRegexp.newRegexp(getRuntime(), ((RubyString)obj).value, 0, quote);
- }
-
- /** rb_str_scan
- *
- */
- @JRubyMethod(name = "scan", required = 1, frame = true, reads = BACKREF, writes = BACKREF)
- public IRubyObject scan(ThreadContext context, IRubyObject arg, Block block) {
- Ruby runtime = context.getRuntime();
- Frame frame = context.getPreviousFrame();
-
- final RubyRegexp rubyRegex = getPattern(arg, true);
- final Regex regex = rubyRegex.getPattern();
-
- int range = value.begin + value.realSize;
- final Matcher matcher = regex.matcher(value.bytes, value.begin, range);
- matcher.value = 0; // implicit start argument to scanOnce(NG)
-
- IRubyObject result;
- if (!block.isGiven()) {
- RubyArray ary = runtime.newArray();
-
- if (regex.numberOfCaptures() == 0) {
- while ((result = scanOnceNG(rubyRegex, matcher, range)) != null) ary.append(result);
- } else {
- while ((result = scanOnce(rubyRegex, matcher, range)) != null) ary.append(result);
- }
-
- if (ary.size() > 0) {
- rubyRegex.updateBackRef(context, this, frame, matcher);
- } else {
- frame.setBackRef(runtime.getNil());
- }
- return ary;
- } else {
- byte[]bytes = value.bytes;
- int size = value.realSize;
- RubyMatchData match = null;
-
- if (regex.numberOfCaptures() == 0) {
- while ((result = scanOnceNG(rubyRegex, matcher, range)) != null) {
- match = rubyRegex.updateBackRef(context, this, frame, matcher);
- match.use();
- block.yield(context, result);
- modifyCheck(bytes, size);
- }
- } else {
- while ((result = scanOnce(rubyRegex, matcher, range)) != null) {
- match = rubyRegex.updateBackRef(context, this, frame, matcher);
- match.use();
- block.yield(context, result);
- modifyCheck(bytes, size);
- }
- }
- frame.setBackRef(match == null ? runtime.getNil() : match);
- return this;
- }
- }
-
- /**
- * rb_enc_check
- */
- @SuppressWarnings("unused")
- private Encoding encodingCheck(RubyRegexp pattern) {
- // For 1.9 compatibility, should check encoding compat between string and pattern
- return pattern.getKCode().getEncoding();
- }
-
- // no group version
- private IRubyObject scanOnceNG(RubyRegexp regex, Matcher matcher, int range) {
- if (matcher.search(matcher.value + value.begin, range, Option.NONE) >= 0) {
- int end = matcher.getEnd();
- if (matcher.getBegin() == end) {
- if (value.realSize > end) {
- matcher.value = end + regex.getPattern().getEncoding().length(value.bytes[value.begin + end]);
- } else {
- matcher.value = end + 1;
- }
- } else {
- matcher.value = end;
- }
- return substr(matcher.getBegin(), end - matcher.getBegin()).infectBy(regex);
- }
- return null;
- }
-
- // group version
- private IRubyObject scanOnce(RubyRegexp regex, Matcher matcher, int range) {
- if (matcher.search(matcher.value + value.begin, range, Option.NONE) >= 0) {
- Region region = matcher.getRegion();
- int end = region.end[0];
- if (region.beg[0] == end) {
- if (value.realSize > end) {
- matcher.value = end + regex.getPattern().getEncoding().length(value.bytes[value.begin + end]);
- } else {
- matcher.value = end + 1;
- }
- } else {
- matcher.value = end;
- }
-
- RubyArray result = getRuntime().newArray(region.numRegs);
- for (int i=1; i<region.numRegs; i++) {
- int beg = region.beg[i];
- if (beg == -1) {
- result.append(getRuntime().getNil());
- } else {
- result.append(substr(beg, region.end[i] - beg).infectBy(regex));
- }
- }
- return result;
- }
- return null;
- }
-
- private static final ByteList SPACE_BYTELIST = new ByteList(ByteList.plain(" "));
-
- private final IRubyObject justify(IRubyObject arg0, char jflag) {
- Ruby runtime = getRuntime();
-
- int width = RubyFixnum.num2int(arg0);
-
- int f, flen = 0;
- byte[]fbuf;
-
- IRubyObject pad;
-
- f = SPACE_BYTELIST.begin;
- flen = SPACE_BYTELIST.realSize;
- fbuf = SPACE_BYTELIST.bytes;
- pad = runtime.getNil();
-
- return justifyCommon(width, jflag, flen, fbuf, f, runtime, pad);
- }
-
- private final IRubyObject justify(IRubyObject arg0, IRubyObject arg1, char jflag) {
- Ruby runtime = getRuntime();
-
- int width = RubyFixnum.num2int(arg0);
-
- int f, flen = 0;
- byte[]fbuf;
-
- IRubyObject pad;
-
- pad = arg1.convertToString();
- ByteList fList = ((RubyString)pad).value;
- f = fList.begin;
- flen = fList.realSize;
-
- if (flen == 0) throw runtime.newArgumentError("zero width padding");
-
- fbuf = fList.bytes;
-
- return justifyCommon(width, jflag, flen, fbuf, f, runtime, pad);
- }
-
- private IRubyObject justifyCommon(int width, char jflag, int flen, byte[] fbuf, int f, Ruby runtime, IRubyObject pad) {
- if (width < 0 || value.realSize >= width) return strDup(runtime);
-
- ByteList res = new ByteList(width);
- res.realSize = width;
-
- int p = res.begin;
- int pend;
- byte[] pbuf = res.bytes;
-
- if (jflag != 'l') {
- int n = width - value.realSize;
- pend = p + ((jflag == 'r') ? n : n / 2);
- if (flen <= 1) {
- while (p < pend) {
- pbuf[p++] = fbuf[f];
- }
- } else {
- int q = f;
- while (p + flen <= pend) {
- System.arraycopy(fbuf, f, pbuf, p, flen);
- p += flen;
- }
- while (p < pend) {
- pbuf[p++] = fbuf[q++];
- }
- }
- }
-
- System.arraycopy(value.bytes, value.begin, pbuf, p, value.realSize);
-
- if (jflag != 'r') {
- p += value.realSize;
- pend = res.begin + width;
- if (flen <= 1) {
- while (p < pend) {
- pbuf[p++] = fbuf[f];
- }
- } else {
- while (p + flen <= pend) {
- System.arraycopy(fbuf, f, pbuf, p, flen);
- p += flen;
- }
- while (p < pend) {
- pbuf[p++] = fbuf[f++];
- }
- }
- }
-
- RubyString resStr = new RubyString(runtime, getMetaClass(), res);
- resStr.infectBy(this);
- if (flen > 0) {
- resStr.infectBy(pad);
- }
- return resStr;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated use the one or two argument versions.
- */
- public IRubyObject ljust(IRubyObject [] args) {
- switch (args.length) {
- case 1:
- return ljust(args[0]);
- case 2:
- return ljust(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_ljust
- *
- */
- @JRubyMethod
- public IRubyObject ljust(IRubyObject arg0) {
- return justify(arg0, 'l');
- }
-
- /** rb_str_ljust
- *
- */
- @JRubyMethod
- public IRubyObject ljust(IRubyObject arg0, IRubyObject arg1) {
- return justify(arg0, arg1, 'l');
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated use the one or two argument versions.
- */
- public IRubyObject rjust(IRubyObject [] args) {
- switch (args.length) {
- case 1:
- return rjust(args[0]);
- case 2:
- return rjust(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_rjust
- *
- */
- @JRubyMethod
- public IRubyObject rjust(IRubyObject arg0) {
- return justify(arg0, 'r');
- }
-
- /** rb_str_rjust
- *
- */
- @JRubyMethod
- public IRubyObject rjust(IRubyObject arg0, IRubyObject arg1) {
- return justify(arg0, arg1, 'r');
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated use the one or two argument versions.
- */
- public IRubyObject center(IRubyObject [] args) {
- switch (args.length) {
- case 1:
- return center(args[0]);
- case 2:
- return center(args[0], args[1]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
- return null; // not reached
- }
- }
-
- /** rb_str_center
- *
- */
- @JRubyMethod
- public IRubyObject center(IRubyObject arg0) {
- return justify(arg0, 'c');
- }
-
- /** rb_str_center
- *
- */
- @JRubyMethod
- public IRubyObject center(IRubyObject arg0, IRubyObject arg1) {
- return justify(arg0, arg1, 'c');
- }
-
- @JRubyMethod(name = "chop")
- public IRubyObject chop(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.chop_bang();
- return str;
- }
-
- /** rb_str_chop_bang
- *
- */
- @JRubyMethod(name = "chop!")
- public IRubyObject chop_bang() {
- int end = value.realSize - 1;
- if (end < 0) return getRuntime().getNil();
-
- if ((value.bytes[value.begin + end]) == '\n') {
- if (end > 0 && (value.bytes[value.begin + end - 1]) == '\r') end--;
- }
-
- view(0, end);
- return this;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby
- *
- * @param args
- * @return
- * @deprecated Use the zero or one argument versions.
- */
- public RubyString chomp(IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return chomp();
- case 1:
- return chomp(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
-
- /** rb_str_chop
- *
- */
- @JRubyMethod
- public RubyString chomp() {
- RubyString str = strDup(getRuntime());
- str.chomp_bang();
- return str;
- }
-
- /** rb_str_chop
- *
- */
- @JRubyMethod
- public RubyString chomp(IRubyObject arg0) {
- RubyString str = strDup(getRuntime());
- str.chomp_bang(arg0);
- return str;
- }
-
- /**
- * Variable-arity version for compatibility. Not bound to Ruby.
- * @deprecated Use the zero or one argument versions.
- */
- public IRubyObject chomp_bang(IRubyObject[] args) {
- switch (args.length) {
- case 0:
- return chomp_bang();
- case 1:
- return chomp_bang(args[0]);
- default:
- Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
- return null; // not reached
- }
- }
-
- /**
- * rb_str_chomp_bang
- *
- * In the common case, removes CR and LF characters in various ways depending on the value of
- * the optional args[0].
- * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
- * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
- * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
- * all(!)).
- * @param args See method description.
- */
- @JRubyMethod(name = "chomp!")
- public IRubyObject chomp_bang() {
- IRubyObject rsObj;
-
- int len = value.length();
- if (len == 0) return getRuntime().getNil();
- byte[]buff = value.bytes;
-
- rsObj = getRuntime().getGlobalVariables().get("$/");
-
- if (rsObj == getRuntime().getGlobalVariables().getDefaultSeparator()) {
- int realSize = value.realSize;
- int begin = value.begin;
- if (buff[begin + len - 1] == (byte)'\n') {
- realSize--;
- if (realSize > 0 && buff[begin + realSize - 1] == (byte)'\r') realSize--;
- view(0, realSize);
- } else if (buff[begin + len - 1] == (byte)'\r') {
- realSize--;
- view(0, realSize);
- } else {
- modifyCheck();
- return getRuntime().getNil();
- }
- return this;
- }
-
- return chompBangCommon(rsObj);
- }
-
- /**
- * rb_str_chomp_bang
- *
- * In the common case, removes CR and LF characters in various ways depending on the value of
- * the optional args[0].
- * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
- * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
- * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
- * all(!)).
- * @param args See method description.
- */
- @JRubyMethod(name = "chomp!")
- public IRubyObject chomp_bang(IRubyObject arg0) {
- return chompBangCommon(arg0);
- }
-
- private IRubyObject chompBangCommon(IRubyObject rsObj) {
-
- if (rsObj.isNil()) {
- return getRuntime().getNil();
- }
- RubyString rs = rsObj.convertToString();
- int len = value.realSize;
- int begin = value.begin;
- if (len == 0) {
- return getRuntime().getNil();
- }
- byte[] buff = value.bytes;
- int rslen = rs.value.realSize;
-
- if (rslen == 0) {
- while (len > 0 && buff[begin + len - 1] == (byte) '\n') {
- len--;
- if (len > 0 && buff[begin + len - 1] == (byte) '\r') {
- len--;
- }
- }
- if (len < value.realSize) {
- view(0, len);
- return this;
- }
- return getRuntime().getNil();
- }
-
- if (rslen > len) {
- return getRuntime().getNil();
- }
- byte newline = rs.value.bytes[rslen - 1];
-
- if (rslen == 1 && newline == (byte) '\n') {
- buff = value.bytes;
- int realSize = value.realSize;
- if (buff[begin + len - 1] == (byte) '\n') {
- realSize--;
- if (realSize > 0 && buff[begin + realSize - 1] == (byte) '\r') {
- realSize--;
- }
- view(0, realSize);
- } else if (buff[begin + len - 1] == (byte) '\r') {
- realSize--;
- view(0, realSize);
- } else {
- modifyCheck();
- return getRuntime().getNil();
- }
- return this;
- }
-
- if (buff[begin + len - 1] == newline && rslen <= 1 || value.endsWith(rs.value)) {
- view(0, value.realSize - rslen);
- return this;
- }
-
- return getRuntime().getNil();
- }
-
- /** rb_str_lstrip
- *
- */
- @JRubyMethod
- public IRubyObject lstrip(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.lstrip_bang();
- return str;
- }
-
- /** rb_str_lstrip_bang
- */
- @JRubyMethod(name = "lstrip!")
- public IRubyObject lstrip_bang() {
- if (value.realSize == 0) return getRuntime().getNil();
-
- int i=0;
- while (i < value.realSize && ASCII.isSpace(value.bytes[value.begin + i] & 0xff)) i++;
-
- if (i > 0) {
- view(i, value.realSize - i);
- return this;
- }
-
- return getRuntime().getNil();
- }
-
- /** rb_str_rstrip
- *
- */
- @JRubyMethod
- public IRubyObject rstrip(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.rstrip_bang();
- return str;
- }
-
- /** rb_str_rstrip_bang
- */
- @JRubyMethod(name = "rstrip!")
- public IRubyObject rstrip_bang() {
- if (value.realSize == 0) return getRuntime().getNil();
- int i=value.realSize - 1;
-
- while (i >= 0 && value.bytes[value.begin+i] == 0) i--;
- while (i >= 0 && ASCII.isSpace(value.bytes[value.begin + i] & 0xff)) i--;
-
- if (i < value.realSize - 1) {
- view(0, i + 1);
- return this;
- }
-
- return getRuntime().getNil();
- }
-
- /** rb_str_strip
- *
- */
- @JRubyMethod
- public IRubyObject strip(ThreadContext context) {
- RubyString str = strDup(context.getRuntime());
- str.strip_bang();
- return str;
- }
-
- /** rb_str_strip_bang
- */
- @JRubyMethod(name = "strip!")
- public IRubyObject strip_bang() {
- IRubyObject l = lstrip_bang();
- IRubyObject r = rstrip_bang();
-
- if(l.isNil() && r.isNil()) {
- return l;
- }
- return this;
- }
-
- /** rb_str_count
- *
- */
- @JRubyMethod(name = "count", required = 1, rest = true)
- public IRubyObject count(IRubyObject[] args) {
- if (args.length < 1) throw getRuntime().newArgumentError("wrong number of arguments");
- if (value.realSize == 0) return getRuntime().newFixnum(0);
-
- boolean[]table = new boolean[TRANS_SIZE];
- boolean init = true;
- for (int i=0; i<args.length; i++) {
- RubyString s = args[i].convertToString();
- s.setup_table(table, init);
- init = false;
- }
-
- int s = value.begin;
- int send = s + value.realSize;
- byte[]buf = value.bytes;
- int i = 0;
-
- while (s < send) if (table[buf[s++] & 0xff]) i++;
-
- return getRuntime().newFixnum(i);
- }
-
- /** rb_str_delete
- *
- */
- @JRubyMethod(name = "delete", required = 1, rest = true)
- public IRubyObject delete(ThreadContext context, IRubyObject[] args) {
- RubyString str = strDup(context.getRuntime());
- str.delete_bang(args);
- return str;
- }
-
- /** rb_str_delete_bang
- *
- */
- @JRubyMethod(name = "delete!", required = 1, rest = true)
- public IRubyObject delete_bang(IRubyObject[] args) {
- if (args.length < 1) throw getRuntime().newArgumentError("wrong number of arguments");
-
- boolean[]squeeze = new boolean[TRANS_SIZE];
-
- boolean init = true;
- for (int i=0; i<args.length; i++) {
- RubyString s = args[i].convertToString();
- s.setup_table(squeeze, init);
- init = false;
- }
-
- modify();
-
- if (value.realSize == 0) return getRuntime().getNil();
- int s = value.begin;
- int t = s;
- int send = s + value.realSize;
- byte[]buf = value.bytes;
- boolean modify = false;
-
- while (s < send) {
- if (squeeze[buf[s] & 0xff]) {
- modify = true;
- } else {
- buf[t++] = buf[s];
- }
- s++;
- }
- value.realSize = t - value.begin;
-
- if (modify) return this;
- return getRuntime().getNil();
- }
-
- /** rb_str_squeeze
- *
- */
- @JRubyMethod(name = "squeeze", rest = true)
- public IRubyObject squeeze(ThreadContext context, IRubyObject[] args) {
- RubyString str = strDup(context.getRuntime());
- str.squeeze_bang(args);
- return str;
- }
-
- /** rb_str_squeeze_bang
- *
- */
- @JRubyMethod(name = "squeeze!", rest = true)
- public IRubyObject squeeze_bang(IRubyObject[] args) {
- if (value.realSize == 0) {
- modifyCheck();
- return getRuntime().getNil();
- }
-
- final boolean squeeze[] = new boolean[TRANS_SIZE];
-
- if (args.length == 0) {
- for (int i=0; i<TRANS_SIZE; i++) squeeze[i] = true;
- } else {
- boolean init = true;
- for (int i=0; i<args.length; i++) {
- RubyString s = args[i].convertToString();
- s.setup_table(squeeze, init);
- init = false;
- }
- }
-
- modify();
-
- int s = value.begin;
- int t = s;
- int send = s + value.realSize;
- byte[]buf = value.bytes;
- int save = -1;
-
- while (s < send) {
- int c = buf[s++] & 0xff;
- if (c != save || !squeeze[c]) buf[t++] = (byte)(save = c);
- }
-
- if (t - value.begin != value.realSize) { // modified
- value.realSize = t - value.begin;
- return this;
- }
-
- return getRuntime().getNil();
- }
-
- /** rb_str_tr
- *
- */
- @JRubyMethod
- public IRubyObject tr(ThreadContext context, IRubyObject src, IRubyObject repl) {
- RubyString str = strDup(context.getRuntime());
- str.tr_trans(src, repl, false);
- return str;
- }
-
- /** rb_str_tr_bang
- *
- */
- @JRubyMethod(name = "tr!")
- public IRubyObject tr_bang(IRubyObject src, IRubyObject repl) {
- return tr_trans(src, repl, false);
- }
-
- private static final class TR {
- int gen, now, max;
- int p, pend;
- byte[]buf;
- }
-
- private static final int TRANS_SIZE = 256;
-
- /** tr_setup_table
- *
- */
- private final void setup_table(boolean[]table, boolean init) {
- final boolean[]buf = new boolean[TRANS_SIZE];
- final TR tr = new TR();
- int c;
-
- boolean cflag = false;
-
- tr.p = value.begin;
- tr.pend = value.begin + value.realSize;
- tr.buf = value.bytes;
- tr.gen = tr.now = tr.max = 0;
-
- if (value.realSize > 1 && value.bytes[value.begin] == '^') {
- cflag = true;
- tr.p++;
- }
-
- if (init) for (int i=0; i<TRANS_SIZE; i++) table[i] = true;
-
- for (int i=0; i<TRANS_SIZE; i++) buf[i] = cflag;
- while ((c = trnext(tr)) >= 0) buf[c & 0xff] = !cflag;
- for (int i=0; i<TRANS_SIZE; i++) table[i] = table[i] && buf[i];
- }
-
- /** tr_trans
- *
- */
- private final IRubyObject tr_trans(IRubyObject src, IRubyObject repl, boolean sflag) {
- if (value.realSize == 0) return getRuntime().getNil();
-
- ByteList replList = repl.convertToString().value;
-
- if (replList.realSize == 0) return delete_bang(new IRubyObject[]{src});
-
- ByteList srcList = src.convertToString().value;
-
- final TR trsrc = new TR();
- final TR trrepl = new TR();
-
- boolean cflag = false;
- boolean modify = false;
-
- trsrc.p = srcList.begin;
- trsrc.pend = srcList.begin + srcList.realSize;
- trsrc.buf = srcList.bytes;
- if (srcList.realSize >= 2 && srcList.bytes[srcList.begin] == '^') {
- cflag = true;
- trsrc.p++;
- }
-
- trrepl.p = replList.begin;
- trrepl.pend = replList.begin + replList.realSize;
- trrepl.buf = replList.bytes;
-
- trsrc.gen = trrepl.gen = 0;
- trsrc.now = trrepl.now = 0;
- trsrc.max = trrepl.max = 0;
-
- int c;
- final int[]trans = new int[TRANS_SIZE];
- if (cflag) {
- for (int i=0; i<TRANS_SIZE; i++) trans[i] = 1;
- while ((c = trnext(trsrc)) >= 0) trans[c & 0xff] = -1;
- while ((c = trnext(trrepl)) >= 0);
- for (int i=0; i<TRANS_SIZE; i++) {
- if (trans[i] >= 0) trans[i] = trrepl.now;
- }
- } else {
- for (int i=0; i<TRANS_SIZE; i++) trans[i] = -1;
- while ((c = trnext(trsrc)) >= 0) {
- int r = trnext(trrepl);
- if (r == -1) r = trrepl.now;
- trans[c & 0xff] = r;
- }
- }
-
- modify();
-
- int s = value.begin;
- int send = s + value.realSize;
- byte sbuf[] = value.bytes;
-
- if (sflag) {
- int t = s;
- int c0, last = -1;
- while (s < send) {
- c0 = sbuf[s++];
- if ((c = trans[c0 & 0xff]) >= 0) {
- if (last == c) continue;
- last = c;
- sbuf[t++] = (byte)(c & 0xff);
- modify = true;
- } else {
- last = -1;
- sbuf[t++] = (byte)c0;
- }
- }
-
- if (value.realSize > (t - value.begin)) {
- value.realSize = t - value.begin;
- modify = true;
- }
- } else {
- while (s < send) {
- if ((c = trans[sbuf[s] & 0xff]) >= 0) {
- sbuf[s] = (byte)(c & 0xff);
- modify = true;
- }
- s++;
- }
- }
-
- if (modify) return this;
- return getRuntime().getNil();
- }
-
- /** trnext
- *
- */
- private final int trnext(TR t) {
- byte [] buf = t.buf;
-
- for (;;) {
- if (t.gen == 0) {
- if (t.p == t.pend) return -1;
- if (t.p < t.pend -1 && buf[t.p] == '\\') t.p++;
- t.now = buf[t.p++];
- if (t.p < t.pend - 1 && buf[t.p] == '-') {
- t.p++;
- if (t.p < t.pend) {
- if (t.now > ((int)buf[t.p] & 0xFF)) {
- t.p++;
- continue;
- }
- t.gen = 1;
- t.max = (int)buf[t.p++] & 0xFF;
- }
- }
- return t.now & 0xff;
- } else if (++t.now < t.max) {
- return t.now & 0xff;
- } else {
- t.gen = 0;
- return t.max & 0xff;
- }
- }
- }
-
- /** rb_str_tr_s
- *
- */
- @JRubyMethod
- public IRubyObject tr_s(ThreadContext context, IRubyObject src, IRubyObject repl) {
- RubyString str = strDup(context.getRuntime());
- str.tr_trans(src, repl, true);
- return str;
- }
-
- /** rb_str_tr_s_bang
- *
- */
- @JRubyMethod(name = "tr_s!")
- public IRubyObject tr_s_bang(IRubyObject src, IRubyObject repl) {
- return tr_trans(src, repl, true);
- }
-
- /** rb_str_each_line
- *
- */
- @JRubyMethod(name = {"each_line", "each"}, required = 0, optional = 1, frame = true)
- public IRubyObject each_line(ThreadContext context, IRubyObject[] args, Block block) {
- byte newline;
- int p = value.begin;
- int pend = p + value.realSize;
- int s;
- int ptr = p;
- int len = value.realSize;
- int rslen;
- IRubyObject line;
-
-
- IRubyObject _rsep;
- if (args.length == 0) {
- _rsep = getRuntime().getGlobalVariables().get("$/");
- } else {
- _rsep = args[0];
- }
-
- if(_rsep.isNil()) {
- block.yield(context, this);
- return this;
- }
-
- RubyString rsep = stringValue(_rsep);
- ByteList rsepValue = rsep.value;
- byte[] strBytes = value.bytes;
-
- rslen = rsepValue.realSize;
-
- if(rslen == 0) {
- newline = '\n';
- } else {
- newline = rsepValue.bytes[rsepValue.begin + rslen-1];
- }
-
- s = p;
- p+=rslen;
-
- for(; p < pend; p++) {
- if(rslen == 0 && strBytes[p] == '\n') {
- if(strBytes[++p] != '\n') {
- continue;
- }
- while(p < pend && strBytes[p] == '\n') {
- p++;
- }
- }
- if(ptr<p && strBytes[p-1] == newline &&
- (rslen <= 1 ||
- ByteList.memcmp(rsepValue.bytes, rsepValue.begin, rslen, strBytes, p-rslen, rslen) == 0)) {
- line = RubyString.newStringShared(getRuntime(), getMetaClass(), this.value.makeShared(s-ptr, p-s));
- line.infectBy(this);
- block.yield(context, line);
- modifyCheck(strBytes,len);
- s = p;
- }
- }
-
- if(s != pend) {
- if(p > pend) {
- p = pend;
- }
- line = RubyString.newStringShared(getRuntime(), getMetaClass(), this.value.makeShared(s-ptr, p-s));
- line.infectBy(this);
- block.yield(context, line);
- }
-
- return this;
- }
-
- /**
- * rb_str_each_byte
- */
- @JRubyMethod(name = "each_byte", frame = true)
- public RubyString each_byte(ThreadContext context, Block block) {
- Ruby runtime = getRuntime();
- // Check the length every iteration, since
- // the block can modify this string.
- for (int i = 0; i < value.length(); i++) {
- block.yield(context, runtime.newFixnum(value.get(i) & 0xFF));
- }
- return this;
- }
-
- /** rb_str_intern
- *
- */
- public RubySymbol intern() {
- String s = toString();
- if (s.length() == 0) {
- throw getRuntime().newArgumentError("interning empty string");
- }
- if (s.indexOf('\0') >= 0) {
- throw getRuntime().newArgumentError("symbol string may not contain '\\0'");
- }
- return getRuntime().newSymbol(s);
- }
-
- @JRubyMethod(name = {"to_sym", "intern"})
- public RubySymbol to_sym() {
- return intern();
- }
-
- @JRubyMethod(name = "sum", optional = 1)
- public RubyInteger sum(IRubyObject[] args) {
- if (args.length > 1) {
- throw getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 1)");
- }
-
- long bitSize = 16;
- if (args.length == 1) {
- long bitSizeArg = ((RubyInteger) args[0].convertToInteger()).getLongValue();
- if (bitSizeArg > 0) {
- bitSize = bitSizeArg;
- }
- }
-
- long result = 0;
- for (int i = 0; i < value.length(); i++) {
- result += value.get(i) & 0xFF;
- }
- return getRuntime().newFixnum(bitSize == 0 ? result : result % (long) Math.pow(2, bitSize));
- }
-
- /** string_to_c
- *
- */
- @JRubyMethod(name = "to_c", reads = BACKREF, writes = BACKREF, compat = CompatVersion.RUBY1_9)
- public IRubyObject to_c(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- Frame frame = context.getCurrentFrame();
- IRubyObject backref = frame.getBackRef();
- if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
-
- IRubyObject s = RuntimeHelpers.invoke(
- context, this, "gsub",
- RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.underscores_pat),
- runtime.newString(new ByteList(new byte[]{'_'})));
-
- RubyArray a = RubyComplex.str_to_c_internal(context, s);
-
- frame.setBackRef(backref);
-
- if (!a.eltInternal(0).isNil()) {
- return a.eltInternal(0);
- } else {
- return RubyComplex.newComplexCanonicalize(context, RubyFixnum.zero(runtime));
- }
- }
-
- /** string_to_r
- *
- */
- @JRubyMethod(name = "to_r", reads = BACKREF, writes = BACKREF, compat = CompatVersion.RUBY1_9)
- public IRubyObject to_r(ThreadContext context) {
- Ruby runtime = context.getRuntime();
- Frame frame = context.getCurrentFrame();
- IRubyObject backref = frame.getBackRef();
- if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
-
- IRubyObject s = RuntimeHelpers.invoke(
- context, this, "gsub",
- RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.underscores_pat),
- runtime.newString(new ByteList(new byte[]{'_'})));
-
- RubyArray a = RubyRational.str_to_r_internal(context, s);
-
- frame.setBackRef(backref);
-
- if (!a.eltInternal(0).isNil()) {
- return a.eltInternal(0);
- } else {
- return RubyRational.newRationalCanonicalize(context, RubyFixnum.zero(runtime));
- }
- }
-
- public static RubyString unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- RubyString result = newString(input.getRuntime(), input.unmarshalString());
- input.registerLinkTarget(result);
- return result;
- }
-
- /**
- * @see org.jruby.util.Pack#unpack
- */
- @JRubyMethod
- public RubyArray unpack(IRubyObject obj) {
- return Pack.unpack(getRuntime(), this.value, stringValue(obj).value);
- }
-
- public void empty() {
- value = ByteList.EMPTY_BYTELIST;
- shareLevel = SHARE_LEVEL_BYTELIST;
- }
-
- /**
- * Mutator for internal string representation.
- *
- * @param value The new java.lang.String this RubyString should encapsulate
- * @deprecated
- */
- public void setValue(CharSequence value) {
- view(ByteList.plain(value));
- }
-
- public void setValue(ByteList value) {
- view(value);
- }
-
- public CharSequence getValue() {
- return toString();
- }
-
- public byte[] getBytes() {
- return value.bytes();
- }
-
- public ByteList getByteList() {
- return value;
- }
-
- /** used by ar-jdbc
- *
- */
- public String getUnicodeValue() {
- try {
- return new String(value.bytes,value.begin,value.realSize, "UTF8");
- } catch (Exception e) {
- throw new RuntimeException("Something's seriously broken with encodings", e);
- }
- }
-
- @Override
- public IRubyObject to_java() {
- return MiniJava.javaToRuby(getRuntime(), new String(getBytes()));
- }
-}
-/***** 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>
- * Copyright (C) 2006 Ryan Bell <ryan.l.bell@gmail.com>
- * Copyright (C) 2007 Thomas E Enebo <enebo@acm.org>
- * 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.util.ArrayList;
-import java.util.List;
-
-import org.jruby.anno.FrameField;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.runtime.Block;
-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.util.ByteList;
-import org.jruby.util.TypeConverter;
-import org.jruby.util.io.InvalidValueException;
-import org.jruby.util.io.ModeFlags;
-import org.jruby.util.io.Stream;
-
-@JRubyClass(name="StringIO")
-public class RubyStringIO extends RubyObject {
- private static ObjectAllocator STRINGIO_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyStringIO(runtime, klass);
- }
- };
-
- public static RubyClass createStringIOClass(final Ruby runtime) {
- RubyClass stringIOClass = runtime.defineClass(
- "StringIO", runtime.fastGetClass("Data"), STRINGIO_ALLOCATOR);
-
- stringIOClass.defineAnnotatedMethods(RubyStringIO.class);
- stringIOClass.includeModule(runtime.getEnumerable());
-
- return stringIOClass;
- }
-
- @JRubyMethod(name = "open", optional = 2, frame = true, meta = true)
- public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- RubyStringIO strio = (RubyStringIO)((RubyClass)recv).newInstance(context, args, Block.NULL_BLOCK);
- IRubyObject val = strio;
-
- if (block.isGiven()) {
- try {
- val = block.yield(context, strio);
- } finally {
- strio.doFinalize();
- }
- }
- return val;
- }
-
- protected RubyStringIO(Ruby runtime, RubyClass klass) {
- super(runtime, klass);
- }
-
- private long pos = 0L;
- private int lineno = 0;
- private boolean eof = false;
-
- /**
- * ATTN: the value of internal might be reset to null
- * (during StringIO.open with block), so watch out for that.
- */
- private RubyString internal;
-
- // Has read/write been closed or is it still open for business
- private boolean closedRead = false;
- private boolean closedWrite = false;
-
- // Support IO modes that this object was opened with
- ModeFlags modes;
-
- private void initializeModes(Object modeArgument) {
- try {
- if (modeArgument == null) {
- modes = new ModeFlags(RubyIO.getIOModesIntFromString(getRuntime(), "r+"));
- } else if (modeArgument instanceof Long) {
- modes = new ModeFlags(((Long)modeArgument).longValue());
- } else {
- modes = new ModeFlags(RubyIO.getIOModesIntFromString(getRuntime(), (String) modeArgument));
- }
- } catch (InvalidValueException e) {
- throw getRuntime().newErrnoEINVALError();
- }
- setupModes();
- }
-
- @JRubyMethod(name = "initialize", optional = 2, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- Object modeArgument = null;
- switch (args.length) {
- case 0:
- internal = RubyString.newEmptyString(getRuntime());
- modeArgument = "r+";
- break;
- case 1:
- internal = args[0].convertToString();
- modeArgument = internal.isFrozen() ? "r" : "r+";
- break;
- case 2:
- internal = args[0].convertToString();
- if (args[1] instanceof RubyFixnum) {
- modeArgument = RubyFixnum.fix2long(args[1]);
- } else {
- modeArgument = args[1].convertToString().toString();
- }
- break;
- }
-
- initializeModes(modeArgument);
-
- if (modes.isWritable() && internal.isFrozen()) {
- throw getRuntime().newErrnoEACCESError("Permission denied");
- }
-
- if (modes.isTruncate()) {
- internal.modifyCheck();
- internal.empty();
- }
-
- return this;
- }
-
- @JRubyMethod(visibility = Visibility.PRIVATE)
- public IRubyObject initialize_copy(IRubyObject other) {
-
- RubyStringIO otherIO = (RubyStringIO) TypeConverter.convertToType(
- other, getRuntime().fastGetClass("StringIO"),
- MethodIndex.getIndex("to_strio"), "to_strio");
-
- if (this == otherIO) {
- return this;
- }
-
- pos = otherIO.pos;
- lineno = otherIO.lineno;
- eof = otherIO.eof;
- closedRead = otherIO.closedRead;
- closedWrite = otherIO.closedWrite;
- internal = otherIO.internal;
- modes = otherIO.modes;
- if (otherIO.isTaint()) {
- setTaint(true);
- }
-
- return this;
- }
-
- @JRubyMethod(name = "<<", required = 1)
- public IRubyObject append(ThreadContext context, IRubyObject arg) {
- writeInternal(context, arg);
- return this;
- }
-
- @JRubyMethod(name = "binmode")
- public IRubyObject binmode() {
- return this;
- }
-
- @JRubyMethod(name = "close", frame=true)
- public IRubyObject close() {
- checkInitialized();
- checkOpen();
-
- closedRead = true;
- closedWrite = true;
-
- return getRuntime().getNil();
- }
-
- private void doFinalize() {
- closedRead = true;
- closedWrite = true;
- internal = null;
- }
-
- @JRubyMethod(name = "closed?")
- public IRubyObject closed_p() {
- checkInitialized();
- return getRuntime().newBoolean(closedRead && closedWrite);
- }
-
- @JRubyMethod(name = "close_read")
- public IRubyObject close_read() {
- checkReadable();
- closedRead = true;
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "closed_read?")
- public IRubyObject closed_read_p() {
- checkInitialized();
- return getRuntime().newBoolean(closedRead);
- }
-
- @JRubyMethod(name = "close_write")
- public IRubyObject close_write() {
- checkWritable();
- closedWrite = true;
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "closed_write?")
- public IRubyObject closed_write_p() {
- checkInitialized();
- return getRuntime().newBoolean(closedWrite);
- }
-
- @JRubyMethod(name = "each", optional = 1, frame = true, writes = FrameField.LASTLINE)
- public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) {
- IRubyObject line = gets(context, args);
-
- while (!line.isNil()) {
- block.yield(context, line);
- line = gets(context, args);
- }
-
- return this;
- }
-
- @JRubyMethod(name = "each_byte", frame = true)
- public IRubyObject each_byte(ThreadContext context, Block block) {
- checkReadable();
- Ruby runtime = context.getRuntime();
- ByteList bytes = internal.getByteList();
-
- // Check the length every iteration, since
- // the block can modify this string.
- while (pos < bytes.length()) {
- block.yield(context, runtime.newFixnum(bytes.get((int) pos++) & 0xFF));
- }
- return runtime.getNil();
- }
-
- @JRubyMethod(name = "each_line", optional = 1, frame = true)
- public IRubyObject each_line(ThreadContext context, IRubyObject[] args, Block block) {
- return each(context, args, block);
- }
-
- @JRubyMethod(name = {"eof", "eof?"})
- public IRubyObject eof() {
- return getRuntime().newBoolean(isEOF());
- }
-
- private boolean isEOF() {
- return (pos >= internal.getByteList().length()) || eof;
- }
-
- @JRubyMethod(name = "fcntl")
- public IRubyObject fcntl() {
- throw getRuntime().newNotImplementedError("fcntl not implemented");
- }
-
- @JRubyMethod(name = "fileno")
- public IRubyObject fileno() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "flush")
- public IRubyObject flush() {
- return this;
- }
-
- @JRubyMethod(name = "fsync")
- public IRubyObject fsync() {
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "getc")
- public IRubyObject getc() {
- checkReadable();
- if (pos >= internal.getByteList().length()) {
- return getRuntime().getNil();
- }
- return getRuntime().newFixnum(internal.getByteList().get((int)pos++) & 0xFF);
- }
-
- private IRubyObject internalGets(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
-
- if (pos < internal.getByteList().realSize && !eof) {
- boolean isParagraph = false;
-
- ByteList sep;
- if (args.length > 0) {
- if (args[0].isNil()) {
- ByteList buf = internal.getByteList().makeShared(
- (int)pos, internal.getByteList().realSize - (int)pos);
- pos += buf.realSize;
- return RubyString.newString(runtime, buf);
- }
- sep = args[0].convertToString().getByteList();
- if (sep.realSize == 0) {
- isParagraph = true;
- sep = Stream.PARAGRAPH_SEPARATOR;
- }
- } else {
- sep = ((RubyString)runtime.getGlobalVariables().get("$/")).getByteList();
- }
-
- ByteList ss = internal.getByteList();
-
- if (isParagraph) {
- swallowLF(ss);
- if (pos == ss.realSize) {
- return runtime.getNil();
- }
- }
-
- int ix = ss.indexOf(sep, (int)pos);
-
- ByteList add;
- if (-1 == ix) {
- ix = internal.getByteList().realSize;
- add = new ByteList(new byte[0], false);
- } else {
- add = isParagraph? NEWLINE : sep;
- }
-
- ByteList line = internal.getByteList().makeShared((int)pos, ix - (int)pos);
- line.unshare();
- line.append(add);
- line.invalidate();
- pos = ix + add.realSize;
- lineno++;
-
- return RubyString.newString(runtime,line);
- }
- return runtime.getNil();
- }
-
- private void swallowLF(ByteList list) {
- while (pos < list.realSize) {
- if (list.get((int)pos) == '\n') {
- pos++;
- } else {
- break;
- }
- }
- }
-
- @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
- public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
- checkReadable();
-
- IRubyObject result = internalGets(context, args);
- context.getCurrentFrame().setLastLine(result);
-
- return result;
- }
-
- @JRubyMethod(name = {"tty?", "isatty"})
- public IRubyObject isatty() {
- return getRuntime().getFalse();
- }
-
- @JRubyMethod(name = {"length", "size"})
- public IRubyObject length() {
- checkFinalized();
- return getRuntime().newFixnum(internal.getByteList().length());
- }
-
- @JRubyMethod(name = "lineno")
- public IRubyObject lineno() {
- return getRuntime().newFixnum(lineno);
- }
-
- @JRubyMethod(name = "lineno=", required = 1)
- public IRubyObject set_lineno(IRubyObject arg) {
- lineno = RubyNumeric.fix2int(arg);
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "path")
- public IRubyObject path() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "pid")
- public IRubyObject pid() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = {"pos", "tell"})
- public IRubyObject pos() {
- return getRuntime().newFixnum(pos);
- }
-
- @JRubyMethod(name = "pos=", required = 1)
- public IRubyObject set_pos(IRubyObject arg) {
- pos = RubyNumeric.fix2int(arg);
- if (pos < 0) {
- throw getRuntime().newErrnoEINVALError("Invalid argument");
- }
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "print", rest = true)
- public IRubyObject print(ThreadContext context, IRubyObject[] args) {
- Ruby runtime = context.getRuntime();
- if (args.length != 0) {
- for (int i=0,j=args.length;i<j;i++) {
- append(context, args[i]);
- }
- } else {
- IRubyObject arg = runtime.getGlobalVariables().get("$_");
- append(context, arg.isNil() ? runtime.newString("nil") : arg);
- }
- IRubyObject sep = runtime.getGlobalVariables().get("$\\");
- if (!sep.isNil()) {
- append(context, sep);
- }
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "printf", required = 1, rest = true)
- public IRubyObject printf(ThreadContext context, IRubyObject[] args) {
- append(context, RubyKernel.sprintf(context, this, args));
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "putc", required = 1)
- public IRubyObject putc(IRubyObject obj) {
- checkWritable();
- byte c = RubyNumeric.num2chr(obj);
- checkFrozen();
-
- internal.modify();
- ByteList bytes = internal.getByteList();
- if (modes.isAppendable()) {
- pos = bytes.length();
- bytes.append(c);
- } else {
- if (pos >= bytes.length()) {
- bytes.length((int)pos + 1);
- }
-
- bytes.set((int) pos, c);
- pos++;
- }
-
- return obj;
- }
-
- public static final ByteList NEWLINE = ByteList.create("\n");
-
- @JRubyMethod(name = "puts", rest = true)
- public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
- checkWritable();
-
- // FIXME: the code below is a copy of RubyIO.puts,
- // and we should avoid copy-paste.
-
- if (args.length == 0) {
- callMethod(context, "write", RubyString.newStringShared(getRuntime(), NEWLINE));
- return getRuntime().getNil();
- }
-
- for (int i = 0; i < args.length; i++) {
- String line;
-
- if (args[i].isNil()) {
- line = "nil";
- } else {
- IRubyObject tmp = args[i].checkArrayType();
- if (!tmp.isNil()) {
- RubyArray arr = (RubyArray) tmp;
- if (getRuntime().isInspecting(arr)) {
- line = "[...]";
- } else {
- inspectPuts(context, arr);
- continue;
- }
- } else {
- line = args[i].toString();
- }
- }
-
- callMethod(context, "write", getRuntime().newString(line));
-
- if (!line.endsWith("\n")) {
- callMethod(context, "write", RubyString.newStringShared(getRuntime(), NEWLINE));
- }
- }
- return getRuntime().getNil();
- }
-
- private IRubyObject inspectPuts(ThreadContext context, RubyArray array) {
- try {
- getRuntime().registerInspecting(array);
- return puts(context, array.toJavaArray());
- } finally {
- getRuntime().unregisterInspecting(array);
- }
- }
-
- @SuppressWarnings("fallthrough")
- @JRubyMethod(name = "read", optional = 2)
- public IRubyObject read(IRubyObject[] args) {
- checkReadable();
-
- ByteList buf = null;
- int length = 0;
- int oldLength = 0;
- RubyString originalString = null;
-
- switch (args.length) {
- case 2:
- originalString = args[1].convertToString();
- // must let original string know we're modifying, so shared buffers aren't damaged
- originalString.modify();
- buf = originalString.getByteList();
- case 1:
- if (!args[0].isNil()) {
- length = RubyNumeric.fix2int(args[0]);
- oldLength = length;
-
- if (length < 0) {
- throw getRuntime().newArgumentError("negative length " + length + " given");
- }
- if (length > 0 && pos >= internal.getByteList().length()) {
- eof = true;
- if (buf != null) buf.realSize = 0;
- return getRuntime().getNil();
- } else if (eof) {
- if (buf != null) buf.realSize = 0;
- return getRuntime().getNil();
- }
- break;
- }
- case 0:
- oldLength = -1;
- length = internal.getByteList().length();
-
- if (length <= pos) {
- eof = true;
- if (buf == null) {
- buf = new ByteList();
- } else {
- buf.realSize = 0;
- }
-
- return getRuntime().newString(buf);
- } else {
- length -= pos;
- }
- break;
- default:
- getRuntime().newArgumentError(args.length, 0);
- }
-
- if (buf == null) {
- int internalLength = internal.getByteList().length();
-
- if (internalLength > 0) {
- if (internalLength >= pos + length) {
- buf = new ByteList(internal.getByteList(), (int) pos, length);
- } else {
- int rest = (int) (internal.getByteList().length() - pos);
-
- if (length > rest) length = rest;
- buf = new ByteList(internal.getByteList(), (int) pos, length);
- }
- }
- } else {
- int rest = (int) (internal.getByteList().length() - pos);
-
- if (length > rest) length = rest;
-
- // Yow...this is ugly
- buf.realSize = length;
- buf.replace(0, length, internal.getByteList().bytes, (int) pos, length);
- }
-
- if (buf == null) {
- if (!eof) buf = new ByteList();
- length = 0;
- } else {
- length = buf.length();
- pos += length;
- }
-
- if (oldLength < 0 || oldLength > length) eof = true;
-
- return originalString != null ? originalString : getRuntime().newString(buf);
- }
-
- @JRubyMethod(name = "readchar")
- public IRubyObject readchar() {
- IRubyObject c = getc();
-
- if (c.isNil()) throw getRuntime().newEOFError();
-
- return c;
- }
-
- @JRubyMethod(name = "readline", optional = 1, writes = FrameField.LASTLINE)
- public IRubyObject readline(ThreadContext context, IRubyObject[] args) {
- IRubyObject line = gets(context, args);
-
- if (line.isNil()) throw getRuntime().newEOFError();
-
- return line;
- }
-
- @JRubyMethod(name = "readlines", optional = 1)
- public IRubyObject readlines(ThreadContext context, IRubyObject[] arg) {
- checkReadable();
-
- List<IRubyObject> lns = new ArrayList<IRubyObject>();
- while (!(isEOF())) {
- IRubyObject line = internalGets(context, arg);
- if (line.isNil()) {
- break;
- }
- lns.add(line);
- }
-
- return getRuntime().newArray(lns);
- }
-
- @JRubyMethod(name = "reopen", required = 0, optional = 2)
- public IRubyObject reopen(IRubyObject[] args) {
- if (args.length == 1 && !(args[0] instanceof RubyString)) {
- return initialize_copy(args[0]);
- }
-
- // reset the state
- doRewind();
- closedRead = false;
- closedWrite = false;
- return initialize(args, Block.NULL_BLOCK);
- }
-
- @JRubyMethod(name = "rewind")
- public IRubyObject rewind() {
- doRewind();
- return RubyFixnum.zero(getRuntime());
- }
-
- private void doRewind() {
- this.pos = 0L;
- this.eof = false;
- this.lineno = 0;
- }
-
- @JRubyMethod(name = "seek", required = 1, optional = 1, frame=true)
- public IRubyObject seek(IRubyObject[] args) {
- // MRI 1.8.7 behavior:
- // checkOpen();
- checkFinalized();
- long amount = RubyNumeric.num2long(args[0]);
- int whence = Stream.SEEK_SET;
- long newPosition = pos;
-
- if (args.length > 1 && !args[0].isNil()) whence = RubyNumeric.fix2int(args[1]);
-
- if (whence == Stream.SEEK_CUR) {
- newPosition += amount;
- } else if (whence == Stream.SEEK_END) {
- newPosition = internal.getByteList().length() + amount;
- } else {
- newPosition = amount;
- }
-
- if (newPosition < 0) throw getRuntime().newErrnoEINVALError();
-
- pos = newPosition;
- eof = false;
-
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "string=", required = 1)
- public IRubyObject set_string(IRubyObject arg) {
- return reopen(new IRubyObject[] { arg.convertToString() });
- }
-
- @JRubyMethod(name = "sync=", required = 1)
- public IRubyObject set_sync(IRubyObject args) {
- return args;
- }
-
- @JRubyMethod(name = "string")
- public IRubyObject string() {
- if (internal == null) {
- return getRuntime().getNil();
- } else {
- return internal;
- }
- }
-
- @JRubyMethod(name = "sync")
- public IRubyObject sync() {
- return getRuntime().getTrue();
- }
-
- @JRubyMethod(name = "sysread", optional = 2)
- public IRubyObject sysread(IRubyObject[] args) {
- IRubyObject obj = read(args);
-
- if (isEOF()) {
- if (obj.isNil() || ((RubyString) obj).getByteList().length() == 0) {
- throw getRuntime().newEOFError();
- }
- }
-
- return obj;
- }
-
- @JRubyMethod(name = "truncate", required = 1)
- public IRubyObject truncate(IRubyObject arg) {
- checkWritable();
-
- int len = RubyFixnum.fix2int(arg);
- if (len < 0) {
- throw getRuntime().newErrnoEINVALError("negative legnth");
- }
-
- internal.modify();
- internal.getByteList().length(len);
- return arg;
- }
-
- @JRubyMethod(name = "ungetc", required = 1)
- public IRubyObject ungetc(IRubyObject arg) {
- checkReadable();
-
- int c = RubyNumeric.num2int(arg);
- if (pos == 0) return getRuntime().getNil();
- internal.modify();
- pos--;
-
- ByteList bytes = internal.getByteList();
-
- if (bytes.length() <= pos) {
- bytes.length((int)pos + 1);
- }
-
- bytes.set((int) pos, c);
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = {"write", "syswrite"}, required = 1)
- public IRubyObject write(ThreadContext context, IRubyObject arg) {
- return context.getRuntime().newFixnum(writeInternal(context, arg));
- }
-
- private int writeInternal(ThreadContext context, IRubyObject arg) {
- checkWritable();
- checkFrozen();
-
- RubyString val = arg.asString();
- internal.modify();
-
- if (modes.isAppendable()) {
- internal.getByteList().append(val.getByteList());
- pos = internal.getByteList().length();
- } else {
- int left = internal.getByteList().length()-(int)pos;
- internal.getByteList().replace((int)pos,Math.min(val.getByteList().length(),left),val.getByteList());
- pos += val.getByteList().length();
- }
-
- if (val.isTaint()) {
- internal.setTaint(true);
- }
-
- return val.getByteList().length();
- }
-
- /* rb: check_modifiable */
- @Override
- protected void checkFrozen() {
- checkInitialized();
- if (internal.isFrozen()) throw getRuntime().newIOError("not modifiable string");
- }
-
- /* rb: readable */
- private void checkReadable() {
- checkInitialized();
- if (closedRead || !modes.isReadable()) {
- throw getRuntime().newIOError("not opened for reading");
- }
- }
-
- /* rb: writable */
- private void checkWritable() {
- checkInitialized();
- if (closedWrite || !modes.isWritable()) {
- throw getRuntime().newIOError("not opened for writing");
- }
-
- // Tainting here if we ever want it. (secure 4)
- }
-
- private void checkInitialized() {
- if (modes == null) {
- throw getRuntime().newIOError("uninitialized stream");
- }
- }
-
- private void checkFinalized() {
- if (internal == null) {
- throw getRuntime().newIOError("not opened");
- }
- }
-
- private void checkOpen() {
- if (closedRead && closedWrite) {
- throw getRuntime().newIOError("closed stream");
- }
- }
-
- private void setupModes() {
- closedWrite = false;
- closedRead = false;
-
- if (modes.isReadOnly()) closedWrite = true;
- if (!modes.isReadable()) closedRead = true;
- }
-}
-package org.jruby;
-
-import org.joni.Matcher;
-import org.joni.Option;
-import org.joni.Regex;
-import org.joni.Region;
-import org.joni.encoding.Encoding;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.runtime.Block;
-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;
-
-/**
- * @author kscott
- *
- */
-@JRubyClass(name="StringScanner")
-public class RubyStringScanner extends RubyObject {
-
- private RubyString str;
- private int pos = 0;
- private int lastPos = -1;
-
- private Region regs;
- private int beg = -1;
- private int end = -1;
- // not to be confused with RubyObject's flags
- private int scannerFlags;
-
- private static final int MATCHED_STR_SCN_F = 1 << 11;
-
- private static ObjectAllocator STRINGSCANNER_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyStringScanner(runtime, klass);
- }
- };
-
- public static RubyClass createScannerClass(final Ruby runtime) {
- RubyClass scannerClass = runtime.defineClass("StringScanner", runtime.getObject(), STRINGSCANNER_ALLOCATOR);
- scannerClass.defineAnnotatedMethods(RubyStringScanner.class);
- ThreadContext context = runtime.getCurrentContext();
- scannerClass.setConstant("Version", runtime.newString("0.7.0").freeze(context));
- scannerClass.setConstant("Id", runtime.newString("$Id: strscan.c 13506 2007-09-24 08:56:24Z nobu $").freeze(context));
-
- RubyClass standardError = runtime.getStandardError();
- RubyClass error = scannerClass.defineClassUnder(
- "Error", standardError, standardError.getAllocator());
-
- RubyClass objClass = runtime.getObject();
- if (!objClass.isConstantDefined("ScanError")) {
- objClass.defineConstant("ScanError", error);
- }
-
- return scannerClass;
- }
-
- private void clearMatched() {
- scannerFlags &= ~MATCHED_STR_SCN_F;
- }
-
- private void setMatched() {
- scannerFlags |= MATCHED_STR_SCN_F;
- }
-
- private boolean isMatched() {
- return (scannerFlags & MATCHED_STR_SCN_F) != 0;
- }
-
- private void check() {
- if (str == null) throw getRuntime().newArgumentError("uninitialized StringScanner object");
- }
-
- protected RubyStringScanner(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- // second argument is allowed, but ignored (MRI)
- @JRubyMethod(name = "initialize", required = 1, optional = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- str = args[0].convertToString();
- return this;
- }
-
- @JRubyMethod(name = "initialize_copy", frame=true, visibility = Visibility.PRIVATE)
- @Override
- public IRubyObject initialize_copy(IRubyObject other) {
- if (this == other) return this;
- if (!(other instanceof RubyStringScanner)) {
- throw getRuntime().newTypeError("wrong argument type "
- + other.getMetaClass() + " (expected StringScanner)");
- }
-
- RubyStringScanner otherScanner = (RubyStringScanner)other;
- str = otherScanner.str;
- pos = otherScanner.pos;
- lastPos = otherScanner.lastPos;
- scannerFlags = otherScanner.scannerFlags;
-
- regs = otherScanner.regs != null ? otherScanner.regs.clone() : null;
- beg = otherScanner.beg;
- end = otherScanner.end;
-
- return this;
- }
-
- @JRubyMethod(name = "reset")
- public IRubyObject reset() {
- check();
- pos = 0;
- clearMatched();
- return this;
- }
-
- @JRubyMethod(name = "terminate")
- public IRubyObject terminate() {
- check();
- pos = str.getByteList().realSize;
- clearMatched();
- return this;
- }
-
- @JRubyMethod(name = "clear")
- public IRubyObject clear() {
- check();
- getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#clear is obsolete; use #terminate instead", "StringScanner#clear", "#terminate");
- return terminate();
- }
-
- @JRubyMethod(name = "string")
- public RubyString string() {
- return str;
- }
-
- @JRubyMethod(name = "string=", required = 1)
- public IRubyObject set_string(ThreadContext context, IRubyObject str) {
- this.str = (RubyString) str.convertToString().strDup(context.getRuntime()).freeze(context);
- pos = 0;
- clearMatched();
- return str;
- }
-
- @JRubyMethod(name = {"concat", "<<"}, required = 1)
- public IRubyObject concat(IRubyObject obj) {
- check();
- str.append(obj); // append will call convertToString()
- return this;
- }
-
- @JRubyMethod(name = {"pos", "pointer"})
- public RubyFixnum pos() {
- check();
- return RubyFixnum.newFixnum(getRuntime(), pos);
- }
-
- @JRubyMethod(name = {"pos=", "pointer="})
- public IRubyObject set_pos(IRubyObject pos) {
- check();
- int i = RubyNumeric.num2int(pos);
- int size = str.getByteList().realSize;
- if (i < 0) i += size;
- if (i < 0 || i > size) throw getRuntime().newRangeError("index out of range.");
- this.pos = i;
- return RubyFixnum.newFixnum(getRuntime(), i);
- }
-
- private IRubyObject extractRange(Ruby runtime, int beg, int end) {
- int size = str.getByteList().realSize;
- if (beg > size) return getRuntime().getNil();
- if (end > size) end = size;
- return str.makeShared(runtime, beg, end - beg);
- }
-
- private IRubyObject extractBegLen(Ruby runtime, int beg, int len) {
- assert len >= 0;
- int size = str.getByteList().realSize;
- if (beg > size) return getRuntime().getNil();
- if (beg + len > size) len = size - beg;
- return str.makeShared(runtime, beg, len);
- }
-
- private IRubyObject scan(IRubyObject regex, boolean succptr, boolean getstr, boolean headonly) {
- if (!(regex instanceof RubyRegexp)) throw getRuntime().newTypeError("wrong argument type " + regex.getMetaClass() + " (expected Regexp)");
- check();
-
- Regex pattern = ((RubyRegexp)regex).getPattern();
-
- clearMatched();
- int rest = str.getByteList().realSize - pos;
- if (rest < 0) return getRuntime().getNil();
-
- ByteList value = str.getByteList();
- Matcher matcher = pattern.matcher(value.bytes, value.begin + pos, value.begin + value.realSize);
-
- final int ret;
- if (headonly) {
- ret = matcher.match(value.begin + pos, value.begin + value.realSize, Option.NONE);
- } else {
- ret = matcher.search(value.begin + pos, value.begin + value.realSize, Option.NONE);
- }
-
- regs = matcher.getRegion();
- if (regs == null) {
- beg = matcher.getBegin();
- end = matcher.getEnd();
- } else {
- beg = regs.beg[0];
- end = regs.end[0];
- }
-
- if (ret < 0) return getRuntime().getNil();
- setMatched();
-
- lastPos = pos;
- if (succptr) pos += end;
- return getstr ? extractBegLen(getRuntime(), lastPos, end) : RubyFixnum.newFixnum(getRuntime(), end);
- }
-
- @JRubyMethod(name = "scan", required = 1)
- public IRubyObject scan(IRubyObject regex) {
- return scan(regex, true, true, true);
- }
-
- @JRubyMethod(name = "match?", required = 1)
- public IRubyObject match_p(IRubyObject regex) {
- return scan(regex, false, false, true);
- }
-
- @JRubyMethod(name = "skip", required = 1)
- public IRubyObject skip(IRubyObject regex) {
- return scan(regex, true, false, true);
- }
-
- @JRubyMethod(name = "check", required = 1)
- public IRubyObject check(IRubyObject regex) {
- return scan(regex, false, true, true);
- }
-
- @JRubyMethod(name = "scan_full", required = 3)
- public IRubyObject scan_full(IRubyObject regex, IRubyObject s, IRubyObject f) {
- return scan(regex, s.isTrue(), f.isTrue(), true);
- }
-
- @JRubyMethod(name = "scan_until", required = 1)
- public IRubyObject scan_until(IRubyObject regex) {
- return scan(regex, true, true, false);
- }
-
- @JRubyMethod(name = "exist?", required = 1)
- public IRubyObject exist_p(IRubyObject regex) {
- return scan(regex, false, false, false);
- }
-
- @JRubyMethod(name = "skip_until", required = 1)
- public IRubyObject skip_until(IRubyObject regex) {
- return scan(regex, true, false, false);
- }
-
- @JRubyMethod(name = "check_until", required = 1)
- public IRubyObject check_until(IRubyObject regex) {
- return scan(regex, false, true, false);
- }
-
- @JRubyMethod(name = "search_full", required = 3)
- public IRubyObject search_full(IRubyObject regex, IRubyObject s, IRubyObject f) {
- return scan(regex, s.isTrue(), f.isTrue(), false);
- }
-
- private void adjustRegisters() {
- beg = 0;
- end = pos - lastPos;
- regs = null;
- }
-
- @JRubyMethod(name = "getch")
- public IRubyObject getch(ThreadContext context) {
- check();
- clearMatched();
-
- Ruby runtime = context.getRuntime();
- ByteList value = str.getByteList();
-
- if (pos >= value.realSize) return runtime.getNil();
-
- Encoding enc = runtime.getKCode().getEncoding();
-
- int len;
- if (enc.isSingleByte()) {
- len = 1;
- } else {
- len = enc.length(value.bytes[value.begin + pos]);
- }
-
- if (pos + len > value.realSize) len = value.realSize - pos;
- lastPos = pos;
- pos += len;
-
- setMatched();
- adjustRegisters();
-
- return extractRange(runtime, lastPos + beg, lastPos + end);
- }
-
- @JRubyMethod(name = "get_byte")
- public IRubyObject get_byte(ThreadContext context) {
- check();
- clearMatched();
- if (pos >= str.getByteList().realSize) return getRuntime().getNil();
-
- lastPos = pos;
- pos++;
-
- setMatched();
- adjustRegisters();
-
- return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
- }
-
- @JRubyMethod(name = "getbyte")
- public IRubyObject getbyte(ThreadContext context) {
- context.getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD,
- "StringScanner#getbyte is obsolete; use #get_byte instead",
- "StringScanner#getbyte", "#get_byte");
- return get_byte(context);
- }
-
- @JRubyMethod(name = "peek", required = 1)
- public IRubyObject peek(ThreadContext context, IRubyObject length) {
- check();
-
- int len = RubyNumeric.num2int(length);
- if (len < 0) {
- throw context.getRuntime().newArgumentError("negative string size (or size too big)");
- }
-
- ByteList value = str.getByteList();
- if (pos >= value.realSize) return RubyString.newEmptyString(getRuntime()).infectBy(str);
- if (pos + len > value.realSize) len = value.realSize - pos;
-
- return extractBegLen(context.getRuntime(), pos, len);
- }
-
- @JRubyMethod(name = "peep", required = 1)
- public IRubyObject peep(ThreadContext context, IRubyObject length) {
- getRuntime().getWarnings().warning(
- ID.DEPRECATED_METHOD, "StringScanner#peep is obsolete; use #peek instead",
- "StringScanner#peep", "#peek");
- return peek(context, length);
- }
-
- @JRubyMethod(name = "unscan")
- public IRubyObject unscan() {
- check();
- Ruby runtime = getRuntime();
-
- if (!isMatched()) {
- RubyClass errorClass = runtime.fastGetClass("StringScanner").fastGetClass("Error");
- throw new RaiseException(RubyException.newException(
- runtime, errorClass, "unscan failed: previous match had failed"));
- }
- pos = lastPos;
- clearMatched();
- return this;
- }
-
- @JRubyMethod(name = "beginning_of_line?", alias = "bol?")
- public IRubyObject bol_p() {
- check();
- ByteList value = str.getByteList();
- if (pos > value.realSize) return getRuntime().getNil();
- if (pos == 0) return getRuntime().getTrue();
- return value.bytes[(value.begin + pos) - 1] == (byte)'\n' ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "eos?")
- public RubyBoolean eos_p(ThreadContext context) {
- check();
- return pos >= str.getByteList().realSize ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "empty?")
- public RubyBoolean empty_p(ThreadContext context) {
- getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#empty? is obsolete; use #eos? instead", "StringScanner#empty?", "#eos?");
- return eos_p(context);
- }
-
- @JRubyMethod(name = "rest?")
- public RubyBoolean rest_p(ThreadContext context) {
- check();
- return pos >= str.getByteList().realSize ? context.getRuntime().getFalse() : context.getRuntime().getTrue();
- }
-
- @JRubyMethod(name = "matched?")
- public RubyBoolean matched_p(ThreadContext context) {
- check();
- return isMatched() ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "matched")
- public IRubyObject matched(ThreadContext context) {
- check();
- if (!isMatched()) return getRuntime().getNil();
- return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
- }
-
- @JRubyMethod(name = "matched_size")
- public IRubyObject matched_size() {
- check();
- if (!isMatched()) return getRuntime().getNil();
- return RubyFixnum.newFixnum(getRuntime(), end - beg);
- }
-
- @JRubyMethod(name = "matchedsize")
- public IRubyObject matchedsize() {
- getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#matchedsize is obsolete; use #matched_size instead",
- "StringScanner#matchedize", "#matched_size");
- return matched_size();
- }
-
- @JRubyMethod(name = "[]", required = 1)
- public IRubyObject op_aref(ThreadContext context, IRubyObject idx) {
- check();
- if (!isMatched()) return context.getRuntime().getNil();
- int i = RubyNumeric.num2int(idx);
-
- int numRegs = regs == null ? 1 : regs.numRegs;
- if (i < 0) i += numRegs;
- if (i < 0 || i >= numRegs) return context.getRuntime().getNil();
-
- if (regs == null) {
- assert i == 0;
- if (beg == -1) return getRuntime().getNil();
- return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
- } else {
- if (regs.beg[i] == -1) return getRuntime().getNil();
- return extractRange(context.getRuntime(), lastPos + regs.beg[i], lastPos + regs.end[i]);
- }
- }
-
- @JRubyMethod(name = "pre_match")
- public IRubyObject pre_match(ThreadContext context) {
- check();
- if (!isMatched()) return context.getRuntime().getNil();
- return extractRange(context.getRuntime(), 0, lastPos + beg);
- }
-
- @JRubyMethod(name = "post_match")
- public IRubyObject post_match(ThreadContext context) {
- check();
- if (!isMatched()) return context.getRuntime().getNil();
- return extractRange(context.getRuntime(), lastPos + end, str.getByteList().realSize);
- }
-
- @JRubyMethod(name = "rest")
- public IRubyObject rest(ThreadContext context) {
- check();
- ByteList value = str.getByteList();
- if (pos >= value.realSize) return RubyString.newEmptyString(context.getRuntime()).infectBy(str);
- return extractRange(context.getRuntime(), pos, value.realSize);
- }
-
- @JRubyMethod(name = "rest_size")
- public RubyFixnum rest_size() {
- check();
- ByteList value = str.getByteList();
- if (pos >= value.realSize) return RubyFixnum.zero(getRuntime());
- return RubyFixnum.newFixnum(getRuntime(), value.realSize - pos);
- }
-
- @JRubyMethod(name = "restsize")
- public RubyFixnum restsize() {
- getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#restsize is obsolete; use #rest_size instead", "StringScanner#restsize", "#rest_size");
- return rest_size();
- }
-
- @JRubyMethod(name = "inspect")
- @Override
- public IRubyObject inspect() {
- if (str == null) return inspect("(uninitialized)");
- if (pos >= str.getByteList().realSize) return inspect("fin");
- if (pos == 0) return inspect(pos + "/" + str.getByteList().realSize + " @ " + inspect2());
- return inspect(pos + "/" + str.getByteList().realSize + " " + inspect1() + " @ " + inspect2());
- }
-
- private IRubyObject inspect(String msg) {
- IRubyObject result = getRuntime().newString("#<" + getMetaClass() + " " + msg + ">");
- if (str != null) result.infectBy(str);
- return result;
- }
-
- private static final int INSPECT_LENGTH = 5;
-
- private IRubyObject inspect1() {
- if (pos == 0) return RubyString.newEmptyString(getRuntime());
- if (pos > INSPECT_LENGTH) {
- return RubyString.newString(getRuntime(), "...".getBytes()).append(str.substr(pos - INSPECT_LENGTH, INSPECT_LENGTH)).inspect();
- } else {
- return str.substr(0, pos).inspect();
- }
- }
-
- private IRubyObject inspect2() {
- if (pos >= str.getByteList().realSize) return RubyString.newEmptyString(getRuntime());
- int len = str.getByteList().realSize - pos;
- if (len > INSPECT_LENGTH) {
- return ((RubyString)str.substr(pos, INSPECT_LENGTH)).cat("...".getBytes()).inspect();
- } else {
- return str.substr(pos, len).inspect();
- }
- }
-
- @JRubyMethod(name = "must_C_version", meta = true)
- public static IRubyObject mustCversion(IRubyObject recv) {
- return recv;
- }
-}
-/***** 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-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) 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 java.util.List;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.Frame;
-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.IdUtil;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.internal.runtime.methods.CallConfiguration;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-import org.jruby.runtime.ClassIndex;
-
-/**
- * @author jpetersen
- */
-@JRubyClass(name="Struct")
-public class RubyStruct extends RubyObject {
- private IRubyObject[] values;
-
- /**
- * Constructor for RubyStruct.
- * @param runtime
- * @param rubyClass
- */
- public RubyStruct(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
-
- int size = RubyNumeric.fix2int(getInternalVariable((RubyClass)rubyClass, "__size__"));
-
- values = new IRubyObject[size];
-
- for (int i = 0; i < size; i++) {
- values[i] = getRuntime().getNil();
- }
- }
-
- public static RubyClass createStructClass(Ruby runtime) {
- // TODO: NOT_ALLOCATABLE_ALLOCATOR may be ok here, but it's unclear how Structs
- // work with marshalling. Confirm behavior and ensure we're doing this correctly. JRUBY-415
- RubyClass structClass = runtime.defineClass("Struct", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setStructClass(structClass);
- structClass.index = ClassIndex.STRUCT;
- structClass.includeModule(runtime.getEnumerable());
- structClass.defineAnnotatedMethods(RubyStruct.class);
-
- return structClass;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.STRUCT;
- }
-
- private static IRubyObject getInternalVariable(RubyClass type, String internedName) {
- RubyClass structClass = type.getRuntime().getStructClass();
- IRubyObject variable;
-
- while (type != null && type != structClass) {
- if ((variable = type.fastGetInternalVariable(internedName)) != null) {
- return variable;
- }
-
- type = type.getSuperClass();
- }
-
- return type.getRuntime().getNil();
- }
-
- private RubyClass classOf() {
- return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass();
- }
-
- private void modify() {
- testFrozen("Struct is frozen");
-
- if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
- throw getRuntime().newSecurityError("Insecure: can't modify struct");
- }
- }
-
- @JRubyMethod
- public RubyFixnum hash(ThreadContext context) {
- Ruby runtime = getRuntime();
- int h = getMetaClass().getRealClass().hashCode();
-
- for (int i = 0; i < values.length; i++) {
- h = (h << 1) | (h < 0 ? 1 : 0);
- h ^= RubyNumeric.num2long(values[i].callMethod(context, MethodIndex.HASH, "hash"));
- }
-
- return runtime.newFixnum(h);
- }
-
- private IRubyObject setByName(String name, IRubyObject value) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- modify();
-
- for (int i = 0,k=member.getLength(); i < k; i++) {
- if (member.eltInternal(i).asJavaString().equals(name)) {
- return values[i] = value;
- }
- }
-
- throw notStructMemberError(name);
- }
-
- private IRubyObject getByName(String name) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- for (int i = 0,k=member.getLength(); i < k; i++) {
- if (member.eltInternal(i).asJavaString().equals(name)) {
- return values[i];
- }
- }
-
- throw notStructMemberError(name);
- }
-
- // Struct methods
-
- /** Create new Struct class.
- *
- * MRI: rb_struct_s_def / make_struct
- *
- */
- @JRubyMethod(name = "new", required = 1, rest = true, frame = true, meta = true)
- public static RubyClass newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
- String name = null;
- boolean nilName = false;
- Ruby runtime = recv.getRuntime();
-
- if (args.length > 0) {
- IRubyObject firstArgAsString = args[0].checkStringType();
- if (!firstArgAsString.isNil()) {
- name = ((RubyString)firstArgAsString).getByteList().toString();
- } else if (args[0].isNil()) {
- nilName = true;
- }
- }
-
- RubyArray member = runtime.newArray();
-
- for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
- member.append(runtime.newSymbol(args[i].asJavaString()));
- }
-
- RubyClass newStruct;
- RubyClass superClass = (RubyClass)recv;
-
- if (name == null || nilName) {
- newStruct = RubyClass.newClass(runtime, superClass);
- newStruct.setAllocator(STRUCT_INSTANCE_ALLOCATOR);
- newStruct.makeMetaClass(superClass.getMetaClass());
- newStruct.inherit(superClass);
- } else {
- if (!IdUtil.isConstant(name)) {
- throw runtime.newNameError("identifier " + name + " needs to be constant", name);
- }
-
- IRubyObject type = superClass.getConstantAt(name);
- if (type != null) {
- ThreadContext context = runtime.getCurrentContext();
- Frame frame = context.getCurrentFrame();
- runtime.getWarnings().warn(ID.STRUCT_CONSTANT_REDEFINED, frame.getFile(), frame.getLine(), "redefining constant Struct::" + name, name);
- superClass.remove_const(context, runtime.newString(name));
- }
- newStruct = superClass.defineClassUnder(name, superClass, STRUCT_INSTANCE_ALLOCATOR);
- }
-
- newStruct.index = ClassIndex.STRUCT;
-
- newStruct.fastSetInternalVariable("__size__", member.length());
- newStruct.fastSetInternalVariable("__member__", member);
-
- newStruct.getSingletonClass().defineAnnotatedMethods(StructMethods.class);
-
- // define access methods.
- for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
- final String memberName = args[i].asJavaString();
- // if we are storing a name as well, index is one too high for values
- final int index = (name == null && !nilName) ? i : i - 1;
- newStruct.addMethod(memberName, new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.NO_FRAME_NO_SCOPE) {
- @Override
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
- Arity.checkArgumentCount(self.getRuntime(), args, 0, 0);
- return ((RubyStruct)self).get(index);
- }
-
- @Override
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
- return ((RubyStruct)self).get(index);
- }
-
- @Override
- public DynamicMethod dup() {
- return this;
- }
- });
- newStruct.addMethod(memberName + "=", new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.NO_FRAME_NO_SCOPE) {
- @Override
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
- Arity.checkArgumentCount(self.getRuntime(), args, 1, 1);
- return ((RubyStruct)self).set(args[0], index);
- }
-
- @Override
- public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg) {
- return ((RubyStruct)self).set(arg, index);
- }
-
- @Override
- public DynamicMethod dup() {
- return this;
- }
- });
- }
-
- if (block.isGiven()) {
- // Struct bodies should be public by default, so set block visibility to public. JRUBY-1185.
- block.getBinding().setVisibility(Visibility.PUBLIC);
- block.yield(runtime.getCurrentContext(), null, newStruct, newStruct, false);
- }
-
- return newStruct;
- }
-
- // For binding purposes on the newly created struct types
- public static class StructMethods {
- @JRubyMethod(name = {"new", "[]"}, rest = true, frame = true)
- public static IRubyObject newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
- return RubyStruct.newStruct(recv, args, block);
- }
-
- @JRubyMethod
- public static IRubyObject members(IRubyObject recv, Block block) {
- return RubyStruct.members(recv, block);
- }
- }
-
- /** Create new Structure.
- *
- * MRI: struct_alloc
- *
- */
- public static RubyStruct newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
- RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClass) recv);
-
- struct.callInit(args, block);
-
- return struct;
- }
-
- @JRubyMethod(rest = true, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
- modify();
-
- int size = RubyNumeric.fix2int(getInternalVariable(getMetaClass(), "__size__"));
-
- if (args.length > size) {
- throw getRuntime().newArgumentError("struct size differs (" + args.length +" for " + size + ")");
- }
-
- for (int i = 0; i < args.length; i++) {
- values[i] = args[i];
- }
-
- return getRuntime().getNil();
- }
-
- public static RubyArray members(IRubyObject recv, Block block) {
- RubyArray member = (RubyArray) getInternalVariable((RubyClass) recv, "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- RubyArray result = recv.getRuntime().newArray(member.getLength());
- for (int i = 0,k=member.getLength(); i < k; i++) {
- result.append(recv.getRuntime().newString(member.eltInternal(i).asJavaString()));
- }
-
- return result;
- }
-
- @JRubyMethod
- public RubyArray members() {
- return members(classOf(), Block.NULL_BLOCK);
- }
-
- @JRubyMethod
- public RubyArray select(ThreadContext context, Block block) {
- RubyArray array = RubyArray.newArray(context.getRuntime());
-
- for (int i = 0; i < values.length; i++) {
- if (block.yield(context, values[i]).isTrue()) {
- array.append(values[i]);
- }
- }
-
- return array;
- }
-
- public IRubyObject set(IRubyObject value, int index) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- modify();
-
- return values[index] = value;
- }
-
- private RaiseException notStructMemberError(String name) {
- return getRuntime().newNameError(name + " is not struct member", name);
- }
-
- public IRubyObject get(int index) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- return values[index];
- }
-
- @Override
- public void copySpecialInstanceVariables(IRubyObject clone) {
- RubyStruct struct = (RubyStruct)clone;
- struct.values = new IRubyObject[values.length];
- System.arraycopy(values, 0, struct.values, 0, values.length);
- }
-
- @JRubyMethod(name = "==", required = 1)
- public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
- if (this == other) return getRuntime().getTrue();
- if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
- if (getMetaClass().getRealClass() != other.getMetaClass().getRealClass()) return getRuntime().getFalse();
-
- Ruby runtime = getRuntime();
- RubyStruct otherStruct = (RubyStruct)other;
- for (int i = 0; i < values.length; i++) {
- if (!equalInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
- }
- return runtime.getTrue();
- }
-
- @JRubyMethod(name = "eql?", required = 1)
- public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
- if (this == other) return getRuntime().getTrue();
- if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
- if (getMetaClass() != other.getMetaClass()) return getRuntime().getFalse();
-
- Ruby runtime = getRuntime();
- RubyStruct otherStruct = (RubyStruct)other;
- for (int i = 0; i < values.length; i++) {
- if (!eqlInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
- }
- return runtime.getTrue();
- }
-
- /** inspect_struct
- *
- */
- private IRubyObject inspectStruct(final ThreadContext context) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- ByteList buffer = new ByteList("#<struct ".getBytes());
- buffer.append(getMetaClass().getRealClass().getRealClass().getName().getBytes());
- buffer.append(' ');
-
- for (int i = 0,k=member.getLength(); i < k; i++) {
- if (i > 0) buffer.append(',').append(' ');
- // FIXME: MRI has special case for constants here
- buffer.append(RubyString.objAsString(context, member.eltInternal(i)).getByteList());
- buffer.append('=');
- buffer.append(inspect(context, values[i]).getByteList());
- }
-
- buffer.append('>');
- return getRuntime().newString(buffer); // OBJ_INFECT
- }
-
- @JRubyMethod(name = {"inspect", "to_s"})
- public IRubyObject inspect(ThreadContext context) {
- if (getRuntime().isInspecting(this)) return getRuntime().newString("#<struct " + getMetaClass().getRealClass().getName() + ":...>");
-
- try {
- getRuntime().registerInspecting(this);
- return inspectStruct(context);
- } finally {
- getRuntime().unregisterInspecting(this);
- }
- }
-
- @JRubyMethod(name = {"to_a", "values"})
- public RubyArray to_a() {
- return getRuntime().newArray(values);
- }
-
- @JRubyMethod(name = {"size", "length"} )
- public RubyFixnum size() {
- return getRuntime().newFixnum(values.length);
- }
-
- @JRubyMethod(name = "each", backtrace = true)
- public IRubyObject each(ThreadContext context, Block block) {
- for (int i = 0; i < values.length; i++) {
- block.yield(context, values[i]);
- }
-
- return this;
- }
-
- @JRubyMethod(frame = true)
- public IRubyObject each_pair(ThreadContext context, Block block) {
- RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
-
- assert !member.isNil() : "uninitialized struct";
-
- for (int i = 0; i < values.length; i++) {
- block.yield(context, getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), values[i]}));
- }
-
- return this;
- }
-
- @JRubyMethod(name = "[]", required = 1)
- public IRubyObject aref(IRubyObject key) {
- if (key instanceof RubyString || key instanceof RubySymbol) {
- return getByName(key.asJavaString());
- }
-
- int idx = RubyNumeric.fix2int(key);
-
- idx = idx < 0 ? values.length + idx : idx;
-
- if (idx < 0) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- } else if (idx >= values.length) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- }
-
- return values[idx];
- }
-
- @JRubyMethod(name = "[]=", required = 2)
- public IRubyObject aset(IRubyObject key, IRubyObject value) {
- if (key instanceof RubyString || key instanceof RubySymbol) {
- return setByName(key.asJavaString(), value);
- }
-
- int idx = RubyNumeric.fix2int(key);
-
- idx = idx < 0 ? values.length + idx : idx;
-
- if (idx < 0) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- } else if (idx >= values.length) {
- throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
- }
-
- modify();
- return values[idx] = value;
- }
-
- // FIXME: This is copied code from RubyArray. Both RE, Struct, and Array should share one impl
- // This is also hacky since I construct ruby objects to access ruby arrays through aref instead
- // of something lower.
- @JRubyMethod(rest = true)
- public IRubyObject values_at(IRubyObject[] args) {
- long olen = values.length;
- RubyArray result = getRuntime().newArray(args.length);
-
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof RubyFixnum) {
- result.append(aref(args[i]));
- 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 = len;
- for (int j = 0; j < end; j++) {
- result.append(aref(getRuntime().newFixnum(j + beg)));
- }
- continue;
- }
- result.append(aref(getRuntime().newFixnum(RubyNumeric.num2long(args[i]))));
- }
-
- return result;
- }
-
- public static void marshalTo(RubyStruct struct, MarshalStream output) throws java.io.IOException {
- output.registerLinkTarget(struct);
- output.dumpDefaultObjectHeader('S', struct.getMetaClass());
-
- List members = ((RubyArray) getInternalVariable(struct.classOf(), "__member__")).getList();
- output.writeInt(members.size());
-
- for (int i = 0; i < members.size(); i++) {
- RubySymbol name = (RubySymbol) members.get(i);
- output.dumpObject(name);
- output.dumpObject(struct.values[i]);
- }
- }
-
- public static RubyStruct unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- Ruby runtime = input.getRuntime();
-
- RubySymbol className = (RubySymbol) input.unmarshalObject();
- RubyClass rbClass = pathToClass(runtime, className.asJavaString());
- if (rbClass == null) {
- throw runtime.newNameError("uninitialized constant " + className, className.asJavaString());
- }
-
- RubyArray mem = members(rbClass, Block.NULL_BLOCK);
-
- int len = input.unmarshalInt();
- IRubyObject[] values = new IRubyObject[len];
- for(int i = 0; i < len; i++) {
- values[i] = runtime.getNil();
- }
- RubyStruct result = newStruct(rbClass, values, Block.NULL_BLOCK);
- input.registerLinkTarget(result);
- for(int i = 0; i < len; i++) {
- IRubyObject slot = input.unmarshalObject();
- if(!mem.eltInternal(i).toString().equals(slot.toString())) {
- throw runtime.newTypeError("struct " + rbClass.getName() + " not compatible (:" + slot + " for :" + mem.eltInternal(i) + ")");
- }
- result.aset(runtime.newFixnum(i), input.unmarshalObject());
- }
- return result;
- }
-
- private static RubyClass pathToClass(Ruby runtime, String path) {
- // FIXME: Throw the right ArgumentError's if the class is missing
- // or if it's a module.
- return (RubyClass) runtime.getClassFromPath(path);
- }
-
- private static ObjectAllocator STRUCT_INSTANCE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyStruct instance = new RubyStruct(runtime, klass);
-
- instance.setMetaClass(klass);
-
- return instance;
- }
- };
-
- @Override
- @JRubyMethod(required = 1)
- public IRubyObject initialize_copy(IRubyObject arg) {
- if (this == arg) return this;
- RubyStruct original = (RubyStruct) arg;
-
- values = new IRubyObject[original.values.length];
- System.arraycopy(original.values, 0, values, 0, original.values.length);
-
- 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 Joey Gibson <joey@joeygibson.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Derek Berner <derek.berner@state.nm.us>
- * 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.concurrent.locks.ReentrantLock;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.ClassIndex;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.BlockCallback;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.marshal.UnmarshalStream;
-import org.jruby.util.ByteList;
-
-/**
- * Represents a Ruby symbol (e.g. :bar)
- */
-@JRubyClass(name="Symbol")
-public class RubySymbol extends RubyObject {
- private final String symbol;
- private final int id;
- private final ByteList symbolBytes;
-
- /**
- *
- * @param runtime
- * @param internedSymbol the String value of the new Symbol. This <em>must</em>
- * have been previously interned
- */
- private RubySymbol(Ruby runtime, String internedSymbol) {
- super(runtime, runtime.getSymbol(), false);
- // symbol string *must* be interned
-
- assert internedSymbol == internedSymbol.intern() : internedSymbol + " is not interned";
-
- this.symbol = internedSymbol;
- this.symbolBytes = ByteList.create(symbol);
-
- this.id = runtime.allocSymbolId();
- }
-
- public static RubyClass createSymbolClass(Ruby runtime) {
- RubyClass symbolClass = runtime.defineClass("Symbol", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setSymbol(symbolClass);
- RubyClass symbolMetaClass = symbolClass.getMetaClass();
- symbolClass.index = ClassIndex.SYMBOL;
- symbolClass.kindOf = new RubyModule.KindOf() {
- public boolean isKindOf(IRubyObject obj, RubyModule type) {
- return obj instanceof RubySymbol;
- }
- };
-
- symbolClass.defineAnnotatedMethods(RubySymbol.class);
- symbolMetaClass.undefineMethod("new");
-
- return symbolClass;
- }
-
- @Override
- public int getNativeTypeIndex() {
- return ClassIndex.SYMBOL;
- }
-
- /** rb_to_id
- *
- * @return a String representation of the symbol
- */
- @Override
- public String asJavaString() {
- return symbol;
- }
-
- /** short circuit for Symbol key comparison
- *
- */
- @Override
- public final boolean eql(IRubyObject other) {
- return other == this;
- }
-
- @Override
- public boolean isImmediate() {
- return true;
- }
-
- @Override
- public RubyClass getSingletonClass() {
- throw getRuntime().newTypeError("can't define singleton");
- }
-
- public static RubySymbol getSymbolLong(Ruby runtime, long id) {
- return runtime.getSymbolTable().lookup(id);
- }
-
- /* Symbol class methods.
- *
- */
-
- public static RubySymbol newSymbol(Ruby runtime, String name) {
- return runtime.getSymbolTable().getSymbol(name);
- }
-
- @JRubyMethod(name = "to_i")
- public RubyFixnum to_i() {
- return getRuntime().newFixnum(id);
- }
-
- @JRubyMethod(name = "to_int")
- public RubyFixnum to_int() {
- if (getRuntime().getVerbose().isTrue()) {
- getRuntime().getWarnings().warn(ID.SYMBOL_AS_INTEGER, "treating Symbol as an integer");
- }
- return to_i();
- }
-
- @JRubyMethod(name = "inspect")
- @Override
- public IRubyObject inspect() {
- Ruby runtime = getRuntime();
- return runtime.newString(":" +
- (isSymbolName(symbol) ? symbol : RubyString.newStringShared(runtime, symbolBytes).dump().toString()));
- }
-
- @JRubyMethod(name = "to_s")
- @Override
- public IRubyObject to_s() {
- return RubyString.newStringShared(getRuntime(), symbolBytes);
- }
-
- @JRubyMethod(name = "id2name")
- public IRubyObject id2name() {
- return to_s();
- }
-
- @JRubyMethod(name = "===", required = 1)
- @Override
- public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
- return super.op_equal(context, other);
- }
-
- @Override
- public RubyFixnum hash() {
- return getRuntime().newFixnum(hashCode());
- }
-
- @Override
- public int hashCode() {
- return id;
- }
-
- public int getId() {
- return id;
- }
-
- @Override
- public boolean equals(Object other) {
- return other == this;
- }
-
- @JRubyMethod(name = "to_sym")
- public IRubyObject to_sym() {
- return this;
- }
-
- @Override
- public IRubyObject freeze(ThreadContext context) {
- return this;
- }
-
- @Override
- public IRubyObject taint(ThreadContext context) {
- return this;
- }
-
- private static class ToProcCallback implements BlockCallback {
- private RubySymbol symbol;
- public ToProcCallback(RubySymbol symbol) {
- this.symbol = symbol;
- }
-
- public IRubyObject call(ThreadContext ctx, IRubyObject[] args, Block blk) {
- IRubyObject[] currentArgs = args;
- switch(currentArgs.length) {
- case 0: throw symbol.getRuntime().newArgumentError("no receiver given");
- case 1: {
- if((currentArgs[0] instanceof RubyArray) && ((RubyArray)currentArgs[0]).getLength() != 0) {
- // This is needed to unpack stuff
- currentArgs = ((RubyArray)currentArgs[0]).toJavaArrayMaybeUnsafe();
- IRubyObject[] args2 = new IRubyObject[currentArgs.length-1];
- System.arraycopy(currentArgs, 1, args2, 0, args2.length);
- return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol, args2);
- } else {
- return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol);
- }
- }
- default: {
- IRubyObject[] args2 = new IRubyObject[currentArgs.length-1];
- System.arraycopy(currentArgs, 1, args2, 0, args2.length);
- return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol, args2);
- }
- }
- }
- }
- /*
- @JRubyMethod
- public IRubyObject to_proc() {
- return RubyProc.newProc(getRuntime(),
- CallBlock.newCallClosure(this, getRuntime().getSymbol(), Arity.noArguments(), new ToProcCallback(this), getRuntime().getCurrentContext()),
- Block.Type.PROC);
- }
- */
- private static boolean isIdentStart(char c) {
- return ((c >= 'a' && c <= 'z')|| (c >= 'A' && c <= 'Z')
- || c == '_');
- }
- private static boolean isIdentChar(char c) {
- return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
- || c == '_');
- }
-
- private static boolean isIdentifier(String s) {
- if (s == null || s.length() <= 0) {
- return false;
- }
-
- if (!isIdentStart(s.charAt(0))) {
- return false;
- }
- for (int i = 1; i < s.length(); i++) {
- if (!isIdentChar(s.charAt(i))) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * is_special_global_name from parse.c.
- * @param s
- * @return
- */
- private static boolean isSpecialGlobalName(String s) {
- if (s == null || s.length() <= 0) {
- return false;
- }
-
- int length = s.length();
-
- switch (s.charAt(0)) {
- case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\':
- case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"':
- case '&': case '`': case '\'': case '+': case '0':
- return length == 1;
- case '-':
- return (length == 1 || (length == 2 && isIdentChar(s.charAt(1))));
-
- default:
- // we already confirmed above that length > 0
- for (int i = 0; i < length; i++) {
- if (!Character.isDigit(s.charAt(i))) {
- return false;
- }
- }
- }
- return true;
- }
-
- private static boolean isSymbolName(String s) {
- if (s == null || s.length() < 1) {
- return false;
- }
-
- int length = s.length();
-
- char c = s.charAt(0);
- switch (c) {
- case '$':
- if (length > 1 && isSpecialGlobalName(s.substring(1))) {
- return true;
- }
- return isIdentifier(s.substring(1));
- case '@':
- int offset = 1;
- if (length >= 2 && s.charAt(1) == '@') {
- offset++;
- }
-
- return isIdentifier(s.substring(offset));
- case '<':
- return (length == 1 || (length == 2 && (s.equals("<<") || s.equals("<="))) ||
- (length == 3 && s.equals("<=>")));
- case '>':
- return (length == 1) || (length == 2 && (s.equals(">>") || s.equals(">=")));
- case '=':
- return ((length == 2 && (s.equals("==") || s.equals("=~"))) ||
- (length == 3 && s.equals("===")));
- case '*':
- return (length == 1 || (length == 2 && s.equals("**")));
- case '+':
- return (length == 1 || (length == 2 && s.equals("+@")));
- case '-':
- return (length == 1 || (length == 2 && s.equals("-@")));
- case '|': case '^': case '&': case '/': case '%': case '~': case '`':
- return length == 1;
- case '[':
- return s.equals("[]") || s.equals("[]=");
- }
-
- if (!isIdentStart(c)) {
- return false;
- }
-
- boolean localID = (c >= 'a' && c <= 'z');
- int last = 1;
-
- for (; last < length; last++) {
- char d = s.charAt(last);
-
- if (!isIdentChar(d)) {
- break;
- }
- }
-
- if (last == length) {
- return true;
- } else if (localID && last == length - 1) {
- char d = s.charAt(last);
-
- return d == '!' || d == '?' || d == '=';
- }
-
- return false;
- }
-
- @JRubyMethod(name = "all_symbols", meta = true)
- public static IRubyObject all_symbols(IRubyObject recv) {
- return recv.getRuntime().getSymbolTable().all_symbols();
- }
-
- public static RubySymbol unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
- RubySymbol result = newSymbol(input.getRuntime(), RubyString.byteListToString(input.unmarshalString()));
- input.registerLinkTarget(result);
- return result;
- }
-
- public static class SymbolTable {
- static final int DEFAULT_INITIAL_CAPACITY = 2048; // *must* be power of 2!
- static final int MAXIMUM_CAPACITY = 1 << 30;
- static final float DEFAULT_LOAD_FACTOR = 0.75f;
-
- private final ReentrantLock tableLock = new ReentrantLock();
- private volatile SymbolEntry[] symbolTable;
- private int size;
- private int threshold;
- private final float loadFactor;
- private final Ruby runtime;
-
- public SymbolTable(Ruby runtime) {
- this.runtime = runtime;
- this.loadFactor = DEFAULT_LOAD_FACTOR;
- this.threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
- this.symbolTable = new SymbolEntry[DEFAULT_INITIAL_CAPACITY];
- }
-
- // note all fields are final -- rehash creates new entries when necessary.
- // as documented in java.util.concurrent.ConcurrentHashMap.java, that will
- // statistically affect only a small percentage (< 20%) of entries for a given rehash.
- static class SymbolEntry {
- final int hash;
- final String name;
- final RubySymbol symbol;
- final SymbolEntry next;
-
- SymbolEntry(int hash, String name, RubySymbol symbol, SymbolEntry next) {
- this.hash = hash;
- this.name = name;
- this.symbol = symbol;
- this.next = next;
- }
- }
-
- public RubySymbol getSymbol(String name) {
- int hash = name.hashCode();
- SymbolEntry[] table;
- for (SymbolEntry e = (table = symbolTable)[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return e.symbol;
- }
- }
- ReentrantLock lock;
- (lock = tableLock).lock();
- try {
- int potentialNewSize;
- if ((potentialNewSize = size + 1) > threshold) {
- table = rehash();
- } else {
- table = symbolTable;
- }
- int index;
- // try lookup again under lock
- for (SymbolEntry e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return e.symbol;
- }
- }
- String internedName;
- RubySymbol symbol = new RubySymbol(runtime, internedName = name.intern());
- table[index] = new SymbolEntry(hash, internedName, symbol, table[index]);
- size = potentialNewSize;
- // write-volatile
- symbolTable = table;
- return symbol;
- } finally {
- lock.unlock();
- }
- }
-
- public RubySymbol fastGetSymbol(String internedName) {
- assert internedName == internedName.intern() : internedName + " is not interned";
- SymbolEntry[] table;
- for (SymbolEntry e = (table = symbolTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- return e.symbol;
- }
- }
- ReentrantLock lock;
- (lock = tableLock).lock();
- try {
- int potentialNewSize;
- if ((potentialNewSize = size + 1) > threshold) {
- table = rehash();
- } else {
- table = symbolTable;
- }
- int index;
- int hash;
- // try lookup again under lock
- for (SymbolEntry e = table[index = (hash = internedName.hashCode()) & (table.length - 1)]; e != null; e = e.next) {
- if (internedName == e.name) {
- return e.symbol;
- }
- }
- RubySymbol symbol = new RubySymbol(runtime, internedName);
- table[index] = new SymbolEntry(hash, internedName, symbol, table[index]);
- size = potentialNewSize;
- // write-volatile
- symbolTable = table;
- return symbol;
- } finally {
- lock.unlock();
- }
- }
-
- // backwards-compatibility, but threadsafe now
- public RubySymbol lookup(String name) {
- int hash = name.hashCode();
- SymbolEntry[] table;
- for (SymbolEntry e = (table = symbolTable)[hash & (table.length - 1)]; e != null; e = e.next) {
- if (hash == e.hash && name.equals(e.name)) {
- return e.symbol;
- }
- }
- return null;
- }
-
- public RubySymbol lookup(long id) {
- SymbolEntry[] table = symbolTable;
- for (int i = table.length; --i >= 0; ) {
- for (SymbolEntry e = table[i]; e != null; e = e.next) {
- if (id == e.symbol.id) {
- return e.symbol;
- }
- }
- }
- return null;
- }
-
- public RubyArray all_symbols() {
- SymbolEntry[] table = this.symbolTable;
- RubyArray array = runtime.newArray(this.size);
- for (int i = table.length; --i >= 0; ) {
- for (SymbolEntry e = table[i]; e != null; e = e.next) {
- array.append(e.symbol);
- }
- }
- return array;
- }
-
- // not so backwards-compatible here, but no one should have been
- // calling this anyway.
- @Deprecated
- public void store(RubySymbol symbol) {
- throw new UnsupportedOperationException();
- }
-
- private SymbolEntry[] rehash() {
- SymbolEntry[] oldTable = symbolTable;
- int oldCapacity;
- if ((oldCapacity = oldTable.length) >= MAXIMUM_CAPACITY) {
- return oldTable;
- }
-
- int newCapacity = oldCapacity << 1;
- SymbolEntry[] newTable = new SymbolEntry[newCapacity];
- threshold = (int)(newCapacity * loadFactor);
- int sizeMask = newCapacity - 1;
- SymbolEntry e;
- for (int i = oldCapacity; --i >= 0; ) {
- // We need to guarantee that any existing reads of old Map can
- // proceed. So we cannot yet null out each bin.
- e = oldTable[i];
-
- if (e != null) {
- SymbolEntry next = e.next;
- int idx = e.hash & sizeMask;
-
- // Single node on list
- if (next == null)
- newTable[idx] = e;
-
- else {
- // Reuse trailing consecutive sequence at same slot
- SymbolEntry lastRun = e;
- int lastIdx = idx;
- for (SymbolEntry last = next;
- last != null;
- last = last.next) {
- int k = last.hash & sizeMask;
- if (k != lastIdx) {
- lastIdx = k;
- lastRun = last;
- }
- }
- newTable[lastIdx] = lastRun;
-
- // Clone all remaining nodes
- for (SymbolEntry p = e; p != lastRun; p = p.next) {
- int k = p.hash & sizeMask;
- SymbolEntry n = newTable[k];
- newTable[k] = new SymbolEntry(p.hash, p.name, p.symbol, n);
- }
- }
- }
- }
- symbolTable = newTable;
- return newTable;
- }
-
- }
-}
-package org.jruby;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-import java.util.HashMap;
-
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ObjectMarshal;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.builtin.Variable;
-import org.jruby.runtime.component.VariableEntry;
-import org.jruby.runtime.marshal.MarshalStream;
-import org.jruby.runtime.marshal.UnmarshalStream;
-
-@JRubyClass(name="SystemCallError", parent="StandardError")
-public class RubySystemCallError extends RubyException {
- private IRubyObject errno = getRuntime().getNil();
-
- private final static Map<String, String> defaultMessages = new HashMap<String, String>();
- static {
- defaultMessages.put("Errno::EPERM", "Operation not permitted");
- defaultMessages.put("Errno::ENOENT", "No such file or directory");
- defaultMessages.put("Errno::ESRCH", "No such process");
- defaultMessages.put("Errno::EINTR", "Interrupted system call");
- defaultMessages.put("Errno::EIO", "Input/output error");
- defaultMessages.put("Errno::ENXIO", "Device not configured");
- defaultMessages.put("Errno::E2BIG", "Argument list too long");
- defaultMessages.put("Errno::ENOEXEC", "Exec format error");
- defaultMessages.put("Errno::EBADF", "Bad file descriptor");
- defaultMessages.put("Errno::ECHILD", "No child processes");
- defaultMessages.put("Errno::EDEADLK", "Resource deadlock avoided");
- defaultMessages.put("Errno::ENOMEM", "Cannot allocate memory");
- defaultMessages.put("Errno::EACCES", "Permission denied");
- defaultMessages.put("Errno::EFAULT", "Bad address");
- defaultMessages.put("Errno::ENOTBLK", "Block device required");
- defaultMessages.put("Errno::EBUSY", "Resource busy");
- defaultMessages.put("Errno::EEXIST", "File exists");
- defaultMessages.put("Errno::EXDEV", "Cross-device link");
- defaultMessages.put("Errno::ENODEV", "Operation not supported by device");
- defaultMessages.put("Errno::ENOTDIR", "Not a directory");
- defaultMessages.put("Errno::EISDIR", "Is a directory");
- defaultMessages.put("Errno::EINVAL", "Invalid argument");
- defaultMessages.put("Errno::ENFILE", "Too many open files in system");
- defaultMessages.put("Errno::EMFILE", "Too many open files");
- defaultMessages.put("Errno::ENOTTY", "Inappropriate ioctl for device");
- defaultMessages.put("Errno::ETXTBSY", "Text file busy");
- defaultMessages.put("Errno::EFBIG", "File too large");
- defaultMessages.put("Errno::ENOSPC", "No space left on device");
- defaultMessages.put("Errno::ESPIPE", "Illegal seek");
- defaultMessages.put("Errno::EROFS", "Read-only file system");
- defaultMessages.put("Errno::EMLINK", "Too many links");
- defaultMessages.put("Errno::EPIPE", "Broken pipe");
- defaultMessages.put("Errno::EDOM", "Numerical argument out of domain");
- defaultMessages.put("Errno::ERANGE", "Result too large");
- defaultMessages.put("Errno::EAGAIN", "Resource temporarily unavailable");
- defaultMessages.put("Errno::EWOULDBLOCK", "Resource temporarily unavailable");
- defaultMessages.put("Errno::EINPROGRESS", "Operation now in progress");
- defaultMessages.put("Errno::EALREADY", "Operation already in progress");
- defaultMessages.put("Errno::ENOTSOCK", "Socket operation on non-socket");
- defaultMessages.put("Errno::EDESTADDRREQ", "Destination address required");
- defaultMessages.put("Errno::EMSGSIZE", "Message too long");
- defaultMessages.put("Errno::EPROTOTYPE", "Protocol wrong type for socket");
- defaultMessages.put("Errno::ENOPROTOOPT", "Protocol not available");
- defaultMessages.put("Errno::EPROTONOSUPPORT", "Protocol not supported");
- defaultMessages.put("Errno::ESOCKTNOSUPPORT", "Socket type not supported");
- defaultMessages.put("Errno::EPFNOSUPPORT", "Protocol family not supported");
- defaultMessages.put("Errno::EAFNOSUPPORT", "Address family not supported by protocol family");
- defaultMessages.put("Errno::EADDRINUSE", "Address already in use");
- defaultMessages.put("Errno::EADDRNOTAVAIL", "Can't assign requested address");
- defaultMessages.put("Errno::ENETDOWN", "Network is down");
- defaultMessages.put("Errno::ENETUNREACH", "Network is unreachable");
- defaultMessages.put("Errno::ENETRESET", "Network dropped connection on reset");
- defaultMessages.put("Errno::ECONNABORTED", "Software caused connection abort");
- defaultMessages.put("Errno::ECONNRESET", "Connection reset by peer");
- defaultMessages.put("Errno::ENOBUFS", "No buffer space available");
- defaultMessages.put("Errno::EISCONN", "Socket is already connected");
- defaultMessages.put("Errno::ENOTCONN", "Socket is not connected");
- defaultMessages.put("Errno::ESHUTDOWN", "Can't send after socket shutdown");
- defaultMessages.put("Errno::ETOOMANYREFS", "Too many references: can't splice");
- defaultMessages.put("Errno::ETIMEDOUT", "Operation timed out");
- defaultMessages.put("Errno::ECONNREFUSED", "Connection refused");
- defaultMessages.put("Errno::ELOOP", "Too many levels of symbolic links");
- defaultMessages.put("Errno::ENAMETOOLONG", "File name too long");
- defaultMessages.put("Errno::EHOSTDOWN", "Host is down");
- defaultMessages.put("Errno::EHOSTUNREACH", "No route to host");
- defaultMessages.put("Errno::ENOTEMPTY", "Directory not empty");
- defaultMessages.put("Errno::EUSERS", "Too many users");
- defaultMessages.put("Errno::EDQUOT", "Disc quota exceeded");
- defaultMessages.put("Errno::ESTALE", "Stale NFS file handle");
- defaultMessages.put("Errno::EREMOTE", "Too many levels of remote in path");
- defaultMessages.put("Errno::ENOLCK", "No locks available");
- defaultMessages.put("Errno::ENOSYS", "Function not implemented");
- defaultMessages.put("Errno::EOVERFLOW", "Value too large to be stored in data type");
- defaultMessages.put("Errno::EIDRM", "Identifier removed");
- defaultMessages.put("Errno::ENOMSG", "No message of desired type");
- defaultMessages.put("Errno::EILSEQ", "Illegal byte sequence");
- defaultMessages.put("Errno::EBADMSG", "Bad message");
- defaultMessages.put("Errno::EMULTIHOP", "EMULTIHOP (Reserved)");
- defaultMessages.put("Errno::ENODATA", "No message available on STREAM");
- defaultMessages.put("Errno::ENOLINK", "ENOLINK (Reserved)");
- defaultMessages.put("Errno::ENOSR", "No STREAM resources");
- defaultMessages.put("Errno::ENOSTR", "Not a STREAM");
- defaultMessages.put("Errno::EPROTO", "Protocol error");
- defaultMessages.put("Errno::ETIME", "STREAM ioctl timeout");
- defaultMessages.put("Errno::EOPNOTSUPP", "Operation not supported");
- defaultMessages.put("Errno::EOPNOTSUPP_DARWIN", "Operation not supported");
- }
-
- protected RubySystemCallError(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass, null);
- }
-
- public RubySystemCallError(Ruby runtime, RubyClass rubyClass, String message, int errno) {
- super(runtime, rubyClass, message);
- this.errno = runtime.newFixnum(errno);
- }
-
- private static ObjectAllocator SYSTEM_CALL_ERROR_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- RubyException instance = new RubySystemCallError(runtime, klass);
-
- instance.setMetaClass(klass);
-
- return instance;
- }
- };
-
- private static final ObjectMarshal SYSTEM_CALL_ERROR_MARSHAL = new ObjectMarshal() {
- public void marshalTo(Ruby runtime, Object obj, RubyClass type,
- MarshalStream marshalStream) throws IOException {
- RubySystemCallError exc = (RubySystemCallError) obj;
- marshalStream.registerLinkTarget(exc);
-
- List<Variable<IRubyObject>> attrs = exc.getVariableList();
- attrs.add(new VariableEntry<IRubyObject>(
- "mesg", exc.message == null ? runtime.getNil() : exc.message));
- attrs.add(new VariableEntry<IRubyObject>("errno", exc.errno));
- attrs.add(new VariableEntry<IRubyObject>("bt", exc.getBacktrace()));
- marshalStream.dumpVariables(attrs);
- }
-
- public Object unmarshalFrom(Ruby runtime, RubyClass type,
- UnmarshalStream unmarshalStream) throws IOException {
- RubySystemCallError exc = (RubySystemCallError) type.allocate();
-
- unmarshalStream.registerLinkTarget(exc);
- unmarshalStream.defaultVariablesUnmarshal(exc);
-
- exc.message = exc.removeInternalVariable("mesg");
- exc.errno = exc.removeInternalVariable("errno");
- exc.set_backtrace(exc.removeInternalVariable("bt"));
-
- return exc;
- }
- };
-
- public static RubyClass createSystemCallErrorClass(Ruby runtime, RubyClass standardError) {
- RubyClass exceptionClass = runtime.defineClass("SystemCallError", standardError, SYSTEM_CALL_ERROR_ALLOCATOR);
-
- exceptionClass.setMarshal(SYSTEM_CALL_ERROR_MARSHAL);
-
- runtime.callbackFactory(RubyClass.class);
- exceptionClass.defineAnnotatedMethods(RubySystemCallError.class);
-
- return exceptionClass;
- }
-
- @JRubyMethod(optional = 2, required=0, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- RubyClass sCallErorrClass = getRuntime().getSystemCallError();
- RubyClass klass = getMetaClass().getRealClass();
-
- IRubyObject msg = getRuntime().getNil();
- IRubyObject err = getRuntime().getNil();
-
- boolean isErrnoClass = !klass.equals(sCallErorrClass);
-
- if (!isErrnoClass) {
- // one optional, one required args
- Arity.checkArgumentCount(getRuntime(), args, 1, 2);
- msg = args[0];
- if (args.length == 2) {
- err = args[1];
- }
- if (args.length == 1 && (msg instanceof RubyFixnum)) {
- err = msg;
- msg = getRuntime().getNil();
- }
- } else {
- // one optional and no required args
- Arity.checkArgumentCount(getRuntime(), args, 0, 1);
- if (args.length == 1) {
- msg = args[0];
- }
- // try to get errno out of the class
- err = klass.fastGetConstant("Errno");
- }
-
- if (!err.isNil()) {
- errno = err.convertToInteger();
- }
-
- String val = defaultMessages.get(klass.getName());
- if (val == null) {
- val = "Unknown error";
- }
-
- // MRI behavior: we don't print errno for actual Errno errors
- if (!errno.isNil() && !isErrnoClass) {
- val += " " + errno.toString();
- }
-
- if (!msg.isNil()) {
- val += " - " + msg.convertToString();
- }
-
- message = getRuntime().newString(val);
- return this;
- }
-
- @JRubyMethod
- public IRubyObject errno() {
- return errno;
- }
-}
-/***** 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 org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.Visibility;
-import org.jruby.runtime.builtin.IRubyObject;
-
-@JRubyClass(name="SystemExit", parent="Exception")
-public class RubySystemExit extends RubyException {
- IRubyObject status;
-
- private static ObjectAllocator SYSTEMEXIT_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubySystemExit(runtime, klass);
- }
- };
-
- public static RubyClass createSystemExitClass(Ruby runtime, RubyClass exceptionClass) {
- RubyClass systemExitClass = runtime.defineClass("SystemExit", exceptionClass, SYSTEMEXIT_ALLOCATOR);
-
- systemExitClass.defineAnnotatedMethods(RubySystemExit.class);
-
- return systemExitClass;
- }
-
- public static RubySystemExit newInstance(Ruby runtime, int status) {
- RubyClass exc = runtime.getSystemExit();
- IRubyObject[] exArgs = new IRubyObject[] {
- runtime.newFixnum(status),
- runtime.newString("exit") };
- return (RubySystemExit) exc.newInstance(runtime.getCurrentContext(), exArgs, Block.NULL_BLOCK);
- }
-
- protected RubySystemExit(Ruby runtime, RubyClass exceptionClass) {
- super(runtime, exceptionClass);
- status = runtime.getNil();
- }
-
- @JRubyMethod(name = "initialize", optional = 2, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[]args, Block block) {
- status = RubyFixnum.zero(getRuntime());
- if (args.length > 0 && args[0] instanceof RubyFixnum) {
- status = args[0];
- IRubyObject[]tmpArgs = new IRubyObject[args.length - 1];
- System.arraycopy(args, 1, tmpArgs, 0, tmpArgs.length);
- args = tmpArgs;
- }
- super.initialize(args, block);
- return this;
- }
-
- @JRubyMethod(name = "status")
- public IRubyObject status() {
- return status;
- }
-
- @JRubyMethod(name = "success?")
- public IRubyObject success_p() {
- if (status.isNil()) return getRuntime().getTrue();
- if (status.equals(RubyFixnum.zero(getRuntime()))) return getRuntime().getTrue();
- return getRuntime().getFalse();
- }
-
-}
-/***** 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 Jason Voegele <jason@jvoegele.com>
- * 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.IOException;
-import java.nio.channels.Channel;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.util.HashMap;
-import java.util.Map;
-
-import java.util.Set;
-import org.jruby.common.IRubyWarnings.ID;
-import org.jruby.exceptions.RaiseException;
-import org.jruby.exceptions.ThreadKill;
-import org.jruby.internal.runtime.FutureThread;
-import org.jruby.internal.runtime.NativeThread;
-import org.jruby.internal.runtime.RubyRunnable;
-import org.jruby.internal.runtime.ThreadLike;
-import org.jruby.internal.runtime.ThreadService;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.locks.ReentrantLock;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.runtime.ObjectMarshal;
-import org.jruby.runtime.Visibility;
-
-/**
- * Implementation of Ruby's <code>Thread</code> class. Each Ruby thread is
- * mapped to an underlying Java Virtual Machine thread.
- * <p>
- * Thread encapsulates the behavior of a thread of execution, including the main
- * thread of the Ruby script. In the descriptions that follow, the parameter
- * <code>aSymbol</code> refers to a symbol, which is either a quoted string or a
- * <code>Symbol</code> (such as <code>:name</code>).
- *
- * Note: For CVS history, see ThreadClass.java.
- */
-@JRubyClass(name="Thread")
-public class RubyThread extends RubyObject {
- private ThreadLike threadImpl;
- private RubyFixnum priority;
- private transient Map<IRubyObject, IRubyObject> threadLocalVariables;
- private boolean abortOnException;
- private IRubyObject finalResult;
- private RaiseException exitingException;
- private IRubyObject receivedException;
- private RubyThreadGroup threadGroup;
-
- private final ThreadService threadService;
- private volatile boolean isStopped = false;
- private volatile boolean isDead = false;
- public Object stopLock = new Object();
-
- private volatile boolean killed = false;
- public Object killLock = new Object();
-
- public final ReentrantLock lock = new ReentrantLock();
-
- private static final boolean DEBUG = false;
-
- protected RubyThread(Ruby runtime, RubyClass type) {
- super(runtime, type);
- this.threadService = runtime.getThreadService();
- finalResult = runtime.getNil();
- }
-
- /**
- * Dispose of the current thread by removing it from its parent ThreadGroup.
- */
- public void dispose() {
- threadGroup.remove(this);
- }
-
- public static RubyClass createThreadClass(Ruby runtime) {
- // FIXME: In order for Thread to play well with the standard 'new' behavior,
- // it must provide an allocator that can create empty object instances which
- // initialize then fills with appropriate data.
- RubyClass threadClass = runtime.defineClass("Thread", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setThread(threadClass);
-
- threadClass.defineAnnotatedMethods(RubyThread.class);
-
- RubyThread rubyThread = new RubyThread(runtime, threadClass);
- // TODO: need to isolate the "current" thread from class creation
- rubyThread.threadImpl = new NativeThread(rubyThread, Thread.currentThread());
- runtime.getThreadService().setMainThread(Thread.currentThread(), rubyThread);
-
- // set to default thread group
- runtime.getDefaultThreadGroup().addDirectly(rubyThread);
-
- threadClass.setMarshal(ObjectMarshal.NOT_MARSHALABLE_MARSHAL);
-
- return threadClass;
- }
-
- /**
- * <code>Thread.new</code>
- * <p>
- * Thread.new( <i>[ arg ]*</i> ) {| args | block } -> aThread
- * <p>
- * Creates a new thread to execute the instructions given in block, and
- * begins running it. Any arguments passed to Thread.new are passed into the
- * block.
- * <pre>
- * x = Thread.new { sleep .1; print "x"; print "y"; print "z" }
- * a = Thread.new { print "a"; print "b"; sleep .2; print "c" }
- * x.join # Let the threads finish before
- * a.join # main thread exits...
- * </pre>
- * <i>produces:</i> abxyzc
- */
- @JRubyMethod(name = {"new", "fork"}, rest = true, frame = true, meta = true)
- public static IRubyObject newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
- return startThread(recv, args, true, block);
- }
-
- /**
- * Basically the same as Thread.new . However, if class Thread is
- * subclassed, then calling start in that subclass will not invoke the
- * subclass's initialize method.
- */
- @JRubyMethod(name = "start", rest = true, frame = true, meta = true)
- public static RubyThread start(IRubyObject recv, IRubyObject[] args, Block block) {
- return startThread(recv, args, false, block);
- }
-
- public static RubyThread adopt(IRubyObject recv, Thread t) {
- return adoptThread(recv, t, Block.NULL_BLOCK);
- }
-
- private static RubyThread adoptThread(final IRubyObject recv, Thread t, Block block) {
- final Ruby runtime = recv.getRuntime();
- final RubyThread rubyThread = new RubyThread(runtime, (RubyClass) recv);
-
- rubyThread.threadImpl = new NativeThread(rubyThread, t);
- ThreadContext context = runtime.getThreadService().registerNewThread(rubyThread);
-
- context.preAdoptThread();
-
- // set to default thread group
- runtime.getDefaultThreadGroup().addDirectly(rubyThread);
-
- return rubyThread;
- }
-
- @JRubyMethod(name = "initialize", rest = true, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject[] args, Block block) {
- Ruby runtime = getRuntime();
- if (!block.isGiven()) throw runtime.newThreadError("must be called with a block");
-
- RubyRunnable runnable = new RubyRunnable(this, args, block);
- if (RubyInstanceConfig.POOLING_ENABLED) {
- threadImpl = new FutureThread(this, runnable);
- } else {
- Thread thread = new Thread(runnable);
- thread.setDaemon(true);
- threadImpl = new NativeThread(this, thread);
- }
-
- // set to default thread group
- runtime.getDefaultThreadGroup().addDirectly(this);
-
- threadImpl.start();
-
- // We yield here to hopefully permit the target thread to schedule
- // MRI immediately schedules it, so this is close but not exact
- Thread.yield();
-
- return this;
- }
-
- private static RubyThread startThread(final IRubyObject recv, final IRubyObject[] args, boolean callInit, Block block) {
- RubyThread rubyThread = new RubyThread(recv.getRuntime(), (RubyClass) recv);
-
- if (callInit) {
- rubyThread.callInit(args, block);
- } else {
- // for Thread::start, which does not call the subclass's initialize
- rubyThread.initialize(args, block);
- }
-
- return rubyThread;
- }
-
- private void ensureNotCurrent() {
- if (this == getRuntime().getCurrentContext().getThread()) {
- throw new RuntimeException("internal thread method called from another thread");
- }
- }
-
- public synchronized void cleanTerminate(IRubyObject result) {
- finalResult = result;
- isStopped = true;
- isDead = true;
- }
-
- public void pollThreadEvents() {
- pollThreadEvents(getRuntime().getCurrentContext());
- }
-
- public void pollThreadEvents(ThreadContext context) {
- // check for criticalization *before* locking ourselves
- threadService.waitForCritical();
-
- if (killed) throwThreadKill();
- if (receivedException != null) receivedAnException(context);
- }
-
- private void throwThreadKill() {
- throw new ThreadKill();
- }
-
- /**
- * Returns the status of the global ``abort on exception'' condition. The
- * default is false. When set to true, will cause all threads to abort (the
- * process will exit(0)) if an exception is raised in any thread. See also
- * Thread.abort_on_exception= .
- */
- @JRubyMethod(name = "abort_on_exception", meta = true)
- public static RubyBoolean abort_on_exception_x(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- return runtime.isGlobalAbortOnExceptionEnabled() ? runtime.getTrue() : runtime.getFalse();
- }
-
- @JRubyMethod(name = "abort_on_exception=", required = 1, meta = true)
- public static IRubyObject abort_on_exception_set_x(IRubyObject recv, IRubyObject value) {
- recv.getRuntime().setGlobalAbortOnExceptionEnabled(value.isTrue());
- return value;
- }
-
- @JRubyMethod(name = "current", meta = true)
- public static RubyThread current(IRubyObject recv) {
- return recv.getRuntime().getCurrentContext().getThread();
- }
-
- @JRubyMethod(name = "main", meta = true)
- public static RubyThread main(IRubyObject recv) {
- return recv.getRuntime().getThreadService().getMainThread();
- }
-
- @JRubyMethod(name = "pass", meta = true)
- public static IRubyObject pass(IRubyObject recv) {
- Ruby runtime = recv.getRuntime();
- ThreadService ts = runtime.getThreadService();
- boolean critical = ts.getCritical();
-
- ts.setCritical(false);
-
- Thread.yield();
-
- ts.setCritical(critical);
-
- return recv.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "list", meta = true)
- public static RubyArray list(IRubyObject recv) {
- RubyThread[] activeThreads = recv.getRuntime().getThreadService().getActiveRubyThreads();
-
- return recv.getRuntime().newArrayNoCopy(activeThreads);
- }
-
- private IRubyObject getSymbolKey(IRubyObject originalKey) {
- if (originalKey instanceof RubySymbol) {
- return originalKey;
- } else if (originalKey instanceof RubyString) {
- return getRuntime().newSymbol(originalKey.asJavaString());
- } else if (originalKey instanceof RubyFixnum) {
- getRuntime().getWarnings().warn(ID.FIXNUMS_NOT_SYMBOLS, "Do not use Fixnums as Symbols");
- throw getRuntime().newArgumentError(originalKey + " is not a symbol");
- } else {
- throw getRuntime().newTypeError(originalKey + " is not a symbol");
- }
- }
-
- private synchronized Map<IRubyObject, IRubyObject> getThreadLocals() {
- if (threadLocalVariables == null) {
- threadLocalVariables = new HashMap<IRubyObject, IRubyObject>();
- }
- return threadLocalVariables;
- }
-
- @JRubyMethod(name = "[]", required = 1)
- public IRubyObject op_aref(IRubyObject key) {
- IRubyObject value;
- if ((value = getThreadLocals().get(getSymbolKey(key))) != null) {
- return value;
- }
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "[]=", required = 2)
- public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
- key = getSymbolKey(key);
-
- getThreadLocals().put(key, value);
- return value;
- }
-
- @JRubyMethod(name = "abort_on_exception")
- public RubyBoolean abort_on_exception() {
- return abortOnException ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "abort_on_exception=", required = 1)
- public IRubyObject abort_on_exception_set(IRubyObject val) {
- abortOnException = val.isTrue();
- return val;
- }
-
- @JRubyMethod(name = "alive?")
- public RubyBoolean alive_p() {
- return !isDead && threadImpl.isAlive() ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "join", optional = 1, backtrace = true)
- public IRubyObject join(IRubyObject[] args) {
- long timeoutMillis = Long.MAX_VALUE;
- if (args.length > 0) {
- if (args.length > 1) {
- throw getRuntime().newArgumentError(args.length,1);
- }
- // MRI behavior: value given in seconds; converted to Float; less
- // than or equal to zero returns immediately; returns nil
- timeoutMillis = (long)(1000.0D * args[0].convertToFloat().getValue());
- if (timeoutMillis <= 0) {
- // TODO: not sure that we should skip calling join() altogether.
- // Thread.join() has some implications for Java Memory Model, etc.
- if (threadImpl.isAlive()) {
- return getRuntime().getNil();
- } else {
- return this;
- }
- }
- }
- if (isCurrent()) {
- throw getRuntime().newThreadError("thread tried to join itself");
- }
- try {
- if (threadService.getCritical()) {
- // If the target thread is sleeping or stopped, wake it
- synchronized (stopLock) {
- stopLock.notify();
- }
-
- // interrupt the target thread in case it's blocking or waiting
- // WARNING: We no longer interrupt the target thread, since this usually means
- // interrupting IO and with NIO that means the channel is no longer usable.
- // We either need a new way to handle waking a target thread that's waiting
- // on IO, or we need to accept that we can't wake such threads and must wait
- // for them to complete their operation.
- //threadImpl.interrupt();
- }
-
- RubyThread currentThread = getRuntime().getCurrentContext().getThread();
- final long timeToWait = Math.min(timeoutMillis, 200);
-
- // We need this loop in order to be able to "unblock" the
- // join call without actually calling interrupt.
- long start = System.currentTimeMillis();
- while(true) {
- currentThread.pollThreadEvents();
- threadImpl.join(timeToWait);
- if (!threadImpl.isAlive()) {
- break;
- }
- if (System.currentTimeMillis() - start > timeoutMillis) {
- break;
- }
- }
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- assert false : ie;
- } catch (ExecutionException ie) {
- ie.printStackTrace();
- assert false : ie;
- }
-
- if (exitingException != null) {
- throw exitingException;
- }
-
- if (threadImpl.isAlive()) {
- return getRuntime().getNil();
- } else {
- return this;
- }
- }
-
- @JRubyMethod(name = "value")
- public IRubyObject value() {
- join(new IRubyObject[0]);
- synchronized (this) {
- return finalResult;
- }
- }
-
- @JRubyMethod(name = "group")
- public IRubyObject group() {
- if (threadGroup == null) {
- return getRuntime().getNil();
- }
-
- return threadGroup;
- }
-
- void setThreadGroup(RubyThreadGroup rubyThreadGroup) {
- threadGroup = rubyThreadGroup;
- }
-
- @JRubyMethod(name = "inspect")
- @Override
- public IRubyObject inspect() {
- // FIXME: There's some code duplication here with RubyObject#inspect
- StringBuilder part = new StringBuilder();
- String cname = getMetaClass().getRealClass().getName();
- part.append("#<").append(cname).append(":0x");
- part.append(Integer.toHexString(System.identityHashCode(this)));
-
- if (threadImpl.isAlive()) {
- if (isStopped) {
- part.append(getRuntime().newString(" sleep"));
- } else if (killed) {
- part.append(getRuntime().newString(" aborting"));
- } else {
- part.append(getRuntime().newString(" run"));
- }
- } else {
- part.append(" dead");
- }
-
- part.append(">");
- return getRuntime().newString(part.toString());
- }
-
- @JRubyMethod(name = "key?", required = 1)
- public RubyBoolean key_p(IRubyObject key) {
- key = getSymbolKey(key);
-
- return getRuntime().newBoolean(getThreadLocals().containsKey(key));
- }
-
- @JRubyMethod(name = "keys")
- public RubyArray keys() {
- IRubyObject[] keys = new IRubyObject[getThreadLocals().size()];
-
- return RubyArray.newArrayNoCopy(getRuntime(), getThreadLocals().keySet().toArray(keys));
- }
-
- @JRubyMethod(name = "critical=", required = 1, meta = true)
- public static IRubyObject critical_set(IRubyObject receiver, IRubyObject value) {
- receiver.getRuntime().getThreadService().setCritical(value.isTrue());
-
- return value;
- }
-
- @JRubyMethod(name = "critical", meta = true)
- public static IRubyObject critical(IRubyObject receiver) {
- return receiver.getRuntime().newBoolean(receiver.getRuntime().getThreadService().getCritical());
- }
-
- @JRubyMethod(name = "stop", meta = true)
- public static IRubyObject stop(IRubyObject receiver) {
- RubyThread rubyThread = receiver.getRuntime().getThreadService().getCurrentContext().getThread();
- Object stopLock = rubyThread.stopLock;
-
- synchronized (stopLock) {
- rubyThread.pollThreadEvents();
- try {
- rubyThread.isStopped = true;
- // attempt to decriticalize all if we're the critical thread
- receiver.getRuntime().getThreadService().setCritical(false);
-
- stopLock.wait();
- } catch (InterruptedException ie) {
- rubyThread.pollThreadEvents();
- }
- rubyThread.isStopped = false;
- }
-
- return receiver.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "kill", required = 1, frame = true, meta = true)
- public static IRubyObject kill(IRubyObject receiver, IRubyObject rubyThread, Block block) {
- if (!(rubyThread instanceof RubyThread)) throw receiver.getRuntime().newTypeError(rubyThread, receiver.getRuntime().getThread());
- return ((RubyThread)rubyThread).kill();
- }
-
- @JRubyMethod(name = "exit", frame = true, meta = true)
- public static IRubyObject s_exit(IRubyObject receiver, Block block) {
- RubyThread rubyThread = receiver.getRuntime().getThreadService().getCurrentContext().getThread();
-
- rubyThread.killed = true;
- // attempt to decriticalize all if we're the critical thread
- receiver.getRuntime().getThreadService().setCritical(false);
-
- throw new ThreadKill();
- }
-
- @JRubyMethod(name = "stop?")
- public RubyBoolean stop_p() {
- // not valid for "dead" state
- return getRuntime().newBoolean(isStopped);
- }
-
- @JRubyMethod(name = "wakeup")
- public RubyThread wakeup() {
- synchronized (stopLock) {
- stopLock.notifyAll();
- }
-
- return this;
- }
-
- @JRubyMethod(name = "priority")
- public RubyFixnum priority() {
- return priority;
- }
-
- @JRubyMethod(name = "priority=", required = 1)
- public IRubyObject priority_set(IRubyObject priority) {
- // FIXME: This should probably do some translation from Ruby priority levels to Java priority levels (until we have green threads)
- int iPriority = RubyNumeric.fix2int(priority);
-
- if (iPriority < Thread.MIN_PRIORITY) {
- iPriority = Thread.MIN_PRIORITY;
- } else if (iPriority > Thread.MAX_PRIORITY) {
- iPriority = Thread.MAX_PRIORITY;
- }
-
- this.priority = RubyFixnum.newFixnum(getRuntime(), iPriority);
-
- if (threadImpl.isAlive()) {
- threadImpl.setPriority(iPriority);
- }
- return this.priority;
- }
-
- @JRubyMethod(name = "raise", optional = 2, frame = true)
- public IRubyObject raise(IRubyObject[] args, Block block) {
- ensureNotCurrent();
- Ruby runtime = getRuntime();
-
- if (DEBUG) System.out.println("thread " + Thread.currentThread() + " before raising");
- RubyThread currentThread = getRuntime().getCurrentContext().getThread();
- try {
- while (!(currentThread.lock.tryLock() && this.lock.tryLock())) {
- if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
- }
-
- currentThread.pollThreadEvents();
- if (DEBUG) System.out.println("thread " + Thread.currentThread() + " raising");
- receivedException = prepareRaiseException(runtime, args, block);
-
- // If the target thread is sleeping or stopped, wake it
- synchronized (stopLock) {
- stopLock.notify();
- }
-
- // interrupt the target thread in case it's blocking or waiting
- // WARNING: We no longer interrupt the target thread, since this usually means
- // interrupting IO and with NIO that means the channel is no longer usable.
- // We either need a new way to handle waking a target thread that's waiting
- // on IO, or we need to accept that we can't wake such threads and must wait
- // for them to complete their operation.
- //threadImpl.interrupt();
-
- // new interrupt, to hopefully wake it out of any blocking IO
- this.interrupt();
- } finally {
- if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
- if (this.lock.isHeldByCurrentThread()) this.lock.unlock();
- }
-
- return this;
- }
-
- private IRubyObject prepareRaiseException(Ruby runtime, IRubyObject[] args, Block block) {
- if(args.length == 0) {
- IRubyObject lastException = runtime.getGlobalVariables().get("$!");
- if(lastException.isNil()) {
- return new RaiseException(runtime, runtime.getRuntimeError(), "", false).getException();
- }
- return lastException;
- }
-
- IRubyObject exception;
- ThreadContext context = getRuntime().getCurrentContext();
-
- if(args.length == 1) {
- if(args[0] instanceof RubyString) {
- return runtime.getRuntimeError().newInstance(context, args, block);
- }
-
- if(!args[0].respondsTo("exception")) {
- return runtime.newTypeError("exception class/object expected").getException();
- }
- exception = args[0].callMethod(context, "exception");
- } else {
- if (!args[0].respondsTo("exception")) {
- return runtime.newTypeError("exception class/object expected").getException();
- }
-
- exception = args[0].callMethod(context, "exception", args[1]);
- }
-
- if (!runtime.getException().isInstance(exception)) {
- return runtime.newTypeError("exception object expected").getException();
- }
-
- if (args.length == 3) {
- ((RubyException) exception).set_backtrace(args[2]);
- }
-
- return exception;
- }
-
- @JRubyMethod(name = "run")
- public IRubyObject run() {
- // if stopped, unstop
- synchronized (stopLock) {
- if (isStopped) {
- isStopped = false;
- stopLock.notifyAll();
- }
- }
-
- return this;
- }
-
- public void sleep(long millis) throws InterruptedException {
- assert this == getRuntime().getCurrentContext().getThread();
- synchronized (stopLock) {
- pollThreadEvents();
- try {
- isStopped = true;
- stopLock.wait(millis);
- } finally {
- isStopped = false;
- pollThreadEvents();
- }
- }
- }
-
- @JRubyMethod(name = "status")
- public IRubyObject status() {
- if (threadImpl.isAlive()) {
- if (isStopped || currentSelector != null && currentSelector.isOpen()) {
- return getRuntime().newString("sleep");
- } else if (killed) {
- return getRuntime().newString("aborting");
- }
-
- return getRuntime().newString("run");
- } else if (exitingException != null) {
- return getRuntime().getNil();
- } else {
- return getRuntime().getFalse();
- }
- }
-
- @JRubyMethod(name = {"kill", "exit", "terminate"})
- public IRubyObject kill() {
- // need to reexamine this
- RubyThread currentThread = getRuntime().getCurrentContext().getThread();
-
- // If the killee thread is the same as the killer thread, just die
- if (currentThread == this) throwThreadKill();
-
- try {
- if (DEBUG) System.out.println("thread " + Thread.currentThread() + " trying to kill");
- while (!(currentThread.lock.tryLock() && this.lock.tryLock())) {
- if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
- }
-
- currentThread.pollThreadEvents();
-
- if (DEBUG) System.out.println("thread " + Thread.currentThread() + " succeeded with kill");
- killed = true;
-
- // If the target thread is sleeping or stopped, wake it
- synchronized (stopLock) {
- stopLock.notify();
- }
-
- // interrupt the target thread in case it's blocking or waiting
- // WARNING: We no longer interrupt the target thread, since this usually means
- // interrupting IO and with NIO that means the channel is no longer usable.
- // We either need a new way to handle waking a target thread that's waiting
- // on IO, or we need to accept that we can't wake such threads and must wait
- // for them to complete their operation.
- //threadImpl.interrupt();
-
- // new interrupt, to hopefully wake it out of any blocking IO
- this.interrupt();
- } finally {
- if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
- if (this.lock.isHeldByCurrentThread()) this.lock.unlock();
- }
-
- try {
- threadImpl.join();
- } catch (InterruptedException ie) {
- // we were interrupted, check thread events again
- currentThread.pollThreadEvents();
- } catch (ExecutionException ie) {
- // we were interrupted, check thread events again
- currentThread.pollThreadEvents();
- }
-
- return this;
- }
-
- @JRubyMethod(name = {"kill!", "exit!", "terminate!"})
- public IRubyObject kill_bang() {
- throw getRuntime().newNotImplementedError("Thread#kill!, exit!, and terminate! are not safe and not supported");
- }
-
- @JRubyMethod(name = "safe_level")
- public IRubyObject safe_level() {
- throw getRuntime().newNotImplementedError("Thread-specific SAFE levels are not supported");
- }
-
- private boolean isCurrent() {
- return threadImpl.isCurrent();
- }
-
- public void exceptionRaised(RaiseException exception) {
- assert isCurrent();
-
- RubyException rubyException = exception.getException();
- Ruby runtime = rubyException.getRuntime();
- if (runtime.getSystemExit().isInstance(rubyException)) {
- threadService.getMainThread().raise(new IRubyObject[] {rubyException}, Block.NULL_BLOCK);
- } else if (abortOnException(runtime)) {
- runtime.printError(rubyException);
- RubyException systemExit = RubySystemExit.newInstance(runtime, 1);
- systemExit.message = rubyException.message;
- systemExit.set_backtrace(rubyException.backtrace());
- threadService.getMainThread().raise(new IRubyObject[] {systemExit}, Block.NULL_BLOCK);
- return;
- } else if (runtime.getDebug().isTrue()) {
- runtime.printError(exception.getException());
- }
- exitingException = exception;
- }
-
- private boolean abortOnException(Ruby runtime) {
- return (runtime.isGlobalAbortOnExceptionEnabled() || abortOnException);
- }
-
- public static RubyThread mainThread(IRubyObject receiver) {
- return receiver.getRuntime().getThreadService().getMainThread();
- }
-
- private Selector currentSelector;
-
- @Deprecated
- public boolean selectForAccept(RubyIO io) {
- return select(io, SelectionKey.OP_ACCEPT);
- }
-
- public boolean select(RubyIO io, int ops) {
- Channel channel = io.getChannel();
-
- if (channel instanceof SelectableChannel) {
- SelectableChannel selectable = (SelectableChannel)channel;
-
- synchronized (selectable.blockingLock()) {
- boolean oldBlocking = selectable.isBlocking();
-
- try {
- selectable.configureBlocking(false);
-
- io.addBlockingThread(this);
- currentSelector = selectable.provider().openSelector();
-
- SelectionKey key = selectable.register(currentSelector, ops);
-
- int result = currentSelector.select();
-
- // check for thread events, in case we've been woken up to die
- pollThreadEvents();
-
- if (result == 1) {
- Set<SelectionKey> keySet = currentSelector.selectedKeys();
-
- if (keySet.iterator().next() == key) {
- return true;
- }
- }
-
- return false;
- } catch (IOException ioe) {
- throw io.getRuntime().newRuntimeError("Error with selector: " + ioe);
- } finally {
- if (currentSelector != null) {
- try {
- currentSelector.close();
- } catch (IOException ioe) {
- throw io.getRuntime().newRuntimeError("Could not close selector");
- }
- }
- currentSelector = null;
- io.removeBlockingThread(this);
- try {
- selectable.configureBlocking(oldBlocking);
- } catch (IOException ioe) {
- // ignore; I don't like doing it, but it seems like we
- // really just need to make all channels non-blocking by
- // default and use select when implementing blocking ops,
- // so if this remains set non-blocking, perhaps it's not
- // such a big deal...
- }
- }
- }
- } else {
- // can't select, just have to do a blocking call
- return true;
- }
- }
-
- public void interrupt() {
- if (currentSelector != null) {
- currentSelector.wakeup();
- }
- }
-
- public void beforeBlockingCall() {
- isStopped = true;
- }
-
- public void afterBlockingCall() {
- isStopped = false;
- }
-
- private void receivedAnException(ThreadContext context) {
- // clear this so we don't keep re-throwing
- IRubyObject raiseException = receivedException;
- receivedException = null;
- RubyModule kernelModule = getRuntime().getKernel();
- if (DEBUG) {
- System.out.println("thread " + Thread.currentThread() + " before propagating exception: " + killed);
- }
- kernelModule.callMethod(context, "raise", raiseException);
- }
-}
-/***** 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) 2004 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
- * 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.util.HashMap;
-import java.util.Map;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- * Implementation of Ruby's <code>ThreadGroup</code> class. This is currently
- * just a stub.
- * <p>
- *
- * @author Charles O Nutter (headius@headius.com)
- */
-@JRubyClass(name="ThreadGroup")
-public class RubyThreadGroup extends RubyObject {
- private Map<Integer, IRubyObject> rubyThreadList = new HashMap<Integer, IRubyObject>();
- private boolean enclosed = false;
-
- // ENEBO: Can these be fast?
- public static RubyClass createThreadGroupClass(Ruby runtime) {
- RubyClass threadGroupClass = runtime.defineClass("ThreadGroup", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setThreadGroup(threadGroupClass);
-
- threadGroupClass.defineAnnotatedMethods(RubyThreadGroup.class);
-
- // create the default thread group
- RubyThreadGroup defaultThreadGroup = new RubyThreadGroup(runtime, threadGroupClass);
- runtime.setDefaultThreadGroup(defaultThreadGroup);
- threadGroupClass.defineConstant("Default", defaultThreadGroup);
-
- return threadGroupClass;
- }
-
- @JRubyMethod(name = "new", frame = true, meta = true)
- public static IRubyObject newInstance(IRubyObject recv, Block block) {
- return new RubyThreadGroup(recv.getRuntime(), (RubyClass)recv);
- }
-
- @JRubyMethod(name = "add", required = 1, frame = true)
- public synchronized IRubyObject add(IRubyObject rubyThread, Block block) {
- if (!(rubyThread instanceof RubyThread)) throw getRuntime().newTypeError(rubyThread, getRuntime().getThread());
-
- // synchronize on the RubyThread for threadgroup updates
- if (isFrozen()) {
- throw getRuntime().newTypeError("can't add to frozen ThreadGroup");
- }
-
- RubyThread thread = (RubyThread)rubyThread;
-
- // we only add live threads
- if (thread.alive_p().isTrue()) {
- addDirectly(thread);
- }
-
- return this;
- }
-
- void addDirectly(RubyThread rubyThread) {
- synchronized (rubyThread) {
- IRubyObject oldGroup = rubyThread.group();
- if (oldGroup != getRuntime().getNil()) {
- RubyThreadGroup threadGroup = (RubyThreadGroup) oldGroup;
- threadGroup.rubyThreadList.remove(System.identityHashCode(rubyThread));
- }
-
- rubyThread.setThreadGroup(this);
- rubyThreadList.put(System.identityHashCode(rubyThread), rubyThread);
- }
- }
-
- public synchronized void remove(RubyThread rubyThread) {
- rubyThread.setThreadGroup(null);
- rubyThreadList.remove(System.identityHashCode(rubyThread));
- }
-
- @JRubyMethod(name = "enclose", frame = true)
- public IRubyObject enclose(Block block) {
- enclosed = true;
-
- return this;
- }
-
- @JRubyMethod(name = "enclosed?", frame = true)
- public IRubyObject enclosed_p(Block block) {
- return new RubyBoolean(getRuntime(), enclosed);
- }
-
- @JRubyMethod(name = "list", frame = true)
- public synchronized IRubyObject list(Block block) {
- return getRuntime().newArrayNoCopy((IRubyObject[]) rubyThreadList.values().toArray(new IRubyObject[rubyThreadList.size()]));
- }
-
- private RubyThreadGroup(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
-}
-/***** 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-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) 2004 Joey Gibson <joey@joeygibson.com>
- * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
- * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
- * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
- * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
- * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@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.lang.ref.SoftReference;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.TimeZone;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.joda.time.DateTime;
-import org.joda.time.DateTimeZone;
-import org.joda.time.format.DateTimeFormat;
-import org.joda.time.format.DateTimeFormatter;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyMethod;
-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.util.ByteList;
-import org.jruby.util.RubyDateFormat;
-
-/** The Time class.
- *
- * @author chadfowler, jpetersen
- */
-@JRubyClass(name="Time", include="Comparable")
-public class RubyTime extends RubyObject {
- public static final String UTC = "UTC";
- private DateTime dt;
- private long usec;
-
- private final static DateTimeFormatter ONE_DAY_CTIME_FORMATTER = DateTimeFormat.forPattern("EEE MMM d HH:mm:ss yyyy").withLocale(Locale.ENGLISH);
- private final static DateTimeFormatter TWO_DAY_CTIME_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss yyyy").withLocale(Locale.ENGLISH);
-
- private final static DateTimeFormatter TO_S_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss Z yyyy").withLocale(Locale.ENGLISH);
- private final static DateTimeFormatter TO_S_UTC_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss 'UTC' yyyy").withLocale(Locale.ENGLISH);
-
- // There are two different popular TZ formats: legacy (AST+3:00:00, GMT-3), and
- // newer one (US/Pacific, America/Los_Angeles). This pattern is to detect
- // the legacy TZ format in order to convert it to the newer format
- // understood by Java API.
- private static final Pattern TZ_PATTERN
- = Pattern.compile("(\\D+?)([\\+-]?)(\\d+)(:\\d+)?(:\\d+)?");
-
- private static final ByteList TZ_STRING = ByteList.create("TZ");
-
- public static DateTimeZone getLocalTimeZone(Ruby runtime) {
- RubyString tzVar = runtime.newString(TZ_STRING);
- RubyHash h = ((RubyHash)runtime.getObject().fastGetConstant("ENV"));
- IRubyObject tz = h.op_aref(runtime.getCurrentContext(), tzVar);
- if (tz == null || ! (tz instanceof RubyString)) {
- return DateTimeZone.getDefault();
- } else {
- String zone = tz.toString();
- DateTimeZone cachedZone = runtime.getLocalTimezoneCache().get(zone);
-
- if (cachedZone != null) return cachedZone;
-
- String originalZone = zone;
-
- // Value of "TZ" property is of a bit different format,
- // which confuses the Java's TimeZone.getTimeZone(id) method,
- // and so, we need to convert it.
-
- Matcher tzMatcher = TZ_PATTERN.matcher(zone);
- if (tzMatcher.matches()) {
- String sign = tzMatcher.group(2);
- String hours = tzMatcher.group(3);
- String minutes = tzMatcher.group(4);
-
- // GMT+00:00 --> Etc/GMT, see "MRI behavior"
- // comment below.
- if (("00".equals(hours) || "0".equals(hours))
- && (minutes == null || ":00".equals(minutes) || ":0".equals(minutes))) {
- zone = "Etc/GMT";
- } else {
- // Invert the sign, since TZ format and Java format
- // use opposite signs, sigh... Also, Java API requires
- // the sign to be always present, be it "+" or "-".
- sign = ("-".equals(sign)? "+" : "-");
-
- // Always use "GMT" since that's required by Java API.
- zone = "GMT" + sign + hours;
-
- if (minutes != null) {
- zone += minutes;
- }
- }
- }
-
- // MRI behavior: With TZ equal to "GMT" or "UTC", Time.now
- // is *NOT* considered as a proper GMT/UTC time:
- // ENV['TZ']="GMT"
- // Time.now.gmt? ==> false
- // ENV['TZ']="UTC"
- // Time.now.utc? ==> false
- // Hence, we need to adjust for that.
- if ("GMT".equalsIgnoreCase(zone) || "UTC".equalsIgnoreCase(zone)) {
- zone = "Etc/" + zone;
- }
-
- DateTimeZone dtz = DateTimeZone.forTimeZone(TimeZone.getTimeZone(zone));
- runtime.getLocalTimezoneCache().put(originalZone, dtz);
- return dtz;
- }
- }
-
- public RubyTime(Ruby runtime, RubyClass rubyClass) {
- super(runtime, rubyClass);
- }
-
- public RubyTime(Ruby runtime, RubyClass rubyClass, DateTime dt) {
- super(runtime, rubyClass);
- this.dt = dt;
- }
-
- // We assume that these two time instances
- // occurred at the same time.
- private static final long BASE_TIME_MILLIS = System.currentTimeMillis();
- private static final long BASE_TIME_NANOS = System.nanoTime();
-
- private static ObjectAllocator TIME_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- long usecsPassed = (System.nanoTime() - BASE_TIME_NANOS) / 1000L;
- long millisTime = BASE_TIME_MILLIS + usecsPassed / 1000L;
- long usecs = usecsPassed % 1000L;
-
- DateTimeZone dtz = getLocalTimeZone(runtime);
- DateTime dt = new DateTime(millisTime, dtz);
- RubyTime rt = new RubyTime(runtime, klass, dt);
- rt.setUSec(usecs);
-
- return rt;
- }
- };
-
- public static RubyClass createTimeClass(Ruby runtime) {
- RubyClass timeClass = runtime.defineClass("Time", runtime.getObject(), TIME_ALLOCATOR);
- timeClass.index = ClassIndex.TIME;
- runtime.setTime(timeClass);
-
- timeClass.includeModule(runtime.getComparable());
-
- timeClass.defineAnnotatedMethods(RubyTime.class);
-
- return timeClass;
- }
-
- public void setUSec(long usec) {
- this.usec = usec;
- }
-
- public long getUSec() {
- return usec;
- }
-
- public void updateCal(DateTime dt) {
- this.dt = dt;
- }
-
- protected long getTimeInMillis() {
- return dt.getMillis(); // For JDK 1.4 we can use "cal.getTimeInMillis()"
- }
-
- public static RubyTime newTime(Ruby runtime, long milliseconds) {
- return newTime(runtime, new DateTime(milliseconds));
- }
-
- public static RubyTime newTime(Ruby runtime, DateTime dt) {
- return new RubyTime(runtime, runtime.getTime(), dt);
- }
-
- public static RubyTime newTime(Ruby runtime, DateTime dt, long usec) {
- RubyTime t = new RubyTime(runtime, runtime.getTime(), dt);
- t.setUSec(usec);
- return t;
- }
-
- @Override
- public Class<?> getJavaClass() {
- return Date.class;
- }
-
- @JRubyMethod(name = "initialize_copy", required = 1)
- @Override
- public IRubyObject initialize_copy(IRubyObject original) {
- if (!(original instanceof RubyTime)) {
- throw getRuntime().newTypeError("Expecting an instance of class Time");
- }
-
- RubyTime originalTime = (RubyTime) original;
-
- // We can just use dt, since it is immutable
- dt = originalTime.dt;
- usec = originalTime.usec;
-
- return this;
- }
-
- @JRubyMethod(name = "succ")
- public RubyTime succ() {
- return newTime(getRuntime(),dt.plusSeconds(1));
- }
-
- @JRubyMethod(name = {"gmtime", "utc"})
- public RubyTime gmtime() {
- dt = dt.withZone(DateTimeZone.UTC);
- return this;
- }
-
- @JRubyMethod(name = "localtime")
- public RubyTime localtime() {
- dt = dt.withZone(getLocalTimeZone(getRuntime()));
- return this;
- }
-
- @JRubyMethod(name = {"gmt?", "utc?", "gmtime?"})
- public RubyBoolean gmt() {
- return getRuntime().newBoolean(dt.getZone().getID().equals("UTC"));
- }
-
- @JRubyMethod(name = {"getgm", "getutc"})
- public RubyTime getgm() {
- return newTime(getRuntime(), dt.withZone(DateTimeZone.UTC), getUSec());
- }
-
- @JRubyMethod(name = "getlocal")
- public RubyTime getlocal() {
- return newTime(getRuntime(), dt.withZone(getLocalTimeZone(getRuntime())), getUSec());
- }
-
- @JRubyMethod(name = "strftime", required = 1)
- public RubyString strftime(IRubyObject format) {
- final RubyDateFormat rubyDateFormat = new RubyDateFormat("-", Locale.US);
- rubyDateFormat.applyPattern(format.toString());
- rubyDateFormat.setDateTime(dt);
- String result = rubyDateFormat.format(null);
- return getRuntime().newString(result);
- }
-
- @JRubyMethod(name = ">=", required = 1)
- public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyTime) {
- return getRuntime().newBoolean(cmp((RubyTime) other) >= 0);
- }
-
- return RubyComparable.op_ge(context, this, other);
- }
-
- @JRubyMethod(name = ">", required = 1)
- public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyTime) {
- return getRuntime().newBoolean(cmp((RubyTime) other) > 0);
- }
-
- return RubyComparable.op_gt(context, this, other);
- }
-
- @JRubyMethod(name = "<=", required = 1)
- public IRubyObject op_le(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyTime) {
- return getRuntime().newBoolean(cmp((RubyTime) other) <= 0);
- }
-
- return RubyComparable.op_le(context, this, other);
- }
-
- @JRubyMethod(name = "<", required = 1)
- public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyTime) {
- return getRuntime().newBoolean(cmp((RubyTime) other) < 0);
- }
-
- return RubyComparable.op_lt(context, this, other);
- }
-
- private int cmp(RubyTime other) {
- long millis = getTimeInMillis();
- long millis_other = other.getTimeInMillis();
- long usec_other = other.usec;
-
- if (millis > millis_other || (millis == millis_other && usec > usec_other)) {
- return 1;
- } else if (millis < millis_other || (millis == millis_other && usec < usec_other)) {
- return -1;
- }
-
- return 0;
- }
-
- @JRubyMethod(name = "+", required = 1)
- public IRubyObject op_plus(IRubyObject other) {
- long time = getTimeInMillis();
-
- if (other instanceof RubyTime) {
- throw getRuntime().newTypeError("time + time ?");
- }
- long adjustment = (long) (RubyNumeric.num2dbl(other) * 1000000);
- int micro = (int) (adjustment % 1000);
- adjustment = adjustment / 1000;
-
- time += adjustment;
-
- RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
- newTime.dt = new DateTime(time).withZone(dt.getZone());
- newTime.setUSec(micro);
-
- return newTime;
- }
-
- private IRubyObject opMinus(RubyTime other) {
- long time = getTimeInMillis() * 1000 + getUSec();
-
- time -= other.getTimeInMillis() * 1000 + other.getUSec();
-
- return RubyFloat.newFloat(getRuntime(), time / 1000000.0); // float number of seconds
- }
-
- @JRubyMethod(name = "-", required = 1)
- public IRubyObject op_minus(IRubyObject other) {
- if (other instanceof RubyTime) return opMinus((RubyTime) other);
-
- long time = getTimeInMillis();
- long adjustment = (long) (RubyNumeric.num2dbl(other) * 1000000);
- int micro = (int) (adjustment % 1000);
- adjustment = adjustment / 1000;
-
- time -= adjustment;
-
- RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
- newTime.dt = new DateTime(time).withZone(dt.getZone());
- newTime.setUSec(micro);
-
- return newTime;
- }
-
- @JRubyMethod(name = "===", required = 1)
- @Override
- public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
- return (RubyNumeric.fix2int(callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other)) == 0) ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "<=>", required = 1)
- public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
- if (other instanceof RubyTime) {
- return context.getRuntime().newFixnum(cmp((RubyTime) other));
- }
-
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "eql?", required = 1)
- @Override
- public IRubyObject eql_p(IRubyObject other) {
- if (other instanceof RubyTime) {
- RubyTime otherTime = (RubyTime)other;
- return (usec == otherTime.usec && getTimeInMillis() == otherTime.getTimeInMillis()) ? getRuntime().getTrue() : getRuntime().getFalse();
- }
- return getRuntime().getFalse();
- }
-
- @JRubyMethod(name = {"asctime", "ctime"})
- public RubyString asctime() {
- DateTimeFormatter simpleDateFormat;
-
- if (dt.getDayOfMonth() < 10) {
- simpleDateFormat = ONE_DAY_CTIME_FORMATTER;
- } else {
- simpleDateFormat = TWO_DAY_CTIME_FORMATTER;
- }
- String result = simpleDateFormat.print(dt);
- return getRuntime().newString(result);
- }
-
- @JRubyMethod(name = {"to_s", "inspect"})
- @Override
- public IRubyObject to_s() {
- DateTimeFormatter simpleDateFormat;
- if (dt.getZone() == DateTimeZone.UTC) {
- simpleDateFormat = TO_S_UTC_FORMATTER;
- } else {
- simpleDateFormat = TO_S_FORMATTER;
- }
-
- String result = simpleDateFormat.print(dt);
-
- return getRuntime().newString(result);
- }
-
- @JRubyMethod(name = "to_a")
- @Override
- public RubyArray to_a() {
- return getRuntime().newArrayNoCopy(new IRubyObject[] { sec(), min(), hour(), mday(), month(),
- year(), wday(), yday(), isdst(), zone() });
- }
-
- @JRubyMethod(name = "to_f")
- public RubyFloat to_f() {
- long time = getTimeInMillis();
- time = time * 1000 + usec;
- return RubyFloat.newFloat(getRuntime(), time / 1000000.0);
- }
-
- @JRubyMethod(name = {"to_i", "tv_sec"})
- public RubyInteger to_i() {
- return getRuntime().newFixnum(getTimeInMillis() / 1000);
- }
-
- @JRubyMethod(name = {"usec", "tv_usec"})
- public RubyInteger usec() {
- return getRuntime().newFixnum(dt.getMillisOfSecond() * 1000 + getUSec());
- }
-
- public void setMicroseconds(long mic) {
- long millis = getTimeInMillis() % 1000;
- long withoutMillis = getTimeInMillis() - millis;
- withoutMillis += (mic / 1000);
- dt = dt.withMillis(withoutMillis);
- usec = mic % 1000;
- }
-
- public long microseconds() {
- return getTimeInMillis() % 1000 * 1000 + usec;
- }
-
- @JRubyMethod(name = "sec")
- public RubyInteger sec() {
- return getRuntime().newFixnum(dt.getSecondOfMinute());
- }
-
- @JRubyMethod(name = "min")
- public RubyInteger min() {
- return getRuntime().newFixnum(dt.getMinuteOfHour());
- }
-
- @JRubyMethod(name = "hour")
- public RubyInteger hour() {
- return getRuntime().newFixnum(dt.getHourOfDay());
- }
-
- @JRubyMethod(name = {"mday", "day"})
- public RubyInteger mday() {
- return getRuntime().newFixnum(dt.getDayOfMonth());
- }
-
- @JRubyMethod(name = {"month", "mon"})
- public RubyInteger month() {
- return getRuntime().newFixnum(dt.getMonthOfYear());
- }
-
- @JRubyMethod(name = "year")
- public RubyInteger year() {
- return getRuntime().newFixnum(dt.getYear());
- }
-
- @JRubyMethod(name = "wday")
- public RubyInteger wday() {
- return getRuntime().newFixnum((dt.getDayOfWeek()%7));
- }
-
- @JRubyMethod(name = "yday")
- public RubyInteger yday() {
- return getRuntime().newFixnum(dt.getDayOfYear());
- }
-
- @JRubyMethod(name = {"gmt_offset", "gmtoff", "utc_offset"})
- public RubyInteger gmt_offset() {
- int offset = dt.getZone().getOffsetFromLocal(dt.getMillis());
-
- return getRuntime().newFixnum((int)(offset/1000));
- }
-
- @JRubyMethod(name = {"isdst", "dst?"})
- public RubyBoolean isdst() {
- return getRuntime().newBoolean(!dt.getZone().isStandardOffset(dt.getMillis()));
- }
-
- @JRubyMethod(name = "zone")
- public RubyString zone() {
- String zone = dt.getZone().getShortName(dt.getMillis());
- if(zone.equals("+00:00")) {
- zone = "GMT";
- }
- return getRuntime().newString(zone);
- }
-
- public void setDateTime(DateTime dt) {
- this.dt = dt;
- }
-
- public DateTime getDateTime() {
- return this.dt;
- }
-
- public Date getJavaDate() {
- return this.dt.toDate();
- }
-
- @JRubyMethod(name = "hash")
- @Override
- public RubyFixnum hash() {
- // modified to match how hash is calculated in 1.8.2
- return getRuntime().newFixnum((int)(((dt.getMillis() / 1000) ^ microseconds()) << 1) >> 1);
- }
-
- @JRubyMethod(name = "_dump", optional = 1, frame = true)
- public RubyString dump(IRubyObject[] args, Block unusedBlock) {
- RubyString str = (RubyString) mdump(new IRubyObject[] { this });
- str.syncVariables(this.getVariableList());
- return str;
- }
-
- public RubyObject mdump(final IRubyObject[] args) {
- RubyTime obj = (RubyTime)args[0];
- DateTime dateTime = obj.dt.withZone(DateTimeZone.UTC);
- byte dumpValue[] = new byte[8];
- int pe =
- 0x1 << 31 |
- (dateTime.getYear()-1900) << 14 |
- (dateTime.getMonthOfYear()-1) << 10 |
- dateTime.getDayOfMonth() << 5 |
- dateTime.getHourOfDay();
- int se =
- dateTime.getMinuteOfHour() << 26 |
- dateTime.getSecondOfMinute() << 20 |
- (dateTime.getMillisOfSecond() * 1000 + (int)usec); // dump usec, not msec
-
- for(int i = 0; i < 4; i++) {
- dumpValue[i] = (byte)(pe & 0xFF);
- pe >>>= 8;
- }
- for(int i = 4; i < 8 ;i++) {
- dumpValue[i] = (byte)(se & 0xFF);
- se >>>= 8;
- }
- return RubyString.newString(obj.getRuntime(), new ByteList(dumpValue,false));
- }
-
- @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(Block block) {
- return this;
- }
-
- /* Time class methods */
-
- public static IRubyObject s_new(IRubyObject recv, IRubyObject[] args, Block block) {
- Ruby runtime = recv.getRuntime();
- RubyTime time = new RubyTime(runtime, (RubyClass) recv, new DateTime(getLocalTimeZone(runtime)));
- time.callInit(args,block);
- return time;
- }
-
- /**
- * @deprecated Use {@link #newInstance(ThreadContext, IRubyObject)}
- */
- @Deprecated
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
- return newInstance(context, recv);
- }
-
- @JRubyMethod(name = "now", backtrace = true, meta = true)
- public static IRubyObject newInstance(ThreadContext context, IRubyObject recv) {
- IRubyObject obj = ((RubyClass) recv).allocate();
- obj.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, obj);
- return obj;
- }
-
- @JRubyMethod(name = "at", meta = true)
- public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObject arg) {
- Ruby runtime = context.getRuntime();
- final RubyTime time;
-
- if (arg instanceof RubyTime) {
- RubyTime other = (RubyTime) arg;
- time = new RubyTime(runtime, (RubyClass) recv, other.dt);
- time.setUSec(other.getUSec());
- } else {
- time = new RubyTime(runtime, (RubyClass) recv,
- new DateTime(0L, getLocalTimeZone(runtime)));
-
- long seconds = RubyNumeric.num2long(arg);
- long millisecs = 0;
- long microsecs = 0;
-
- // In the case of two arguments, MRI will discard the portion of
- // the first argument after a decimal point (i.e., "floor").
- // However in the case of a single argument, any portion after
- // the decimal point is honored.
- if (arg instanceof RubyFloat) {
- double dbl = ((RubyFloat) arg).getDoubleValue();
- long micro = (long) ((dbl - seconds) * 1000000);
- millisecs = micro / 1000;
- microsecs = micro % 1000;
- }
- time.setUSec(microsecs);
- time.dt = time.dt.withMillis(seconds * 1000 + millisecs);
- }
-
- time.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, time);
-
- return time;
- }
-
- @JRubyMethod(name = "at", meta = true)
- public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
- Ruby runtime = context.getRuntime();
-
- RubyTime time = new RubyTime(runtime, (RubyClass) recv,
- new DateTime(0L, getLocalTimeZone(runtime)));
-
- long seconds = RubyNumeric.num2long(arg1);
- long millisecs = 0;
- long microsecs = 0;
-
- long tmp = RubyNumeric.num2long(arg2);
- millisecs = tmp / 1000;
- microsecs = tmp % 1000;
-
- time.setUSec(microsecs);
- time.dt = time.dt.withMillis(seconds * 1000 + millisecs);
-
- time.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, time);
-
- return time;
- }
-
- @JRubyMethod(name = {"local", "mktime"}, required = 1, optional = 9, meta = true)
- public static RubyTime new_local(IRubyObject recv, IRubyObject[] args) {
- return createTime(recv, args, false);
- }
-
- @JRubyMethod(name = {"utc", "gm"}, required = 1, optional = 9, meta = true)
- public static RubyTime new_utc(IRubyObject recv, IRubyObject[] args) {
- return createTime(recv, args, true);
- }
-
- @JRubyMethod(name = "_load", required = 1, frame = true, meta = true)
- public static RubyTime load(IRubyObject recv, IRubyObject from, Block block) {
- return s_mload(recv, (RubyTime)(((RubyClass)recv).allocate()), from);
- }
-
- protected static RubyTime s_mload(IRubyObject recv, RubyTime time, IRubyObject from) {
- Ruby runtime = recv.getRuntime();
-
- DateTime dt = new DateTime(DateTimeZone.UTC);
-
- byte[] fromAsBytes = null;
- fromAsBytes = from.convertToString().getBytes();
- if(fromAsBytes.length != 8) {
- throw runtime.newTypeError("marshaled time format differ");
- }
- int p=0;
- int s=0;
- for (int i = 0; i < 4; i++) {
- p |= ((int)fromAsBytes[i] & 0xFF) << (8 * i);
- }
- for (int i = 4; i < 8; i++) {
- s |= ((int)fromAsBytes[i] & 0xFF) << (8 * (i - 4));
- }
- if ((p & (1<<31)) == 0) {
- dt = dt.withMillis(p * 1000L + s);
- } else {
- p &= ~(1<<31);
- dt = dt.withYear(((p >>> 14) & 0xFFFF) + 1900);
- dt = dt.withMonthOfYear(((p >>> 10) & 0xF) + 1);
- dt = dt.withDayOfMonth(((p >>> 5) & 0x1F));
- dt = dt.withHourOfDay((p & 0x1F));
- dt = dt.withMinuteOfHour(((s >>> 26) & 0x3F));
- dt = dt.withSecondOfMinute(((s >>> 20) & 0x3F));
- // marsaling dumps usec, not msec
- dt = dt.withMillisOfSecond((s & 0xFFFFF) / 1000);
- dt = dt.withZone(getLocalTimeZone(runtime));
- time.setUSec((s & 0xFFFFF) % 1000);
- }
- time.setDateTime(dt);
- return time;
- }
-
- private static final String[] MONTHS = {"jan", "feb", "mar", "apr", "may", "jun",
- "jul", "aug", "sep", "oct", "nov", "dec"};
-
- private static final Map<String, Integer> MONTHS_MAP = new HashMap<String, Integer>();
- static {
- for (int i = 0; i < MONTHS.length; i++) {
- MONTHS_MAP.put(MONTHS[i], i + 1);
- }
- }
-
- private static final int[] time_min = {1, 0, 0, 0, Integer.MIN_VALUE};
- private static final int[] time_max = {31, 23, 59, 60, Integer.MAX_VALUE};
-
- private static final int ARG_SIZE = 7;
-
- private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean gmt) {
- Ruby runtime = recv.getRuntime();
- int len = ARG_SIZE;
-
- if (args.length == 10) {
- args = new IRubyObject[] { args[5], args[4], args[3], args[2], args[1], args[0], runtime.getNil() };
- } else {
- // MRI accepts additional wday argument which appears to be ignored.
- len = args.length;
-
- if (len < ARG_SIZE) {
- IRubyObject[] newArgs = new IRubyObject[ARG_SIZE];
- System.arraycopy(args, 0, newArgs, 0, args.length);
- for (int i = len; i < ARG_SIZE; i++) {
- newArgs[i] = runtime.getNil();
- }
- args = newArgs;
- len = ARG_SIZE;
- }
- }
-
- if (args[0] instanceof RubyString) {
- args[0] = RubyNumeric.str2inum(runtime, (RubyString) args[0], 10, false);
- }
-
- int year = (int) RubyNumeric.num2long(args[0]);
- int month = 1;
-
- if (len > 1) {
- if (!args[1].isNil()) {
- IRubyObject tmp = args[1].checkStringType();
- if (!tmp.isNil()) {
- String monthString = tmp.toString().toLowerCase();
- Integer monthInt = MONTHS_MAP.get(monthString);
-
- if (monthInt != null) {
- month = monthInt;
- } else {
- try {
- month = Integer.parseInt(monthString);
- } catch (NumberFormatException nfExcptn) {
- throw runtime.newArgumentError("Argument out of range.");
- }
- }
- } else {
- month = (int) RubyNumeric.num2long(args[1]);
- }
- }
- if (1 > month || month > 12) {
- throw runtime.newArgumentError("Argument out of range: for month: " + month);
- }
- }
-
- int[] int_args = { 1, 0, 0, 0, 0, 0 };
-
- for (int i = 0; int_args.length >= i + 2; i++) {
- if (!args[i + 2].isNil()) {
- if (!(args[i + 2] instanceof RubyNumeric)) {
- args[i + 2] = args[i + 2].callMethod(
- runtime.getCurrentContext(), "to_i");
- }
-
- long value = RubyNumeric.num2long(args[i + 2]);
- if (time_min[i] > value || value > time_max[i]) {
- throw runtime.newArgumentError("argument out of range.");
- }
- int_args[i] = (int) value;
- }
- }
-
- if (0 <= year && year < 39) {
- year += 2000;
- } else if (69 <= year && year < 139) {
- year += 1900;
- }
-
- DateTimeZone dtz;
- if (gmt) {
- dtz = DateTimeZone.UTC;
- } else {
- dtz = getLocalTimeZone(runtime);
- }
-
- DateTime dt;
- // set up with min values and then add to allow rolling over
- try {
- dt = new DateTime(year, 1, 1, 0, 0 , 0, 0, dtz);
-
- dt = dt.plusMonths(month - 1)
- .plusDays(int_args[0] - 1)
- .plusHours(int_args[1])
- .plusMinutes(int_args[2])
- .plusSeconds(int_args[3]);
- } catch (org.joda.time.IllegalFieldValueException e) {
- throw runtime.newArgumentError("time out of range");
- }
-
- RubyTime time = new RubyTime(runtime, (RubyClass) recv, dt);
- // Ignores usec if 8 args (for compatibility with parsedate) or if not supplied.
- if (args.length != 8 && !args[6].isNil()) {
- int usec = int_args[4] % 1000;
- int msec = int_args[4] / 1000;
-
- if (int_args[4] < 0) {
- msec -= 1;
- usec += 1000;
- }
- time.dt = dt.withMillis(dt.getMillis() + msec);
- time.setUSec(usec);
- }
-
- time.callInit(IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
- return time;
- }
-}
-/***** 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) 2004 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 org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.internal.runtime.methods.DynamicMethod;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ObjectAllocator;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-/**
- *
- * Note: This was renamed from UnboundMethod.java
- *
- * @author jpetersen
- */
-@JRubyClass(name="UnboundMethod", parent="Method")
-public class RubyUnboundMethod extends RubyMethod {
- protected RubyUnboundMethod(Ruby runtime) {
- super(runtime, runtime.getUnboundMethod());
- }
-
- public static RubyUnboundMethod newUnboundMethod(
- RubyModule implementationModule,
- String methodName,
- RubyModule originModule,
- String originName,
- DynamicMethod method) {
- RubyUnboundMethod newMethod = new RubyUnboundMethod(implementationModule.getRuntime());
-
- newMethod.implementationModule = implementationModule;
- newMethod.methodName = methodName;
- newMethod.originModule = originModule;
- newMethod.originName = originName;
- newMethod.method = method;
-
- return newMethod;
- }
-
- public static RubyClass defineUnboundMethodClass(Ruby runtime) {
- // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
- RubyClass newClass =
- runtime.defineClass("UnboundMethod", runtime.getMethod(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- runtime.setUnboundMethod(newClass);
-
- newClass.defineAnnotatedMethods(RubyUnboundMethod.class);
-
- return newClass;
- }
-
- /**
- * @see org.jruby.RubyMethod#call(IRubyObject[])
- */
- @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
- @Override
- public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
- throw context.getRuntime().newTypeError("you cannot call unbound method; bind first");
- }
-
- /**
- * @see org.jruby.RubyMethod#unbind()
- */
- @JRubyMethod(name = "unbind", frame = true)
- @Override
- public RubyUnboundMethod unbind(Block block) {
- return this;
- }
-
- @JRubyMethod(name = "bind", required = 1, frame = true)
- public RubyMethod bind(ThreadContext context, IRubyObject aReceiver, Block block) {
- RubyClass receiverClass = aReceiver.getMetaClass();
-
- if (!originModule.isInstance(aReceiver)) {
- if (originModule instanceof MetaClass) {
- throw context.getRuntime().newTypeError("singleton method called for a different object");
- } else if (receiverClass instanceof MetaClass && receiverClass.getMethods().containsKey(originName)) {
- throw context.getRuntime().newTypeError("method `" + originName + "' overridden");
- } else if (
- !(originModule.isModule() ? originModule.isInstance(aReceiver) : aReceiver.getType() == originModule)) {
- // FIX replace type() == ... with isInstanceOf(...)
- throw context.getRuntime().newTypeError("bind argument must be an instance of " + originModule.getName());
- }
- }
- return RubyMethod.newMethod(implementationModule, methodName, receiverClass, originName, method, aReceiver);
- }
-
- @JRubyMethod(name = "clone")
- @Override
- public RubyMethod rbClone() {
- return newUnboundMethod(implementationModule, methodName, originModule, originName, method);
- }
-
- @JRubyMethod(name = "to_proc", frame = true)
- @Override
- public IRubyObject to_proc(ThreadContext context, Block unusedBlock) {
- return super.to_proc(context, unusedBlock);
- }
-}
-/***** 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.bini@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.IOException;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import java.util.regex.Pattern;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyModule;
-
-import org.jruby.runtime.Block;
-import org.jruby.runtime.ThreadContext;
-import org.jruby.runtime.builtin.IRubyObject;
-
-import org.jruby.javasupport.JavaEmbedUtils;
-
-import org.jruby.javasupport.JavaUtil;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.MethodIndex;
-import org.jruby.runtime.Visibility;
-
-import org.jruby.yaml.JRubyRepresenter;
-import org.jruby.yaml.JRubyConstructor;
-import org.jruby.yaml.JRubySerializer;
-import org.jruby.util.IOInputStream;
-import org.jruby.util.IOOutputStream;
-
-import org.jvyamlb.Representer;
-import org.jvyamlb.Constructor;
-import org.jvyamlb.ParserImpl;
-import org.jvyamlb.PositioningParserImpl;
-import org.jvyamlb.Scanner;
-import org.jvyamlb.ScannerImpl;
-import org.jvyamlb.Composer;
-import org.jvyamlb.ComposerImpl;
-import org.jvyamlb.PositioningScannerImpl;
-import org.jvyamlb.PositioningComposerImpl;
-import org.jvyamlb.Serializer;
-import org.jvyamlb.ResolverImpl;
-import org.jvyamlb.EmitterImpl;
-import org.jvyamlb.exceptions.YAMLException;
-import org.jvyamlb.YAMLConfig;
-import org.jvyamlb.YAML;
-import org.jvyamlb.PositioningScanner;
-import org.jvyamlb.Positionable;
-import org.jvyamlb.Position;
-
-/**
- * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
- */
-@JRubyModule(name="YAML")
-public class RubyYAML {
- public static RubyModule createYAMLModule(Ruby runtime) {
- RubyModule result = runtime.defineModule("YAML");
-
- runtime.getKernel().callMethod(runtime.getCurrentContext(),"require", runtime.newString("stringio"));
-
- result.defineAnnotatedMethods(RubyYAML.class);
-
- RubyClass obj = runtime.getObject();
- RubyClass clazz = runtime.getClassClass();
- RubyClass hash = runtime.getHash();
- RubyClass array = runtime.getArray();
- RubyClass struct = runtime.getStructClass();
- RubyClass exception = runtime.getException();
- RubyClass string = runtime.getString();
- RubyClass symbol = runtime.getSymbol();
- RubyClass range = runtime.getRange();
- RubyClass regexp = runtime.getRegexp();
- RubyClass time = runtime.getTime();
- RubyClass date = runtime.fastGetClass("Date");
- RubyClass fixnum = runtime.getFixnum();
- RubyClass bignum = runtime.getBignum();
- RubyClass flt = runtime.getFloat();
- RubyClass trueClass = runtime.getTrueClass();
- RubyClass falseClass = runtime.getFalseClass();
- RubyClass nilClass = runtime.getNilClass();
-
- clazz.defineAnnotatedMethods(YAMLClassMethods.class);
-
- obj.defineAnnotatedMethods(YAMLObjectMethods.class);
-
- hash.defineAnnotatedMethods(YAMLHashMethods.class);
-
- array.defineAnnotatedMethods(YAMLArrayMethods.class);
-
- struct.defineAnnotatedMethods(YAMLStructMethods.class);
-
- exception.defineAnnotatedMethods(YAMLExceptionMethods.class);
-
- string.defineAnnotatedMethods(YAMLStringMethods.class);
-
- symbol.defineAnnotatedMethods(YAMLSymbolMethods.class);
-
- range.defineAnnotatedMethods(YAMLRangeMethods.class);
-
- regexp.defineAnnotatedMethods(YAMLRegexpMethods.class);
-
- time.defineAnnotatedMethods(YAMLTimeMethods.class);
-
- date.defineAnnotatedMethods(YAMLDateMethods.class);
-
- bignum.defineAnnotatedMethods(YAMLNumericMethods.class);
-
- fixnum.defineAnnotatedMethods(YAMLNumericMethods.class);
-
- flt.defineAnnotatedMethods(YAMLNumericMethods.class);
-
- trueClass.defineAnnotatedMethods(YAMLTrueMethods.class);
-
- falseClass.defineAnnotatedMethods(YAMLFalseMethods.class);
-
- nilClass.defineAnnotatedMethods(YAMLNilMethods.class);
-
- runtime.setObjectToYamlMethod(runtime.getObject().searchMethod("to_yaml"));
-
- return result;
- }
-
- @JRubyMethod(name = "dump", required = 1, optional = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject dump(IRubyObject self, IRubyObject[] args) {
- IRubyObject obj = args[0];
- Ruby runtime = self.getRuntime();
- IRubyObject val = runtime.newArray(obj);
- if(args.length>1) {
- return RuntimeHelpers.invoke(runtime.getCurrentContext(), self,"dump_all", val, args[1]);
- } else {
- return self.callMethod(runtime.getCurrentContext(),"dump_all", val);
- }
- }
-
- @JRubyMethod(name = "dump_all", required = 1, optional = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject dump_all(IRubyObject self, IRubyObject[] args) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- RubyArray objs = (RubyArray)args[0];
- IRubyObject io = null;
- IRubyObject io2 = null;
- if(args.length == 2 && args[1] != null && !args[1].isNil()) {
- io = args[1];
- }
- YAMLConfig cfg = YAML.config().version("1.0");
- IOOutputStream iox = null;
- if(null == io) {
- io2 = self.getRuntime().fastGetClass("StringIO").callMethod(context, "new");
- iox = new IOOutputStream(io2);
- } else {
- iox = new IOOutputStream(io);
- }
- Serializer ser = new JRubySerializer(new EmitterImpl(iox,cfg),new ResolverImpl(),cfg);
- try {
- ser.open();
- Representer r = new JRubyRepresenter(ser, cfg);
- for(Iterator iter = objs.getList().iterator();iter.hasNext();) {
- r.represent(iter.next());
- }
- ser.close();
- } catch(IOException e) {
- throw self.getRuntime().newIOErrorFromException(e);
- }
- if(null == io) {
- io2.callMethod(context, "rewind");
- return io2.callMethod(context, "read");
- } else {
- return io;
- }
- }
-
- @JRubyMethod(name = "_parse_internal", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject parse_internal(IRubyObject self, IRubyObject arg) {
- boolean debug = self.getRuntime().getDebug().isTrue();
- IRubyObject io = check_yaml_port(arg);
- Scanner scn = null;
- try {
- if(io instanceof RubyString) {
- scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
- } else {
- scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
- }
- Composer ctor =
- debug ?
- new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl()) :
- new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl())
- ;
- if(ctor.checkNode()) {
- return JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getNode());
- }
- return self.getRuntime().getNil();
- } catch(YAMLException e) {
- if(self.getRuntime().getDebug().isTrue()) {
- Position.Range range = ((Positionable)e).getRange();
- throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
- } else {
- throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
- }
- }
- }
-
- @JRubyMethod(name = "load", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject load(IRubyObject self, IRubyObject arg) {
- boolean debug = self.getRuntime().getDebug().isTrue();
- IRubyObject io = check_yaml_port(arg);
- Scanner scn = null;
- try {
- if(io instanceof RubyString) {
- scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
- } else {
- scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
- }
- Constructor ctor =
- debug ?
- new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
- new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
- ;
- if(ctor.checkData()) {
- return JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData());
- }
- return self.getRuntime().getNil();
- } catch(YAMLException e) {
- if(self.getRuntime().getDebug().isTrue()) {
- Position.Range range = ((Positionable)e).getRange();
- throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
- } else {
- throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
- }
- }
- }
-
- @JRubyMethod(name = "load_file", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject load_file(IRubyObject self, IRubyObject arg) {
- Ruby runtime = self.getRuntime();
- ThreadContext context = runtime.getCurrentContext();
- IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(),"open", arg, runtime.newString("r"));
- IRubyObject val = self.callMethod(context,"load", io);
- io.callMethod(context, "close");
- return val;
- }
-
- @JRubyMethod(name = "each_document", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject each_document(IRubyObject self, IRubyObject arg, Block block) {
- boolean debug = self.getRuntime().getDebug().isTrue();
- ThreadContext context = self.getRuntime().getCurrentContext();
- IRubyObject io = arg;
- Scanner scn = null;
- try {
- if(io instanceof RubyString) {
- scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
- } else {
- scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
- }
- Constructor ctor =
- debug ?
- new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
- new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
- ;
- while(ctor.checkData()) {
- block.yield(context, JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
- }
- return self.getRuntime().getNil();
- } catch(YAMLException e) {
- if(self.getRuntime().getDebug().isTrue()) {
- Position.Range range = ((Positionable)e).getRange();
- throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
- } else {
- throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
- }
- }
- }
-
- @JRubyMethod(name = "load_documents", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject load_documents(IRubyObject self, IRubyObject arg, Block block) {
- boolean debug = self.getRuntime().getDebug().isTrue();
- ThreadContext context = self.getRuntime().getCurrentContext();
- IRubyObject io = check_yaml_port(arg);
- Scanner scn = null;
- try {
- if(io instanceof RubyString) {
- scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
- } else {
- scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
- }
- Constructor ctor =
- debug ?
- new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
- new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
- ;
- while(ctor.checkData()) {
- block.yield(context, JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
- }
- return self.getRuntime().getNil();
- } catch(YAMLException e) {
- if(self.getRuntime().getDebug().isTrue()) {
- Position.Range range = ((Positionable)e).getRange();
- throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
- } else {
- throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
- }
- }
- }
-
- @JRubyMethod(name = "load_stream", required = 1, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject load_stream(IRubyObject self, IRubyObject arg) {
- boolean debug = self.getRuntime().getDebug().isTrue();
- ThreadContext context = self.getRuntime().getCurrentContext();
- IRubyObject d = self.getRuntime().getNil();
- IRubyObject io = arg;
- Scanner scn = null;
- try {
- if(io instanceof RubyString) {
- scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
- } else {
- scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
- }
- Constructor ctor =
- debug ?
- new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
- new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
- ;
- while(ctor.checkData()) {
- if(d.isNil()) {
- d = self.getRuntime().fastGetModule("YAML").fastGetClass("Stream").callMethod(context,"new", d);
- }
- d.callMethod(context,"add", JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
- }
- return d;
- } catch(YAMLException e) {
- if(self.getRuntime().getDebug().isTrue()) {
- Position.Range range = ((Positionable)e).getRange();
- throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
- } else {
- throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
- }
- }
- }
-
- @JRubyMethod(name = "dump_stream", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject dump_stream(IRubyObject self, IRubyObject[] args) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- IRubyObject stream = self.getRuntime().fastGetModule("YAML").fastGetClass("Stream").callMethod(context, "new");
- for(int i=0,j=args.length;i<j;i++) {
- stream.callMethod(context,"add", args[i]);
- }
- return stream.callMethod(context, "emit");
- }
-
- @JRubyMethod(name = "quick_emit_node", required = 1, rest = true, frame = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject quick_emit_node(IRubyObject self, IRubyObject[] args, Block block) {
- return block.yield(self.getRuntime().getCurrentContext(), args[0]);
- }
-
-// @JRubyMethod(name = "quick_emit_node", rest = true, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject quick_emit(IRubyObject self, IRubyObject[] args) {
- return self.getRuntime().getNil();
- }
-
- // prepares IO port type for load (ported from ext/syck/rubyext.c)
- private static IRubyObject check_yaml_port(IRubyObject port) {
- if (port instanceof RubyString) {
- // OK
- }
- else if (port.respondsTo("read")) {
- if (port.respondsTo("binmode")) {
- ThreadContext context = port.getRuntime().getCurrentContext();
- port.callMethod(context, "binmode");
- }
- }
- else {
- throw port.getRuntime().newTypeError("instance of IO needed");
- }
- return port;
- }
-
- @JRubyClass(name="Hash")
- public static class YAMLHashMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject hash_to_yaml_node(IRubyObject self, IRubyObject arg) {
- Ruby runtime = self.getRuntime();
- ThreadContext context = runtime.getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), self, self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Object")
- public static class YAMLObjectMethods {
- @JRubyMethod(name = "to_yaml_properties")
- public static IRubyObject obj_to_yaml_properties(IRubyObject self) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return self.callMethod(context, "instance_variables").callMethod(context, "sort");
- }
- @JRubyMethod(name = "to_yaml_style")
- public static IRubyObject obj_to_yaml_style(IRubyObject self) {
- return self.getRuntime().getNil();
- }
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject obj_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- Map mep = (Map)(new RubyHash(self.getRuntime()));
- RubyArray props = (RubyArray)self.callMethod(context, "to_yaml_properties");
- for(Iterator iter = props.getList().iterator(); iter.hasNext();) {
- String m = iter.next().toString();
- mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
- }
- return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "to_yaml", rest = true)
- public static IRubyObject obj_to_yaml(IRubyObject self, IRubyObject[] args) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return self.getRuntime().fastGetModule("YAML").callMethod(context,"dump", self);
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject obj_taguri(IRubyObject self) {
- return self.getRuntime().newString("!ruby/object:" + self.getType().getName());
- }
- }
-
- @JRubyClass(name="Class")
- public static class YAMLClassMethods {
- @JRubyMethod(name = "to_yaml", rest = true)
- public static IRubyObject class_to_yaml(IRubyObject self, IRubyObject[] args) {
- throw self.getRuntime().newTypeError("can't dump anonymous class " + self.getType().getName());
- }
- }
-
- @JRubyClass(name="Array")
- public static class YAMLArrayMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject array_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "seq", self.callMethod(context, "taguri"), self, self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Struct")
- public static class YAMLStructMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject struct_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- Map mep = (Map)(new RubyHash(self.getRuntime()));
- for(Iterator iter = ((RubyArray)self.callMethod(context, "members")).getList().iterator();iter.hasNext();) {
- IRubyObject key = self.getRuntime().newString(iter.next().toString());
- mep.put(key,self.callMethod(context,MethodIndex.AREF, "[]", key));
- }
- for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
- String m = iter.next().toString();
- mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
- }
- return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject struct_taguri(IRubyObject self) {
- return self.getRuntime().newString("!ruby/struct:" + self.getType().getName());
- }
- }
-
- @JRubyClass(name="Exception")
- public static class YAMLExceptionMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject exception_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- Map mep = (Map)(new RubyHash(self.getRuntime()));
- mep.put(self.getRuntime().newString("message"),self.callMethod(context, "message"));
- for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
- String m = iter.next().toString();
- mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
- }
- return RuntimeHelpers.invoke(context, arg,"map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject exception_taguri(IRubyObject self) {
- return self.getRuntime().newString("!ruby/exception:" + self.getType().getName());
- }
- }
-
- private static final Pattern AFTER_NEWLINE = Pattern.compile("\n.+", Pattern.DOTALL);
- @JRubyClass(name="String")
- public static class YAMLStringMethods {
- @JRubyMethod(name = "is_complex_yaml?")
- public static IRubyObject string_is_complex(IRubyObject self) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return (self.callMethod(context, "to_yaml_style").isTrue() ||
- ((List)self.callMethod(context, "to_yaml_properties")).isEmpty() ||
- AFTER_NEWLINE.matcher(self.toString()).find()) ? self.getRuntime().getTrue() : self.getRuntime().getFalse();
- }
- @JRubyMethod(name = "is_binary_data?")
- public static IRubyObject string_is_binary(IRubyObject self) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- if(self.callMethod(context, MethodIndex.EMPTY_P, "empty?").isTrue()) {
- return self.getRuntime().getNil();
- }
- return self.toString().indexOf('\0') != -1 ? self.getRuntime().getTrue() : self.getRuntime().getFalse();
- }
- private static JRubyRepresenter into(IRubyObject arg) {
- IRubyObject jobj = arg.getInstanceVariables().fastGetInstanceVariable("@java_object");
- if(jobj != null) {
- return (JRubyRepresenter)(((org.jruby.javasupport.JavaObject)jobj).getValue());
- }
- return null;
- }
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject string_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- Ruby rt = self.getRuntime();
- if(self.callMethod(context, "is_binary_data?").isTrue()) {
- return RuntimeHelpers.invoke(context, arg, "scalar", rt.newString("tag:yaml.org,2002:binary"), rt.newArray(self).callMethod(context, "pack", rt.newString("m")), rt.newString("|"));
- }
- if(((List)self.callMethod(context, "to_yaml_properties")).isEmpty()) {
- JRubyRepresenter rep = into(arg);
- if(rep != null) {
- try {
- return JavaUtil.convertJavaToRuby(rt,rep.scalar(self.callMethod(context, "taguri").toString(),self.convertToString().getByteList(),self.toString().startsWith(":") ? "\"" : self.callMethod(context, "to_yaml_style").toString()));
- } catch(IOException e) {
- throw rt.newIOErrorFromException(e);
- }
- } else {
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self, self.toString().startsWith(":") ? rt.newString("\"") : self.callMethod(context, "to_yaml_style"));
- }
- }
-
- Map mep = (Map)(new RubyHash(self.getRuntime()));
- mep.put(self.getRuntime().newString("str"),rt.newString(self.toString()));
- for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
- String m = iter.next().toString();
- mep.put(self.getRuntime().newString(m), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
- }
- return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Symbol")
- public static class YAMLSymbolMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject symbol_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, "inspect"), self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject symbol_taguri(IRubyObject self) {
- return self.getRuntime().newString("tag:yaml.org,2002:str");
- }
- }
-
- @JRubyClass(name="Numeric")
- public static class YAMLNumericMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject numeric_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- String val = self.toString();
- if("Infinity".equals(val)) {
- val = ".Inf";
- } else if("-Infinity".equals(val)) {
- val = "-.Inf";
- } else if("NaN".equals(val)) {
- val = ".NaN";
- }
- return RuntimeHelpers.invoke(context, arg,"scalar", self.callMethod(context, "taguri"), self.getRuntime().newString(val), self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Range")
- public static class YAMLRangeMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject range_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- Map mep = (Map)(new RubyHash(self.getRuntime()));
- mep.put(self.getRuntime().newString("begin"),self.callMethod(context, "begin"));
- mep.put(self.getRuntime().newString("end"),self.callMethod(context, "end"));
- mep.put(self.getRuntime().newString("excl"),self.callMethod(context, "exclude_end?"));
- for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
- String m = iter.next().toString();
- mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
- }
- return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Regexp")
- public static class YAMLRegexpMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject regexp_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, "inspect"), self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Time")
- public static class YAMLTimeMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject time_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- IRubyObject tz = self.getRuntime().newString("Z");
- IRubyObject difference_sign = self.getRuntime().newString("-");
- self = self.dup();
- if(!self.callMethod(context, "utc?").isTrue()) {
- IRubyObject utc_same_instant = self.callMethod(context, "utc");
- IRubyObject utc_same_writing = RuntimeHelpers.invoke(context, self.getRuntime().getTime(), "utc", new IRubyObject[]{
- self.callMethod(context, "year"),self.callMethod(context, "month"),self.callMethod(context, "day"),self.callMethod(context, "hour"),
- self.callMethod(context, "min"),self.callMethod(context, "sec"),self.callMethod(context, "usec")});
- IRubyObject difference_to_utc = utc_same_writing.callMethod(context,MethodIndex.OP_MINUS, "-", utc_same_instant);
- IRubyObject absolute_difference;
- if(difference_to_utc.callMethod(context,MethodIndex.OP_LT, "<", RubyFixnum.zero(self.getRuntime())).isTrue()) {
- difference_sign = self.getRuntime().newString("-");
- absolute_difference = RubyFixnum.zero(self.getRuntime()).callMethod(context,MethodIndex.OP_MINUS, "-", difference_to_utc);
- } else {
- difference_sign = self.getRuntime().newString("+");
- absolute_difference = difference_to_utc;
- }
- IRubyObject difference_minutes = absolute_difference.callMethod(context,"/", self.getRuntime().newFixnum(60)).callMethod(context, "round");
- tz = self.getRuntime().newString("%s%02d:%02d").callMethod(context,"%", self.getRuntime().newArrayNoCopy(new IRubyObject[]{difference_sign,difference_minutes.callMethod(context,"/", self.getRuntime().newFixnum(60)),difference_minutes.callMethod(context,"%", self.getRuntime().newFixnum(60))}));
- }
- IRubyObject standard = self.callMethod(context,"strftime", self.getRuntime().newString("%Y-%m-%d %H:%M:%S"));
- if(self.callMethod(context, "usec").callMethod(context, "nonzero?").isTrue()) {
- standard = standard.callMethod(context, MethodIndex.OP_PLUS, "+", self.getRuntime().newString(".%06d").callMethod(context,"%", self.getRuntime().newArray(self.callMethod(context, "usec"))));
- }
- standard = standard.callMethod(context,MethodIndex.OP_PLUS, "+", self.getRuntime().newString(" %s").callMethod(context,"%", self.getRuntime().newArray(tz)));
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), standard, self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="Date")
- public static class YAMLDateMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject date_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
- }
- }
-
- @JRubyClass(name="TrueClass")
- public static class YAMLTrueMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject true_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject true_taguri(IRubyObject self) {
- return self.getRuntime().newString("tag:yaml.org,2002:bool");
- }
- }
-
- @JRubyClass(name="FalseClass")
- public static class YAMLFalseMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject false_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
- }
- @JRubyMethod(name = "taguri")
- public static IRubyObject false_taguri(IRubyObject self) {
- return self.getRuntime().newString("tag:yaml.org,2002:bool");
- }
- }
-
- @JRubyClass(name="NilClass")
- public static class YAMLNilMethods {
- @JRubyMethod(name = "to_yaml_node", required = 1)
- public static IRubyObject nil_to_yaml_node(IRubyObject self, IRubyObject arg) {
- ThreadContext context = self.getRuntime().getCurrentContext();
- return RuntimeHelpers.invoke(context, arg,"scalar", self.callMethod(context, "taguri"), self.getRuntime().newString(""), self.callMethod(context, "to_yaml_style"));
- }
- }
-}// RubyYAML
-/***** 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.io.InputStream;
-import java.io.IOException;
-
-import java.util.List;
-import java.util.ArrayList;
-
-import java.util.zip.GZIPInputStream;
-import java.util.zip.GZIPOutputStream;
-import org.jruby.anno.FrameField;
-import org.jruby.anno.JRubyMethod;
-import org.jruby.anno.JRubyClass;
-import org.jruby.anno.JRubyModule;
-
-import org.jruby.exceptions.RaiseException;
-import org.jruby.javasupport.util.RuntimeHelpers;
-import org.jruby.runtime.Arity;
-
-import org.jruby.runtime.Block;
-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.IOInputStream;
-import org.jruby.util.IOOutputStream;
-import org.jruby.util.CRC32Ext;
-import org.jruby.util.Adler32Ext;
-import org.jruby.util.ZlibInflate;
-import org.jruby.util.ZlibDeflate;
-
-import org.jruby.util.ByteList;
-
-@JRubyModule(name="Zlib")
-public class RubyZlib {
- /** Create the Zlib module and add it to the Ruby runtime.
- *
- */
- public static RubyModule createZlibModule(Ruby runtime) {
- RubyModule result = runtime.defineModule("Zlib");
-
- RubyClass gzfile = result.defineClassUnder("GzipFile", runtime.getObject(), RubyGzipFile.GZIPFILE_ALLOCATOR);
- gzfile.defineAnnotatedMethods(RubyGzipFile.class);
-
- RubyClass gzreader = result.defineClassUnder("GzipReader", gzfile, RubyGzipReader.GZIPREADER_ALLOCATOR);
- gzreader.includeModule(runtime.getEnumerable());
- gzreader.defineAnnotatedMethods(RubyGzipReader.class);
-
- RubyClass standardError = runtime.getStandardError();
- RubyClass zlibError = result.defineClassUnder("Error", standardError, standardError.getAllocator());
- gzreader.defineClassUnder("Error", zlibError, zlibError.getAllocator());
-
- RubyClass gzwriter = result.defineClassUnder("GzipWriter", gzfile, RubyGzipWriter.GZIPWRITER_ALLOCATOR);
- gzwriter.defineAnnotatedMethods(RubyGzipWriter.class);
-
- result.defineConstant("ZLIB_VERSION",runtime.newString("1.2.1"));
- result.defineConstant("VERSION",runtime.newString("0.6.0"));
-
- result.defineConstant("BINARY",runtime.newFixnum(0));
- result.defineConstant("ASCII",runtime.newFixnum(1));
- result.defineConstant("UNKNOWN",runtime.newFixnum(2));
-
- result.defineConstant("DEF_MEM_LEVEL",runtime.newFixnum(8));
- result.defineConstant("MAX_MEM_LEVEL",runtime.newFixnum(9));
-
- result.defineConstant("OS_UNIX",runtime.newFixnum(3));
- result.defineConstant("OS_UNKNOWN",runtime.newFixnum(255));
- result.defineConstant("OS_CODE",runtime.newFixnum(11));
- result.defineConstant("OS_ZSYSTEM",runtime.newFixnum(8));
- result.defineConstant("OS_VMCMS",runtime.newFixnum(4));
- result.defineConstant("OS_VMS",runtime.newFixnum(2));
- result.defineConstant("OS_RISCOS",runtime.newFixnum(13));
- result.defineConstant("OS_MACOS",runtime.newFixnum(7));
- result.defineConstant("OS_OS2",runtime.newFixnum(6));
- result.defineConstant("OS_AMIGA",runtime.newFixnum(1));
- result.defineConstant("OS_QDOS",runtime.newFixnum(12));
- result.defineConstant("OS_WIN32",runtime.newFixnum(11));
- result.defineConstant("OS_ATARI",runtime.newFixnum(5));
- result.defineConstant("OS_MSDOS",runtime.newFixnum(0));
- result.defineConstant("OS_CPM",runtime.newFixnum(9));
- result.defineConstant("OS_TOPS20",runtime.newFixnum(10));
-
- result.defineConstant("DEFAULT_STRATEGY",runtime.newFixnum(0));
- result.defineConstant("FILTERED",runtime.newFixnum(1));
- result.defineConstant("HUFFMAN_ONLY",runtime.newFixnum(2));
-
- result.defineConstant("NO_FLUSH",runtime.newFixnum(0));
- result.defineConstant("SYNC_FLUSH",runtime.newFixnum(2));
- result.defineConstant("FULL_FLUSH",runtime.newFixnum(3));
- result.defineConstant("FINISH",runtime.newFixnum(4));
-
- result.defineConstant("NO_COMPRESSION",runtime.newFixnum(0));
- result.defineConstant("BEST_SPEED",runtime.newFixnum(1));
- result.defineConstant("DEFAULT_COMPRESSION",runtime.newFixnum(-1));
- result.defineConstant("BEST_COMPRESSION",runtime.newFixnum(9));
-
- result.defineConstant("MAX_WBITS",runtime.newFixnum(15));
-
- result.defineAnnotatedMethods(RubyZlib.class);
-
- result.defineClassUnder("StreamEnd",zlibError, zlibError.getAllocator());
- result.defineClassUnder("StreamError",zlibError, zlibError.getAllocator());
- result.defineClassUnder("BufError",zlibError, zlibError.getAllocator());
- result.defineClassUnder("NeedDict",zlibError, zlibError.getAllocator());
- result.defineClassUnder("MemError",zlibError, zlibError.getAllocator());
- result.defineClassUnder("VersionError",zlibError, zlibError.getAllocator());
- result.defineClassUnder("DataError",zlibError, zlibError.getAllocator());
-
- RubyClass gzError = gzfile.defineClassUnder("Error",zlibError, zlibError.getAllocator());
- gzfile.defineClassUnder("CRCError",gzError, gzError.getAllocator());
- gzfile.defineClassUnder("NoFooter",gzError, gzError.getAllocator());
- gzfile.defineClassUnder("LengthError",gzError, gzError.getAllocator());
-
- // ZStream actually *isn't* allocatable
- RubyClass zstream = result.defineClassUnder("ZStream", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
- zstream.defineAnnotatedMethods(ZStream.class);
- zstream.undefineMethod("new");
-
- RubyClass infl = result.defineClassUnder("Inflate", zstream, Inflate.INFLATE_ALLOCATOR);
- infl.defineAnnotatedMethods(Inflate.class);
-
- RubyClass defl = result.defineClassUnder("Deflate", zstream, Deflate.DEFLATE_ALLOCATOR);
- defl.defineAnnotatedMethods(Deflate.class);
-
- runtime.getKernel().callMethod(runtime.getCurrentContext(),"require",runtime.newString("stringio"));
-
- return result;
- }
-
- @JRubyClass(name="Zlib::Error", parent="StandardError")
- public static class Error {}
- @JRubyClass(name="Zlib::StreamEnd", parent="Zlib::Error")
- public static class StreamEnd extends Error {}
- @JRubyClass(name="Zlib::StreamError", parent="Zlib::Error")
- public static class StreamError extends Error {}
- @JRubyClass(name="Zlib::BufError", parent="Zlib::Error")
- public static class BufError extends Error {}
- @JRubyClass(name="Zlib::NeedDict", parent="Zlib::Error")
- public static class NeedDict extends Error {}
- @JRubyClass(name="Zlib::MemError", parent="Zlib::Error")
- public static class MemError extends Error {}
- @JRubyClass(name="Zlib::VersionError", parent="Zlib::Error")
- public static class VersionError extends Error {}
- @JRubyClass(name="Zlib::DataError", parent="Zlib::Error")
- public static class DataError extends Error {}
-
- @JRubyMethod(name = "zlib_version", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject zlib_version(IRubyObject recv) {
- return ((RubyModule)recv).fastGetConstant("ZLIB_VERSION");
- }
-
- @JRubyMethod(name = "version", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject version(IRubyObject recv) {
- return ((RubyModule)recv).fastGetConstant("VERSION");
- }
-
- @JRubyMethod(name = "crc32", optional = 2, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject crc32(IRubyObject recv, IRubyObject[] args) throws Exception {
- args = Arity.scanArgs(recv.getRuntime(),args,0,2);
- long crc = 0;
- ByteList bytes = null;
-
- if (!args[0].isNil()) bytes = args[0].convertToString().getByteList();
- if (!args[1].isNil()) crc = RubyNumeric.num2long(args[1]);
-
- CRC32Ext ext = new CRC32Ext((int)crc);
- if (bytes != null) {
- ext.update(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- }
-
- return recv.getRuntime().newFixnum(ext.getValue());
- }
-
- @JRubyMethod(name = "adler32", optional = 2, module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject adler32(IRubyObject recv, IRubyObject[] args) throws Exception {
- args = Arity.scanArgs(recv.getRuntime(),args,0,2);
- int adler = 1;
- ByteList bytes = null;
- if (!args[0].isNil()) bytes = args[0].convertToString().getByteList();
- if (!args[1].isNil()) adler = RubyNumeric.fix2int(args[1]);
-
- Adler32Ext ext = new Adler32Ext(adler);
- if (bytes != null) {
- ext.update(bytes.unsafeBytes(), bytes.begin(), bytes.length()); // it's safe since adler.update doesn't modify the array
- }
- return recv.getRuntime().newFixnum(ext.getValue());
- }
-
- private final static long[] crctab = new long[]{
- 0L, 1996959894L, 3993919788L, 2567524794L, 124634137L, 1886057615L, 3915621685L, 2657392035L, 249268274L, 2044508324L, 3772115230L, 2547177864L, 162941995L,
- 2125561021L, 3887607047L, 2428444049L, 498536548L, 1789927666L, 4089016648L, 2227061214L, 450548861L, 1843258603L, 4107580753L, 2211677639L, 325883990L,
- 1684777152L, 4251122042L, 2321926636L, 335633487L, 1661365465L, 4195302755L, 2366115317L, 997073096L, 1281953886L, 3579855332L, 2724688242L, 1006888145L,
- 1258607687L, 3524101629L, 2768942443L, 901097722L, 1119000684L, 3686517206L, 2898065728L, 853044451L, 1172266101L, 3705015759L, 2882616665L, 651767980L,
- 1373503546L, 3369554304L, 3218104598L, 565507253L, 1454621731L, 3485111705L, 3099436303L, 671266974L, 1594198024L, 3322730930L, 2970347812L, 795835527L,
- 1483230225L, 3244367275L, 3060149565L, 1994146192L, 31158534L, 2563907772L, 4023717930L, 1907459465L, 112637215L, 2680153253L, 3904427059L, 2013776290L,
- 251722036L, 2517215374L, 3775830040L, 2137656763L, 141376813L, 2439277719L, 3865271297L, 1802195444L, 476864866L, 2238001368L, 4066508878L, 1812370925L,
- 453092731L, 2181625025L, 4111451223L, 1706088902L, 314042704L, 2344532202L, 4240017532L, 1658658271L, 366619977L, 2362670323L, 4224994405L, 1303535960L,
- 984961486L, 2747007092L, 3569037538L, 1256170817L, 1037604311L, 2765210733L, 3554079995L, 1131014506L, 879679996L, 2909243462L, 3663771856L, 1141124467L,
- 855842277L, 2852801631L, 3708648649L, 1342533948L, 654459306L, 3188396048L, 3373015174L, 1466479909L, 544179635L, 3110523913L, 3462522015L, 1591671054L,
- 702138776L, 2966460450L, 3352799412L, 1504918807L, 783551873L, 3082640443L, 3233442989L, 3988292384L, 2596254646L, 62317068L, 1957810842L, 3939845945L,
- 2647816111L, 81470997L, 1943803523L, 3814918930L, 2489596804L, 225274430L, 2053790376L, 3826175755L, 2466906013L, 167816743L, 2097651377L, 4027552580L,
- 2265490386L, 503444072L, 1762050814L, 4150417245L, 2154129355L, 426522225L, 1852507879L, 4275313526L, 2312317920L, 282753626L, 1742555852L, 4189708143L,
- 2394877945L, 397917763L, 1622183637L, 3604390888L, 2714866558L, 953729732L, 1340076626L, 3518719985L, 2797360999L, 1068828381L, 1219638859L, 3624741850L,
- 2936675148L, 906185462L, 1090812512L, 3747672003L, 2825379669L, 829329135L, 1181335161L, 3412177804L, 3160834842L, 628085408L, 1382605366L, 3423369109L,
- 3138078467L, 570562233L, 1426400815L, 3317316542L, 2998733608L, 733239954L, 1555261956L, 3268935591L, 3050360625L, 752459403L, 1541320221L, 2607071920L,
- 3965973030L, 1969922972L, 40735498L, 2617837225L, 3943577151L, 1913087877L, 83908371L, 2512341634L, 3803740692L, 2075208622L, 213261112L, 2463272603L,
- 3855990285L, 2094854071L, 198958881L, 2262029012L, 4057260610L, 1759359992L, 534414190L, 2176718541L, 4139329115L, 1873836001L, 414664567L, 2282248934L,
- 4279200368L, 1711684554L, 285281116L, 2405801727L, 4167216745L, 1634467795L, 376229701L, 2685067896L, 3608007406L, 1308918612L, 956543938L, 2808555105L,
- 3495958263L, 1231636301L, 1047427035L, 2932959818L, 3654703836L, 1088359270L, 936918000L, 2847714899L, 3736837829L, 1202900863L, 817233897L, 3183342108L,
- 3401237130L, 1404277552L, 615818150L, 3134207493L, 3453421203L, 1423857449L, 601450431L, 3009837614L, 3294710456L, 1567103746L, 711928724L, 3020668471L,
- 3272380065L, 1510334235L, 755167117};
-
- @JRubyMethod(name = "crc_table", module = true, visibility = Visibility.PRIVATE)
- public static IRubyObject crc_table(IRubyObject recv) {
- List<IRubyObject> ll = new ArrayList<IRubyObject>(crctab.length);
- for(int i=0;i<crctab.length;i++) {
- ll.add(recv.getRuntime().newFixnum(crctab[i]));
- }
- return recv.getRuntime().newArray(ll);
- }
-
-
- @JRubyClass(name="Zlib::ZStream")
- public static abstract class ZStream extends RubyObject {
- protected boolean closed = false;
- protected boolean ended = false;
- protected boolean finished = false;
-
- protected abstract int internalTotalOut();
- protected abstract boolean internalStreamEndP();
- protected abstract void internalEnd();
- protected abstract void internalReset();
- protected abstract int internalAdler();
- protected abstract IRubyObject internalFinish() throws Exception;
- protected abstract int internalTotalIn();
- protected abstract void internalClose();
-
- public ZStream(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(Block unusedBlock) {
- return this;
- }
-
- @JRubyMethod(name = "flust_next_out")
- public IRubyObject flush_next_out() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "total_out")
- public IRubyObject total_out() {
- return getRuntime().newFixnum(internalTotalOut());
- }
-
- @JRubyMethod(name = "stream_end?")
- public IRubyObject stream_end_p() {
- return internalStreamEndP() ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "data_type")
- public IRubyObject data_type() {
- return getRuntime().fastGetModule("Zlib").fastGetConstant("UNKNOWN");
- }
-
- @JRubyMethod(name = "closed?")
- public IRubyObject closed_p() {
- return closed ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "ended?")
- public IRubyObject ended_p() {
- return ended ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "end")
- public IRubyObject end() {
- if(!ended) {
- internalEnd();
- ended = true;
- }
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "reset")
- public IRubyObject reset() {
- internalReset();
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "avail_out")
- public IRubyObject avail_out() {
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "avail_out=", required = 1)
- public IRubyObject set_avail_out(IRubyObject p1) {
- return p1;
- }
-
- @JRubyMethod(name = "adler")
- public IRubyObject adler() {
- return getRuntime().newFixnum(internalAdler());
- }
-
- @JRubyMethod(name = "finish")
- public IRubyObject finish() throws Exception {
- if(!finished) {
- finished = true;
- return internalFinish();
- }
- return RubyString.newEmptyString(getRuntime());
- }
-
- @JRubyMethod(name = "avail_in")
- public IRubyObject avail_in() {
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "flush_next_in")
- public IRubyObject flush_next_in() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "total_in")
- public IRubyObject total_in() {
- return getRuntime().newFixnum(internalTotalIn());
- }
-
- @JRubyMethod(name = "finished?")
- public IRubyObject finished_p() {
- return finished ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "close")
- public IRubyObject close() {
- if(!closed) {
- internalClose();
- closed = true;
- }
- return getRuntime().getNil();
- }
- }
-
- @JRubyClass(name="Zlib::Inflate", parent="Zlib::ZStream")
- public static class Inflate extends ZStream {
- protected static final ObjectAllocator INFLATE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new Inflate(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "inflate", required = 1, meta = true)
- public static IRubyObject s_inflate(IRubyObject recv, IRubyObject string) throws Exception {
- return ZlibInflate.s_inflate(recv,string.convertToString().getByteList());
- }
-
- public Inflate(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private ZlibInflate infl;
-
- @JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE)
- public IRubyObject _initialize(IRubyObject[] args) throws Exception {
- infl = new ZlibInflate(this);
- return this;
- }
-
- @JRubyMethod(name = "<<", required = 1)
- public IRubyObject append(IRubyObject arg) {
- infl.append(arg);
- return this;
- }
-
- @JRubyMethod(name = "sync_point?")
- public IRubyObject sync_point_p() {
- return infl.sync_point();
- }
-
- @JRubyMethod(name = "set_dictionary", required = 1)
- public IRubyObject set_dictionary(IRubyObject arg) throws Exception {
- return infl.set_dictionary(arg);
- }
-
- @JRubyMethod(name = "inflate", required = 1)
- public IRubyObject inflate(IRubyObject string) throws Exception {
- return infl.inflate(string.convertToString().getByteList());
- }
-
- @JRubyMethod(name = "sync", required = 1)
- public IRubyObject sync(IRubyObject string) {
- return infl.sync(string);
- }
-
- protected int internalTotalOut() {
- return infl.getInflater().getTotalOut();
- }
-
- protected boolean internalStreamEndP() {
- return infl.getInflater().finished();
- }
-
- protected void internalEnd() {
- infl.getInflater().end();
- }
-
- protected void internalReset() {
- infl.getInflater().reset();
- }
-
- protected int internalAdler() {
- return infl.getInflater().getAdler();
- }
-
- protected IRubyObject internalFinish() throws Exception {
- infl.finish();
- return getRuntime().getNil();
- }
-
- public IRubyObject finished_p() {
- return infl.getInflater().finished() ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- protected int internalTotalIn() {
- return infl.getInflater().getTotalIn();
- }
-
- protected void internalClose() {
- infl.close();
- }
- }
-
- @JRubyClass(name="Zlib::Deflate", parent="Zlib::ZStream")
- public static class Deflate extends ZStream {
- protected static final ObjectAllocator DEFLATE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new Deflate(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "deflate", required = 1, optional = 1, meta = true)
- public static IRubyObject s_deflate(IRubyObject recv, IRubyObject[] args) throws Exception {
- args = Arity.scanArgs(recv.getRuntime(),args,1,1);
- int level = -1;
- if(!args[1].isNil()) {
- level = RubyNumeric.fix2int(args[1]);
- }
- return ZlibDeflate.s_deflate(recv,args[0].convertToString().getByteList(),level);
- }
-
- public Deflate(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private ZlibDeflate defl;
-
- @JRubyMethod(name = "initialize", optional = 4, visibility = Visibility.PRIVATE)
- public IRubyObject _initialize(IRubyObject[] args) throws Exception {
- args = Arity.scanArgs(getRuntime(),args,0,4);
- int level = -1;
- int window_bits = 15;
- int memlevel = 8;
- int strategy = 0;
- if(!args[0].isNil()) {
- level = RubyNumeric.fix2int(args[0]);
- }
- if(!args[1].isNil()) {
- window_bits = RubyNumeric.fix2int(args[1]);
- }
- if(!args[2].isNil()) {
- memlevel = RubyNumeric.fix2int(args[2]);
- }
- if(!args[3].isNil()) {
- strategy = RubyNumeric.fix2int(args[3]);
- }
- defl = new ZlibDeflate(this,level,window_bits,memlevel,strategy);
- return this;
- }
-
- @JRubyMethod(name = "<<", required = 1)
- public IRubyObject append(IRubyObject arg) throws Exception {
- defl.append(arg);
- return this;
- }
-
- @JRubyMethod(name = "params", required = 2)
- public IRubyObject params(IRubyObject level, IRubyObject strategy) {
- defl.params(RubyNumeric.fix2int(level),RubyNumeric.fix2int(strategy));
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "set_dictionary", required = 1)
- public IRubyObject set_dictionary(IRubyObject arg) throws Exception {
- return defl.set_dictionary(arg);
- }
-
- @JRubyMethod(name = "flush", optional = 1)
- public IRubyObject flush(IRubyObject[] args) throws Exception {
- int flush = 2; // SYNC_FLUSH
- if(args.length == 1) {
- if(!args[0].isNil()) {
- flush = RubyNumeric.fix2int(args[0]);
- }
- }
- return defl.flush(flush);
- }
-
- @JRubyMethod(name = "deflate", required = 1, optional = 1)
- public IRubyObject deflate(IRubyObject[] args) throws Exception {
- args = Arity.scanArgs(getRuntime(),args,1,1);
- int flush = 0; // NO_FLUSH
- if(!args[1].isNil()) {
- flush = RubyNumeric.fix2int(args[1]);
- }
- return defl.deflate(args[0].convertToString().getByteList(),flush);
- }
-
- protected int internalTotalOut() {
- return defl.getDeflater().getTotalOut();
- }
-
- protected boolean internalStreamEndP() {
- return defl.getDeflater().finished();
- }
-
- protected void internalEnd() {
- defl.getDeflater().end();
- }
-
- protected void internalReset() {
- defl.getDeflater().reset();
- }
-
- protected int internalAdler() {
- return defl.getDeflater().getAdler();
- }
-
- protected IRubyObject internalFinish() throws Exception {
- return defl.finish();
- }
-
- protected int internalTotalIn() {
- return defl.getDeflater().getTotalIn();
- }
-
- protected void internalClose() {
- defl.close();
- }
- }
-
- @JRubyClass(name="Zlib::GzipFile")
- public static class RubyGzipFile extends RubyObject {
- @JRubyClass(name="Zlib::GzipFile::Error", parent="Zlib::Error")
- public static class Error {}
- @JRubyClass(name="Zlib::GzipFile::CRCError", parent="Zlib::GzipFile::Error")
- public static class CRCError extends Error {}
- @JRubyClass(name="Zlib::GzipFile::NoFooter", parent="Zlib::GzipFile::Error")
- public static class NoFooter extends Error {}
- @JRubyClass(name="Zlib::GzipFile::LengthError", parent="Zlib::GzipFile::Error")
- public static class LengthError extends Error {}
-
- private static IRubyObject wrap(ThreadContext context, RubyGzipFile instance,
- IRubyObject io, Block block) throws IOException {
- if (block.isGiven()) {
- try {
- block.yield(context, instance);
-
- return instance.getRuntime().getNil();
- } finally {
- if (!instance.isClosed()) instance.close();
- }
- }
-
- return io;
- }
-
- @JRubyMethod(name = "wrap", required = 1, frame = true, meta = true)
- public static IRubyObject wrap(ThreadContext context, IRubyObject recv, IRubyObject io, Block block) throws IOException {
- Ruby runtime = recv.getRuntime();
- RubyGzipFile instance;
-
- // TODO: People extending GzipWriter/reader will break. Find better way here.
- if (recv == runtime.getModule("Zlib").getClass("GzipWriter")) {
- instance = RubyGzipWriter.newGzipWriter(recv, new IRubyObject[] { io }, block);
- } else {
- instance = RubyGzipReader.newInstance(recv, new IRubyObject[] { io }, block);
- }
-
- return wrap(context, instance, io, block);
- }
-
- protected static final ObjectAllocator GZIPFILE_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyGzipFile(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "new", frame = true, meta = true)
- public static RubyGzipFile newInstance(IRubyObject recv, Block block) {
- RubyClass klass = (RubyClass)recv;
-
- RubyGzipFile result = (RubyGzipFile) klass.allocate();
-
- result.callInit(new IRubyObject[0], block);
-
- return result;
- }
-
- protected boolean closed = false;
- protected boolean finished = false;
- private int os_code = 255;
- private int level = -1;
- private String orig_name;
- private String comment;
- protected IRubyObject realIo;
- private IRubyObject mtime;
-
- public RubyGzipFile(Ruby runtime, RubyClass type) {
- super(runtime, type);
- mtime = runtime.getNil();
- }
-
- @JRubyMethod(name = "os_code")
- public IRubyObject os_code() {
- return getRuntime().newFixnum(os_code);
- }
-
- @JRubyMethod(name = "closed?")
- public IRubyObject closed_p() {
- return closed ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- protected boolean isClosed() {
- return closed;
- }
-
- @JRubyMethod(name = "orig_name")
- public IRubyObject orig_name() {
- return orig_name == null ? getRuntime().getNil() : getRuntime().newString(orig_name);
- }
-
- @JRubyMethod(name = "to_io")
- public IRubyObject to_io() {
- return realIo;
- }
-
- @JRubyMethod(name = "comment")
- public IRubyObject comment() {
- return comment == null ? getRuntime().getNil() : getRuntime().newString(comment);
- }
-
- @JRubyMethod(name = "crc")
- public IRubyObject crc() {
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "mtime")
- public IRubyObject mtime() {
- return mtime;
- }
-
- @JRubyMethod(name = "sync")
- public IRubyObject sync() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "finish")
- public IRubyObject finish() throws IOException {
- if (!finished) {
- //io.finish();
- }
- finished = true;
- return realIo;
- }
-
- @JRubyMethod(name = "close")
- public IRubyObject close() throws IOException {
- return null;
- }
-
- @JRubyMethod(name = "level")
- public IRubyObject level() {
- return getRuntime().newFixnum(level);
- }
-
- @JRubyMethod(name = "sync=", required = 1)
- public IRubyObject set_sync(IRubyObject ignored) {
- return getRuntime().getNil();
- }
- }
-
- @JRubyClass(name="Zlib::GzipReader", parent="Zlib::GzipFile", include="Enumerable")
- public static class RubyGzipReader extends RubyGzipFile {
- @JRubyClass(name="Zlib::GzipReader::Error", parent="Zlib::GzipReader")
- public static class Error {}
- protected static final ObjectAllocator GZIPREADER_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyGzipReader(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
- public static RubyGzipReader newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
- RubyClass klass = (RubyClass)recv;
- RubyGzipReader result = (RubyGzipReader)klass.allocate();
- result.callInit(args, block);
- return result;
- }
-
- @JRubyMethod(name = "open", required = 1, frame = true, meta = true)
- public static IRubyObject open(final ThreadContext context, IRubyObject recv, IRubyObject filename, Block block) throws IOException {
- Ruby runtime = recv.getRuntime();
- IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", filename, runtime.newString("rb"));
- RubyGzipFile instance = newInstance(recv, new IRubyObject[]{io}, Block.NULL_BLOCK);
-
- return RubyGzipFile.wrap(context, instance, io, block);
- }
-
- public RubyGzipReader(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private int line;
- private InputStream io;
-
- @JRubyMethod(name = "initialize", required = 1, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize(IRubyObject io, Block unusedBlock) {
- realIo = io;
- try {
- this.io = new GZIPInputStream(new IOInputStream(io));
- } catch (IOException e) {
- Ruby runtime = io.getRuntime();
- RubyClass errorClass = runtime.fastGetModule("Zlib").fastGetClass("GzipReader").fastGetClass("Error");
- throw new RaiseException(RubyException.newException(runtime, errorClass, e.getMessage()));
- }
-
- line = 1;
-
- return this;
- }
-
- @JRubyMethod(name = "rewind")
- public IRubyObject rewind() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "lineno")
- public IRubyObject lineno() {
- return getRuntime().newFixnum(line);
- }
-
- @JRubyMethod(name = "readline", writes = FrameField.LASTLINE)
- public IRubyObject readline(ThreadContext context) throws IOException {
- IRubyObject dst = gets(context, new IRubyObject[0]);
- if (dst.isNil()) {
- throw getRuntime().newEOFError();
- }
- return dst;
- }
-
- public IRubyObject internalGets(IRubyObject[] args) throws IOException {
- ByteList sep = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
- if (args.length > 0) {
- sep = args[0].convertToString().getByteList();
- }
- return internalSepGets(sep);
- }
-
- private IRubyObject internalSepGets(ByteList sep) throws IOException {
- ByteList result = new ByteList();
- int ce = io.read();
- while (ce != -1 && sep.indexOf(ce) == -1) {
- result.append((byte)ce);
- ce = io.read();
- }
- line++;
- result.append(sep);
- return RubyString.newString(getRuntime(),result);
- }
-
- @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
- public IRubyObject gets(ThreadContext context, IRubyObject[] args) throws IOException {
- IRubyObject result = internalGets(args);
- if (!result.isNil()) {
- context.getCurrentFrame().setLastLine(result);
- }
- return result;
- }
-
- private final static int BUFF_SIZE = 4096;
-
- @JRubyMethod(name = "read", optional = 1)
- public IRubyObject read(IRubyObject[] args) throws IOException {
- if (args.length == 0 || args[0].isNil()) {
- ByteList val = new ByteList(10);
- byte[] buffer = new byte[BUFF_SIZE];
- int read = io.read(buffer);
- while (read != -1) {
- val.append(buffer,0,read);
- read = io.read(buffer);
- }
- return RubyString.newString(getRuntime(),val);
- }
-
- int len = RubyNumeric.fix2int(args[0]);
- if (len < 0) {
- throw getRuntime().newArgumentError("negative length " + len + " given");
- } else if (len > 0) {
- byte[] buffer = new byte[len];
- int toRead = len;
- int offset = 0;
- int read = 0;
- while (toRead > 0) {
- read = io.read(buffer,offset,toRead);
- if (read == -1) {
- break;
- }
- toRead -= read;
- offset += read;
- } // hmm...
- return RubyString.newString(getRuntime(),new ByteList(buffer,0,len-toRead,false));
- }
-
- return RubyString.newEmptyString(getRuntime());
- }
-
- @JRubyMethod(name = "lineno=", required = 1)
- public IRubyObject set_lineno(IRubyObject lineArg) {
- line = RubyNumeric.fix2int(lineArg);
- return lineArg;
- }
-
- @JRubyMethod(name = "pos")
- public IRubyObject pos() {
- return RubyFixnum.zero(getRuntime());
- }
-
- @JRubyMethod(name = "readchar")
- public IRubyObject readchar() throws IOException {
- int value = io.read();
- if (value == -1) {
- throw getRuntime().newEOFError();
- }
- return getRuntime().newFixnum(value);
- }
-
- @JRubyMethod(name = "getc")
- public IRubyObject getc() throws IOException {
- int value = io.read();
- return value == -1 ? getRuntime().getNil() : getRuntime().newFixnum(value);
- }
-
- private boolean isEof() throws IOException {
- return ((GZIPInputStream)io).available() != 1;
- }
-
- @JRubyMethod(name = "close")
- public IRubyObject close() throws IOException {
- if (!closed) {
- io.close();
- }
- this.closed = true;
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "eof")
- public IRubyObject eof() throws IOException {
- return isEof() ? getRuntime().getTrue() : getRuntime().getFalse();
- }
-
- @JRubyMethod(name = "eof?")
- public IRubyObject eof_p() throws IOException {
- return eof();
- }
-
- @JRubyMethod(name = "unused")
- public IRubyObject unused() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "tell")
- public IRubyObject tell() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "each", optional = 1, frame = true)
- public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) throws IOException {
- ByteList sep = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
-
- if (args.length > 0 && !args[0].isNil()) {
- sep = args[0].convertToString().getByteList();
- }
-
- while (!isEof()) {
- block.yield(context, internalSepGets(sep));
- }
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "ungetc", required = 1)
- public IRubyObject ungetc(IRubyObject arg) {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "readlines", optional = 1)
- public IRubyObject readlines(IRubyObject[] args) throws IOException {
- List<IRubyObject> array = new ArrayList<IRubyObject>();
-
- if (args.length != 0 && args[0].isNil()) {
- array.add(read(new IRubyObject[0]));
- } else {
- ByteList seperator = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
- if (args.length > 0) {
- seperator = args[0].convertToString().getByteList();
- }
- while (!isEof()) {
- array.add(internalSepGets(seperator));
- }
- }
- return getRuntime().newArray(array);
- }
-
- @JRubyMethod(name = "each_byte", frame = true)
- public IRubyObject each_byte(ThreadContext context, Block block) throws IOException {
- int value = io.read();
-
- while (value != -1) {
- block.yield(context, getRuntime().newFixnum(value));
- value = io.read();
- }
-
- return getRuntime().getNil();
- }
- }
-
- @JRubyClass(name="Zlib::GzipWriter", parent="Zlib::GzipFile")
- public static class RubyGzipWriter extends RubyGzipFile {
- protected static final ObjectAllocator GZIPWRITER_ALLOCATOR = new ObjectAllocator() {
- public IRubyObject allocate(Ruby runtime, RubyClass klass) {
- return new RubyGzipWriter(runtime, klass);
- }
- };
-
- @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
- public static RubyGzipWriter newGzipWriter(IRubyObject recv, IRubyObject[] args, Block block) {
- RubyClass klass = (RubyClass)recv;
-
- RubyGzipWriter result = (RubyGzipWriter)klass.allocate();
- result.callInit(args, block);
- return result;
- }
-
- @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, meta = true)
- public static IRubyObject open(final ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) throws IOException {
- Ruby runtime = recv.getRuntime();
- IRubyObject level = runtime.getNil();
- IRubyObject strategy = runtime.getNil();
-
- if (args.length > 1) {
- level = args[1];
- if (args.length > 2) strategy = args[2];
- }
-
- IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("wb"));
- RubyGzipFile instance = newGzipWriter(recv, new IRubyObject[]{io, level, strategy}, Block.NULL_BLOCK);
-
- return RubyGzipFile.wrap(context, instance, io, block);
- }
-
- public RubyGzipWriter(Ruby runtime, RubyClass type) {
- super(runtime, type);
- }
-
- private GZIPOutputStream io;
-
- @JRubyMethod(name = "initialize", required = 1, rest = true, frame = true, visibility = Visibility.PRIVATE)
- public IRubyObject initialize2(IRubyObject[] args, Block unusedBlock) throws IOException {
- realIo = (RubyObject)args[0];
- this.io = new GZIPOutputStream(new IOOutputStream(args[0]));
-
- return this;
- }
-
- @JRubyMethod(name = "close")
- public IRubyObject close() throws IOException {
- if (!closed) {
- io.close();
- }
- this.closed = true;
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "append", required = 1)
- public IRubyObject append(IRubyObject p1) throws IOException {
- this.write(p1);
- return this;
- }
-
- @JRubyMethod(name = "printf", required = 1, rest = true)
- public IRubyObject printf(ThreadContext context, IRubyObject[] args) throws IOException {
- write(RubyKernel.sprintf(context, this, args));
- return context.getRuntime().getNil();
- }
-
- @JRubyMethod(name = "print", rest = true)
- public IRubyObject print(IRubyObject[] args) throws IOException {
- if (args.length != 0) {
- for (int i = 0, j = args.length; i < j; i++) {
- write(args[i]);
- }
- }
-
- IRubyObject sep = getRuntime().getGlobalVariables().get("$\\");
- if (!sep.isNil()) {
- write(sep);
- }
-
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "pos")
- public IRubyObject pos() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "orig_name=", required = 1)
- public IRubyObject set_orig_name(IRubyObject ignored) {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "comment=", required = 1)
- public IRubyObject set_comment(IRubyObject ignored) {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "putc", required = 1)
- public IRubyObject putc(IRubyObject p1) throws IOException {
- io.write(RubyNumeric.fix2int(p1));
- return p1;
- }
-
- @JRubyMethod(name = "puts", rest = true)
- public IRubyObject puts(ThreadContext context, IRubyObject[] args) throws IOException {
- RubyStringIO sio = (RubyStringIO)getRuntime().fastGetClass("StringIO").newInstance(context, new IRubyObject[0], Block.NULL_BLOCK);
- sio.puts(context, args);
- write(sio.string());
-
- return getRuntime().getNil();
- }
-
- public IRubyObject finish() throws IOException {
- if (!finished) {
- io.finish();
- }
- finished = true;
- return realIo;
- }
-
- @JRubyMethod(name = "flush", optional = 1)
- public IRubyObject flush(IRubyObject[] args) throws IOException {
- if (args.length == 0 || args[0].isNil() || RubyNumeric.fix2int(args[0]) != 0) { // Zlib::NO_FLUSH
- io.flush();
- }
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "mtime=", required = 1)
- public IRubyObject set_mtime(IRubyObject ignored) {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "tell")
- public IRubyObject tell() {
- return getRuntime().getNil();
- }
-
- @JRubyMethod(name = "write", required = 1)
- public IRubyObject write(IRubyObject p1) throws IOException {
- ByteList bytes = p1.convertToString().getByteList();
- io.write(bytes.unsafeBytes(), bytes.begin(), bytes.length());
- return getRuntime().newFixnum(bytes.length());
- }
- }
-}
-/***** 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 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.runtime.Arity;
-import org.jruby.runtime.Block;
-import org.jruby.runtime.builtin.IRubyObject;
-import org.jruby.runtime.callback.Callback;
-
-/**
- *
- * @author jpetersen
- */
-public final class TopSelfFactory {
-
- /**
- * Constructor for TopSelfFactory.
- */
- private TopSelfFactory() {
- super();
- }
-
- public static IRubyObject createTopSelf(final Ruby runtime) {
- IRubyObject topSelf = new RubyObject(runtime, runtime.getObject());
-
- topSelf.getSingletonClass().defineFastMethod("to_s", new Callback() {
- /**
- * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
- */
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- return runtime.newString("main");
- }
-
- /**
- * @see org.jruby.runtime.callback.Callback#getArity()
- */
- public Arity getArity() {
- return Arity.noArguments();
- }
- });
-
- // The following three methods must be defined fast, since they expect to modify the current frame
- // (i.e. they expect no frame will be allocated for them). JRUBY-1185.
- topSelf.getSingletonClass().defineFastPrivateMethod("include", new Callback() {
- /**
- * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
- */
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
- runtime.secure(4);
- return runtime.getObject().include(args);
- }
-
- /**
- * @see org.jruby.runtime.callback.Callback#getArity()
- */
- public Arity getArity() {
- return Arity.optional();
- }
- });
-
- topSelf.getSingletonClass().defineFastPrivateMethod("public", new Callback() {
- /**
- * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
- */
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- return runtime.getObject().rbPublic(recv.getRuntime().getCurrentContext(), args);
- }
-
- /**
- * @see org.jruby.runtime.callback.Callback#getArity()
- */
- public Arity getArity() {
- return Arity.optional();
- }
- });
-
- topSelf.getSingletonClass().defineFastPrivateMethod("private", new Callback() {
- /**
- * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
- */
- public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
- return runtime.getObject().rbPrivate(recv.getRuntime().getCurrentContext(), args);
- }
-
- /**
- * @see org.jruby.runtime.callback.Callback#getArity()
- */
- public Arity getArity() {
- return Arity.optional();
- }
- });
-
- return topSelf;
- }
-}