diff options
Diffstat (limited to 'test/scanners/java/jruby.in.java')
-rw-r--r-- | test/scanners/java/jruby.in.java | 51193 |
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; - } -} |