/text/src/test/resources/examples/java/jruby.in.java

https://github.com/rimolive/core · Java · 51193 lines · 35445 code · 7339 blank · 8409 comment · 6897 complexity · 177e0295a7887cd9ab9c9ef722d9455d MD5 · raw file

  1. package org.jruby;
  2. public enum CompatVersion {
  3. RUBY1_8, RUBY1_9, BOTH;
  4. public static CompatVersion getVersionFromString(String compatString) {
  5. if (compatString.equalsIgnoreCase("RUBY1_8")) {
  6. return CompatVersion.RUBY1_8;
  7. } else if (compatString.equalsIgnoreCase("RUBY1_9")) {
  8. return CompatVersion.RUBY1_9;
  9. } else {
  10. return null;
  11. }
  12. }
  13. }
  14. /***** BEGIN LICENSE BLOCK *****
  15. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  16. *
  17. * The contents of this file are subject to the Common Public
  18. * License Version 1.0 (the "License"); you may not use this file
  19. * except in compliance with the License. You may obtain a copy of
  20. * the License at http://www.eclipse.org/legal/cpl-v10.html
  21. *
  22. * Software distributed under the License is distributed on an "AS
  23. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24. * implied. See the License for the specific language governing
  25. * rights and limitations under the License.
  26. *
  27. * Copyright (C) 2007 Damian Steer <pldms@mac.com>
  28. *
  29. * Alternatively, the contents of this file may be used under the terms of
  30. * either of the GNU General Public License Version 2 or later (the "GPL"),
  31. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32. * in which case the provisions of the GPL or the LGPL are applicable instead
  33. * of those above. If you wish to allow use of your version of this file only
  34. * under the terms of either the GPL or the LGPL, and not to allow others to
  35. * use your version of this file under the terms of the CPL, indicate your
  36. * decision by deleting the provisions above and replace them with the notice
  37. * and other provisions required by the GPL or the LGPL. If you do not delete
  38. * the provisions above, a recipient may use your version of this file under
  39. * the terms of any one of the CPL, the GPL or the LGPL.
  40. ***** END LICENSE BLOCK *****/
  41. package org.jruby;
  42. /**
  43. * An almost entirely useless interface for those objects that we _really_ want
  44. * to finalise.
  45. *
  46. * @author pldms
  47. *
  48. */
  49. public interface Finalizable {
  50. public void finalize();
  51. }
  52. /***** BEGIN LICENSE BLOCK *****
  53. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  54. *
  55. * The contents of this file are subject to the Common Public
  56. * License Version 1.0 (the "License"); you may not use this file
  57. * except in compliance with the License. You may obtain a copy of
  58. * the License at http://www.eclipse.org/legal/cpl-v10.html
  59. *
  60. * Software distributed under the License is distributed on an "AS
  61. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  62. * implied. See the License for the specific language governing
  63. * rights and limitations under the License.
  64. *
  65. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  66. * Copyright (C) 2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  67. *
  68. * Alternatively, the contents of this file may be used under the terms of
  69. * either of the GNU General Public License Version 2 or later (the "GPL"),
  70. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  71. * in which case the provisions of the GPL or the LGPL are applicable instead
  72. * of those above. If you wish to allow use of your version of this file only
  73. * under the terms of either the GPL or the LGPL, and not to allow others to
  74. * use your version of this file under the terms of the CPL, indicate your
  75. * decision by deleting the provisions above and replace them with the notice
  76. * and other provisions required by the GPL or the LGPL. If you do not delete
  77. * the provisions above, a recipient may use your version of this file under
  78. * the terms of any one of the CPL, the GPL or the LGPL.
  79. ***** END LICENSE BLOCK *****/
  80. package org.jruby;
  81. /**
  82. * Error numbers.
  83. * @fixme
  84. * this interface is a big hack defining a bunch of arbitrary valor as system call error numbers
  85. * this is actually because I need them but will probably need to be changed to something smarter
  86. * sooner or later.
  87. * The purpose of this class it to help implement the Errno module which in turn in needed by rubicon.
  88. * @author Benoit Cerrina
  89. **/
  90. public interface IErrno
  91. {
  92. int EPERM = 1;
  93. int ENOENT = 2;
  94. int ESRCH = 3;
  95. int EINTR = 4;
  96. int EIO = 5;
  97. int ENXIO = 6;
  98. int E2BIG = 7;
  99. int ENOEXEC = 8;
  100. int EBADF = 9;
  101. int ECHILD = 10;
  102. int EDEADLK = 11;
  103. int ENOMEM = 12;
  104. int EACCES = 13;
  105. int EFAULT = 14;
  106. int ENOTBLK = 15;
  107. int EBUSY = 16;
  108. int EEXIST = 17;
  109. int EXDEV = 18;
  110. int ENODEV = 19;
  111. int ENOTDIR = 20;
  112. int EISDIR = 21;
  113. int EINVAL = 22;
  114. int ENFILE = 23;
  115. int EMFILE = 24;
  116. int ENOTTY = 25;
  117. int ETXTBSY = 26;
  118. int EFBIG = 27;
  119. int ENOSPC = 28;
  120. int ESPIPE = 29;
  121. int EROFS = 30;
  122. int EMLINK = 31;
  123. int EPIPE = 32;
  124. int EDOM = 33;
  125. int ERANGE = 34;
  126. int EWOULDBLOCK = 35;
  127. int EAGAIN = 35;
  128. int EINPROGRESS = 36;
  129. int EALREADY = 37;
  130. int ENOTSOCK = 38;
  131. int EDESTADDRREQ = 39;
  132. int EMSGSIZE = 40;
  133. int EPROTOTYPE = 41;
  134. int ENOPROTOOPT = 42;
  135. int EPROTONOSUPPORT = 43;
  136. int ESOCKTNOSUPPORT = 44;
  137. int EOPNOTSUPP = 45;
  138. int EPFNOSUPPORT = 46;
  139. int EAFNOSUPPORT = 47;
  140. int EADDRINUSE = 48;
  141. int EADDRNOTAVAIL = 49;
  142. int ENETDOWN = 50;
  143. int ENETUNREACH = 51;
  144. int ENETRESET = 52;
  145. int ECONNABORTED = 53;
  146. int ECONNRESET = 54;
  147. int ENOBUFS = 55;
  148. int EISCONN = 56;
  149. int ENOTCONN = 57;
  150. int ESHUTDOWN = 58;
  151. int ETOOMANYREFS = 59;
  152. int ETIMEDOUT = 60;
  153. int ECONNREFUSED = 61;
  154. int ELOOP = 62;
  155. int ENAMETOOLONG = 63;
  156. int EHOSTDOWN = 64;
  157. int EHOSTUNREACH = 65;
  158. int ENOTEMPTY = 66;
  159. int EUSERS = 68;
  160. int EDQUOT = 69;
  161. int ESTALE = 70;
  162. int EREMOTE = 71;
  163. int ENOLCK = 77;
  164. int ENOSYS = 78;
  165. int EOVERFLOW = 84;
  166. int EIDRM = 90;
  167. int ENOMSG = 91;
  168. int EILSEQ = 92;
  169. int EBADMSG = 94;
  170. int EMULTIHOP = 95;
  171. int ENODATA = 96;
  172. int ENOLINK = 97;
  173. int ENOSR = 98;
  174. int ENOSTR = 99;
  175. int EPROTO = 100;
  176. int ETIME = 101;
  177. int EOPNOTSUPP_DARWIN = 102;
  178. }
  179. /*
  180. ***** BEGIN LICENSE BLOCK *****
  181. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  182. *
  183. * The contents of this file are subject to the Common Public
  184. * License Version 1.0 (the "License"); you may not use this file
  185. * except in compliance with the License. You may obtain a copy of
  186. * the License at http://www.eclipse.org/legal/cpl-v10.html
  187. *
  188. * Software distributed under the License is distributed on an "AS
  189. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  190. * implied. See the License for the specific language governing
  191. * rights and limitations under the License.
  192. *
  193. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  194. * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
  195. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  196. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  197. * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
  198. *
  199. * Alternatively, the contents of this file may be used under the terms of
  200. * either of the GNU General Public License Version 2 or later (the "GPL"),
  201. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  202. * in which case the provisions of the GPL or the LGPL are applicable instead
  203. * of those above. If you wish to allow use of your version of this file only
  204. * under the terms of either the GPL or the LGPL, and not to allow others to
  205. * use your version of this file under the terms of the CPL, indicate your
  206. * decision by deleting the provisions above and replace them with the notice
  207. * and other provisions required by the GPL or the LGPL. If you do not delete
  208. * the provisions above, a recipient may use your version of this file under
  209. * the terms of any one of the CPL, the GPL or the LGPL.
  210. ***** END LICENSE BLOCK *****/
  211. package org.jruby;
  212. import java.util.List;
  213. import java.util.Map;
  214. import org.jruby.internal.runtime.methods.DynamicMethod;
  215. import org.jruby.runtime.builtin.IRubyObject;
  216. import org.jruby.runtime.builtin.Variable;
  217. /**
  218. * This class is used to provide an intermediate superclass for modules and classes that include
  219. * other modules. It inserts itself as the immediate superClass of the includer, but defers all
  220. * module methods to the actual superclass. Multiple of these intermediate superclasses can be
  221. * added for multiple included modules.
  222. *
  223. * This allows the normal superclass-based searches (searchMethod, getConstant, etc) to traverse
  224. * the superclass ancestors as normal while the included modules do not actually show up in
  225. * direct inheritance traversal.
  226. *
  227. * @see org.jruby.RubyModule
  228. */
  229. public final class IncludedModuleWrapper extends RubyClass {
  230. private final RubyModule delegate;
  231. public IncludedModuleWrapper(Ruby runtime, RubyClass superClass, RubyModule delegate) {
  232. super(runtime, superClass, false);
  233. this.delegate = delegate;
  234. this.metaClass = delegate.metaClass;
  235. }
  236. /**
  237. * Overridden newIncludeClass implementation to allow attaching future includes to the correct module
  238. * (i.e. the one to which this is attached)
  239. *
  240. * @see org.jruby.RubyModule#newIncludeClass(RubyClass)
  241. */
  242. @Override
  243. public IncludedModuleWrapper newIncludeClass(RubyClass superClass) {
  244. IncludedModuleWrapper includedModule = new IncludedModuleWrapper(getRuntime(), superClass, getNonIncludedClass());
  245. // include its parent (and in turn that module's parents)
  246. if (getSuperClass() != null) {
  247. includedModule.includeModule(getSuperClass());
  248. }
  249. return includedModule;
  250. }
  251. @Override
  252. public boolean isModule() {
  253. return false;
  254. }
  255. @Override
  256. public boolean isClass() {
  257. return false;
  258. }
  259. @Override
  260. public boolean isIncluded() {
  261. return true;
  262. }
  263. @Override
  264. public boolean isImmediate() {
  265. return true;
  266. }
  267. @Override
  268. public void setMetaClass(RubyClass newRubyClass) {
  269. throw new UnsupportedOperationException("An included class is only a wrapper for a module");
  270. }
  271. @Override
  272. public Map<String, DynamicMethod> getMethods() {
  273. return delegate.getMethods();
  274. }
  275. @Override
  276. public void addMethod(String name, DynamicMethod method) {
  277. throw new UnsupportedOperationException("An included class is only a wrapper for a module");
  278. }
  279. public void setMethods(Map newMethods) {
  280. throw new UnsupportedOperationException("An included class is only a wrapper for a module");
  281. }
  282. @Override
  283. public String getName() {
  284. return delegate.getName();
  285. }
  286. @Override
  287. public RubyModule getNonIncludedClass() {
  288. return delegate;
  289. }
  290. @Override
  291. public RubyClass getRealClass() {
  292. return getSuperClass().getRealClass();
  293. }
  294. @Override
  295. protected boolean isSame(RubyModule module) {
  296. return delegate.isSame(module);
  297. }
  298. /**
  299. * We don't want to reveal ourselves to Ruby code, so delegate this
  300. * operation.
  301. */
  302. @Override
  303. public IRubyObject id() {
  304. return delegate.id();
  305. }
  306. //
  307. // VARIABLE TABLE METHODS - pass to delegate
  308. //
  309. @Override
  310. protected boolean variableTableContains(String name) {
  311. return delegate.variableTableContains(name);
  312. }
  313. @Override
  314. protected boolean variableTableFastContains(String internedName) {
  315. return delegate.variableTableFastContains(internedName);
  316. }
  317. @Override
  318. protected IRubyObject variableTableFetch(String name) {
  319. return delegate.variableTableFetch(name);
  320. }
  321. @Override
  322. protected IRubyObject variableTableFastFetch(String internedName) {
  323. return delegate.variableTableFastFetch(internedName);
  324. }
  325. @Override
  326. protected IRubyObject variableTableStore(String name, IRubyObject value) {
  327. return delegate.variableTableStore(name, value);
  328. }
  329. @Override
  330. protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
  331. return delegate.variableTableFastStore(internedName, value);
  332. }
  333. @Override
  334. protected IRubyObject variableTableRemove(String name) {
  335. return delegate.variableTableRemove(name);
  336. }
  337. @Override
  338. protected VariableTableEntry[] variableTableGetTable() {
  339. return delegate.variableTableGetTable();
  340. }
  341. @Override
  342. protected int variableTableGetSize() {
  343. return delegate.variableTableGetSize();
  344. }
  345. @Override
  346. protected void variableTableSync(List<Variable<IRubyObject>> vars) {
  347. delegate.variableTableSync(vars);
  348. }
  349. @Override
  350. protected IRubyObject variableTableReadLocked(VariableTableEntry entry) {
  351. return delegate.variableTableReadLocked(entry);
  352. }
  353. /**
  354. * Method to help ease transition to new variables implementation.
  355. * Will likely be deprecated in the near future.
  356. */
  357. @SuppressWarnings("unchecked")
  358. @Override
  359. @Deprecated // born deprecated
  360. protected Map variableTableGetMap() {
  361. return delegate.variableTableGetMap();
  362. }
  363. /**
  364. * Method to help ease transition to new variables implementation.
  365. * Will likely be deprecated in the near future.
  366. */
  367. @SuppressWarnings("unchecked")
  368. @Override
  369. @Deprecated // born deprecated
  370. protected Map variableTableGetMap(Map map) {
  371. return delegate.variableTableGetMap(map);
  372. }
  373. //
  374. // CONSTANT TABLE METHODS - pass to delegate
  375. //
  376. @Override
  377. protected boolean constantTableContains(String name) {
  378. return delegate.constantTableContains(name);
  379. }
  380. @Override
  381. protected boolean constantTableFastContains(String internedName) {
  382. return delegate.constantTableFastContains(internedName);
  383. }
  384. @Override
  385. protected IRubyObject constantTableFetch(String name) {
  386. return delegate.constantTableFetch(name);
  387. }
  388. @Override
  389. protected IRubyObject constantTableFastFetch(String internedName) {
  390. return delegate.constantTableFastFetch(internedName);
  391. }
  392. @Override
  393. protected IRubyObject constantTableStore(String name, IRubyObject value) {
  394. // FIXME: legal here? may want UnsupportedOperationException
  395. return delegate.constantTableStore(name, value);
  396. }
  397. @Override
  398. protected IRubyObject constantTableFastStore(String internedName, IRubyObject value) {
  399. // FIXME: legal here? may want UnsupportedOperationException
  400. return delegate.constantTableFastStore(internedName, value);
  401. }
  402. @Override
  403. protected IRubyObject constantTableRemove(String name) {
  404. // this _is_ legal (when removing an undef)
  405. return delegate.constantTableRemove(name);
  406. }
  407. @Override
  408. protected ConstantTableEntry[] constantTableGetTable() {
  409. return delegate.constantTableGetTable();
  410. }
  411. @Override
  412. protected int constantTableGetSize() {
  413. return delegate.constantTableGetSize();
  414. }
  415. @Override
  416. protected void constantTableSync(List<Variable<IRubyObject>> vars) {
  417. // FIXME: legal here? may want UnsupportedOperationException
  418. delegate.constantTableSync(vars);
  419. }
  420. /**
  421. * Method to help ease transition to new variables implementation.
  422. * Will likely be deprecated in the near future.
  423. */
  424. @SuppressWarnings("unchecked")
  425. @Override
  426. @Deprecated // born deprecated
  427. protected Map constantTableGetMap() {
  428. return delegate.constantTableGetMap();
  429. }
  430. /**
  431. * Method to help ease transition to new variables implementation.
  432. * Will likely be deprecated in the near future.
  433. */
  434. @SuppressWarnings("unchecked")
  435. @Override
  436. @Deprecated // born deprecated
  437. protected Map constantTableGetMap(Map map) {
  438. return delegate.constantTableGetMap(map);
  439. }
  440. }
  441. /***** BEGIN LICENSE BLOCK *****
  442. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  443. *
  444. * The contents of this file are subject to the Common Public
  445. * License Version 1.0 (the "License"); you may not use this file
  446. * except in compliance with the License. You may obtain a copy of
  447. * the License at http://www.eclipse.org/legal/cpl-v10.html
  448. *
  449. * Software distributed under the License is distributed on an "AS
  450. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  451. * implied. See the License for the specific language governing
  452. * rights and limitations under the License.
  453. *
  454. * Copyright (C) 2007 Charles Nutter <charles.o.nutter@sun.com>
  455. * Copyright (C) 2008 MenTaLguY <mental@rydia.net>
  456. *
  457. * Alternatively, the contents of this file may be used under the terms of
  458. * either of the GNU General Public License Version 2 or later (the "GPL"),
  459. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  460. * in which case the provisions of the GPL or the LGPL are applicable instead
  461. * of those above. If you wish to allow use of your version of this file only
  462. * under the terms of either the GPL or the LGPL, and not to allow others to
  463. * use your version of this file under the terms of the CPL, indicate your
  464. * decision by deleting the provisions above and replace them with the notice
  465. * and other provisions required by the GPL or the LGPL. If you do not delete
  466. * the provisions above, a recipient may use your version of this file under
  467. * the terms of any one of the CPL, the GPL or the LGPL.
  468. ***** END LICENSE BLOCK *****/
  469. package org.jruby;
  470. import java.applet.Applet;
  471. import java.awt.BorderLayout;
  472. import java.awt.Color;
  473. import java.awt.Container;
  474. import java.awt.EventQueue;
  475. import java.awt.Font;
  476. import java.awt.Insets;
  477. import java.awt.Graphics;
  478. import java.awt.GraphicsConfiguration;
  479. import java.awt.GraphicsEnvironment;
  480. import java.awt.image.VolatileImage;
  481. import java.io.IOException;
  482. import java.io.InputStream;
  483. import java.io.PrintStream;
  484. import java.net.URL;
  485. import java.util.Arrays;
  486. import java.lang.reflect.InvocationTargetException;
  487. import org.jruby.anno.JRubyMethod;
  488. import org.jruby.demo.TextAreaReadline;
  489. import org.jruby.javasupport.JavaUtil;
  490. import org.jruby.runtime.Block;
  491. import org.jruby.runtime.ThreadContext;
  492. import org.jruby.runtime.builtin.IRubyObject;
  493. import javax.swing.JScrollPane;
  494. import javax.swing.JTextPane;
  495. /**
  496. * @author <a href="mailto:mental@rydia.net">MenTaLguY</a>
  497. *
  498. * The JRubyApplet class provides a simple way to write Java applets using
  499. * JRuby without needing to create a custom Java applet class. At applet
  500. * initialization time, JRubyApplet starts up a JRuby runtime, then evaluates
  501. * the scriptlet given as the "eval" applet parameter.
  502. *
  503. * The Java applet instance is available to the Ruby script as
  504. * JRUBY_APPLET; the script can define callbacks for applet start, stop,
  505. * and destroy by passing blocks to JRUBY_APPLET.on_start,
  506. * JRUBY_APPLET.on_stop, and JRUBY_APPLET.on_destroy, respectively.
  507. *
  508. * Ruby code can install a custom paint callback using JRUBY_APPLET.on_paint
  509. * (the Graphics2D object is passed as an argument to the callback). By
  510. * default, JRubyApplet painting is double-buffered, but you can select
  511. * single-buffered painting via JRUBY_APPLET.double_buffered = false.
  512. *
  513. * The applet's background color can be set via JRUBY_APPLET.background_color=.
  514. * You may want to set it to nil if you're not using double-buffering, so that
  515. * no background color will be drawn (your own paint code is then responsible
  516. * for filling the area).
  517. *
  518. * Beyond these things, you should be able to use JRuby's Java integration
  519. * to do whatever you would do in Java with the applet instance.
  520. *
  521. */
  522. public class JRubyApplet extends Applet {
  523. private Ruby runtime;
  524. private boolean doubleBuffered = true;
  525. private Color backgroundColor = Color.WHITE;
  526. private RubyProc startProc;
  527. private RubyProc stopProc;
  528. private RubyProc destroyProc;
  529. private RubyProc paintProc;
  530. private Graphics priorGraphics;
  531. private IRubyObject wrappedGraphics;
  532. private VolatileImage backBuffer;
  533. private Graphics backBufferGraphics;
  534. private Facade facade;
  535. private interface Facade {
  536. public InputStream getInputStream();
  537. public PrintStream getOutputStream();
  538. public PrintStream getErrorStream();
  539. public void attach(Ruby runtime, Applet applet);
  540. public void destroy();
  541. }
  542. private static RubyProc blockToProc(Ruby runtime, Block block) {
  543. if (block.isGiven()) {
  544. RubyProc proc = block.getProcObject();
  545. if (proc == null) {
  546. proc = RubyProc.newProc(runtime, block, block.type);
  547. }
  548. return proc;
  549. } else {
  550. return null;
  551. }
  552. }
  553. private boolean getBooleanParameter(String name, boolean defaultValue) {
  554. String value = getParameter(name);
  555. if ( value != null ) {
  556. return value.equals("true");
  557. } else {
  558. return defaultValue;
  559. }
  560. }
  561. private InputStream getCodeResourceAsStream(String name) {
  562. if (name == null) {
  563. return null;
  564. }
  565. try {
  566. final URL directURL = new URL(getCodeBase(), name);
  567. return directURL.openStream();
  568. } catch (IOException e) {
  569. }
  570. return JRubyApplet.class.getClassLoader().getResourceAsStream(name);
  571. }
  572. private static void safeInvokeAndWait(Runnable runnable) throws InvocationTargetException, InterruptedException {
  573. if (EventQueue.isDispatchThread()) {
  574. try {
  575. runnable.run();
  576. } catch (Exception e) {
  577. throw new InvocationTargetException(e);
  578. }
  579. } else {
  580. EventQueue.invokeAndWait(runnable);
  581. }
  582. }
  583. public static class RubyMethods {
  584. @JRubyMethod
  585. public static IRubyObject on_start(IRubyObject recv, Block block) {
  586. JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
  587. synchronized (applet) {
  588. applet.startProc = blockToProc(applet.runtime, block);
  589. }
  590. return recv;
  591. }
  592. @JRubyMethod
  593. public static IRubyObject on_stop(IRubyObject recv, Block block) {
  594. JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
  595. synchronized (applet) {
  596. applet.stopProc = blockToProc(applet.runtime, block);
  597. }
  598. return recv;
  599. }
  600. @JRubyMethod
  601. public static IRubyObject on_destroy(IRubyObject recv, Block block) {
  602. JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
  603. synchronized (applet) {
  604. applet.destroyProc = blockToProc(applet.runtime, block);
  605. }
  606. return recv;
  607. }
  608. @JRubyMethod
  609. public static IRubyObject on_paint(IRubyObject recv, Block block) {
  610. JRubyApplet applet = (JRubyApplet)recv.dataGetStruct();
  611. synchronized (applet) {
  612. applet.paintProc = blockToProc(applet.runtime, block);
  613. applet.repaint();
  614. }
  615. return recv;
  616. }
  617. }
  618. @Override
  619. public void init() {
  620. super.init();
  621. if (getBooleanParameter("jruby.console", false)) {
  622. facade = new ConsoleFacade(getParameter("jruby.banner"));
  623. } else {
  624. facade = new TrivialFacade();
  625. }
  626. synchronized (this) {
  627. if (runtime != null) {
  628. return;
  629. }
  630. final RubyInstanceConfig config = new RubyInstanceConfig() {{
  631. setInput(facade.getInputStream());
  632. setOutput(facade.getOutputStream());
  633. setError(facade.getErrorStream());
  634. setObjectSpaceEnabled(getBooleanParameter("jruby.objectspace", false));
  635. }};
  636. Ruby.setSecurityRestricted(true);
  637. runtime = Ruby.newInstance(config);
  638. }
  639. final String scriptName = getParameter("jruby.script");
  640. final InputStream scriptStream = getCodeResourceAsStream(scriptName);
  641. final String evalString = getParameter("jruby.eval");
  642. try {
  643. final JRubyApplet applet = this;
  644. safeInvokeAndWait(new Runnable() {
  645. public void run() {
  646. applet.setLayout(new BorderLayout());
  647. applet.facade.attach(applet.runtime, applet);
  648. if (scriptStream != null) {
  649. applet.runtime.runFromMain(scriptStream, scriptName);
  650. }
  651. if (evalString != null) {
  652. applet.runtime.evalScriptlet(evalString);
  653. }
  654. }
  655. });
  656. } catch (InterruptedException e) {
  657. } catch (InvocationTargetException e) {
  658. throw new RuntimeException("Error running script", e.getCause());
  659. }
  660. }
  661. private void invokeCallback(final RubyProc proc, final IRubyObject[] args) {
  662. if (proc == null) {
  663. return;
  664. }
  665. final Ruby runtime = this.runtime;
  666. try {
  667. safeInvokeAndWait(new Runnable() {
  668. public void run() {
  669. ThreadContext context = runtime.getCurrentContext();
  670. proc.call(context, args);
  671. }
  672. });
  673. } catch (InterruptedException e) {
  674. } catch (InvocationTargetException e) {
  675. throw new RuntimeException("Ruby callback failed", e.getCause());
  676. }
  677. }
  678. public synchronized void setBackgroundColor(Color color) {
  679. backgroundColor = color;
  680. repaint();
  681. }
  682. public synchronized Color getBackgroundColor() {
  683. return backgroundColor;
  684. }
  685. public synchronized boolean isDoubleBuffered() {
  686. return doubleBuffered;
  687. }
  688. public synchronized void setDoubleBuffered(boolean shouldBuffer) {
  689. doubleBuffered = shouldBuffer;
  690. repaint();
  691. }
  692. @Override
  693. public synchronized void start() {
  694. super.start();
  695. invokeCallback(startProc, new IRubyObject[] {});
  696. }
  697. @Override
  698. public synchronized void stop() {
  699. invokeCallback(stopProc, new IRubyObject[] {});
  700. super.stop();
  701. }
  702. @Override
  703. public synchronized void destroy() {
  704. try {
  705. invokeCallback(destroyProc, new IRubyObject[] {});
  706. } finally {
  707. facade.destroy();
  708. final Ruby runtime = this.runtime;
  709. this.runtime = null;
  710. startProc = null;
  711. stopProc = null;
  712. destroyProc = null;
  713. paintProc = null;
  714. priorGraphics = null;
  715. wrappedGraphics = null;
  716. runtime.tearDown();
  717. super.destroy();
  718. }
  719. }
  720. @Override
  721. public void update(Graphics g) {
  722. paint(g);
  723. }
  724. @Override
  725. public synchronized void paint(Graphics g) {
  726. if (doubleBuffered) {
  727. paintBuffered(g);
  728. } else {
  729. paintUnbuffered(g);
  730. }
  731. }
  732. private synchronized void paintBuffered(Graphics g) {
  733. do {
  734. GraphicsConfiguration config = getGraphicsConfiguration();
  735. int width = getWidth();
  736. int height = getHeight();
  737. if (backBuffer == null || width != backBuffer.getWidth() || height != backBuffer.getHeight() || backBuffer.validate(config) == VolatileImage.IMAGE_INCOMPATIBLE) {
  738. if (backBuffer != null) {
  739. backBufferGraphics.dispose();
  740. backBufferGraphics = null;
  741. backBuffer.flush();
  742. backBuffer = null;
  743. }
  744. backBuffer = config.createCompatibleVolatileImage(width, height);
  745. backBufferGraphics = backBuffer.createGraphics();
  746. }
  747. backBufferGraphics.setClip(g.getClip());
  748. paintUnbuffered(backBufferGraphics);
  749. g.drawImage(backBuffer, 0, 0, this);
  750. } while (backBuffer.contentsLost());
  751. }
  752. private synchronized void paintUnbuffered(Graphics g) {
  753. if (backgroundColor != null) {
  754. g.setColor(backgroundColor);
  755. g.fillRect(0, 0, getWidth(), getHeight());
  756. }
  757. if (paintProc != null) {
  758. if (priorGraphics != g) {
  759. wrappedGraphics = JavaUtil.convertJavaToUsableRubyObject(runtime, g);
  760. priorGraphics = g;
  761. }
  762. ThreadContext context = runtime.getCurrentContext();
  763. paintProc.call(context, new IRubyObject[] {wrappedGraphics});
  764. }
  765. super.paint(g);
  766. }
  767. private static class TrivialFacade implements Facade {
  768. public TrivialFacade() {}
  769. public InputStream getInputStream() { return System.in; }
  770. public PrintStream getOutputStream() { return System.out; }
  771. public PrintStream getErrorStream() { return System.err; }
  772. public void attach(Ruby runtime, Applet applet) {
  773. final IRubyObject wrappedApplet = JavaUtil.convertJavaToUsableRubyObject(runtime, applet);
  774. wrappedApplet.dataWrapStruct(applet);
  775. runtime.defineGlobalConstant("JRUBY_APPLET", wrappedApplet);
  776. wrappedApplet.getMetaClass().defineAnnotatedMethods(RubyMethods.class);
  777. }
  778. public void destroy() {}
  779. }
  780. private static class ConsoleFacade implements Facade {
  781. private JTextPane textPane;
  782. private JScrollPane scrollPane;
  783. private TextAreaReadline adaptor;
  784. private InputStream inputStream;
  785. private PrintStream outputStream;
  786. private PrintStream errorStream;
  787. public ConsoleFacade(String bannerText) {
  788. textPane = new JTextPane();
  789. textPane.setMargin(new Insets(4, 4, 0, 4));
  790. textPane.setCaretColor(new Color(0xa4, 0x00, 0x00));
  791. textPane.setBackground(new Color(0xf2, 0xf2, 0xf2));
  792. textPane.setForeground(new Color(0xa4, 0x00, 0x00));
  793. Font font = findFont("Monospaced", Font.PLAIN, 14,
  794. new String[] {"Monaco", "Andale Mono"});
  795. textPane.setFont(font);
  796. scrollPane = new JScrollPane(textPane);
  797. scrollPane.setDoubleBuffered(true);
  798. if ( bannerText != null ) {
  799. bannerText = " " + bannerText + " \n\n";
  800. }
  801. adaptor = new TextAreaReadline(textPane, bannerText);
  802. inputStream = adaptor.getInputStream();
  803. outputStream = new PrintStream(adaptor.getOutputStream());
  804. errorStream = new PrintStream(adaptor.getOutputStream());
  805. }
  806. public InputStream getInputStream() { return inputStream; }
  807. public PrintStream getOutputStream() { return outputStream; }
  808. public PrintStream getErrorStream() { return errorStream; }
  809. public void attach(Ruby runtime, Applet applet) {
  810. adaptor.hookIntoRuntime(runtime);
  811. applet.add(scrollPane);
  812. applet.validate();
  813. }
  814. public void destroy() {
  815. Container parent = scrollPane.getParent();
  816. adaptor.shutdown();
  817. if (parent != null) {
  818. parent.remove(scrollPane);
  819. }
  820. }
  821. private Font findFont(String otherwise, int style, int size, String[] families) {
  822. String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
  823. Arrays.sort(fonts);
  824. for (int i = 0; i < families.length; i++) {
  825. if (Arrays.binarySearch(fonts, families[i]) >= 0) {
  826. return new Font(families[i], style, size);
  827. }
  828. }
  829. return new Font(otherwise, style, size);
  830. }
  831. }
  832. }
  833. /***** BEGIN LICENSE BLOCK *****
  834. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  835. *
  836. * The contents of this file are subject to the Common Public
  837. * License Version 1.0 (the "License"); you may not use this file
  838. * except in compliance with the License. You may obtain a copy of
  839. * the License at http://www.eclipse.org/legal/cpl-v10.html
  840. *
  841. * Software distributed under the License is distributed on an "AS
  842. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  843. * implied. See the License for the specific language governing
  844. * rights and limitations under the License.
  845. *
  846. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  847. *
  848. * Alternatively, the contents of this file may be used under the terms of
  849. * either of the GNU General Public License Version 2 or later (the "GPL"),
  850. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  851. * in which case the provisions of the GPL or the LGPL are applicable instead
  852. * of those above. If you wish to allow use of your version of this file only
  853. * under the terms of either the GPL or the LGPL, and not to allow others to
  854. * use your version of this file under the terms of the CPL, indicate your
  855. * decision by deleting the provisions above and replace them with the notice
  856. * and other provisions required by the GPL or the LGPL. If you do not delete
  857. * the provisions above, a recipient may use your version of this file under
  858. * the terms of any one of the CPL, the GPL or the LGPL.
  859. ***** END LICENSE BLOCK *****/
  860. package org.jruby;
  861. import java.io.BufferedWriter;
  862. import java.io.OutputStreamWriter;
  863. import java.net.InetAddress;
  864. import java.net.Socket;
  865. /**
  866. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  867. */
  868. public class JRubyClient extends JRubyService {
  869. public JRubyClient(String[] args) throws Exception {
  870. Configuration conf = new Configuration(args[0]);
  871. if(conf.isDebug()) {
  872. System.err.println("Starting client with port " + conf.getPort() + ", key " + conf.getKey() + " and command " + conf.getCommand());
  873. }
  874. Socket socket = new Socket(InetAddress.getLocalHost(), conf.getPort());
  875. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  876. if(conf.terminate()) {
  877. writer.write(CMD_TERM + " " + conf.getKey() + "\n");
  878. } else if(conf.noMore()) {
  879. writer.write(CMD_NO_MORE + " " + conf.getKey() + "\n");
  880. } else {
  881. writer.write(CMD_START + " " + conf.getKey() + " " + conf.getCommand() + "\n");
  882. }
  883. writer.flush();
  884. writer.close();
  885. socket.close();
  886. }
  887. public static void main(String[] args) throws Exception {
  888. new JRubyClient(args);
  889. }
  890. }// JRubyClient
  891. /*
  892. ***** BEGIN LICENSE BLOCK *****
  893. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  894. *
  895. * The contents of this file are subject to the Common Public
  896. * License Version 1.0 (the "License"); you may not use this file
  897. * except in compliance with the License. You may obtain a copy of
  898. * the License at http://www.eclipse.org/legal/cpl-v10.html
  899. *
  900. * Software distributed under the License is distributed on an "AS
  901. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  902. * implied. See the License for the specific language governing
  903. * rights and limitations under the License.
  904. *
  905. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  906. *
  907. * Alternatively, the contents of this file may be used under the terms of
  908. * either of the GNU General Public License Version 2 or later (the "GPL"),
  909. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  910. * in which case the provisions of the GPL or the LGPL are applicable instead
  911. * of those above. If you wish to allow use of your version of this file only
  912. * under the terms of either the GPL or the LGPL, and not to allow others to
  913. * use your version of this file under the terms of the CPL, indicate your
  914. * decision by deleting the provisions above and replace them with the notice
  915. * and other provisions required by the GPL or the LGPL. If you do not delete
  916. * the provisions above, a recipient may use your version of this file under
  917. * the terms of any one of the CPL, the GPL or the LGPL.
  918. ***** END LICENSE BLOCK *****/
  919. package org.jruby;
  920. import java.io.BufferedReader;
  921. import java.io.InputStreamReader;
  922. import java.net.InetAddress;
  923. import java.net.InetSocketAddress;
  924. import java.net.ServerSocket;
  925. import java.net.Socket;
  926. import java.util.List;
  927. import java.util.ArrayList;
  928. /**
  929. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  930. */
  931. public class JRubyServer extends JRubyService {
  932. private Configuration conf;
  933. private boolean stillStarting = true;
  934. private JRubyServer(String[] args) throws Exception {
  935. conf = new Configuration(args[0]);
  936. if(conf.isDebug()) {
  937. System.err.println("Starting server with port " + conf.getPort() + " and key " + conf.getKey());
  938. }
  939. ServerSocket server = new ServerSocket();
  940. server.bind(new InetSocketAddress(InetAddress.getLocalHost(),conf.getPort()));
  941. while(true) {
  942. Thread t1 = new Thread(new Handler(server.accept()));
  943. t1.setDaemon(true);
  944. t1.start();
  945. }
  946. }
  947. private class Handler implements Runnable {
  948. private Socket socket;
  949. public Handler(Socket socket) {
  950. this.socket = socket;
  951. }
  952. public void run() {
  953. try {
  954. BufferedReader rr = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
  955. String command = rr.readLine();
  956. rr.close();
  957. this.socket.close();
  958. this.socket = null;
  959. if(conf.isDebug()) {
  960. System.err.println("Got command: " + command);
  961. }
  962. String[] cmds = command.split(" ", 3);
  963. if(cmds[1].equals(conf.getKey())) {
  964. if(cmds[0].equals(CMD_TERM)) {
  965. if(conf.isDebug()) {
  966. System.err.println("Terminating hard");
  967. }
  968. System.exit(0);
  969. } else if(cmds[0].equals(CMD_NO_MORE)) {
  970. if(conf.isDebug()) {
  971. System.err.println("Accepting no more START");
  972. }
  973. stillStarting = false;
  974. } else if(cmds[0].equals(CMD_START)) {
  975. if(stillStarting) {
  976. if(conf.isDebug()) {
  977. System.err.println("Doing START on command " + cmds[2]);
  978. }
  979. new Main().run(intoCommandArguments(cmds[2].trim()));
  980. } else {
  981. if(conf.isDebug()) {
  982. System.err.println("Not doing START anymore, invalid command");
  983. }
  984. }
  985. } else {
  986. if(conf.isDebug()) {
  987. System.err.println("Unrecognized command");
  988. }
  989. }
  990. } else {
  991. if(conf.isDebug()) {
  992. System.err.println("Invalid key");
  993. }
  994. }
  995. } catch(Exception e) {}
  996. }
  997. }
  998. protected static String[] intoCommandArguments(String str) {
  999. List<String> args = new ArrayList<String>();
  1000. boolean inSingle = false;
  1001. int contentStart = -1;
  1002. for(int i=0,j=str.length();i<j;i++) {
  1003. if(str.charAt(i) == ' ' && !inSingle && contentStart != -1) {
  1004. args.add(str.substring(contentStart,i));
  1005. contentStart = -1;
  1006. continue;
  1007. }
  1008. if(str.charAt(i) == ' ') {
  1009. continue;
  1010. }
  1011. if(str.charAt(i) == '\'' && !inSingle) {
  1012. inSingle = true;
  1013. contentStart = i+1;
  1014. continue;
  1015. }
  1016. if(str.charAt(i) == '\'') {
  1017. inSingle = false;
  1018. args.add(str.substring(contentStart,i));
  1019. contentStart = -1;
  1020. continue;
  1021. }
  1022. if(contentStart == -1) {
  1023. contentStart = i;
  1024. }
  1025. }
  1026. if(contentStart != -1) {
  1027. args.add(str.substring(contentStart));
  1028. }
  1029. return (String[])args.toArray(new String[0]);
  1030. }
  1031. public static void main(String[] args) throws Exception {
  1032. new JRubyServer(args);
  1033. }
  1034. }// JRubyServer
  1035. /***** BEGIN LICENSE BLOCK *****
  1036. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1037. *
  1038. * The contents of this file are subject to the Common Public
  1039. * License Version 1.0 (the "License"); you may not use this file
  1040. * except in compliance with the License. You may obtain a copy of
  1041. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1042. *
  1043. * Software distributed under the License is distributed on an "AS
  1044. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1045. * implied. See the License for the specific language governing
  1046. * rights and limitations under the License.
  1047. *
  1048. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  1049. *
  1050. * Alternatively, the contents of this file may be used under the terms of
  1051. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1052. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1053. * in which case the provisions of the GPL or the LGPL are applicable instead
  1054. * of those above. If you wish to allow use of your version of this file only
  1055. * under the terms of either the GPL or the LGPL, and not to allow others to
  1056. * use your version of this file under the terms of the CPL, indicate your
  1057. * decision by deleting the provisions above and replace them with the notice
  1058. * and other provisions required by the GPL or the LGPL. If you do not delete
  1059. * the provisions above, a recipient may use your version of this file under
  1060. * the terms of any one of the CPL, the GPL or the LGPL.
  1061. ***** END LICENSE BLOCK *****/
  1062. package org.jruby;
  1063. /**
  1064. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  1065. */
  1066. public abstract class JRubyService {
  1067. protected static class Configuration {
  1068. private final static int DEFAULT_PORT = 19222;
  1069. private String key;
  1070. private int port = DEFAULT_PORT;
  1071. private boolean terminate;
  1072. private boolean noMore;
  1073. private boolean debug;
  1074. private String command;
  1075. public Configuration(String args) {
  1076. int i=0;
  1077. int stop;
  1078. loop: for(int j=args.length();i<j;i++) {
  1079. if(args.charAt(i) == '-' && i+1 < j) {
  1080. switch(args.charAt(++i)) {
  1081. case 'k':
  1082. stop = args.indexOf(" ", (++i) + 1);
  1083. if(stop == -1) {
  1084. stop = args.length();
  1085. }
  1086. key = args.substring(i, stop).trim();
  1087. i = stop;
  1088. break;
  1089. case 'p':
  1090. stop = args.indexOf(" ", (++i) + 1);
  1091. if(stop == -1) {
  1092. stop = args.length();
  1093. }
  1094. port = Integer.parseInt(args.substring(i, stop).trim());
  1095. i = stop;
  1096. break;
  1097. case 't':
  1098. terminate = true;
  1099. i++;
  1100. break;
  1101. case 'n':
  1102. noMore = true;
  1103. i++;
  1104. break;
  1105. case 'd':
  1106. debug = true;
  1107. i++;
  1108. break;
  1109. case '-': // handle everything after -- as arguments to the jruby process
  1110. i++;
  1111. break loop;
  1112. default:
  1113. i--;
  1114. break loop;
  1115. }
  1116. } else if(args.charAt(i) != ' ') {
  1117. break loop;
  1118. }
  1119. }
  1120. if(i<args.length()) {
  1121. command = args.substring(i).trim();
  1122. }
  1123. }
  1124. public String getKey() {
  1125. return key;
  1126. }
  1127. public int getPort() {
  1128. return port;
  1129. }
  1130. public boolean terminate() {
  1131. return terminate;
  1132. }
  1133. public boolean noMore() {
  1134. return noMore;
  1135. }
  1136. public boolean isDebug() {
  1137. return debug;
  1138. }
  1139. public String getCommand() {
  1140. return command;
  1141. }
  1142. }
  1143. public static final String CMD_START = "START";
  1144. public static final String CMD_NO_MORE = "NO_MORE";
  1145. public static final String CMD_TERM = "TERM";
  1146. }// JRubyService
  1147. /*
  1148. ***** BEGIN LICENSE BLOCK *****
  1149. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1150. *
  1151. * The contents of this file are subject to the Common Public
  1152. * License Version 1.0 (the "License"); you may not use this file
  1153. * except in compliance with the License. You may obtain a copy of
  1154. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1155. *
  1156. * Software distributed under the License is distributed on an "AS
  1157. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1158. * implied. See the License for the specific language governing
  1159. * rights and limitations under the License.
  1160. *
  1161. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  1162. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  1163. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  1164. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  1165. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  1166. * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
  1167. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  1168. * Copyright (C) 2005 Kiel Hodges <jruby-devel@selfsosoft.com>
  1169. * Copyright (C) 2005 Jason Voegele <jason@jvoegele.com>
  1170. * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
  1171. *
  1172. * Alternatively, the contents of this file may be used under the terms of
  1173. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1174. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1175. * in which case the provisions of the GPL or the LGPL are applicable instead
  1176. * of those above. If you wish to allow use of your version of this file only
  1177. * under the terms of either the GPL or the LGPL, and not to allow others to
  1178. * use your version of this file under the terms of the CPL, indicate your
  1179. * decision by deleting the provisions above and replace them with the notice
  1180. * and other provisions required by the GPL or the LGPL. If you do not delete
  1181. * the provisions above, a recipient may use your version of this file under
  1182. * the terms of any one of the CPL, the GPL or the LGPL.
  1183. ***** END LICENSE BLOCK *****/
  1184. package org.jruby;
  1185. import java.io.InputStream;
  1186. import java.io.PrintStream;
  1187. import org.jruby.exceptions.MainExitException;
  1188. import org.jruby.exceptions.RaiseException;
  1189. import org.jruby.exceptions.ThreadKill;
  1190. import org.jruby.runtime.ThreadContext;
  1191. import org.jruby.runtime.builtin.IRubyObject;
  1192. import org.jruby.util.SafePropertyAccessor;
  1193. import org.jruby.util.SimpleSampler;
  1194. /**
  1195. * Class used to launch the interpreter.
  1196. * This is the main class as defined in the jruby.mf manifest.
  1197. * It is very basic and does not support yet the same array of switches
  1198. * as the C interpreter.
  1199. * Usage: java -jar jruby.jar [switches] [rubyfile.rb] [arguments]
  1200. * -e 'command' one line of script. Several -e's allowed. Omit [programfile]
  1201. * @author jpetersen
  1202. */
  1203. public class Main {
  1204. private boolean hasPrintedUsage = false;
  1205. private final RubyInstanceConfig config;
  1206. public Main(RubyInstanceConfig config) {
  1207. this.config = config;
  1208. }
  1209. public Main(final InputStream in, final PrintStream out, final PrintStream err) {
  1210. this(new RubyInstanceConfig(){{
  1211. setInput(in);
  1212. setOutput(out);
  1213. setError(err);
  1214. }});
  1215. }
  1216. public Main() {
  1217. this(new RubyInstanceConfig());
  1218. }
  1219. public static void main(String[] args) {
  1220. Main main = new Main();
  1221. try {
  1222. int status = main.run(args);
  1223. if (status != 0) {
  1224. System.exit(status);
  1225. }
  1226. } catch (RaiseException re) {
  1227. throw re;
  1228. } catch (Throwable t) {
  1229. // print out as a nice Ruby backtrace
  1230. System.err.println(ThreadContext.createRawBacktraceStringFromThrowable(t));
  1231. System.exit(1);
  1232. }
  1233. }
  1234. public int run(String[] args) {
  1235. try {
  1236. config.processArguments(args);
  1237. return run();
  1238. } catch (MainExitException mee) {
  1239. if (!mee.isAborted()) {
  1240. config.getOutput().println(mee.getMessage());
  1241. if (mee.isUsageError()) {
  1242. printUsage();
  1243. }
  1244. }
  1245. return mee.getStatus();
  1246. } catch (OutOfMemoryError oome) {
  1247. // produce a nicer error since Rubyists aren't used to seeing this
  1248. System.gc();
  1249. String memoryMax = SafePropertyAccessor.getProperty("jruby.memory.max");
  1250. String message = "";
  1251. if (memoryMax != null) {
  1252. message = " of " + memoryMax;
  1253. }
  1254. System.err.println("Error: Your application used more memory than the safety cap" + message + ".");
  1255. System.err.println("Specify -J-Xmx####m to increase it (#### = cap size in MB).");
  1256. if (config.getVerbose()) {
  1257. System.err.println("Exception trace follows:");
  1258. oome.printStackTrace();
  1259. } else {
  1260. System.err.println("Specify -w for full OutOfMemoryError stack trace");
  1261. }
  1262. return 1;
  1263. } catch (StackOverflowError soe) {
  1264. // produce a nicer error since Rubyists aren't used to seeing this
  1265. System.gc();
  1266. String stackMax = SafePropertyAccessor.getProperty("jruby.stack.max");
  1267. String message = "";
  1268. if (stackMax != null) {
  1269. message = " of " + stackMax;
  1270. }
  1271. System.err.println("Error: Your application used more stack memory than the safety cap" + message + ".");
  1272. System.err.println("Specify -J-Xss####k to increase it (#### = cap size in KB).");
  1273. if (config.getVerbose()) {
  1274. System.err.println("Exception trace follows:");
  1275. soe.printStackTrace();
  1276. } else {
  1277. System.err.println("Specify -w for full StackOverflowError stack trace");
  1278. }
  1279. return 1;
  1280. } catch (UnsupportedClassVersionError ucve) {
  1281. System.err.println("Error: Some library (perhaps JRuby) was built with a later JVM version.");
  1282. System.err.println("Please use libraries built with the version you intend to use or an earlier one.");
  1283. if (config.getVerbose()) {
  1284. System.err.println("Exception trace follows:");
  1285. ucve.printStackTrace();
  1286. } else {
  1287. System.err.println("Specify -w for full UnsupportedClassVersionError stack trace");
  1288. }
  1289. return 1;
  1290. } catch (ThreadKill kill) {
  1291. return 0;
  1292. }
  1293. }
  1294. public int run() {
  1295. if (config.isShowVersion()) {
  1296. showVersion();
  1297. }
  1298. if (config.isShowCopyright()) {
  1299. showCopyright();
  1300. }
  1301. if (!config.shouldRunInterpreter() ) {
  1302. if (config.shouldPrintUsage()) {
  1303. printUsage();
  1304. }
  1305. if (config.shouldPrintProperties()) {
  1306. printProperties();
  1307. }
  1308. return 0;
  1309. }
  1310. InputStream in = config.getScriptSource();
  1311. String filename = config.displayedFileName();
  1312. Ruby runtime = Ruby.newInstance(config);
  1313. // set thread context JRuby classloader here, for the main thread
  1314. try {
  1315. Thread.currentThread().setContextClassLoader(runtime.getJRubyClassLoader());
  1316. } catch (SecurityException se) {
  1317. // can't set TC classloader
  1318. if (runtime.getInstanceConfig().isVerbose()) {
  1319. System.err.println("WARNING: Security restrictions disallowed setting context classloader for main thread.");
  1320. }
  1321. }
  1322. if (in == null) {
  1323. // no script to run, return success below
  1324. } else if (config.isShouldCheckSyntax()) {
  1325. runtime.parseFromMain(in, filename);
  1326. config.getOutput().println("Syntax OK");
  1327. } else {
  1328. long now = -1;
  1329. try {
  1330. if (config.isBenchmarking()) {
  1331. now = System.currentTimeMillis();
  1332. }
  1333. if (config.isSamplingEnabled()) {
  1334. SimpleSampler.startSampleThread();
  1335. }
  1336. try {
  1337. runtime.runFromMain(in, filename);
  1338. } finally {
  1339. runtime.tearDown();
  1340. if (config.isBenchmarking()) {
  1341. config.getOutput().println("Runtime: " + (System.currentTimeMillis() - now) + " ms");
  1342. }
  1343. if (config.isSamplingEnabled()) {
  1344. org.jruby.util.SimpleSampler.report();
  1345. }
  1346. }
  1347. } catch (RaiseException rj) {
  1348. RubyException raisedException = rj.getException();
  1349. if (runtime.getSystemExit().isInstance(raisedException)) {
  1350. IRubyObject status = raisedException.callMethod(runtime.getCurrentContext(), "status");
  1351. if (status != null && !status.isNil()) {
  1352. return RubyNumeric.fix2int(status);
  1353. }
  1354. } else {
  1355. runtime.printError(raisedException);
  1356. return 1;
  1357. }
  1358. }
  1359. }
  1360. return 0;
  1361. }
  1362. private void showVersion() {
  1363. config.getOutput().print(config.getVersionString());
  1364. }
  1365. private void showCopyright() {
  1366. config.getOutput().print(config.getCopyrightString());
  1367. }
  1368. public void printUsage() {
  1369. if (!hasPrintedUsage) {
  1370. config.getOutput().print(config.getBasicUsageHelp());
  1371. hasPrintedUsage = true;
  1372. }
  1373. }
  1374. public void printProperties() {
  1375. config.getOutput().print(config.getPropertyHelp());
  1376. }
  1377. }
  1378. /***** BEGIN LICENSE BLOCK *****
  1379. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1380. *
  1381. * The contents of this file are subject to the Common Public
  1382. * License Version 1.0 (the "License"); you may not use this file
  1383. * except in compliance with the License. You may obtain a copy of
  1384. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1385. *
  1386. * Software distributed under the License is distributed on an "AS
  1387. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1388. * implied. See the License for the specific language governing
  1389. * rights and limitations under the License.
  1390. *
  1391. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  1392. * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
  1393. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  1394. *
  1395. * Alternatively, the contents of this file may be used under the terms of
  1396. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1397. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1398. * in which case the provisions of the GPL or the LGPL are applicable instead
  1399. * of those above. If you wish to allow use of your version of this file only
  1400. * under the terms of either the GPL or the LGPL, and not to allow others to
  1401. * use your version of this file under the terms of the CPL, indicate your
  1402. * decision by deleting the provisions above and replace them with the notice
  1403. * and other provisions required by the GPL or the LGPL. If you do not delete
  1404. * the provisions above, a recipient may use your version of this file under
  1405. * the terms of any one of the CPL, the GPL or the LGPL.
  1406. ***** END LICENSE BLOCK *****/
  1407. package org.jruby;
  1408. import java.lang.ref.SoftReference;
  1409. import org.jruby.runtime.builtin.IRubyObject;
  1410. public final class MetaClass extends RubyClass {
  1411. private SoftReference<IRubyObject> attached = new SoftReference<IRubyObject>(null);
  1412. /** NEWOBJ (in RubyObject#getSingletonClassClone())
  1413. *
  1414. */
  1415. public MetaClass(Ruby runtime) {
  1416. super(runtime, null, false);
  1417. }
  1418. /** rb_class_boot (for MetaClasses) (in makeMetaClass(RubyClass))
  1419. *
  1420. */
  1421. public MetaClass(Ruby runtime, RubyClass superClass) {
  1422. super(runtime, superClass, false);
  1423. index = superClass.index; // use same ClassIndex as metaclass, since we're technically still of that type
  1424. }
  1425. public boolean isSingleton() {
  1426. return true;
  1427. }
  1428. /**
  1429. * If an object uses an anonymous class 'class << obj', then this grabs the original
  1430. * metaclass and not the one that get injected as a result of 'class << obj'.
  1431. */
  1432. public RubyClass getRealClass() {
  1433. return superClass.getRealClass();
  1434. }
  1435. public final IRubyObject allocate(){
  1436. throw getRuntime().newTypeError("can't create instance of virtual class");
  1437. }
  1438. public IRubyObject getAttached() {
  1439. return attached.get();
  1440. }
  1441. public void setAttached(IRubyObject attached) {
  1442. this.attached = new SoftReference<IRubyObject>(attached);
  1443. }
  1444. }
  1445. /***** BEGIN LICENSE BLOCK *****
  1446. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1447. *
  1448. * The contents of this file are subject to the Common Public
  1449. * License Version 1.0 (the "License"); you may not use this file
  1450. * except in compliance with the License. You may obtain a copy of
  1451. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1452. *
  1453. * Software distributed under the License is distributed on an "AS
  1454. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1455. * implied. See the License for the specific language governing
  1456. * rights and limitations under the License.
  1457. *
  1458. * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
  1459. *
  1460. * Alternatively, the contents of this file may be used under the terms of
  1461. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1462. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1463. * in which case the provisions of the GPL or the LGPL are applicable instead
  1464. * of those above. If you wish to allow use of your version of this file only
  1465. * under the terms of either the GPL or the LGPL, and not to allow others to
  1466. * use your version of this file under the terms of the CPL, indicate your
  1467. * decision by deleting the provisions above and replace them with the notice
  1468. * and other provisions required by the GPL or the LGPL. If you do not delete
  1469. * the provisions above, a recipient may use your version of this file under
  1470. * the terms of any one of the CPL, the GPL or the LGPL.
  1471. ***** END LICENSE BLOCK *****/
  1472. package org.jruby;
  1473. import java.io.PrintStream;
  1474. import org.jruby.anno.JRubyClass;
  1475. import org.jruby.anno.JRubyMethod;
  1476. import org.jruby.javasupport.Java;
  1477. import org.jruby.javasupport.JavaObject;
  1478. import org.jruby.runtime.Block;
  1479. import org.jruby.runtime.ObjectAllocator;
  1480. import org.jruby.runtime.builtin.IRubyObject;
  1481. @JRubyClass(name = "NativeException", parent = "RuntimeError")
  1482. public class NativeException extends RubyException {
  1483. private final Throwable cause;
  1484. public static final String CLASS_NAME = "NativeException";
  1485. private final Ruby runtime;
  1486. public NativeException(Ruby runtime, RubyClass rubyClass, Throwable cause) {
  1487. super(runtime, rubyClass, cause.getClass().getName() + ": " + cause.getMessage());
  1488. this.runtime = runtime;
  1489. this.cause = cause;
  1490. }
  1491. public static RubyClass createClass(Ruby runtime, RubyClass baseClass) {
  1492. // FIXME: If NativeException is expected to be used from Ruby code, it should provide
  1493. // a real allocator to be used. Otherwise Class.new will fail, as will marshalling. JRUBY-415
  1494. RubyClass exceptionClass = runtime.defineClass(CLASS_NAME, baseClass, ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  1495. exceptionClass.defineAnnotatedMethods(NativeException.class);
  1496. return exceptionClass;
  1497. }
  1498. @JRubyMethod(frame = true)
  1499. public IRubyObject cause(Block unusedBlock) {
  1500. return Java.wrap(getRuntime(), JavaObject.wrap(getRuntime(), cause));
  1501. }
  1502. public IRubyObject backtrace() {
  1503. IRubyObject rubyTrace = super.backtrace();
  1504. if (rubyTrace.isNil()) {
  1505. return rubyTrace;
  1506. }
  1507. RubyArray array = (RubyArray) rubyTrace.dup();
  1508. StackTraceElement[] stackTrace = cause.getStackTrace();
  1509. for (int i = stackTrace.length - 1; i >= 0; i--) {
  1510. StackTraceElement element = stackTrace[i];
  1511. String className = element.getClassName();
  1512. String line = null;
  1513. if (element.getFileName() == null) {
  1514. line = className + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'";
  1515. } else {
  1516. int index = className.lastIndexOf(".");
  1517. String packageName = null;
  1518. if (index == -1) {
  1519. packageName = "";
  1520. } else {
  1521. packageName = className.substring(0, index) + "/";
  1522. }
  1523. line = packageName.replace(".", "/") + element.getFileName() + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'";
  1524. }
  1525. RubyString string = runtime.newString(line);
  1526. array.unshift(string);
  1527. }
  1528. return array;
  1529. }
  1530. public void printBacktrace(PrintStream errorStream) {
  1531. super.printBacktrace(errorStream);
  1532. errorStream.println("Complete Java stackTrace");
  1533. cause.printStackTrace(errorStream);
  1534. }
  1535. public Throwable getCause() {
  1536. return cause;
  1537. }
  1538. }
  1539. /***** BEGIN LICENSE BLOCK *****
  1540. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1541. *
  1542. * The contents of this file are subject to the Common Public
  1543. * License Version 1.0 (the "License"); you may not use this file
  1544. * except in compliance with the License. You may obtain a copy of
  1545. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1546. *
  1547. * Software distributed under the License is distributed on an "AS
  1548. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1549. * implied. See the License for the specific language governing
  1550. * rights and limitations under the License.
  1551. *
  1552. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  1553. *
  1554. * Alternatively, the contents of this file may be used under the terms of
  1555. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1556. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1557. * in which case the provisions of the GPL or the LGPL are applicable instead
  1558. * of those above. If you wish to allow use of your version of this file only
  1559. * under the terms of either the GPL or the LGPL, and not to allow others to
  1560. * use your version of this file under the terms of the CPL, indicate your
  1561. * decision by deleting the provisions above and replace them with the notice
  1562. * and other provisions required by the GPL or the LGPL. If you do not delete
  1563. * the provisions above, a recipient may use your version of this file under
  1564. * the terms of any one of the CPL, the GPL or the LGPL.
  1565. ***** END LICENSE BLOCK *****/
  1566. package org.jruby;
  1567. /**
  1568. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  1569. */
  1570. public interface Profile {
  1571. Profile ALL = new Profile() {
  1572. public boolean allowBuiltin(String name) { return true; }
  1573. public boolean allowClass(String name) { return true; }
  1574. public boolean allowModule(String name) { return true; }
  1575. public boolean allowLoad(String name) { return true; }
  1576. public boolean allowRequire(String name) { return true; }
  1577. };
  1578. Profile DEBUG_ALLOW = new Profile() {
  1579. public boolean allowBuiltin(String name) { System.err.println("allowBuiltin("+name+")"); return true; }
  1580. public boolean allowClass(String name) { System.err.println("allowClass("+name+")"); return true; }
  1581. public boolean allowModule(String name) { System.err.println("allowModule("+name+")"); return true; }
  1582. public boolean allowLoad(String name) { System.err.println("allowLoad("+name+")"); return true; }
  1583. public boolean allowRequire(String name) { System.err.println("allowRequire("+name+")"); return true; }
  1584. };
  1585. Profile NO_FILE_CLASS = new Profile() {
  1586. public boolean allowBuiltin(String name) { return true; }
  1587. public boolean allowClass(String name) { return !name.equals("File"); }
  1588. public boolean allowModule(String name) { return true; }
  1589. public boolean allowLoad(String name) { return true; }
  1590. public boolean allowRequire(String name) { return true; }
  1591. };
  1592. Profile ANY = ALL;
  1593. Profile DEFAULT = ALL;
  1594. boolean allowBuiltin(String name);
  1595. boolean allowClass(String name);
  1596. boolean allowModule(String name);
  1597. boolean allowLoad(String name);
  1598. boolean allowRequire(String name);
  1599. }// Profile
  1600. /*
  1601. **** BEGIN LICENSE BLOCK *****
  1602. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  1603. *
  1604. * The contents of this file are subject to the Common Public
  1605. * License Version 1.0 (the "License"); you may not use this file
  1606. * except in compliance with the License. You may obtain a copy of
  1607. * the License at http://www.eclipse.org/legal/cpl-v10.html
  1608. *
  1609. * Software distributed under the License is distributed on an "AS
  1610. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  1611. * implied. See the License for the specific language governing
  1612. * rights and limitations under the License.
  1613. *
  1614. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  1615. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  1616. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  1617. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  1618. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  1619. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  1620. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  1621. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  1622. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  1623. * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
  1624. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  1625. * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  1626. *
  1627. * Alternatively, the contents of this file may be used under the terms of
  1628. * either of the GNU General Public License Version 2 or later (the "GPL"),
  1629. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  1630. * in which case the provisions of the GPL or the LGPL are applicable instead
  1631. * of those above. If you wish to allow use of your version of this file only
  1632. * under the terms of either the GPL or the LGPL, and not to allow others to
  1633. * use your version of this file under the terms of the CPL, indicate your
  1634. * decision by deleting the provisions above and replace them with the notice
  1635. * and other provisions required by the GPL or the LGPL. If you do not delete
  1636. * the provisions above, a recipient may use your version of this file under
  1637. * the terms of any one of the CPL, the GPL or the LGPL.
  1638. ***** END LICENSE BLOCK *****/
  1639. package org.jruby;
  1640. import java.io.ByteArrayInputStream;
  1641. import java.io.File;
  1642. import java.io.FileDescriptor;
  1643. import java.io.IOException;
  1644. import java.io.InputStream;
  1645. import java.io.PrintStream;
  1646. import java.io.UnsupportedEncodingException;
  1647. import java.lang.ref.WeakReference;
  1648. import java.lang.reflect.Field;
  1649. import java.util.ArrayList;
  1650. import java.util.Collections;
  1651. import java.util.HashMap;
  1652. import java.util.Hashtable;
  1653. import java.util.IdentityHashMap;
  1654. import java.util.Iterator;
  1655. import java.util.List;
  1656. import java.util.Map;
  1657. import java.util.Random;
  1658. import java.util.Set;
  1659. import java.util.Stack;
  1660. import java.util.Vector;
  1661. import java.util.WeakHashMap;
  1662. import java.util.concurrent.ConcurrentHashMap;
  1663. import java.util.concurrent.ExecutorService;
  1664. import java.util.concurrent.Executors;
  1665. import java.util.concurrent.SynchronousQueue;
  1666. import java.util.concurrent.ThreadFactory;
  1667. import java.util.concurrent.ThreadPoolExecutor;
  1668. import java.util.concurrent.TimeUnit;
  1669. import java.util.concurrent.atomic.AtomicInteger;
  1670. import org.joda.time.DateTimeZone;
  1671. import org.jruby.ast.Node;
  1672. import org.jruby.ast.executable.RubiniusRunner;
  1673. import org.jruby.ast.executable.Script;
  1674. import org.jruby.ast.executable.YARVCompiledRunner;
  1675. import org.jruby.common.RubyWarnings;
  1676. import org.jruby.common.IRubyWarnings.ID;
  1677. import org.jruby.compiler.ASTCompiler;
  1678. import org.jruby.compiler.ASTInspector;
  1679. import org.jruby.compiler.JITCompiler;
  1680. import org.jruby.compiler.NotCompilableException;
  1681. import org.jruby.compiler.impl.StandardASMCompiler;
  1682. import org.jruby.compiler.yarv.StandardYARVCompiler;
  1683. import org.jruby.exceptions.JumpException;
  1684. import org.jruby.exceptions.RaiseException;
  1685. import org.jruby.ext.JRubyPOSIXHandler;
  1686. import org.jruby.ext.LateLoadingLibrary;
  1687. import org.jruby.ext.posix.POSIX;
  1688. import org.jruby.ext.posix.POSIXFactory;
  1689. import org.jruby.internal.runtime.GlobalVariables;
  1690. import org.jruby.internal.runtime.ThreadService;
  1691. import org.jruby.internal.runtime.ValueAccessor;
  1692. import org.jruby.javasupport.JavaSupport;
  1693. import org.jruby.management.BeanManager;
  1694. import org.jruby.management.ClassCache;
  1695. import org.jruby.management.Config;
  1696. import org.jruby.parser.Parser;
  1697. import org.jruby.parser.ParserConfiguration;
  1698. import org.jruby.runtime.Binding;
  1699. import org.jruby.runtime.Block;
  1700. import org.jruby.runtime.CacheMap;
  1701. import org.jruby.runtime.CallSite;
  1702. import org.jruby.runtime.CallbackFactory;
  1703. import org.jruby.runtime.DynamicScope;
  1704. import org.jruby.runtime.EventHook;
  1705. import org.jruby.runtime.GlobalVariable;
  1706. import org.jruby.runtime.IAccessor;
  1707. import org.jruby.runtime.ObjectAllocator;
  1708. import org.jruby.runtime.ObjectSpace;
  1709. import org.jruby.runtime.RubyEvent;
  1710. import org.jruby.runtime.ThreadContext;
  1711. import org.jruby.runtime.builtin.IRubyObject;
  1712. import org.jruby.runtime.load.Library;
  1713. import org.jruby.runtime.load.LoadService;
  1714. import org.jruby.util.BuiltinScript;
  1715. import org.jruby.util.ByteList;
  1716. import org.jruby.util.IOInputStream;
  1717. import org.jruby.util.IOOutputStream;
  1718. import org.jruby.util.JRubyClassLoader;
  1719. import org.jruby.util.JavaNameMangler;
  1720. import org.jruby.util.KCode;
  1721. import org.jruby.util.SafePropertyAccessor;
  1722. import org.jruby.util.collections.WeakHashSet;
  1723. import org.jruby.util.io.ChannelDescriptor;
  1724. /**
  1725. * The Ruby object represents the top-level of a JRuby "instance" in a given VM.
  1726. * JRuby supports spawning multiple instances in the same JVM. Generally, objects
  1727. * created under these instances are tied to a given runtime, for such details
  1728. * as identity and type, because multiple Ruby instances means there are
  1729. * multiple instances of each class. This means that in multi-runtime mode
  1730. * (or really, multi-VM mode, where each JRuby instance is a ruby "VM"), objects
  1731. * generally can't be transported across runtimes without marshaling.
  1732. *
  1733. * This class roots everything that makes the JRuby runtime function, and
  1734. * provides a number of utility methods for constructing global types and
  1735. * accessing global runtime structures.
  1736. */
  1737. public final class Ruby {
  1738. /**
  1739. * Returns a new instance of the JRuby runtime configured with defaults.
  1740. *
  1741. * @return the JRuby runtime
  1742. * @see org.jruby.RubyInstanceConfig
  1743. */
  1744. public static Ruby newInstance() {
  1745. return newInstance(new RubyInstanceConfig());
  1746. }
  1747. /**
  1748. * Returns a new instance of the JRuby runtime configured as specified.
  1749. *
  1750. * @param config The instance configuration
  1751. * @return The JRuby runtime
  1752. * @see org.jruby.RubyInstanceConfig
  1753. */
  1754. public static Ruby newInstance(RubyInstanceConfig config) {
  1755. Ruby ruby = new Ruby(config);
  1756. ruby.init();
  1757. return ruby;
  1758. }
  1759. /**
  1760. * Returns a new instance of the JRuby runtime configured with the given
  1761. * input, output and error streams and otherwise default configuration
  1762. * (except where specified system properties alter defaults).
  1763. *
  1764. * @param in the custom input stream
  1765. * @param out the custom output stream
  1766. * @param err the custom error stream
  1767. * @return the JRuby runtime
  1768. * @see org.jruby.RubyInstanceConfig
  1769. */
  1770. public static Ruby newInstance(InputStream in, PrintStream out, PrintStream err) {
  1771. RubyInstanceConfig config = new RubyInstanceConfig();
  1772. config.setInput(in);
  1773. config.setOutput(out);
  1774. config.setError(err);
  1775. return newInstance(config);
  1776. }
  1777. /**
  1778. * Create and initialize a new JRuby runtime. The properties of the
  1779. * specified RubyInstanceConfig will be used to determine various JRuby
  1780. * runtime characteristics.
  1781. *
  1782. * @param config The configuration to use for the new instance
  1783. * @see org.jruby.RubyInstanceConfig
  1784. */
  1785. private Ruby(RubyInstanceConfig config) {
  1786. this.config = config;
  1787. this.threadService = new ThreadService(this);
  1788. if(config.isSamplingEnabled()) {
  1789. org.jruby.util.SimpleSampler.registerThreadContext(threadService.getCurrentContext());
  1790. }
  1791. this.in = config.getInput();
  1792. this.out = config.getOutput();
  1793. this.err = config.getError();
  1794. this.objectSpaceEnabled = config.isObjectSpaceEnabled();
  1795. this.profile = config.getProfile();
  1796. this.currentDirectory = config.getCurrentDirectory();
  1797. this.kcode = config.getKCode();
  1798. this.beanManager = new BeanManager(this, config.isManagementEnabled());
  1799. this.jitCompiler = new JITCompiler(this);
  1800. this.beanManager.register(new Config(this));
  1801. this.beanManager.register(new ClassCache(this));
  1802. this.cacheMap = new CacheMap(this);
  1803. }
  1804. /**
  1805. * Evaluates a script under the current scope (perhaps the top-level
  1806. * scope) and returns the result (generally the last value calculated).
  1807. * This version goes straight into the interpreter, bypassing compilation
  1808. * and runtime preparation typical to normal script runs.
  1809. *
  1810. * @param script The scriptlet to run
  1811. * @returns The result of the eval
  1812. */
  1813. public IRubyObject evalScriptlet(String script) {
  1814. ThreadContext context = getCurrentContext();
  1815. Node node = parseEval(script, "<script>", context.getCurrentScope(), 0);
  1816. try {
  1817. return node.interpret(this, context, context.getFrameSelf(), Block.NULL_BLOCK);
  1818. } catch (JumpException.ReturnJump rj) {
  1819. throw newLocalJumpError("return", (IRubyObject)rj.getValue(), "unexpected return");
  1820. } catch (JumpException.BreakJump bj) {
  1821. throw newLocalJumpError("break", (IRubyObject)bj.getValue(), "unexpected break");
  1822. } catch (JumpException.RedoJump rj) {
  1823. throw newLocalJumpError("redo", (IRubyObject)rj.getValue(), "unexpected redo");
  1824. }
  1825. }
  1826. /**
  1827. * Parse and execute the specified script
  1828. * This differs from the other methods in that it accepts a string-based script and
  1829. * parses and runs it as though it were loaded at a command-line. This is the preferred
  1830. * way to start up a new script when calling directly into the Ruby object (which is
  1831. * generally *dis*couraged.
  1832. *
  1833. * @param script The contents of the script to run as a normal, root script
  1834. * @return The last value of the script
  1835. */
  1836. public IRubyObject executeScript(String script, String filename) {
  1837. byte[] bytes;
  1838. try {
  1839. bytes = script.getBytes(KCode.NONE.getKCode());
  1840. } catch (UnsupportedEncodingException e) {
  1841. bytes = script.getBytes();
  1842. }
  1843. Node node = parseInline(new ByteArrayInputStream(bytes), filename, null);
  1844. ThreadContext context = getCurrentContext();
  1845. String oldFile = context.getFile();
  1846. int oldLine = context.getLine();
  1847. try {
  1848. context.setFile(node.getPosition().getFile());
  1849. context.setLine(node.getPosition().getStartLine());
  1850. return runNormally(node, false);
  1851. } finally {
  1852. context.setFile(oldFile);
  1853. context.setLine(oldLine);
  1854. }
  1855. }
  1856. /**
  1857. * Run the script contained in the specified input stream, using the
  1858. * specified filename as the name of the script being executed. The stream
  1859. * will be read fully before being parsed and executed. The given filename
  1860. * will be used for the ruby $PROGRAM_NAME and $0 global variables in this
  1861. * runtime.
  1862. *
  1863. * This method is intended to be called once per runtime, generally from
  1864. * Main or from main-like top-level entry points.
  1865. *
  1866. * As part of executing the script loaded from the input stream, various
  1867. * RubyInstanceConfig properties will be used to determine whether to
  1868. * compile the script before execution or run with various wrappers (for
  1869. * looping, printing, and so on, see jruby -help).
  1870. *
  1871. * @param inputStream The InputStream from which to read the script contents
  1872. * @param filename The filename to use when parsing, and for $PROGRAM_NAME
  1873. * and $0 ruby global variables.
  1874. */
  1875. public void runFromMain(InputStream inputStream, String filename) {
  1876. IAccessor d = new ValueAccessor(newString(filename));
  1877. getGlobalVariables().define("$PROGRAM_NAME", d);
  1878. getGlobalVariables().define("$0", d);
  1879. for (Iterator i = config.getOptionGlobals().entrySet().iterator(); i.hasNext();) {
  1880. Map.Entry entry = (Map.Entry) i.next();
  1881. Object value = entry.getValue();
  1882. IRubyObject varvalue;
  1883. if (value != null) {
  1884. varvalue = newString(value.toString());
  1885. } else {
  1886. varvalue = getTrue();
  1887. }
  1888. getGlobalVariables().set("$" + entry.getKey().toString(), varvalue);
  1889. }
  1890. if(config.isYARVEnabled()) {
  1891. if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
  1892. new YARVCompiledRunner(this, inputStream, filename).run();
  1893. } else if(config.isRubiniusEnabled()) {
  1894. if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
  1895. new RubiniusRunner(this, inputStream, filename).run();
  1896. } else {
  1897. Node scriptNode = parseFromMain(inputStream, filename);
  1898. ThreadContext context = getCurrentContext();
  1899. String oldFile = context.getFile();
  1900. int oldLine = context.getLine();
  1901. try {
  1902. context.setFile(scriptNode.getPosition().getFile());
  1903. context.setLine(scriptNode.getPosition().getStartLine());
  1904. if (config.isAssumePrinting() || config.isAssumeLoop()) {
  1905. runWithGetsLoop(scriptNode, config.isAssumePrinting(), config.isProcessLineEnds(),
  1906. config.isSplit(), config.isYARVCompileEnabled());
  1907. } else {
  1908. runNormally(scriptNode, config.isYARVCompileEnabled());
  1909. }
  1910. } finally {
  1911. context.setFile(oldFile);
  1912. context.setLine(oldLine);
  1913. }
  1914. }
  1915. }
  1916. /**
  1917. * Parse the script contained in the given input stream, using the given
  1918. * filename as the name of the script, and return the root Node. This
  1919. * is used to verify that the script syntax is valid, for jruby -c. The
  1920. * current scope (generally the top-level scope) is used as the parent
  1921. * scope for parsing.
  1922. *
  1923. * @param inputStream The input stream from which to read the script
  1924. * @param filename The filename to use for parsing
  1925. * @returns The root node of the parsed script
  1926. */
  1927. public Node parseFromMain(InputStream inputStream, String filename) {
  1928. if (config.isInlineScript()) {
  1929. return parseInline(inputStream, filename, getCurrentContext().getCurrentScope());
  1930. } else {
  1931. return parseFile(inputStream, filename, getCurrentContext().getCurrentScope());
  1932. }
  1933. }
  1934. /**
  1935. * Run the given script with a "while gets; end" loop wrapped around it.
  1936. * This is primarily used for the -n command-line flag, to allow writing
  1937. * a short script that processes input lines using the specified code.
  1938. *
  1939. * @param scriptNode The root node of the script to execute
  1940. * @param printing Whether $_ should be printed after each loop (as in the
  1941. * -p command-line flag)
  1942. * @param processLineEnds Whether line endings should be processed by
  1943. * setting $\ to $/ and <code>chop!</code>ing every line read
  1944. * @param split Whether to split each line read using <code>String#split</code>
  1945. * @param yarvCompile Whether to compile the target script to YARV (Ruby 1.9)
  1946. * bytecode before executing.
  1947. * @return The result of executing the specified script
  1948. */
  1949. public IRubyObject runWithGetsLoop(Node scriptNode, boolean printing, boolean processLineEnds, boolean split, boolean yarvCompile) {
  1950. ThreadContext context = getCurrentContext();
  1951. Script script = null;
  1952. YARVCompiledRunner runner = null;
  1953. boolean compile = getInstanceConfig().getCompileMode().shouldPrecompileCLI();
  1954. if (compile || !yarvCompile) {
  1955. script = tryCompile(scriptNode);
  1956. if (compile && script == null) {
  1957. // terminate; tryCompile will have printed out an error and we're done
  1958. return getNil();
  1959. }
  1960. } else if (yarvCompile) {
  1961. runner = tryCompileYarv(scriptNode);
  1962. }
  1963. if (processLineEnds) {
  1964. getGlobalVariables().set("$\\", getGlobalVariables().get("$/"));
  1965. }
  1966. while (RubyKernel.gets(context, getTopSelf(), IRubyObject.NULL_ARRAY).isTrue()) {
  1967. loop: while (true) { // Used for the 'redo' command
  1968. try {
  1969. if (processLineEnds) {
  1970. getGlobalVariables().get("$_").callMethod(context, "chop!");
  1971. }
  1972. if (split) {
  1973. getGlobalVariables().set("$F", getGlobalVariables().get("$_").callMethod(context, "split"));
  1974. }
  1975. if (script != null) {
  1976. runScript(script);
  1977. } else if (runner != null) {
  1978. runYarv(runner);
  1979. } else {
  1980. runInterpreter(scriptNode);
  1981. }
  1982. if (printing) RubyKernel.print(context, getKernel(), new IRubyObject[] {getGlobalVariables().get("$_")});
  1983. break loop;
  1984. } catch (JumpException.RedoJump rj) {
  1985. // do nothing, this iteration restarts
  1986. } catch (JumpException.NextJump nj) {
  1987. // recheck condition
  1988. break loop;
  1989. } catch (JumpException.BreakJump bj) {
  1990. // end loop
  1991. return (IRubyObject) bj.getValue();
  1992. }
  1993. }
  1994. }
  1995. return getNil();
  1996. }
  1997. /**
  1998. * Run the specified script without any of the loop-processing wrapper
  1999. * code.
  2000. *
  2001. * @param scriptNode The root node of the script to be executed
  2002. * @param yarvCompile Whether to compile the script to YARV (Ruby 1.9)
  2003. * bytecode before execution
  2004. * @return The result of executing the script
  2005. */
  2006. public IRubyObject runNormally(Node scriptNode, boolean yarvCompile) {
  2007. Script script = null;
  2008. YARVCompiledRunner runner = null;
  2009. boolean compile = getInstanceConfig().getCompileMode().shouldPrecompileCLI();
  2010. boolean forceCompile = getInstanceConfig().getCompileMode().shouldPrecompileAll();
  2011. if (yarvCompile) {
  2012. runner = tryCompileYarv(scriptNode);
  2013. } else if (compile) {
  2014. script = tryCompile(scriptNode);
  2015. if (forceCompile && script == null) {
  2016. System.err.println("Error, could not compile; pass -J-Djruby.jit.logging.verbose=true for more details");
  2017. return getNil();
  2018. }
  2019. }
  2020. if (script != null) {
  2021. if (config.isShowBytecode()) {
  2022. return nilObject;
  2023. } else {
  2024. return runScript(script);
  2025. }
  2026. } else if (runner != null) {
  2027. return runYarv(runner);
  2028. } else {
  2029. if (config.isShowBytecode()) System.err.print("error: bytecode printing only works with JVM bytecode");
  2030. return runInterpreter(scriptNode);
  2031. }
  2032. }
  2033. private Script tryCompile(Node node) {
  2034. return tryCompile(node, new JRubyClassLoader(getJRubyClassLoader()));
  2035. }
  2036. private Script tryCompile(Node node, JRubyClassLoader classLoader) {
  2037. Script script = null;
  2038. try {
  2039. String filename = node.getPosition().getFile();
  2040. String classname = JavaNameMangler.mangledFilenameForStartupClasspath(filename);
  2041. ASTInspector inspector = new ASTInspector();
  2042. inspector.inspect(node);
  2043. StandardASMCompiler asmCompiler = new StandardASMCompiler(classname, filename);
  2044. ASTCompiler compiler = new ASTCompiler();
  2045. if (config.isShowBytecode()) {
  2046. compiler.compileRoot(node, asmCompiler, inspector, false, false);
  2047. asmCompiler.dumpClass(System.out);
  2048. } else {
  2049. compiler.compileRoot(node, asmCompiler, inspector, true, false);
  2050. }
  2051. script = (Script)asmCompiler.loadClass(classLoader).newInstance();
  2052. if (config.isJitLogging()) {
  2053. System.err.println("compiled: " + node.getPosition().getFile());
  2054. }
  2055. } catch (NotCompilableException nce) {
  2056. if (config.isJitLoggingVerbose()) {
  2057. System.err.println("Error -- Not compileable: " + nce.getMessage());
  2058. nce.printStackTrace();
  2059. }
  2060. } catch (ClassNotFoundException e) {
  2061. if (config.isJitLoggingVerbose()) {
  2062. System.err.println("Error -- Not compileable: " + e.getMessage());
  2063. e.printStackTrace();
  2064. }
  2065. } catch (InstantiationException e) {
  2066. if (config.isJitLoggingVerbose()) {
  2067. System.err.println("Error -- Not compileable: " + e.getMessage());
  2068. e.printStackTrace();
  2069. }
  2070. } catch (IllegalAccessException e) {
  2071. if (config.isJitLoggingVerbose()) {
  2072. System.err.println("Error -- Not compileable: " + e.getMessage());
  2073. e.printStackTrace();
  2074. }
  2075. } catch (Throwable t) {
  2076. if (config.isJitLoggingVerbose()) {
  2077. System.err.println("could not compile: " + node.getPosition().getFile() + " because of: \"" + t.getMessage() + "\"");
  2078. t.printStackTrace();
  2079. }
  2080. }
  2081. return script;
  2082. }
  2083. private YARVCompiledRunner tryCompileYarv(Node node) {
  2084. try {
  2085. StandardYARVCompiler compiler = new StandardYARVCompiler(this);
  2086. ASTCompiler.getYARVCompiler().compile(node, compiler);
  2087. org.jruby.lexer.yacc.ISourcePosition p = node.getPosition();
  2088. if(p == null && node instanceof org.jruby.ast.RootNode) {
  2089. p = ((org.jruby.ast.RootNode)node).getBodyNode().getPosition();
  2090. }
  2091. return new YARVCompiledRunner(this,compiler.getInstructionSequence("<main>",p.getFile(),"toplevel"));
  2092. } catch (NotCompilableException nce) {
  2093. System.err.println("Error -- Not compileable: " + nce.getMessage());
  2094. return null;
  2095. } catch (JumpException.ReturnJump rj) {
  2096. return null;
  2097. }
  2098. }
  2099. private IRubyObject runScript(Script script) {
  2100. ThreadContext context = getCurrentContext();
  2101. try {
  2102. return script.load(context, context.getFrameSelf(), IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
  2103. } catch (JumpException.ReturnJump rj) {
  2104. return (IRubyObject) rj.getValue();
  2105. }
  2106. }
  2107. private IRubyObject runYarv(YARVCompiledRunner runner) {
  2108. try {
  2109. return runner.run();
  2110. } catch (JumpException.ReturnJump rj) {
  2111. return (IRubyObject) rj.getValue();
  2112. }
  2113. }
  2114. private IRubyObject runInterpreter(Node scriptNode) {
  2115. ThreadContext context = getCurrentContext();
  2116. assert scriptNode != null : "scriptNode is not null";
  2117. try {
  2118. return scriptNode.interpret(this, context, getTopSelf(), Block.NULL_BLOCK);
  2119. } catch (JumpException.ReturnJump rj) {
  2120. return (IRubyObject) rj.getValue();
  2121. }
  2122. }
  2123. public BeanManager getBeanManager() {
  2124. return beanManager;
  2125. }
  2126. public JITCompiler getJITCompiler() {
  2127. return jitCompiler;
  2128. }
  2129. /**
  2130. * @deprecated use #newInstance()
  2131. */
  2132. public static Ruby getDefaultInstance() {
  2133. return newInstance();
  2134. }
  2135. @Deprecated
  2136. public static Ruby getCurrentInstance() {
  2137. return null;
  2138. }
  2139. @Deprecated
  2140. public static void setCurrentInstance(Ruby runtime) {
  2141. }
  2142. public int allocSymbolId() {
  2143. return symbolLastId.incrementAndGet();
  2144. }
  2145. public int allocModuleId() {
  2146. return moduleLastId.incrementAndGet();
  2147. }
  2148. /**
  2149. * Retrieve the module with the given name from the Object namespace.
  2150. *
  2151. * @param name The name of the module
  2152. * @return The module or null if not found
  2153. */
  2154. public RubyModule getModule(String name) {
  2155. return (RubyModule) objectClass.getConstantAt(name);
  2156. }
  2157. /**
  2158. * Retrieve the module with the given name from the Object namespace. The
  2159. * module name must be an interned string, but this method will be faster
  2160. * than the non-interned version.
  2161. *
  2162. * @param internedName The name of the module; <em>must</em> be an interned String
  2163. * @return The module or null if not found
  2164. */
  2165. public RubyModule fastGetModule(String internedName) {
  2166. return (RubyModule) objectClass.fastGetConstantAt(internedName);
  2167. }
  2168. /**
  2169. * Retrieve the class with the given name from the Object namespace.
  2170. *
  2171. * @param name The name of the class
  2172. * @return The class
  2173. */
  2174. public RubyClass getClass(String name) {
  2175. return objectClass.getClass(name);
  2176. }
  2177. /**
  2178. * Retrieve the class with the given name from the Object namespace. The
  2179. * module name must be an interned string, but this method will be faster
  2180. * than the non-interned version.
  2181. *
  2182. * @param internedName the name of the class; <em>must</em> be an interned String!
  2183. * @return
  2184. */
  2185. public RubyClass fastGetClass(String internedName) {
  2186. return objectClass.fastGetClass(internedName);
  2187. }
  2188. /**
  2189. * Define a new class under the Object namespace. Roughly equivalent to
  2190. * rb_define_class in MRI.
  2191. *
  2192. * @param name The name for the new class
  2193. * @param superClass The super class for the new class
  2194. * @param allocator An ObjectAllocator instance that can construct
  2195. * instances of the new class.
  2196. * @return The new class
  2197. */
  2198. public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator) {
  2199. return defineClassUnder(name, superClass, allocator, objectClass);
  2200. }
  2201. /**
  2202. * A variation of defineClass that allows passing in an array of subplementary
  2203. * call sites for improving dynamic invocation performance.
  2204. *
  2205. * @param name The name for the new class
  2206. * @param superClass The super class for the new class
  2207. * @param allocator An ObjectAllocator instance that can construct
  2208. * instances of the new class.
  2209. * @return The new class
  2210. */
  2211. public RubyClass defineClass(String name, RubyClass superClass, ObjectAllocator allocator, CallSite[] callSites) {
  2212. return defineClassUnder(name, superClass, allocator, objectClass, callSites);
  2213. }
  2214. /**
  2215. * Define a new class with the given name under the given module or class
  2216. * namespace. Roughly equivalent to rb_define_class_under in MRI.
  2217. *
  2218. * If the name specified is already bound, its value will be returned if:
  2219. * * It is a class
  2220. * * No new superclass is being defined
  2221. *
  2222. * @param name The name for the new class
  2223. * @param superClass The super class for the new class
  2224. * @param allocator An ObjectAllocator instance that can construct
  2225. * instances of the new class.
  2226. * @param parent The namespace under which to define the new class
  2227. * @return The new class
  2228. */
  2229. public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent) {
  2230. return defineClassUnder(name, superClass, allocator, parent, null);
  2231. }
  2232. /**
  2233. * A variation of defineClassUnder that allows passing in an array of
  2234. * supplementary call sites to improve dynamic invocation.
  2235. *
  2236. * @param name The name for the new class
  2237. * @param superClass The super class for the new class
  2238. * @param allocator An ObjectAllocator instance that can construct
  2239. * instances of the new class.
  2240. * @param parent The namespace under which to define the new class
  2241. * @param callSites The array of call sites to add
  2242. * @return The new class
  2243. */
  2244. public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator, RubyModule parent, CallSite[] callSites) {
  2245. IRubyObject classObj = parent.getConstantAt(name);
  2246. if (classObj != null) {
  2247. if (!(classObj instanceof RubyClass)) throw newTypeError(name + " is not a class");
  2248. RubyClass klazz = (RubyClass)classObj;
  2249. if (klazz.getSuperClass().getRealClass() != superClass) {
  2250. throw newNameError(name + " is already defined", name);
  2251. }
  2252. // If we define a class in Ruby, but later want to allow it to be defined in Java,
  2253. // the allocator needs to be updated
  2254. if (klazz.getAllocator() != allocator) {
  2255. klazz.setAllocator(allocator);
  2256. }
  2257. return klazz;
  2258. }
  2259. boolean parentIsObject = parent == objectClass;
  2260. if (superClass == null) {
  2261. String className = parentIsObject ? name : parent.getName() + "::" + name;
  2262. warnings.warn(ID.NO_SUPER_CLASS, "no super class for `" + className + "', Object assumed", className);
  2263. superClass = objectClass;
  2264. }
  2265. return RubyClass.newClass(this, superClass, name, allocator, parent, !parentIsObject, callSites);
  2266. }
  2267. /**
  2268. * Define a new module under the Object namespace. Roughly equivalent to
  2269. * rb_define_module in MRI.
  2270. *
  2271. * @param name The name of the new module
  2272. * @returns The new module
  2273. */
  2274. public RubyModule defineModule(String name) {
  2275. return defineModuleUnder(name, objectClass);
  2276. }
  2277. /**
  2278. * Define a new module with the given name under the given module or
  2279. * class namespace. Roughly equivalent to rb_define_module_under in MRI.
  2280. *
  2281. * @param name The name of the new module
  2282. * @param parent The class or module namespace under which to define the
  2283. * module
  2284. * @returns The new module
  2285. */
  2286. public RubyModule defineModuleUnder(String name, RubyModule parent) {
  2287. IRubyObject moduleObj = parent.getConstantAt(name);
  2288. boolean parentIsObject = parent == objectClass;
  2289. if (moduleObj != null ) {
  2290. if (moduleObj.isModule()) return (RubyModule)moduleObj;
  2291. if (parentIsObject) {
  2292. throw newTypeError(moduleObj.getMetaClass().getName() + " is not a module");
  2293. } else {
  2294. throw newTypeError(parent.getName() + "::" + moduleObj.getMetaClass().getName() + " is not a module");
  2295. }
  2296. }
  2297. return RubyModule.newModule(this, name, parent, !parentIsObject);
  2298. }
  2299. /**
  2300. * From Object, retrieve the named module. If it doesn't exist a
  2301. * new module is created.
  2302. *
  2303. * @param name The name of the module
  2304. * @returns The existing or new module
  2305. */
  2306. public RubyModule getOrCreateModule(String name) {
  2307. IRubyObject module = objectClass.getConstantAt(name);
  2308. if (module == null) {
  2309. module = defineModule(name);
  2310. } else if (getSafeLevel() >= 4) {
  2311. throw newSecurityError("Extending module prohibited.");
  2312. } else if (!module.isModule()) {
  2313. throw newTypeError(name + " is not a Module");
  2314. }
  2315. return (RubyModule) module;
  2316. }
  2317. /**
  2318. * Retrieve the current safe level.
  2319. *
  2320. * @see org.jruby.Ruby#setSaveLevel
  2321. */
  2322. public int getSafeLevel() {
  2323. return this.safeLevel;
  2324. }
  2325. /**
  2326. * Set the current safe level:
  2327. *
  2328. * 0 - strings from streams/environment/ARGV are tainted (default)
  2329. * 1 - no dangerous operation by tainted value
  2330. * 2 - process/file operations prohibited
  2331. * 3 - all generated objects are tainted
  2332. * 4 - no global (non-tainted) variable modification/no direct output
  2333. *
  2334. * The safe level is set using $SAFE in Ruby code. It is not particularly
  2335. * well supported in JRuby.
  2336. */
  2337. public void setSafeLevel(int safeLevel) {
  2338. this.safeLevel = safeLevel;
  2339. }
  2340. public KCode getKCode() {
  2341. return kcode;
  2342. }
  2343. public void setKCode(KCode kcode) {
  2344. this.kcode = kcode;
  2345. }
  2346. public void secure(int level) {
  2347. if (level <= safeLevel) {
  2348. throw newSecurityError("Insecure operation '" + getCurrentContext().getFrameName() + "' at level " + safeLevel);
  2349. }
  2350. }
  2351. // FIXME moved this here to get what's obviously a utility method out of IRubyObject.
  2352. // perhaps security methods should find their own centralized home at some point.
  2353. public void checkSafeString(IRubyObject object) {
  2354. if (getSafeLevel() > 0 && object.isTaint()) {
  2355. ThreadContext tc = getCurrentContext();
  2356. if (tc.getFrameName() != null) {
  2357. throw newSecurityError("Insecure operation - " + tc.getFrameName());
  2358. }
  2359. throw newSecurityError("Insecure operation: -r");
  2360. }
  2361. secure(4);
  2362. if (!(object instanceof RubyString)) {
  2363. throw newTypeError(
  2364. "wrong argument type " + object.getMetaClass().getName() + " (expected String)");
  2365. }
  2366. }
  2367. /** rb_define_global_const
  2368. *
  2369. */
  2370. public void defineGlobalConstant(String name, IRubyObject value) {
  2371. objectClass.defineConstant(name, value);
  2372. }
  2373. public boolean isClassDefined(String name) {
  2374. return getModule(name) != null;
  2375. }
  2376. /**
  2377. * A ThreadFactory for when we're using pooled threads; we want to create
  2378. * the threads with daemon = true so they don't keep us from shutting down.
  2379. */
  2380. public static class DaemonThreadFactory implements ThreadFactory {
  2381. public Thread newThread(Runnable runnable) {
  2382. Thread thread = new Thread(runnable);
  2383. thread.setDaemon(true);
  2384. return thread;
  2385. }
  2386. }
  2387. /**
  2388. * This method is called immediately after constructing the Ruby instance.
  2389. * The main thread is prepared for execution, all core classes and libraries
  2390. * are initialized, and any libraries required on the command line are
  2391. * loaded.
  2392. */
  2393. private void init() {
  2394. // Get the main threadcontext (gets constructed for us)
  2395. ThreadContext tc = getCurrentContext();
  2396. safeLevel = config.getSafeLevel();
  2397. // Construct key services
  2398. loadService = config.createLoadService(this);
  2399. posix = POSIXFactory.getPOSIX(new JRubyPOSIXHandler(this), RubyInstanceConfig.nativeEnabled);
  2400. javaSupport = new JavaSupport(this);
  2401. if (RubyInstanceConfig.POOLING_ENABLED) {
  2402. Executors.newCachedThreadPool();
  2403. executor = new ThreadPoolExecutor(
  2404. RubyInstanceConfig.POOL_MIN,
  2405. RubyInstanceConfig.POOL_MAX,
  2406. RubyInstanceConfig.POOL_TTL,
  2407. TimeUnit.SECONDS,
  2408. new SynchronousQueue<Runnable>(),
  2409. new DaemonThreadFactory());
  2410. }
  2411. // initialize the root of the class hierarchy completely
  2412. initRoot(tc);
  2413. // Construct the top-level execution frame and scope for the main thread
  2414. tc.prepareTopLevel(objectClass, topSelf);
  2415. // Initialize all the core classes
  2416. bootstrap();
  2417. // Create global constants and variables
  2418. RubyGlobal.createGlobals(tc, this);
  2419. // Prepare LoadService and load path
  2420. getLoadService().init(config.loadPaths());
  2421. // initialize builtin libraries
  2422. initBuiltins();
  2423. // Require in all libraries specified on command line
  2424. for (String scriptName : config.requiredLibraries()) {
  2425. RubyKernel.require(getTopSelf(), newString(scriptName), Block.NULL_BLOCK);
  2426. }
  2427. }
  2428. private void bootstrap() {
  2429. initCore();
  2430. initExceptions();
  2431. }
  2432. private void initRoot(ThreadContext context) {
  2433. // Bootstrap the top of the hierarchy
  2434. objectClass = RubyClass.createBootstrapClass(this, "Object", null, RubyObject.OBJECT_ALLOCATOR);
  2435. moduleClass = RubyClass.createBootstrapClass(this, "Module", objectClass, RubyModule.MODULE_ALLOCATOR);
  2436. classClass = RubyClass.createBootstrapClass(this, "Class", moduleClass, RubyClass.CLASS_ALLOCATOR);
  2437. objectClass.setMetaClass(classClass);
  2438. moduleClass.setMetaClass(classClass);
  2439. classClass.setMetaClass(classClass);
  2440. RubyClass metaClass;
  2441. metaClass = objectClass.makeMetaClass(classClass);
  2442. metaClass = moduleClass.makeMetaClass(metaClass);
  2443. metaClass = classClass.makeMetaClass(metaClass);
  2444. RubyObject.createObjectClass(this, objectClass);
  2445. RubyModule.createModuleClass(this, moduleClass);
  2446. RubyClass.createClassClass(this, classClass);
  2447. // set constants now that they're initialized
  2448. objectClass.setConstant("Object", objectClass);
  2449. objectClass.setConstant("Class", classClass);
  2450. objectClass.setConstant("Module", moduleClass);
  2451. // Initialize Kernel and include into Object
  2452. RubyKernel.createKernelModule(this);
  2453. objectClass.includeModule(kernelModule);
  2454. // Initialize the "dummy" class used as a marker
  2455. dummyClass = new RubyClass(this);
  2456. dummyClass.freeze(context);
  2457. // Object is ready, create top self
  2458. topSelf = TopSelfFactory.createTopSelf(this);
  2459. }
  2460. private void initCore() {
  2461. // Pre-create all the core classes potentially referenced during startup
  2462. RubyNil.createNilClass(this);
  2463. RubyBoolean.createFalseClass(this);
  2464. RubyBoolean.createTrueClass(this);
  2465. nilObject = new RubyNil(this);
  2466. falseObject = new RubyBoolean(this, false);
  2467. trueObject = new RubyBoolean(this, true);
  2468. RubyComparable.createComparable(this);
  2469. RubyEnumerable.createEnumerableModule(this);
  2470. RubyString.createStringClass(this);
  2471. RubySymbol.createSymbolClass(this);
  2472. if (profile.allowClass("ThreadGroup")) {
  2473. RubyThreadGroup.createThreadGroupClass(this);
  2474. }
  2475. if (profile.allowClass("Thread")) {
  2476. RubyThread.createThreadClass(this);
  2477. }
  2478. if (profile.allowClass("Exception")) {
  2479. RubyException.createExceptionClass(this);
  2480. }
  2481. if (profile.allowModule("Precision")) {
  2482. RubyPrecision.createPrecisionModule(this);
  2483. }
  2484. if (profile.allowClass("Numeric")) {
  2485. RubyNumeric.createNumericClass(this);
  2486. }
  2487. if (profile.allowClass("Integer")) {
  2488. RubyInteger.createIntegerClass(this);
  2489. }
  2490. if (profile.allowClass("Fixnum")) {
  2491. RubyFixnum.createFixnumClass(this);
  2492. }
  2493. if (config.getCompatVersion() == CompatVersion.RUBY1_9) {
  2494. if (profile.allowClass("Complex")) {
  2495. RubyComplex.createComplexClass(this);
  2496. }
  2497. if (profile.allowClass("Rational")) {
  2498. RubyRational.createRationalClass(this);
  2499. }
  2500. }
  2501. if (profile.allowClass("Hash")) {
  2502. RubyHash.createHashClass(this);
  2503. }
  2504. if (profile.allowClass("Array")) {
  2505. RubyArray.createArrayClass(this);
  2506. }
  2507. if (profile.allowClass("Float")) {
  2508. RubyFloat.createFloatClass(this);
  2509. }
  2510. if (profile.allowClass("Bignum")) {
  2511. RubyBignum.createBignumClass(this);
  2512. }
  2513. ioClass = RubyIO.createIOClass(this);
  2514. if (profile.allowClass("Struct")) {
  2515. RubyStruct.createStructClass(this);
  2516. }
  2517. if (profile.allowClass("Tms")) {
  2518. tmsStruct = RubyStruct.newInstance(structClass, new IRubyObject[]{newString("Tms"), newSymbol("utime"), newSymbol("stime"), newSymbol("cutime"), newSymbol("cstime")}, Block.NULL_BLOCK);
  2519. }
  2520. if (profile.allowClass("Binding")) {
  2521. RubyBinding.createBindingClass(this);
  2522. }
  2523. // Math depends on all numeric types
  2524. if (profile.allowModule("Math")) {
  2525. RubyMath.createMathModule(this);
  2526. }
  2527. if (profile.allowClass("Regexp")) {
  2528. RubyRegexp.createRegexpClass(this);
  2529. }
  2530. if (profile.allowClass("Range")) {
  2531. RubyRange.createRangeClass(this);
  2532. }
  2533. if (profile.allowModule("ObjectSpace")) {
  2534. RubyObjectSpace.createObjectSpaceModule(this);
  2535. }
  2536. if (profile.allowModule("GC")) {
  2537. RubyGC.createGCModule(this);
  2538. }
  2539. if (profile.allowClass("Proc")) {
  2540. RubyProc.createProcClass(this);
  2541. }
  2542. if (profile.allowClass("Method")) {
  2543. RubyMethod.createMethodClass(this);
  2544. }
  2545. if (profile.allowClass("MatchData")) {
  2546. RubyMatchData.createMatchDataClass(this);
  2547. }
  2548. if (profile.allowModule("Marshal")) {
  2549. RubyMarshal.createMarshalModule(this);
  2550. }
  2551. if (profile.allowClass("Dir")) {
  2552. RubyDir.createDirClass(this);
  2553. }
  2554. if (profile.allowModule("FileTest")) {
  2555. RubyFileTest.createFileTestModule(this);
  2556. }
  2557. // depends on IO, FileTest
  2558. if (profile.allowClass("File")) {
  2559. RubyFile.createFileClass(this);
  2560. }
  2561. if (profile.allowClass("File::Stat")) {
  2562. RubyFileStat.createFileStatClass(this);
  2563. }
  2564. if (profile.allowModule("Process")) {
  2565. RubyProcess.createProcessModule(this);
  2566. }
  2567. if (profile.allowClass("Time")) {
  2568. RubyTime.createTimeClass(this);
  2569. }
  2570. if (profile.allowClass("UnboundMethod")) {
  2571. RubyUnboundMethod.defineUnboundMethodClass(this);
  2572. }
  2573. if (profile.allowClass("Data")) {
  2574. defineClass("Data", objectClass, objectClass.getAllocator());
  2575. }
  2576. if (!isSecurityRestricted()) {
  2577. // Signal uses sun.misc.* classes, this is not allowed
  2578. // in the security-sensitive environments
  2579. if (profile.allowModule("Signal")) {
  2580. RubySignal.createSignal(this);
  2581. }
  2582. }
  2583. if (profile.allowClass("Continuation")) {
  2584. RubyContinuation.createContinuation(this);
  2585. }
  2586. }
  2587. private void initExceptions() {
  2588. standardError = defineClassIfAllowed("StandardError", exceptionClass);
  2589. runtimeError = defineClassIfAllowed("RuntimeError", standardError);
  2590. ioError = defineClassIfAllowed("IOError", standardError);
  2591. scriptError = defineClassIfAllowed("ScriptError", exceptionClass);
  2592. rangeError = defineClassIfAllowed("RangeError", standardError);
  2593. signalException = defineClassIfAllowed("SignalException", exceptionClass);
  2594. if (profile.allowClass("NameError")) {
  2595. nameError = RubyNameError.createNameErrorClass(this, standardError);
  2596. nameErrorMessage = RubyNameError.createNameErrorMessageClass(this, nameError);
  2597. }
  2598. if (profile.allowClass("NoMethodError")) {
  2599. noMethodError = RubyNoMethodError.createNoMethodErrorClass(this, nameError);
  2600. }
  2601. if (profile.allowClass("SystemExit")) {
  2602. systemExit = RubySystemExit.createSystemExitClass(this, exceptionClass);
  2603. }
  2604. if (profile.allowClass("LocalJumpError")) {
  2605. localJumpError = RubyLocalJumpError.createLocalJumpErrorClass(this, standardError);
  2606. }
  2607. if (profile.allowClass("NativeException")) {
  2608. nativeException = NativeException.createClass(this, runtimeError);
  2609. }
  2610. if (profile.allowClass("SystemCallError")) {
  2611. systemCallError = RubySystemCallError.createSystemCallErrorClass(this, standardError);
  2612. }
  2613. fatal = defineClassIfAllowed("Fatal", exceptionClass);
  2614. interrupt = defineClassIfAllowed("Interrupt", signalException);
  2615. typeError = defineClassIfAllowed("TypeError", standardError);
  2616. argumentError = defineClassIfAllowed("ArgumentError", standardError);
  2617. indexError = defineClassIfAllowed("IndexError", standardError);
  2618. syntaxError = defineClassIfAllowed("SyntaxError", scriptError);
  2619. loadError = defineClassIfAllowed("LoadError", scriptError);
  2620. notImplementedError = defineClassIfAllowed("NotImplementedError", scriptError);
  2621. securityError = defineClassIfAllowed("SecurityError", standardError);
  2622. noMemoryError = defineClassIfAllowed("NoMemoryError", exceptionClass);
  2623. regexpError = defineClassIfAllowed("RegexpError", standardError);
  2624. eofError = defineClassIfAllowed("EOFError", ioError);
  2625. threadError = defineClassIfAllowed("ThreadError", standardError);
  2626. concurrencyError = defineClassIfAllowed("ConcurrencyError", threadError);
  2627. systemStackError = defineClassIfAllowed("SystemStackError", standardError);
  2628. zeroDivisionError = defineClassIfAllowed("ZeroDivisionError", standardError);
  2629. floatDomainError = defineClassIfAllowed("FloatDomainError", rangeError);
  2630. initErrno();
  2631. }
  2632. private RubyClass defineClassIfAllowed(String name, RubyClass superClass) {
  2633. // TODO: should probably apply the null object pattern for a
  2634. // non-allowed class, rather than null
  2635. if (superClass != null && profile.allowClass(name)) {
  2636. return defineClass(name, superClass, superClass.getAllocator());
  2637. }
  2638. return null;
  2639. }
  2640. private Map<Integer, RubyClass> errnos = new HashMap<Integer, RubyClass>();
  2641. public RubyClass getErrno(int n) {
  2642. return errnos.get(n);
  2643. }
  2644. /**
  2645. * Create module Errno's Variables. We have this method since Errno does not have it's
  2646. * own java class.
  2647. */
  2648. private void initErrno() {
  2649. if (profile.allowModule("Errno")) {
  2650. errnoModule = defineModule("Errno");
  2651. Field[] fields = IErrno.class.getFields();
  2652. for (int i = 0; i < fields.length; i++) {
  2653. try {
  2654. createSysErr(fields[i].getInt(IErrno.class), fields[i].getName());
  2655. } catch (IllegalAccessException e) {
  2656. throw new RuntimeException("Someone defined a non-public constant in IErrno.java", e);
  2657. }
  2658. }
  2659. }
  2660. }
  2661. /**
  2662. * Creates a system error.
  2663. * @param i the error code (will probably use a java exception instead)
  2664. * @param name of the error to define.
  2665. **/
  2666. private void createSysErr(int i, String name) {
  2667. if(profile.allowClass(name)) {
  2668. RubyClass errno = getErrno().defineClassUnder(name, systemCallError, systemCallError.getAllocator());
  2669. errnos.put(i, errno);
  2670. errno.defineConstant("Errno", newFixnum(i));
  2671. }
  2672. }
  2673. private void initBuiltins() {
  2674. addLazyBuiltin("java.rb", "java", "org.jruby.javasupport.Java");
  2675. addLazyBuiltin("jruby.rb", "jruby", "org.jruby.libraries.JRubyLibrary");
  2676. addLazyBuiltin("minijava.rb", "minijava", "org.jruby.java.MiniJava");
  2677. addLazyBuiltin("jruby/ext.rb", "jruby/ext", "org.jruby.RubyJRuby$ExtLibrary");
  2678. addLazyBuiltin("jruby/type.rb", "jruby/type", "org.jruby.RubyJRuby$TypeLibrary");
  2679. addLazyBuiltin("iconv.so", "iconv", "org.jruby.libraries.IConvLibrary");
  2680. addLazyBuiltin("nkf.so", "nkf", "org.jruby.libraries.NKFLibrary");
  2681. addLazyBuiltin("stringio.so", "stringio", "org.jruby.libraries.StringIOLibrary");
  2682. addLazyBuiltin("strscan.so", "strscan", "org.jruby.libraries.StringScannerLibrary");
  2683. addLazyBuiltin("zlib.so", "zlib", "org.jruby.libraries.ZlibLibrary");
  2684. addLazyBuiltin("yaml_internal.rb", "yaml_internal", "org.jruby.libraries.YamlLibrary");
  2685. addLazyBuiltin("enumerator.so", "enumerator", "org.jruby.libraries.EnumeratorLibrary");
  2686. addLazyBuiltin("generator_internal.rb", "generator_internal", "org.jruby.ext.Generator$Service");
  2687. addLazyBuiltin("readline.so", "readline", "org.jruby.ext.Readline$Service");
  2688. addLazyBuiltin("thread.so", "thread", "org.jruby.libraries.ThreadLibrary");
  2689. addLazyBuiltin("digest.so", "digest", "org.jruby.libraries.DigestLibrary");
  2690. addLazyBuiltin("digest.rb", "digest", "org.jruby.libraries.DigestLibrary");
  2691. addLazyBuiltin("digest/md5.so", "digest/md5", "org.jruby.libraries.DigestLibrary$MD5");
  2692. addLazyBuiltin("digest/rmd160.so", "digest/rmd160", "org.jruby.libraries.DigestLibrary$RMD160");
  2693. addLazyBuiltin("digest/sha1.so", "digest/sha1", "org.jruby.libraries.DigestLibrary$SHA1");
  2694. addLazyBuiltin("digest/sha2.so", "digest/sha2", "org.jruby.libraries.DigestLibrary$SHA2");
  2695. addLazyBuiltin("bigdecimal.so", "bigdecimal", "org.jruby.libraries.BigDecimalLibrary");
  2696. addLazyBuiltin("io/wait.so", "io/wait", "org.jruby.libraries.IOWaitLibrary");
  2697. addLazyBuiltin("etc.so", "etc", "org.jruby.libraries.EtcLibrary");
  2698. addLazyBuiltin("weakref.rb", "weakref", "org.jruby.ext.WeakRef$WeakRefLibrary");
  2699. addLazyBuiltin("socket.so", "socket", "org.jruby.ext.socket.RubySocket$Service");
  2700. addLazyBuiltin("rbconfig.rb", "rbconfig", "org.jruby.libraries.RbConfigLibrary");
  2701. addLazyBuiltin("jruby/serialization.rb", "serialization", "org.jruby.libraries.JRubySerializationLibrary");
  2702. addLazyBuiltin("ffi.so", "ffi", "org.jruby.ext.ffi.Factory$Service");
  2703. if(RubyInstanceConfig.NATIVE_NET_PROTOCOL) {
  2704. addLazyBuiltin("net/protocol.rb", "net/protocol", "org.jruby.libraries.NetProtocolBufferedIOLibrary");
  2705. }
  2706. if (config.getCompatVersion() == CompatVersion.RUBY1_9) {
  2707. addLazyBuiltin("fiber.so", "fiber", "org.jruby.libraries.FiberLibrary");
  2708. }
  2709. addBuiltinIfAllowed("openssl.so", new Library() {
  2710. public void load(Ruby runtime, boolean wrap) throws IOException {
  2711. runtime.getLoadService().require("jruby/openssl/stub");
  2712. }
  2713. });
  2714. String[] builtins = {"fcntl", "yaml", "yaml/syck", "jsignal" };
  2715. for (String library : builtins) {
  2716. addBuiltinIfAllowed(library + ".rb", new BuiltinScript(library));
  2717. }
  2718. getLoadService().require("builtin/core_ext/symbol");
  2719. RubyKernel.autoload(topSelf, newSymbol("Java"), newString("java"));
  2720. getLoadService().require("enumerator");
  2721. }
  2722. private void addLazyBuiltin(String name, String shortName, String className) {
  2723. addBuiltinIfAllowed(name, new LateLoadingLibrary(shortName, className, getJRubyClassLoader()));
  2724. }
  2725. private void addBuiltinIfAllowed(String name, Library lib) {
  2726. if(profile.allowBuiltin(name)) {
  2727. loadService.addBuiltinLibrary(name,lib);
  2728. }
  2729. }
  2730. Object getRespondToMethod() {
  2731. return respondToMethod;
  2732. }
  2733. void setRespondToMethod(Object rtm) {
  2734. this.respondToMethod = rtm;
  2735. }
  2736. public Object getObjectToYamlMethod() {
  2737. return objectToYamlMethod;
  2738. }
  2739. void setObjectToYamlMethod(Object otym) {
  2740. this.objectToYamlMethod = otym;
  2741. }
  2742. /**
  2743. * Retrieve mappings of cached methods to where they have been cached. When a cached
  2744. * method needs to be invalidated this map can be used to remove all places it has been
  2745. * cached.
  2746. *
  2747. * @return the mappings of where cached methods have been stored
  2748. */
  2749. public CacheMap getCacheMap() {
  2750. return cacheMap;
  2751. }
  2752. /** Getter for property rubyTopSelf.
  2753. * @return Value of property rubyTopSelf.
  2754. */
  2755. public IRubyObject getTopSelf() {
  2756. return topSelf;
  2757. }
  2758. public void setCurrentDirectory(String dir) {
  2759. currentDirectory = dir;
  2760. }
  2761. public String getCurrentDirectory() {
  2762. return currentDirectory;
  2763. }
  2764. public RubyModule getEtc() {
  2765. return etcModule;
  2766. }
  2767. public void setEtc(RubyModule etcModule) {
  2768. this.etcModule = etcModule;
  2769. }
  2770. public RubyClass getObject() {
  2771. return objectClass;
  2772. }
  2773. public RubyClass getModule() {
  2774. return moduleClass;
  2775. }
  2776. public RubyClass getClassClass() {
  2777. return classClass;
  2778. }
  2779. public RubyModule getKernel() {
  2780. return kernelModule;
  2781. }
  2782. void setKernel(RubyModule kernelModule) {
  2783. this.kernelModule = kernelModule;
  2784. }
  2785. public RubyClass getDummy() {
  2786. return dummyClass;
  2787. }
  2788. public RubyModule getComparable() {
  2789. return comparableModule;
  2790. }
  2791. void setComparable(RubyModule comparableModule) {
  2792. this.comparableModule = comparableModule;
  2793. }
  2794. public RubyClass getNumeric() {
  2795. return numericClass;
  2796. }
  2797. void setNumeric(RubyClass numericClass) {
  2798. this.numericClass = numericClass;
  2799. }
  2800. public RubyClass getFloat() {
  2801. return floatClass;
  2802. }
  2803. void setFloat(RubyClass floatClass) {
  2804. this.floatClass = floatClass;
  2805. }
  2806. public RubyClass getInteger() {
  2807. return integerClass;
  2808. }
  2809. void setInteger(RubyClass integerClass) {
  2810. this.integerClass = integerClass;
  2811. }
  2812. public RubyClass getFixnum() {
  2813. return fixnumClass;
  2814. }
  2815. void setFixnum(RubyClass fixnumClass) {
  2816. this.fixnumClass = fixnumClass;
  2817. }
  2818. public RubyClass getComplex() {
  2819. return complexClass;
  2820. }
  2821. void setComplex(RubyClass complexClass) {
  2822. this.complexClass = complexClass;
  2823. }
  2824. public RubyClass getRational() {
  2825. return rationalClass;
  2826. }
  2827. void setRational(RubyClass rationalClass) {
  2828. this.rationalClass = rationalClass;
  2829. }
  2830. public RubyModule getEnumerable() {
  2831. return enumerableModule;
  2832. }
  2833. void setEnumerable(RubyModule enumerableModule) {
  2834. this.enumerableModule = enumerableModule;
  2835. }
  2836. public RubyModule getEnumerator() {
  2837. return enumeratorClass;
  2838. }
  2839. void setEnumerator(RubyClass enumeratorClass) {
  2840. this.enumeratorClass = enumeratorClass;
  2841. }
  2842. public RubyClass getString() {
  2843. return stringClass;
  2844. }
  2845. void setString(RubyClass stringClass) {
  2846. this.stringClass = stringClass;
  2847. }
  2848. public RubyClass getSymbol() {
  2849. return symbolClass;
  2850. }
  2851. void setSymbol(RubyClass symbolClass) {
  2852. this.symbolClass = symbolClass;
  2853. }
  2854. public RubyClass getArray() {
  2855. return arrayClass;
  2856. }
  2857. void setArray(RubyClass arrayClass) {
  2858. this.arrayClass = arrayClass;
  2859. }
  2860. public RubyClass getHash() {
  2861. return hashClass;
  2862. }
  2863. void setHash(RubyClass hashClass) {
  2864. this.hashClass = hashClass;
  2865. }
  2866. public RubyClass getRange() {
  2867. return rangeClass;
  2868. }
  2869. void setRange(RubyClass rangeClass) {
  2870. this.rangeClass = rangeClass;
  2871. }
  2872. /** Returns the "true" instance from the instance pool.
  2873. * @return The "true" instance.
  2874. */
  2875. public RubyBoolean getTrue() {
  2876. return trueObject;
  2877. }
  2878. /** Returns the "false" instance from the instance pool.
  2879. * @return The "false" instance.
  2880. */
  2881. public RubyBoolean getFalse() {
  2882. return falseObject;
  2883. }
  2884. /** Returns the "nil" singleton instance.
  2885. * @return "nil"
  2886. */
  2887. public IRubyObject getNil() {
  2888. return nilObject;
  2889. }
  2890. public RubyClass getNilClass() {
  2891. return nilClass;
  2892. }
  2893. void setNilClass(RubyClass nilClass) {
  2894. this.nilClass = nilClass;
  2895. }
  2896. public RubyClass getTrueClass() {
  2897. return trueClass;
  2898. }
  2899. void setTrueClass(RubyClass trueClass) {
  2900. this.trueClass = trueClass;
  2901. }
  2902. public RubyClass getFalseClass() {
  2903. return falseClass;
  2904. }
  2905. void setFalseClass(RubyClass falseClass) {
  2906. this.falseClass = falseClass;
  2907. }
  2908. public RubyClass getProc() {
  2909. return procClass;
  2910. }
  2911. void setProc(RubyClass procClass) {
  2912. this.procClass = procClass;
  2913. }
  2914. public RubyClass getBinding() {
  2915. return bindingClass;
  2916. }
  2917. void setBinding(RubyClass bindingClass) {
  2918. this.bindingClass = bindingClass;
  2919. }
  2920. public RubyClass getMethod() {
  2921. return methodClass;
  2922. }
  2923. void setMethod(RubyClass methodClass) {
  2924. this.methodClass = methodClass;
  2925. }
  2926. public RubyClass getUnboundMethod() {
  2927. return unboundMethodClass;
  2928. }
  2929. void setUnboundMethod(RubyClass unboundMethodClass) {
  2930. this.unboundMethodClass = unboundMethodClass;
  2931. }
  2932. public RubyClass getMatchData() {
  2933. return matchDataClass;
  2934. }
  2935. void setMatchData(RubyClass matchDataClass) {
  2936. this.matchDataClass = matchDataClass;
  2937. }
  2938. public RubyClass getRegexp() {
  2939. return regexpClass;
  2940. }
  2941. void setRegexp(RubyClass regexpClass) {
  2942. this.regexpClass = regexpClass;
  2943. }
  2944. public RubyClass getTime() {
  2945. return timeClass;
  2946. }
  2947. void setTime(RubyClass timeClass) {
  2948. this.timeClass = timeClass;
  2949. }
  2950. public RubyModule getMath() {
  2951. return mathModule;
  2952. }
  2953. void setMath(RubyModule mathModule) {
  2954. this.mathModule = mathModule;
  2955. }
  2956. public RubyModule getMarshal() {
  2957. return marshalModule;
  2958. }
  2959. void setMarshal(RubyModule marshalModule) {
  2960. this.marshalModule = marshalModule;
  2961. }
  2962. public RubyClass getBignum() {
  2963. return bignumClass;
  2964. }
  2965. void setBignum(RubyClass bignumClass) {
  2966. this.bignumClass = bignumClass;
  2967. }
  2968. public RubyClass getDir() {
  2969. return dirClass;
  2970. }
  2971. void setDir(RubyClass dirClass) {
  2972. this.dirClass = dirClass;
  2973. }
  2974. public RubyClass getFile() {
  2975. return fileClass;
  2976. }
  2977. void setFile(RubyClass fileClass) {
  2978. this.fileClass = fileClass;
  2979. }
  2980. public RubyClass getFileStat() {
  2981. return fileStatClass;
  2982. }
  2983. void setFileStat(RubyClass fileStatClass) {
  2984. this.fileStatClass = fileStatClass;
  2985. }
  2986. public RubyModule getFileTest() {
  2987. return fileTestModule;
  2988. }
  2989. void setFileTest(RubyModule fileTestModule) {
  2990. this.fileTestModule = fileTestModule;
  2991. }
  2992. public RubyClass getIO() {
  2993. return ioClass;
  2994. }
  2995. void setIO(RubyClass ioClass) {
  2996. this.ioClass = ioClass;
  2997. }
  2998. public RubyClass getThread() {
  2999. return threadClass;
  3000. }
  3001. void setThread(RubyClass threadClass) {
  3002. this.threadClass = threadClass;
  3003. }
  3004. public RubyClass getThreadGroup() {
  3005. return threadGroupClass;
  3006. }
  3007. void setThreadGroup(RubyClass threadGroupClass) {
  3008. this.threadGroupClass = threadGroupClass;
  3009. }
  3010. public RubyThreadGroup getDefaultThreadGroup() {
  3011. return defaultThreadGroup;
  3012. }
  3013. void setDefaultThreadGroup(RubyThreadGroup defaultThreadGroup) {
  3014. this.defaultThreadGroup = defaultThreadGroup;
  3015. }
  3016. public RubyClass getContinuation() {
  3017. return continuationClass;
  3018. }
  3019. void setContinuation(RubyClass continuationClass) {
  3020. this.continuationClass = continuationClass;
  3021. }
  3022. public RubyClass getStructClass() {
  3023. return structClass;
  3024. }
  3025. void setStructClass(RubyClass structClass) {
  3026. this.structClass = structClass;
  3027. }
  3028. public IRubyObject getTmsStruct() {
  3029. return tmsStruct;
  3030. }
  3031. void setTmsStruct(RubyClass tmsStruct) {
  3032. this.tmsStruct = tmsStruct;
  3033. }
  3034. public IRubyObject getPasswdStruct() {
  3035. return passwdStruct;
  3036. }
  3037. void setPasswdStruct(RubyClass passwdStruct) {
  3038. this.passwdStruct = passwdStruct;
  3039. }
  3040. public IRubyObject getGroupStruct() {
  3041. return groupStruct;
  3042. }
  3043. void setGroupStruct(RubyClass groupStruct) {
  3044. this.groupStruct = groupStruct;
  3045. }
  3046. public RubyModule getGC() {
  3047. return gcModule;
  3048. }
  3049. void setGC(RubyModule gcModule) {
  3050. this.gcModule = gcModule;
  3051. }
  3052. public RubyModule getObjectSpaceModule() {
  3053. return objectSpaceModule;
  3054. }
  3055. void setObjectSpaceModule(RubyModule objectSpaceModule) {
  3056. this.objectSpaceModule = objectSpaceModule;
  3057. }
  3058. public RubyModule getProcess() {
  3059. return processModule;
  3060. }
  3061. void setProcess(RubyModule processModule) {
  3062. this.processModule = processModule;
  3063. }
  3064. public RubyClass getProcStatus() {
  3065. return procStatusClass;
  3066. }
  3067. void setProcStatus(RubyClass procStatusClass) {
  3068. this.procStatusClass = procStatusClass;
  3069. }
  3070. public RubyModule getProcUID() {
  3071. return procUIDModule;
  3072. }
  3073. void setProcUID(RubyModule procUIDModule) {
  3074. this.procUIDModule = procUIDModule;
  3075. }
  3076. public RubyModule getProcGID() {
  3077. return procGIDModule;
  3078. }
  3079. void setProcGID(RubyModule procGIDModule) {
  3080. this.procGIDModule = procGIDModule;
  3081. }
  3082. public RubyModule getProcSysModule() {
  3083. return procSysModule;
  3084. }
  3085. void setProcSys(RubyModule procSysModule) {
  3086. this.procSysModule = procSysModule;
  3087. }
  3088. public RubyModule getPrecision() {
  3089. return precisionModule;
  3090. }
  3091. void setPrecision(RubyModule precisionModule) {
  3092. this.precisionModule = precisionModule;
  3093. }
  3094. public RubyModule getErrno() {
  3095. return errnoModule;
  3096. }
  3097. public RubyClass getException() {
  3098. return exceptionClass;
  3099. }
  3100. void setException(RubyClass exceptionClass) {
  3101. this.exceptionClass = exceptionClass;
  3102. }
  3103. public RubyClass getNameError() {
  3104. return nameError;
  3105. }
  3106. public RubyClass getNameErrorMessage() {
  3107. return nameErrorMessage;
  3108. }
  3109. public RubyClass getNoMethodError() {
  3110. return noMethodError;
  3111. }
  3112. public RubyClass getSignalException() {
  3113. return signalException;
  3114. }
  3115. public RubyClass getRangeError() {
  3116. return rangeError;
  3117. }
  3118. public RubyClass getSystemExit() {
  3119. return systemExit;
  3120. }
  3121. public RubyClass getLocalJumpError() {
  3122. return localJumpError;
  3123. }
  3124. public RubyClass getNativeException() {
  3125. return nativeException;
  3126. }
  3127. public RubyClass getSystemCallError() {
  3128. return systemCallError;
  3129. }
  3130. public RubyClass getFatal() {
  3131. return fatal;
  3132. }
  3133. public RubyClass getInterrupt() {
  3134. return interrupt;
  3135. }
  3136. public RubyClass getTypeError() {
  3137. return typeError;
  3138. }
  3139. public RubyClass getArgumentError() {
  3140. return argumentError;
  3141. }
  3142. public RubyClass getIndexError() {
  3143. return indexError;
  3144. }
  3145. public RubyClass getSyntaxError() {
  3146. return syntaxError;
  3147. }
  3148. public RubyClass getStandardError() {
  3149. return standardError;
  3150. }
  3151. public RubyClass getRuntimeError() {
  3152. return runtimeError;
  3153. }
  3154. public RubyClass getIOError() {
  3155. return ioError;
  3156. }
  3157. public RubyClass getLoadError() {
  3158. return loadError;
  3159. }
  3160. public RubyClass getNotImplementedError() {
  3161. return notImplementedError;
  3162. }
  3163. public RubyClass getSecurityError() {
  3164. return securityError;
  3165. }
  3166. public RubyClass getNoMemoryError() {
  3167. return noMemoryError;
  3168. }
  3169. public RubyClass getRegexpError() {
  3170. return regexpError;
  3171. }
  3172. public RubyClass getEOFError() {
  3173. return eofError;
  3174. }
  3175. public RubyClass getThreadError() {
  3176. return threadError;
  3177. }
  3178. public RubyClass getConcurrencyError() {
  3179. return concurrencyError;
  3180. }
  3181. public RubyClass getSystemStackError() {
  3182. return systemStackError;
  3183. }
  3184. public RubyClass getZeroDivisionError() {
  3185. return zeroDivisionError;
  3186. }
  3187. public RubyClass getFloatDomainError() {
  3188. return floatDomainError;
  3189. }
  3190. private RubyHash charsetMap;
  3191. public RubyHash getCharsetMap() {
  3192. if (charsetMap == null) charsetMap = new RubyHash(this);
  3193. return charsetMap;
  3194. }
  3195. /** Getter for property isVerbose.
  3196. * @return Value of property isVerbose.
  3197. */
  3198. public IRubyObject getVerbose() {
  3199. return verbose;
  3200. }
  3201. /** Setter for property isVerbose.
  3202. * @param verbose New value of property isVerbose.
  3203. */
  3204. public void setVerbose(IRubyObject verbose) {
  3205. this.verbose = verbose;
  3206. }
  3207. /** Getter for property isDebug.
  3208. * @return Value of property isDebug.
  3209. */
  3210. public IRubyObject getDebug() {
  3211. return debug;
  3212. }
  3213. /** Setter for property isDebug.
  3214. * @param debug New value of property isDebug.
  3215. */
  3216. public void setDebug(IRubyObject debug) {
  3217. this.debug = debug;
  3218. }
  3219. public JavaSupport getJavaSupport() {
  3220. return javaSupport;
  3221. }
  3222. public static ClassLoader getClassLoader() {
  3223. // we try to get the classloader that loaded JRuby, falling back on System
  3224. ClassLoader loader = Ruby.class.getClassLoader();
  3225. if (loader == null) {
  3226. loader = ClassLoader.getSystemClassLoader();
  3227. }
  3228. return loader;
  3229. }
  3230. public synchronized JRubyClassLoader getJRubyClassLoader() {
  3231. // FIXME: Get rid of laziness and handle restricted access elsewhere
  3232. if (!Ruby.isSecurityRestricted() && jrubyClassLoader == null) {
  3233. jrubyClassLoader = new JRubyClassLoader(config.getLoader());
  3234. }
  3235. return jrubyClassLoader;
  3236. }
  3237. /** Defines a global variable
  3238. */
  3239. public void defineVariable(final GlobalVariable variable) {
  3240. globalVariables.define(variable.name(), new IAccessor() {
  3241. public IRubyObject getValue() {
  3242. return variable.get();
  3243. }
  3244. public IRubyObject setValue(IRubyObject newValue) {
  3245. return variable.set(newValue);
  3246. }
  3247. });
  3248. }
  3249. /** defines a readonly global variable
  3250. *
  3251. */
  3252. public void defineReadonlyVariable(String name, IRubyObject value) {
  3253. globalVariables.defineReadonly(name, new ValueAccessor(value));
  3254. }
  3255. public Node parseFile(InputStream in, String file, DynamicScope scope) {
  3256. return parser.parse(file, in, scope, new ParserConfiguration(0, false, false, true));
  3257. }
  3258. public Node parseInline(InputStream in, String file, DynamicScope scope) {
  3259. return parser.parse(file, in, scope, new ParserConfiguration(0, false, true));
  3260. }
  3261. public Node parseEval(String content, String file, DynamicScope scope, int lineNumber) {
  3262. byte[] bytes;
  3263. try {
  3264. bytes = content.getBytes(KCode.NONE.getKCode());
  3265. } catch (UnsupportedEncodingException e) {
  3266. bytes = content.getBytes();
  3267. }
  3268. return parser.parse(file, new ByteArrayInputStream(bytes), scope,
  3269. new ParserConfiguration(lineNumber, false));
  3270. }
  3271. public Node parse(String content, String file, DynamicScope scope, int lineNumber,
  3272. boolean extraPositionInformation) {
  3273. byte[] bytes;
  3274. try {
  3275. bytes = content.getBytes(KCode.NONE.getKCode());
  3276. } catch (UnsupportedEncodingException e) {
  3277. bytes = content.getBytes();
  3278. }
  3279. return parser.parse(file, new ByteArrayInputStream(bytes), scope,
  3280. new ParserConfiguration(lineNumber, extraPositionInformation, false));
  3281. }
  3282. public Node parseEval(ByteList content, String file, DynamicScope scope, int lineNumber) {
  3283. return parser.parse(file, content, scope, new ParserConfiguration(lineNumber, false));
  3284. }
  3285. public Node parse(ByteList content, String file, DynamicScope scope, int lineNumber,
  3286. boolean extraPositionInformation) {
  3287. return parser.parse(file, content, scope,
  3288. new ParserConfiguration(lineNumber, extraPositionInformation, false));
  3289. }
  3290. public ThreadService getThreadService() {
  3291. return threadService;
  3292. }
  3293. public ThreadContext getCurrentContext() {
  3294. return threadService.getCurrentContext();
  3295. }
  3296. /**
  3297. * Returns the loadService.
  3298. * @return ILoadService
  3299. */
  3300. public LoadService getLoadService() {
  3301. return loadService;
  3302. }
  3303. public RubyWarnings getWarnings() {
  3304. return warnings;
  3305. }
  3306. public PrintStream getErrorStream() {
  3307. // FIXME: We can't guarantee this will always be a RubyIO...so the old code here is not safe
  3308. /*java.io.OutputStream os = ((RubyIO) getGlobalVariables().get("$stderr")).getOutStream();
  3309. if(null != os) {
  3310. return new PrintStream(os);
  3311. } else {
  3312. return new PrintStream(new org.jruby.util.SwallowingOutputStream());
  3313. }*/
  3314. return new PrintStream(new IOOutputStream(getGlobalVariables().get("$stderr")));
  3315. }
  3316. public InputStream getInputStream() {
  3317. return new IOInputStream(getGlobalVariables().get("$stdin"));
  3318. }
  3319. public PrintStream getOutputStream() {
  3320. return new PrintStream(new IOOutputStream(getGlobalVariables().get("$stdout")));
  3321. }
  3322. public RubyModule getClassFromPath(String path) {
  3323. RubyModule c = getObject();
  3324. if (path.length() == 0 || path.charAt(0) == '#') {
  3325. throw newTypeError("can't retrieve anonymous class " + path);
  3326. }
  3327. int pbeg = 0, p = 0;
  3328. for(int l=path.length(); p<l; ) {
  3329. while(p<l && path.charAt(p) != ':') {
  3330. p++;
  3331. }
  3332. String str = path.substring(pbeg, p);
  3333. if(p<l && path.charAt(p) == ':') {
  3334. if(p+1 < l && path.charAt(p+1) != ':') {
  3335. throw newTypeError("undefined class/module " + path.substring(pbeg,p));
  3336. }
  3337. p += 2;
  3338. pbeg = p;
  3339. }
  3340. IRubyObject cc = c.getConstant(str);
  3341. if(!(cc instanceof RubyModule)) {
  3342. throw newTypeError("" + path + " does not refer to class/module");
  3343. }
  3344. c = (RubyModule)cc;
  3345. }
  3346. return c;
  3347. }
  3348. /** Prints an error with backtrace to the error stream.
  3349. *
  3350. * MRI: eval.c - error_print()
  3351. *
  3352. */
  3353. public void printError(RubyException excp) {
  3354. if (excp == null || excp.isNil()) {
  3355. return;
  3356. }
  3357. ThreadContext context = getCurrentContext();
  3358. IRubyObject backtrace = excp.callMethod(context, "backtrace");
  3359. PrintStream errorStream = getErrorStream();
  3360. if (backtrace.isNil() || !(backtrace instanceof RubyArray)) {
  3361. if (context.getFile() != null) {
  3362. errorStream.print(context.getFile() + ":" + context.getLine());
  3363. } else {
  3364. errorStream.print(context.getLine());
  3365. }
  3366. } else if (((RubyArray) backtrace).getLength() == 0) {
  3367. printErrorPos(context, errorStream);
  3368. } else {
  3369. IRubyObject mesg = ((RubyArray) backtrace).first();
  3370. if (mesg.isNil()) {
  3371. printErrorPos(context, errorStream);
  3372. } else {
  3373. errorStream.print(mesg);
  3374. }
  3375. }
  3376. RubyClass type = excp.getMetaClass();
  3377. String info = excp.toString();
  3378. if (type == getRuntimeError() && (info == null || info.length() == 0)) {
  3379. errorStream.print(": unhandled exception\n");
  3380. } else {
  3381. String path = type.getName();
  3382. if (info.length() == 0) {
  3383. errorStream.print(": " + path + '\n');
  3384. } else {
  3385. if (path.startsWith("#")) {
  3386. path = null;
  3387. }
  3388. String tail = null;
  3389. if (info.indexOf("\n") != -1) {
  3390. tail = info.substring(info.indexOf("\n") + 1);
  3391. info = info.substring(0, info.indexOf("\n"));
  3392. }
  3393. errorStream.print(": " + info);
  3394. if (path != null) {
  3395. errorStream.print(" (" + path + ")\n");
  3396. }
  3397. if (tail != null) {
  3398. errorStream.print(tail + '\n');
  3399. }
  3400. }
  3401. }
  3402. excp.printBacktrace(errorStream);
  3403. }
  3404. private void printErrorPos(ThreadContext context, PrintStream errorStream) {
  3405. if (context.getFile() != null) {
  3406. if (context.getFrameName() != null) {
  3407. errorStream.print(context.getFile() + ":" + context.getLine());
  3408. errorStream.print(":in '" + context.getFrameName() + '\'');
  3409. } else if (context.getLine() != 0) {
  3410. errorStream.print(context.getFile() + ":" + context.getLine());
  3411. } else {
  3412. errorStream.print(context.getFile());
  3413. }
  3414. }
  3415. }
  3416. public void loadFile(String scriptName, InputStream in, boolean wrap) {
  3417. IRubyObject self = wrap ? TopSelfFactory.createTopSelf(this) : getTopSelf();
  3418. ThreadContext context = getCurrentContext();
  3419. String file = context.getFile();
  3420. try {
  3421. secure(4); /* should alter global state */
  3422. context.setFile(scriptName);
  3423. context.preNodeEval(objectClass, self, scriptName);
  3424. parseFile(in, scriptName, null).interpret(this, context, self, Block.NULL_BLOCK);
  3425. } catch (JumpException.ReturnJump rj) {
  3426. return;
  3427. } finally {
  3428. context.postNodeEval();
  3429. context.setFile(file);
  3430. }
  3431. }
  3432. public void compileAndLoadFile(String filename, InputStream in, boolean wrap) {
  3433. IRubyObject self = wrap ? TopSelfFactory.createTopSelf(this) : getTopSelf();
  3434. ThreadContext context = getCurrentContext();
  3435. String file = context.getFile();
  3436. try {
  3437. secure(4); /* should alter global state */
  3438. context.setFile(filename);
  3439. context.preNodeEval(objectClass, self, filename);
  3440. Node scriptNode = parseFile(in, filename, null);
  3441. Script script = tryCompile(scriptNode, new JRubyClassLoader(jrubyClassLoader));
  3442. if (script == null) {
  3443. System.err.println("Error, could not compile; pass -J-Djruby.jit.logging.verbose=true for more details");
  3444. }
  3445. runScript(script);
  3446. } catch (JumpException.ReturnJump rj) {
  3447. return;
  3448. } finally {
  3449. context.postNodeEval();
  3450. context.setFile(file);
  3451. }
  3452. }
  3453. public void loadScript(Script script) {
  3454. IRubyObject self = getTopSelf();
  3455. ThreadContext context = getCurrentContext();
  3456. try {
  3457. secure(4); /* should alter global state */
  3458. context.preNodeEval(objectClass, self);
  3459. script.load(context, self, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
  3460. } catch (JumpException.ReturnJump rj) {
  3461. return;
  3462. } finally {
  3463. context.postNodeEval();
  3464. }
  3465. }
  3466. public class CallTraceFuncHook extends EventHook {
  3467. private RubyProc traceFunc;
  3468. public void setTraceFunc(RubyProc traceFunc) {
  3469. this.traceFunc = traceFunc;
  3470. }
  3471. public void eventHandler(ThreadContext context, String eventName, String file, int line, String name, IRubyObject type) {
  3472. if (!context.isWithinTrace()) {
  3473. if (file == null) file = "(ruby)";
  3474. if (type == null) type = getFalse();
  3475. RubyBinding binding = RubyBinding.newBinding(Ruby.this);
  3476. context.preTrace();
  3477. try {
  3478. traceFunc.call(context, new IRubyObject[] {
  3479. newString(eventName), // event name
  3480. newString(file), // filename
  3481. newFixnum(line), // line numbers should be 1-based
  3482. name != null ? newSymbol(name) : getNil(),
  3483. binding,
  3484. type
  3485. });
  3486. } finally {
  3487. context.postTrace();
  3488. }
  3489. }
  3490. }
  3491. public boolean isInterestedInEvent(RubyEvent event) {
  3492. return true;
  3493. }
  3494. };
  3495. private final CallTraceFuncHook callTraceFuncHook = new CallTraceFuncHook();
  3496. public void addEventHook(EventHook hook) {
  3497. eventHooks.add(hook);
  3498. hasEventHooks = true;
  3499. }
  3500. public void removeEventHook(EventHook hook) {
  3501. eventHooks.remove(hook);
  3502. hasEventHooks = !eventHooks.isEmpty();
  3503. }
  3504. public void setTraceFunction(RubyProc traceFunction) {
  3505. removeEventHook(callTraceFuncHook);
  3506. if (traceFunction == null) {
  3507. return;
  3508. }
  3509. callTraceFuncHook.setTraceFunc(traceFunction);
  3510. addEventHook(callTraceFuncHook);
  3511. }
  3512. public void callEventHooks(ThreadContext context, RubyEvent event, String file, int line, String name, IRubyObject type) {
  3513. for (EventHook eventHook : eventHooks) {
  3514. if (eventHook.isInterestedInEvent(event)) {
  3515. eventHook.event(context, event, file, line, name, type);
  3516. }
  3517. }
  3518. }
  3519. public boolean hasEventHooks() {
  3520. return hasEventHooks;
  3521. }
  3522. public GlobalVariables getGlobalVariables() {
  3523. return globalVariables;
  3524. }
  3525. // For JSR 223 support: see http://scripting.java.net/
  3526. public void setGlobalVariables(GlobalVariables globalVariables) {
  3527. this.globalVariables = globalVariables;
  3528. }
  3529. public CallbackFactory callbackFactory(Class<?> type) {
  3530. return CallbackFactory.createFactory(this, type);
  3531. }
  3532. /**
  3533. * Push block onto exit stack. When runtime environment exits
  3534. * these blocks will be evaluated.
  3535. *
  3536. * @return the element that was pushed onto stack
  3537. */
  3538. public IRubyObject pushExitBlock(RubyProc proc) {
  3539. atExitBlocks.push(proc);
  3540. return proc;
  3541. }
  3542. // use this for JRuby-internal finalizers
  3543. public void addInternalFinalizer(Finalizable finalizer) {
  3544. synchronized (internalFinalizersMutex) {
  3545. if (internalFinalizers == null) {
  3546. internalFinalizers = new WeakHashMap<Finalizable, Object>();
  3547. }
  3548. internalFinalizers.put(finalizer, null);
  3549. }
  3550. }
  3551. // this method is for finalizers registered via ObjectSpace
  3552. public void addFinalizer(Finalizable finalizer) {
  3553. synchronized (finalizersMutex) {
  3554. if (finalizers == null) {
  3555. finalizers = new WeakHashMap<Finalizable, Object>();
  3556. }
  3557. finalizers.put(finalizer, null);
  3558. }
  3559. }
  3560. public void removeInternalFinalizer(Finalizable finalizer) {
  3561. synchronized (internalFinalizersMutex) {
  3562. if (internalFinalizers != null) {
  3563. internalFinalizers.remove(finalizer);
  3564. }
  3565. }
  3566. }
  3567. public void removeFinalizer(Finalizable finalizer) {
  3568. synchronized (finalizersMutex) {
  3569. if (finalizers != null) {
  3570. finalizers.remove(finalizer);
  3571. }
  3572. }
  3573. }
  3574. /**
  3575. * Make sure Kernel#at_exit procs get invoked on runtime shutdown.
  3576. * This method needs to be explicitly called to work properly.
  3577. * I thought about using finalize(), but that did not work and I
  3578. * am not sure the runtime will be at a state to run procs by the
  3579. * time Ruby is going away. This method can contain any other
  3580. * things that need to be cleaned up at shutdown.
  3581. */
  3582. public void tearDown() {
  3583. int status = 0;
  3584. while (!atExitBlocks.empty()) {
  3585. RubyProc proc = atExitBlocks.pop();
  3586. try {
  3587. proc.call(getCurrentContext(), IRubyObject.NULL_ARRAY);
  3588. } catch (RaiseException rj) {
  3589. RubyException raisedException = rj.getException();
  3590. if (!getSystemExit().isInstance(raisedException)) {
  3591. status = 1;
  3592. printError(raisedException);
  3593. } else {
  3594. IRubyObject statusObj = raisedException.callMethod(
  3595. getCurrentContext(), "status");
  3596. if (statusObj != null && !statusObj.isNil()) {
  3597. status = RubyNumeric.fix2int(statusObj);
  3598. }
  3599. }
  3600. }
  3601. }
  3602. if (finalizers != null) {
  3603. synchronized (finalizers) {
  3604. for (Iterator<Finalizable> finalIter = new ArrayList<Finalizable>(finalizers.keySet()).iterator(); finalIter.hasNext();) {
  3605. finalIter.next().finalize();
  3606. finalIter.remove();
  3607. }
  3608. }
  3609. }
  3610. synchronized (internalFinalizersMutex) {
  3611. if (internalFinalizers != null) {
  3612. for (Iterator<Finalizable> finalIter = new ArrayList<Finalizable>(
  3613. internalFinalizers.keySet()).iterator(); finalIter.hasNext();) {
  3614. finalIter.next().finalize();
  3615. finalIter.remove();
  3616. }
  3617. }
  3618. }
  3619. getThreadService().disposeCurrentThread();
  3620. getBeanManager().unregisterCompiler();
  3621. getBeanManager().unregisterConfig();
  3622. getBeanManager().unregisterClassCache();
  3623. getBeanManager().unregisterMethodCache();
  3624. if (status != 0) {
  3625. throw newSystemExit(status);
  3626. }
  3627. }
  3628. // new factory methods ------------------------------------------------------------------------
  3629. public RubyArray newEmptyArray() {
  3630. return RubyArray.newEmptyArray(this);
  3631. }
  3632. public RubyArray newArray() {
  3633. return RubyArray.newArray(this);
  3634. }
  3635. public RubyArray newArrayLight() {
  3636. return RubyArray.newArrayLight(this);
  3637. }
  3638. public RubyArray newArray(IRubyObject object) {
  3639. return RubyArray.newArray(this, object);
  3640. }
  3641. public RubyArray newArray(IRubyObject car, IRubyObject cdr) {
  3642. return RubyArray.newArray(this, car, cdr);
  3643. }
  3644. public RubyArray newArray(IRubyObject[] objects) {
  3645. return RubyArray.newArray(this, objects);
  3646. }
  3647. public RubyArray newArrayNoCopy(IRubyObject[] objects) {
  3648. return RubyArray.newArrayNoCopy(this, objects);
  3649. }
  3650. public RubyArray newArrayNoCopyLight(IRubyObject[] objects) {
  3651. return RubyArray.newArrayNoCopyLight(this, objects);
  3652. }
  3653. public RubyArray newArray(List<IRubyObject> list) {
  3654. return RubyArray.newArray(this, list);
  3655. }
  3656. public RubyArray newArray(int size) {
  3657. return RubyArray.newArray(this, size);
  3658. }
  3659. public RubyBoolean newBoolean(boolean value) {
  3660. return RubyBoolean.newBoolean(this, value);
  3661. }
  3662. public RubyFileStat newFileStat(String filename, boolean lstat) {
  3663. return RubyFileStat.newFileStat(this, filename, lstat);
  3664. }
  3665. public RubyFileStat newFileStat(FileDescriptor descriptor) {
  3666. return RubyFileStat.newFileStat(this, descriptor);
  3667. }
  3668. public RubyFixnum newFixnum(long value) {
  3669. return RubyFixnum.newFixnum(this, value);
  3670. }
  3671. public RubyFixnum newFixnum(int value) {
  3672. return RubyFixnum.newFixnum(this, value);
  3673. }
  3674. public RubyFloat newFloat(double value) {
  3675. return RubyFloat.newFloat(this, value);
  3676. }
  3677. public RubyNumeric newNumeric() {
  3678. return RubyNumeric.newNumeric(this);
  3679. }
  3680. public RubyProc newProc(Block.Type type, Block block) {
  3681. if (type != Block.Type.LAMBDA && block.getProcObject() != null) return block.getProcObject();
  3682. RubyProc proc = RubyProc.newProc(this, type);
  3683. proc.callInit(IRubyObject.NULL_ARRAY, block);
  3684. return proc;
  3685. }
  3686. public RubyProc newBlockPassProc(Block.Type type, Block block) {
  3687. if (type != Block.Type.LAMBDA && block.getProcObject() != null) return block.getProcObject();
  3688. RubyProc proc = RubyProc.newProc(this, type);
  3689. proc.initialize(getCurrentContext(), block);
  3690. return proc;
  3691. }
  3692. public RubyBinding newBinding() {
  3693. return RubyBinding.newBinding(this);
  3694. }
  3695. public RubyBinding newBinding(Binding binding) {
  3696. return RubyBinding.newBinding(this, binding);
  3697. }
  3698. public RubyString newString() {
  3699. return RubyString.newString(this, new ByteList());
  3700. }
  3701. public RubyString newString(String string) {
  3702. return RubyString.newString(this, string);
  3703. }
  3704. public RubyString newString(ByteList byteList) {
  3705. return RubyString.newString(this, byteList);
  3706. }
  3707. @Deprecated
  3708. public RubyString newStringShared(ByteList byteList) {
  3709. return RubyString.newStringShared(this, byteList);
  3710. }
  3711. public RubySymbol newSymbol(String name) {
  3712. return symbolTable.getSymbol(name);
  3713. }
  3714. /**
  3715. * Faster than {@link #newSymbol(String)} if you already have an interned
  3716. * name String. Don't intern your string just to call this version - the
  3717. * overhead of interning will more than wipe out any benefit from the faster
  3718. * lookup.
  3719. *
  3720. * @param internedName the symbol name, <em>must</em> be interned! if in
  3721. * doubt, call {@link #newSymbol(String)} instead.
  3722. * @return the symbol for name
  3723. */
  3724. public RubySymbol fastNewSymbol(String internedName) {
  3725. assert internedName == internedName.intern() : internedName + " is not interned";
  3726. return symbolTable.fastGetSymbol(internedName);
  3727. }
  3728. public RubyTime newTime(long milliseconds) {
  3729. return RubyTime.newTime(this, milliseconds);
  3730. }
  3731. public RaiseException newRuntimeError(String message) {
  3732. return newRaiseException(getRuntimeError(), message);
  3733. }
  3734. public RaiseException newArgumentError(String message) {
  3735. return newRaiseException(getArgumentError(), message);
  3736. }
  3737. public RaiseException newArgumentError(int got, int expected) {
  3738. return newRaiseException(getArgumentError(), "wrong # of arguments(" + got + " for " + expected + ")");
  3739. }
  3740. public RaiseException newErrnoEBADFError() {
  3741. return newRaiseException(getErrno().fastGetClass("EBADF"), "Bad file descriptor");
  3742. }
  3743. public RaiseException newErrnoENOPROTOOPTError() {
  3744. return newRaiseException(getErrno().fastGetClass("ENOPROTOOPT"), "Protocol not available");
  3745. }
  3746. public RaiseException newErrnoEPIPEError() {
  3747. return newRaiseException(getErrno().fastGetClass("EPIPE"), "Broken pipe");
  3748. }
  3749. public RaiseException newErrnoECONNREFUSEDError() {
  3750. return newRaiseException(getErrno().fastGetClass("ECONNREFUSED"), "Connection refused");
  3751. }
  3752. public RaiseException newErrnoECONNRESETError() {
  3753. return newRaiseException(getErrno().fastGetClass("ECONNRESET"), "Connection reset by peer");
  3754. }
  3755. public RaiseException newErrnoEADDRINUSEError() {
  3756. return newRaiseException(getErrno().fastGetClass("EADDRINUSE"), "Address in use");
  3757. }
  3758. public RaiseException newErrnoEINVALError() {
  3759. return newRaiseException(getErrno().fastGetClass("EINVAL"), "Invalid file");
  3760. }
  3761. public RaiseException newErrnoENOENTError() {
  3762. return newRaiseException(getErrno().fastGetClass("ENOENT"), "File not found");
  3763. }
  3764. public RaiseException newErrnoEACCESError(String message) {
  3765. return newRaiseException(getErrno().fastGetClass("EACCES"), message);
  3766. }
  3767. public RaiseException newErrnoEAGAINError(String message) {
  3768. return newRaiseException(getErrno().fastGetClass("EAGAIN"), message);
  3769. }
  3770. public RaiseException newErrnoEISDirError() {
  3771. return newRaiseException(getErrno().fastGetClass("EISDIR"), "Is a directory");
  3772. }
  3773. public RaiseException newErrnoESPIPEError() {
  3774. return newRaiseException(getErrno().fastGetClass("ESPIPE"), "Illegal seek");
  3775. }
  3776. public RaiseException newErrnoEBADFError(String message) {
  3777. return newRaiseException(getErrno().fastGetClass("EBADF"), message);
  3778. }
  3779. public RaiseException newErrnoEINVALError(String message) {
  3780. return newRaiseException(getErrno().fastGetClass("EINVAL"), message);
  3781. }
  3782. public RaiseException newErrnoENOTDIRError(String message) {
  3783. return newRaiseException(getErrno().fastGetClass("ENOTDIR"), message);
  3784. }
  3785. public RaiseException newErrnoENOTSOCKError(String message) {
  3786. return newRaiseException(getErrno().fastGetClass("ENOTSOCK"), message);
  3787. }
  3788. public RaiseException newErrnoENOENTError(String message) {
  3789. return newRaiseException(getErrno().fastGetClass("ENOENT"), message);
  3790. }
  3791. public RaiseException newErrnoESPIPEError(String message) {
  3792. return newRaiseException(getErrno().fastGetClass("ESPIPE"), message);
  3793. }
  3794. public RaiseException newErrnoEEXISTError(String message) {
  3795. return newRaiseException(getErrno().fastGetClass("EEXIST"), message);
  3796. }
  3797. public RaiseException newErrnoEDOMError(String message) {
  3798. return newRaiseException(getErrno().fastGetClass("EDOM"), "Domain error - " + message);
  3799. }
  3800. public RaiseException newErrnoECHILDError() {
  3801. return newRaiseException(getErrno().fastGetClass("ECHILD"), "No child processes");
  3802. }
  3803. public RaiseException newIndexError(String message) {
  3804. return newRaiseException(getIndexError(), message);
  3805. }
  3806. public RaiseException newSecurityError(String message) {
  3807. return newRaiseException(getSecurityError(), message);
  3808. }
  3809. public RaiseException newSystemCallError(String message) {
  3810. return newRaiseException(getSystemCallError(), message);
  3811. }
  3812. public RaiseException newTypeError(String message) {
  3813. return newRaiseException(getTypeError(), message);
  3814. }
  3815. public RaiseException newThreadError(String message) {
  3816. return newRaiseException(getThreadError(), message);
  3817. }
  3818. public RaiseException newConcurrencyError(String message) {
  3819. return newRaiseException(getConcurrencyError(), message);
  3820. }
  3821. public RaiseException newSyntaxError(String message) {
  3822. return newRaiseException(getSyntaxError(), message);
  3823. }
  3824. public RaiseException newRegexpError(String message) {
  3825. return newRaiseException(getRegexpError(), message);
  3826. }
  3827. public RaiseException newRangeError(String message) {
  3828. return newRaiseException(getRangeError(), message);
  3829. }
  3830. public RaiseException newNotImplementedError(String message) {
  3831. return newRaiseException(getNotImplementedError(), message);
  3832. }
  3833. public RaiseException newInvalidEncoding(String message) {
  3834. return newRaiseException(fastGetClass("Iconv").fastGetClass("InvalidEncoding"), message);
  3835. }
  3836. public RaiseException newNoMethodError(String message, String name, IRubyObject args) {
  3837. return new RaiseException(new RubyNoMethodError(this, getNoMethodError(), message, name, args), true);
  3838. }
  3839. public RaiseException newNameError(String message, String name) {
  3840. return newNameError(message, name, null);
  3841. }
  3842. public RaiseException newNameError(String message, String name, Throwable origException) {
  3843. return newNameError(message, name, origException, true);
  3844. }
  3845. public RaiseException newNameError(String message, String name, Throwable origException, boolean printWhenVerbose) {
  3846. if (printWhenVerbose && origException != null && this.getVerbose().isTrue()) {
  3847. origException.printStackTrace(getErrorStream());
  3848. }
  3849. return new RaiseException(new RubyNameError(
  3850. this, getNameError(), message, name), true);
  3851. }
  3852. public RaiseException newLocalJumpError(String reason, IRubyObject exitValue, String message) {
  3853. return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), message, reason, exitValue), true);
  3854. }
  3855. public RaiseException newRedoLocalJumpError() {
  3856. return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), "unexpected redo", "redo", getNil()), true);
  3857. }
  3858. public RaiseException newLoadError(String message) {
  3859. return newRaiseException(getLoadError(), message);
  3860. }
  3861. public RaiseException newFrozenError(String objectType) {
  3862. // TODO: Should frozen error have its own distinct class? If not should more share?
  3863. return newRaiseException(getTypeError(), "can't modify frozen " + objectType);
  3864. }
  3865. public RaiseException newSystemStackError(String message) {
  3866. return newRaiseException(getSystemStackError(), message);
  3867. }
  3868. public RaiseException newSystemExit(int status) {
  3869. return new RaiseException(RubySystemExit.newInstance(this, status));
  3870. }
  3871. public RaiseException newIOError(String message) {
  3872. return newRaiseException(getIOError(), message);
  3873. }
  3874. public RaiseException newStandardError(String message) {
  3875. return newRaiseException(getStandardError(), message);
  3876. }
  3877. public RaiseException newIOErrorFromException(IOException ioe) {
  3878. // TODO: this is kinda gross
  3879. if(ioe.getMessage() != null) {
  3880. if (ioe.getMessage().equals("Broken pipe")) {
  3881. throw newErrnoEPIPEError();
  3882. } else if (ioe.getMessage().equals("Connection reset by peer")) {
  3883. throw newErrnoECONNRESETError();
  3884. }
  3885. return newRaiseException(getIOError(), ioe.getMessage());
  3886. } else {
  3887. return newRaiseException(getIOError(), "IO Error");
  3888. }
  3889. }
  3890. public RaiseException newTypeError(IRubyObject receivedObject, RubyClass expectedType) {
  3891. return newRaiseException(getTypeError(), "wrong argument type " +
  3892. receivedObject.getMetaClass().getRealClass() + " (expected " + expectedType + ")");
  3893. }
  3894. public RaiseException newEOFError() {
  3895. return newRaiseException(getEOFError(), "End of file reached");
  3896. }
  3897. public RaiseException newEOFError(String message) {
  3898. return newRaiseException(getEOFError(), message);
  3899. }
  3900. public RaiseException newZeroDivisionError() {
  3901. return newRaiseException(getZeroDivisionError(), "divided by 0");
  3902. }
  3903. public RaiseException newFloatDomainError(String message){
  3904. return newRaiseException(getFloatDomainError(), message);
  3905. }
  3906. /**
  3907. * @param exceptionClass
  3908. * @param message
  3909. * @return
  3910. */
  3911. private RaiseException newRaiseException(RubyClass exceptionClass, String message) {
  3912. RaiseException re = new RaiseException(this, exceptionClass, message, true);
  3913. return re;
  3914. }
  3915. public RubySymbol.SymbolTable getSymbolTable() {
  3916. return symbolTable;
  3917. }
  3918. public void setStackTraces(int stackTraces) {
  3919. this.stackTraces = stackTraces;
  3920. }
  3921. public int getStackTraces() {
  3922. return stackTraces;
  3923. }
  3924. public void setRandomSeed(long randomSeed) {
  3925. this.randomSeed = randomSeed;
  3926. }
  3927. public long getRandomSeed() {
  3928. return randomSeed;
  3929. }
  3930. public Random getRandom() {
  3931. return random;
  3932. }
  3933. public ObjectSpace getObjectSpace() {
  3934. return objectSpace;
  3935. }
  3936. public Map<Integer, WeakReference<ChannelDescriptor>> getDescriptors() {
  3937. return descriptors;
  3938. }
  3939. public long incrementRandomSeedSequence() {
  3940. return randomSeedSequence++;
  3941. }
  3942. public InputStream getIn() {
  3943. return in;
  3944. }
  3945. public PrintStream getOut() {
  3946. return out;
  3947. }
  3948. public PrintStream getErr() {
  3949. return err;
  3950. }
  3951. public boolean isGlobalAbortOnExceptionEnabled() {
  3952. return globalAbortOnExceptionEnabled;
  3953. }
  3954. public void setGlobalAbortOnExceptionEnabled(boolean enable) {
  3955. globalAbortOnExceptionEnabled = enable;
  3956. }
  3957. public boolean isDoNotReverseLookupEnabled() {
  3958. return doNotReverseLookupEnabled;
  3959. }
  3960. public void setDoNotReverseLookupEnabled(boolean b) {
  3961. doNotReverseLookupEnabled = b;
  3962. }
  3963. private ThreadLocal<Map<Object, Object>> inspect = new ThreadLocal<Map<Object, Object>>();
  3964. public void registerInspecting(Object obj) {
  3965. Map<Object, Object> val = inspect.get();
  3966. if (val == null) inspect.set(val = new IdentityHashMap<Object, Object>());
  3967. val.put(obj, null);
  3968. }
  3969. public boolean isInspecting(Object obj) {
  3970. Map<Object, Object> val = inspect.get();
  3971. return val == null ? false : val.containsKey(obj);
  3972. }
  3973. public void unregisterInspecting(Object obj) {
  3974. Map<Object, Object> val = inspect.get();
  3975. if (val != null ) val.remove(obj);
  3976. }
  3977. public boolean isObjectSpaceEnabled() {
  3978. return objectSpaceEnabled;
  3979. }
  3980. // The method is intentionally not public, since it typically should
  3981. // not be used outside of the core.
  3982. /* package-private */ void setObjectSpaceEnabled(boolean objectSpaceEnabled) {
  3983. this.objectSpaceEnabled = objectSpaceEnabled;
  3984. }
  3985. public long getStartTime() {
  3986. return startTime;
  3987. }
  3988. public Profile getProfile() {
  3989. return profile;
  3990. }
  3991. public String getJRubyHome() {
  3992. return config.getJRubyHome();
  3993. }
  3994. public void setJRubyHome(String home) {
  3995. config.setJRubyHome(home);
  3996. }
  3997. public RubyInstanceConfig getInstanceConfig() {
  3998. return config;
  3999. }
  4000. /** GET_VM_STATE_VERSION */
  4001. public long getGlobalState() {
  4002. synchronized(this) {
  4003. return globalState;
  4004. }
  4005. }
  4006. /** INC_VM_STATE_VERSION */
  4007. public void incGlobalState() {
  4008. synchronized(this) {
  4009. globalState = (globalState+1) & 0x8fffffff;
  4010. }
  4011. }
  4012. public static boolean isSecurityRestricted() {
  4013. return securityRestricted;
  4014. }
  4015. public static void setSecurityRestricted(boolean restricted) {
  4016. securityRestricted = restricted;
  4017. }
  4018. public POSIX getPosix() {
  4019. return posix;
  4020. }
  4021. public void setRecordSeparatorVar(GlobalVariable recordSeparatorVar) {
  4022. this.recordSeparatorVar = recordSeparatorVar;
  4023. }
  4024. public GlobalVariable getRecordSeparatorVar() {
  4025. return recordSeparatorVar;
  4026. }
  4027. public Set<Script> getJittedMethods() {
  4028. return jittedMethods;
  4029. }
  4030. public ExecutorService getExecutor() {
  4031. return executor;
  4032. }
  4033. public Map<String, DateTimeZone> getLocalTimezoneCache() {
  4034. return localTimeZoneCache;
  4035. }
  4036. private final CacheMap cacheMap;
  4037. private final ThreadService threadService;
  4038. private Hashtable<Object, Object> runtimeInformation;
  4039. private POSIX posix;
  4040. private int stackTraces = 0;
  4041. private ObjectSpace objectSpace = new ObjectSpace();
  4042. private final RubySymbol.SymbolTable symbolTable = new RubySymbol.SymbolTable(this);
  4043. private Map<Integer, WeakReference<ChannelDescriptor>> descriptors = new ConcurrentHashMap<Integer, WeakReference<ChannelDescriptor>>();
  4044. private long randomSeed = 0;
  4045. private long randomSeedSequence = 0;
  4046. private Random random = new Random();
  4047. private List<EventHook> eventHooks = new Vector<EventHook>();
  4048. private boolean hasEventHooks;
  4049. private boolean globalAbortOnExceptionEnabled = false;
  4050. private boolean doNotReverseLookupEnabled = false;
  4051. private volatile boolean objectSpaceEnabled;
  4052. private final Set<Script> jittedMethods = Collections.synchronizedSet(new WeakHashSet<Script>());
  4053. private static ThreadLocal<Ruby> currentRuntime = new ThreadLocal<Ruby>();
  4054. private long globalState = 1;
  4055. private int safeLevel = -1;
  4056. // Default objects
  4057. private IRubyObject topSelf;
  4058. private RubyNil nilObject;
  4059. private RubyBoolean trueObject;
  4060. private RubyBoolean falseObject;
  4061. public final RubyFixnum[] fixnumCache = new RubyFixnum[256];
  4062. private IRubyObject verbose;
  4063. private IRubyObject debug;
  4064. private RubyThreadGroup defaultThreadGroup;
  4065. /**
  4066. * All the core classes we keep hard references to. These are here largely
  4067. * so that if someone redefines String or Array we won't start blowing up
  4068. * creating strings and arrays internally. They also provide much faster
  4069. * access than going through normal hash lookup on the Object class.
  4070. */
  4071. private RubyClass
  4072. objectClass, moduleClass, classClass, nilClass, trueClass,
  4073. falseClass, numericClass, floatClass, integerClass, fixnumClass,
  4074. complexClass, rationalClass, enumeratorClass,
  4075. arrayClass, hashClass, rangeClass, stringClass, symbolClass,
  4076. procClass, bindingClass, methodClass, unboundMethodClass,
  4077. matchDataClass, regexpClass, timeClass, bignumClass, dirClass,
  4078. fileClass, fileStatClass, ioClass, threadClass, threadGroupClass,
  4079. continuationClass, structClass, tmsStruct, passwdStruct,
  4080. groupStruct, procStatusClass, exceptionClass, runtimeError, ioError,
  4081. scriptError, nameError, nameErrorMessage, noMethodError, signalException,
  4082. rangeError, dummyClass, systemExit, localJumpError, nativeException,
  4083. systemCallError, fatal, interrupt, typeError, argumentError, indexError,
  4084. syntaxError, standardError, loadError, notImplementedError, securityError, noMemoryError,
  4085. regexpError, eofError, threadError, concurrencyError, systemStackError, zeroDivisionError, floatDomainError;
  4086. /**
  4087. * All the core modules we keep direct references to, for quick access and
  4088. * to ensure they remain available.
  4089. */
  4090. private RubyModule
  4091. kernelModule, comparableModule, enumerableModule, mathModule,
  4092. marshalModule, etcModule, fileTestModule, gcModule,
  4093. objectSpaceModule, processModule, procUIDModule, procGIDModule,
  4094. procSysModule, precisionModule, errnoModule;
  4095. // record separator var, to speed up io ops that use it
  4096. private GlobalVariable recordSeparatorVar;
  4097. // former java.lang.System concepts now internalized for MVM
  4098. private String currentDirectory;
  4099. private long startTime = System.currentTimeMillis();
  4100. private RubyInstanceConfig config;
  4101. private InputStream in;
  4102. private PrintStream out;
  4103. private PrintStream err;
  4104. // Java support
  4105. private JavaSupport javaSupport;
  4106. private JRubyClassLoader jrubyClassLoader;
  4107. // Management/monitoring
  4108. private BeanManager beanManager;
  4109. // Compilation
  4110. private final JITCompiler jitCompiler;
  4111. // Note: this field and the following static initializer
  4112. // must be located be in this order!
  4113. private volatile static boolean securityRestricted = false;
  4114. static {
  4115. if (SafePropertyAccessor.isSecurityProtected("jruby.reflection")) {
  4116. // can't read non-standard properties
  4117. securityRestricted = true;
  4118. } else {
  4119. SecurityManager sm = System.getSecurityManager();
  4120. if (sm != null) {
  4121. try {
  4122. sm.checkCreateClassLoader();
  4123. } catch (SecurityException se) {
  4124. // can't create custom classloaders
  4125. securityRestricted = true;
  4126. }
  4127. }
  4128. }
  4129. }
  4130. private Parser parser = new Parser(this);
  4131. private LoadService loadService;
  4132. private GlobalVariables globalVariables = new GlobalVariables(this);
  4133. private RubyWarnings warnings = new RubyWarnings(this);
  4134. // Contains a list of all blocks (as Procs) that should be called when
  4135. // the runtime environment exits.
  4136. private Stack<RubyProc> atExitBlocks = new Stack<RubyProc>();
  4137. private Profile profile;
  4138. private KCode kcode = KCode.NONE;
  4139. // Atomic integers for symbol and method IDs
  4140. private AtomicInteger symbolLastId = new AtomicInteger(128);
  4141. private AtomicInteger moduleLastId = new AtomicInteger(0);
  4142. private Object respondToMethod;
  4143. private Object objectToYamlMethod;
  4144. private Map<String, DateTimeZone> localTimeZoneCache = new HashMap<String,DateTimeZone>();
  4145. /**
  4146. * A list of "external" finalizers (the ones, registered via ObjectSpace),
  4147. * weakly referenced, to be executed on tearDown.
  4148. */
  4149. private Map<Finalizable, Object> finalizers;
  4150. /**
  4151. * A list of JRuby-internal finalizers, weakly referenced,
  4152. * to be executed on tearDown.
  4153. */
  4154. private Map<Finalizable, Object> internalFinalizers;
  4155. // mutex that controls modifications of user-defined finalizers
  4156. private final Object finalizersMutex = new Object();
  4157. // mutex that controls modifications of internal finalizers
  4158. private final Object internalFinalizersMutex = new Object();
  4159. // A thread pool to use for executing this runtime's Ruby threads
  4160. private ExecutorService executor;
  4161. }
  4162. /***** BEGIN LICENSE BLOCK *****
  4163. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  4164. *
  4165. * The contents of this file are subject to the Common Public
  4166. * License Version 1.0 (the "License"); you may not use this file
  4167. * except in compliance with the License. You may obtain a copy of
  4168. * the License at http://www.eclipse.org/legal/cpl-v10.html
  4169. *
  4170. * Software distributed under the License is distributed on an "AS
  4171. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  4172. * implied. See the License for the specific language governing
  4173. * rights and limitations under the License.
  4174. *
  4175. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  4176. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  4177. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  4178. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  4179. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  4180. *
  4181. * Alternatively, the contents of this file may be used under the terms of
  4182. * either of the GNU General Public License Version 2 or later (the "GPL"),
  4183. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  4184. * in which case the provisions of the GPL or the LGPL are applicable instead
  4185. * of those above. If you wish to allow use of your version of this file only
  4186. * under the terms of either the GPL or the LGPL, and not to allow others to
  4187. * use your version of this file under the terms of the CPL, indicate your
  4188. * decision by deleting the provisions above and replace them with the notice
  4189. * and other provisions required by the GPL or the LGPL. If you do not delete
  4190. * the provisions above, a recipient may use your version of this file under
  4191. * the terms of any one of the CPL, the GPL or the LGPL.
  4192. ***** END LICENSE BLOCK *****/
  4193. package org.jruby;
  4194. import org.jruby.anno.FrameField;
  4195. import org.jruby.anno.JRubyMethod;
  4196. import org.jruby.runtime.Block;
  4197. import org.jruby.runtime.ThreadContext;
  4198. import org.jruby.runtime.builtin.IRubyObject;
  4199. import org.jruby.util.ByteList;
  4200. public class RubyArgsFile {
  4201. private static final class ArgsFileData {
  4202. private final Ruby runtime;
  4203. public ArgsFileData(Ruby runtime) {
  4204. this.runtime = runtime;
  4205. }
  4206. public IRubyObject currentFile;
  4207. public int currentLineNumber;
  4208. public boolean startedProcessing = false;
  4209. public boolean finishedProcessing = false;
  4210. public boolean nextArgsFile(ThreadContext context) {
  4211. if (finishedProcessing) {
  4212. return false;
  4213. }
  4214. RubyArray args = (RubyArray)runtime.getGlobalVariables().get("$*");
  4215. if (args.getLength() == 0) {
  4216. if (!startedProcessing) {
  4217. currentFile = runtime.getGlobalVariables().get("$stdin");
  4218. ((RubyString) runtime.getGlobalVariables().get("$FILENAME")).setValue(new ByteList(new byte[]{'-'}));
  4219. currentLineNumber = 0;
  4220. startedProcessing = true;
  4221. return true;
  4222. } else {
  4223. finishedProcessing = true;
  4224. return false;
  4225. }
  4226. }
  4227. IRubyObject arg = args.shift();
  4228. RubyString filename = (RubyString)((RubyObject)arg).to_s();
  4229. ByteList filenameBytes = filename.getByteList();
  4230. ((RubyString) runtime.getGlobalVariables().get("$FILENAME")).setValue(filenameBytes);
  4231. if (filenameBytes.length() == 1 && filenameBytes.get(0) == '-') {
  4232. currentFile = runtime.getGlobalVariables().get("$stdin");
  4233. } else {
  4234. currentFile = RubyFile.open(context, runtime.getFile(),
  4235. new IRubyObject[] {filename.strDup(context.getRuntime())}, Block.NULL_BLOCK);
  4236. }
  4237. startedProcessing = true;
  4238. return true;
  4239. }
  4240. public static ArgsFileData getDataFrom(IRubyObject recv) {
  4241. ArgsFileData data = (ArgsFileData)recv.dataGetStruct();
  4242. if(data == null) {
  4243. data = new ArgsFileData(recv.getRuntime());
  4244. recv.dataWrapStruct(data);
  4245. }
  4246. return data;
  4247. }
  4248. }
  4249. public static void setCurrentLineNumber(IRubyObject recv, int newLineNumber) {
  4250. ArgsFileData.getDataFrom(recv).currentLineNumber = newLineNumber;
  4251. }
  4252. public static void initArgsFile(Ruby runtime) {
  4253. RubyObject argsFile = new RubyObject(runtime, runtime.getObject());
  4254. runtime.getEnumerable().extend_object(argsFile);
  4255. runtime.defineReadonlyVariable("$<", argsFile);
  4256. runtime.defineGlobalConstant("ARGF", argsFile);
  4257. RubyClass argfClass = argsFile.getMetaClass();
  4258. argfClass.defineAnnotatedMethods(RubyArgsFile.class);
  4259. runtime.defineReadonlyVariable("$FILENAME", runtime.newString("-"));
  4260. }
  4261. @JRubyMethod(name = {"fileno", "to_i"})
  4262. public static IRubyObject fileno(ThreadContext context, IRubyObject recv) {
  4263. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4264. if (data.currentFile == null && !data.nextArgsFile(context)) {
  4265. throw context.getRuntime().newArgumentError("no stream");
  4266. }
  4267. return ((RubyIO) data.currentFile).fileno(context);
  4268. }
  4269. @JRubyMethod(name = "to_io")
  4270. public static IRubyObject to_io(ThreadContext context, IRubyObject recv) {
  4271. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4272. if (data.currentFile == null && !data.nextArgsFile(context)) {
  4273. throw context.getRuntime().newArgumentError("no stream");
  4274. }
  4275. return data.currentFile;
  4276. }
  4277. public static IRubyObject internalGets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4278. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4279. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4280. return context.getRuntime().getNil();
  4281. }
  4282. IRubyObject line = data.currentFile.callMethod(context, "gets", args);
  4283. while (line instanceof RubyNil) {
  4284. data.currentFile.callMethod(context, "close");
  4285. if (!data.nextArgsFile(context)) {
  4286. data.currentFile = null;
  4287. return line;
  4288. }
  4289. line = data.currentFile.callMethod(context, "gets", args);
  4290. }
  4291. data.currentLineNumber++;
  4292. context.getRuntime().getGlobalVariables().set("$.", context.getRuntime().newFixnum(data.currentLineNumber));
  4293. return line;
  4294. }
  4295. // ARGF methods
  4296. /** Read a line.
  4297. *
  4298. */
  4299. @JRubyMethod(name = "gets", optional = 1, frame = true, writes = FrameField.LASTLINE)
  4300. public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4301. IRubyObject result = internalGets(context, recv, args);
  4302. if (!result.isNil()) {
  4303. context.getCurrentFrame().setLastLine(result);
  4304. }
  4305. return result;
  4306. }
  4307. /** Read a line.
  4308. *
  4309. */
  4310. @JRubyMethod(name = "readline", optional = 1, frame = true, writes = FrameField.LASTLINE)
  4311. public static IRubyObject readline(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4312. IRubyObject line = gets(context, recv, args);
  4313. if (line.isNil()) {
  4314. throw context.getRuntime().newEOFError();
  4315. }
  4316. return line;
  4317. }
  4318. @JRubyMethod(name = "readlines", optional = 1, frame = true)
  4319. public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4320. IRubyObject[] separatorArgument;
  4321. if (args.length > 0) {
  4322. if (!context.getRuntime().getNilClass().isInstance(args[0]) &&
  4323. !context.getRuntime().getString().isInstance(args[0])) {
  4324. throw context.getRuntime().newTypeError(args[0], context.getRuntime().getString());
  4325. }
  4326. separatorArgument = new IRubyObject[] { args[0] };
  4327. } else {
  4328. separatorArgument = IRubyObject.NULL_ARRAY;
  4329. }
  4330. RubyArray result = context.getRuntime().newArray();
  4331. IRubyObject line;
  4332. while (! (line = internalGets(context, recv, separatorArgument)).isNil()) {
  4333. result.append(line);
  4334. }
  4335. return result;
  4336. }
  4337. @JRubyMethod(name = "each_byte", frame = true)
  4338. public static IRubyObject each_byte(ThreadContext context, IRubyObject recv, Block block) {
  4339. IRubyObject bt;
  4340. while(!(bt = getc(context, recv)).isNil()) {
  4341. block.yield(context, bt);
  4342. }
  4343. return recv;
  4344. }
  4345. /** Invoke a block for each line.
  4346. *
  4347. */
  4348. @JRubyMethod(name = "each_line", alias = {"each"}, optional = 1, frame = true)
  4349. public static IRubyObject each_line(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  4350. IRubyObject nextLine = internalGets(context, recv, args);
  4351. while (!nextLine.isNil()) {
  4352. block.yield(context, nextLine);
  4353. nextLine = internalGets(context, recv, args);
  4354. }
  4355. return recv;
  4356. }
  4357. @JRubyMethod(name = "file")
  4358. public static IRubyObject file(ThreadContext context, IRubyObject recv) {
  4359. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4360. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4361. return context.getRuntime().getNil();
  4362. }
  4363. return data.currentFile;
  4364. }
  4365. @JRubyMethod(name = "skip")
  4366. public static IRubyObject skip(IRubyObject recv) {
  4367. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4368. data.currentFile = null;
  4369. return recv;
  4370. }
  4371. @JRubyMethod(name = "close")
  4372. public static IRubyObject close(ThreadContext context, IRubyObject recv) {
  4373. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4374. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4375. return recv;
  4376. }
  4377. data.currentFile = null;
  4378. data.currentLineNumber = 0;
  4379. return recv;
  4380. }
  4381. @JRubyMethod(name = "closed?")
  4382. public static IRubyObject closed_p(ThreadContext context, IRubyObject recv) {
  4383. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4384. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4385. return recv;
  4386. }
  4387. return ((RubyIO)data.currentFile).closed_p(context);
  4388. }
  4389. @JRubyMethod(name = "binmode")
  4390. public static IRubyObject binmode(ThreadContext context, IRubyObject recv) {
  4391. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4392. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4393. throw context.getRuntime().newArgumentError("no stream");
  4394. }
  4395. return ((RubyIO)data.currentFile).binmode();
  4396. }
  4397. @JRubyMethod(name = "lineno")
  4398. public static IRubyObject lineno(ThreadContext context, IRubyObject recv) {
  4399. return context.getRuntime().newFixnum(ArgsFileData.getDataFrom(recv).currentLineNumber);
  4400. }
  4401. @JRubyMethod(name = "tell", alias = {"pos"})
  4402. public static IRubyObject tell(ThreadContext context, IRubyObject recv) {
  4403. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4404. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4405. throw context.getRuntime().newArgumentError("no stream to tell");
  4406. }
  4407. return ((RubyIO)data.currentFile).pos(context);
  4408. }
  4409. @JRubyMethod(name = "rewind")
  4410. public static IRubyObject rewind(ThreadContext context, IRubyObject recv) {
  4411. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4412. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4413. throw context.getRuntime().newArgumentError("no stream to rewind");
  4414. }
  4415. return ((RubyIO)data.currentFile).rewind(context);
  4416. }
  4417. @JRubyMethod(name = {"eof", "eof?"})
  4418. public static IRubyObject eof(ThreadContext context, IRubyObject recv) {
  4419. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4420. if (data.currentFile == null && !data.nextArgsFile(context)) {
  4421. return context.getRuntime().getTrue();
  4422. }
  4423. return ((RubyIO) data.currentFile).eof_p(context);
  4424. }
  4425. @JRubyMethod(name = "pos=", required = 1)
  4426. public static IRubyObject set_pos(ThreadContext context, IRubyObject recv, IRubyObject offset) {
  4427. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4428. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4429. throw context.getRuntime().newArgumentError("no stream to set position");
  4430. }
  4431. return ((RubyIO)data.currentFile).pos_set(context, offset);
  4432. }
  4433. @JRubyMethod(name = "seek", required = 1, optional = 1)
  4434. public static IRubyObject seek(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4435. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4436. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4437. throw context.getRuntime().newArgumentError("no stream to seek");
  4438. }
  4439. return ((RubyIO)data.currentFile).seek(context, args);
  4440. }
  4441. @JRubyMethod(name = "lineno=", required = 1)
  4442. public static IRubyObject set_lineno(ThreadContext context, IRubyObject recv, IRubyObject line) {
  4443. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4444. data.currentLineNumber = RubyNumeric.fix2int(line);
  4445. return context.getRuntime().getNil();
  4446. }
  4447. @JRubyMethod(name = "readchar")
  4448. public static IRubyObject readchar(ThreadContext context, IRubyObject recv) {
  4449. IRubyObject c = getc(context, recv);
  4450. if(c.isNil()) throw context.getRuntime().newEOFError();
  4451. return c;
  4452. }
  4453. @JRubyMethod(name = "getc")
  4454. public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
  4455. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4456. IRubyObject bt;
  4457. while(true) {
  4458. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4459. return context.getRuntime().getNil();
  4460. }
  4461. if(!(data.currentFile instanceof RubyFile)) {
  4462. bt = data.currentFile.callMethod(context,"getc");
  4463. } else {
  4464. bt = ((RubyIO)data.currentFile).getc();
  4465. }
  4466. if(bt.isNil()) {
  4467. data.currentFile = null;
  4468. continue;
  4469. }
  4470. return bt;
  4471. }
  4472. }
  4473. @JRubyMethod(name = "read", optional = 2)
  4474. public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  4475. Ruby runtime = context.getRuntime();
  4476. ArgsFileData data = ArgsFileData.getDataFrom(recv);
  4477. IRubyObject tmp, str, length;
  4478. long len = 0;
  4479. if(args.length > 0) {
  4480. length = args[0];
  4481. if(args.length > 1) {
  4482. str = args[1];
  4483. } else {
  4484. str = runtime.getNil();
  4485. }
  4486. } else {
  4487. length = str = runtime.getNil();
  4488. }
  4489. if(!length.isNil()) {
  4490. len = RubyNumeric.num2long(length);
  4491. }
  4492. if(!str.isNil()) {
  4493. str = str.convertToString();
  4494. ((RubyString)str).modify();
  4495. ((RubyString)str).getByteList().length(0);
  4496. args[1] = runtime.getNil();
  4497. }
  4498. while(true) {
  4499. if(data.currentFile == null && !data.nextArgsFile(context)) {
  4500. return str;
  4501. }
  4502. if(!(data.currentFile instanceof RubyIO)) {
  4503. tmp = data.currentFile.callMethod(context, "read", args);
  4504. } else {
  4505. tmp = ((RubyIO)data.currentFile).read(args);
  4506. }
  4507. if(str.isNil()) {
  4508. str = tmp;
  4509. } else if(!tmp.isNil()) {
  4510. ((RubyString)str).append(tmp);
  4511. }
  4512. if(tmp.isNil() || length.isNil()) {
  4513. data.currentFile = null;
  4514. continue;
  4515. } else if(args.length >= 1) {
  4516. if(((RubyString)str).getByteList().length() < len) {
  4517. len -= ((RubyString)str).getByteList().length();
  4518. args[0] = runtime.newFixnum(len);
  4519. continue;
  4520. }
  4521. }
  4522. return str;
  4523. }
  4524. }
  4525. @JRubyMethod(name = "filename", alias = {"path"})
  4526. public static RubyString filename(ThreadContext context, IRubyObject recv) {
  4527. return (RubyString) context.getRuntime().getGlobalVariables().get("$FILENAME");
  4528. }
  4529. @JRubyMethod(name = "to_s")
  4530. public static IRubyObject to_s(IRubyObject recv) {
  4531. return recv.getRuntime().newString("ARGF");
  4532. }
  4533. }
  4534. /*
  4535. **** BEGIN LICENSE BLOCK *****
  4536. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  4537. *
  4538. * The contents of this file are subject to the Common Public
  4539. * License Version 1.0 (the "License"); you may not use this file
  4540. * except in compliance with the License. You may obtain a copy of
  4541. * the License at http://www.eclipse.org/legal/cpl-v10.html
  4542. *
  4543. * Software distributed under the License is distributed on an "AS
  4544. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  4545. * implied. See the License for the specific language governing
  4546. * rights and limitations under the License.
  4547. *
  4548. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  4549. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  4550. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  4551. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  4552. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  4553. * Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
  4554. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  4555. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  4556. * Copyright (C) 2006 Ola Bini <Ola.Bini@ki.se>
  4557. * Copyright (C) 2006 Daniel Steer <damian.steer@hp.com>
  4558. *
  4559. * Alternatively, the contents of this file may be used under the terms of
  4560. * either of the GNU General Public License Version 2 or later (the "GPL"),
  4561. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  4562. * in which case the provisions of the GPL or the LGPL are applicable instead
  4563. * of those above. If you wish to allow use of your version of this file only
  4564. * under the terms of either the GPL or the LGPL, and not to allow others to
  4565. * use your version of this file under the terms of the CPL, indicate your
  4566. * decision by deleting the provisions above and replace them with the notice
  4567. * and other provisions required by the GPL or the LGPL. If you do not delete
  4568. * the provisions above, a recipient may use your version of this file under
  4569. * the terms of any one of the CPL, the GPL or the LGPL.
  4570. ***** END LICENSE BLOCK *****/
  4571. package org.jruby;
  4572. import java.lang.reflect.Array;
  4573. import java.io.IOException;
  4574. import java.util.Arrays;
  4575. import java.util.Collection;
  4576. import java.util.Comparator;
  4577. import java.util.Iterator;
  4578. import java.util.List;
  4579. import java.util.ListIterator;
  4580. import org.jruby.anno.JRubyMethod;
  4581. import org.jruby.anno.JRubyClass;
  4582. import org.jruby.common.IRubyWarnings.ID;
  4583. import org.jruby.javasupport.JavaUtil;
  4584. import org.jruby.runtime.Arity;
  4585. import org.jruby.runtime.Block;
  4586. import org.jruby.runtime.ClassIndex;
  4587. import org.jruby.runtime.MethodIndex;
  4588. import org.jruby.runtime.ObjectAllocator;
  4589. import org.jruby.runtime.ThreadContext;
  4590. import org.jruby.runtime.Visibility;
  4591. import org.jruby.runtime.builtin.IRubyObject;
  4592. import org.jruby.runtime.marshal.MarshalStream;
  4593. import org.jruby.runtime.marshal.UnmarshalStream;
  4594. import org.jruby.util.ByteList;
  4595. import org.jruby.util.Pack;
  4596. /**
  4597. * The implementation of the built-in class Array in Ruby.
  4598. *
  4599. * Concurrency: no synchronization is required among readers, but
  4600. * all users must synchronize externally with writers.
  4601. *
  4602. */
  4603. @JRubyClass(name="Array")
  4604. public class RubyArray extends RubyObject implements List {
  4605. public static RubyClass createArrayClass(Ruby runtime) {
  4606. RubyClass arrayc = runtime.defineClass("Array", runtime.getObject(), ARRAY_ALLOCATOR);
  4607. runtime.setArray(arrayc);
  4608. arrayc.index = ClassIndex.ARRAY;
  4609. arrayc.kindOf = new RubyModule.KindOf() {
  4610. @Override
  4611. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  4612. return obj instanceof RubyArray;
  4613. }
  4614. };
  4615. arrayc.includeModule(runtime.getEnumerable());
  4616. arrayc.defineAnnotatedMethods(RubyArray.class);
  4617. return arrayc;
  4618. }
  4619. private static ObjectAllocator ARRAY_ALLOCATOR = new ObjectAllocator() {
  4620. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  4621. return new RubyArray(runtime, klass);
  4622. }
  4623. };
  4624. @Override
  4625. public int getNativeTypeIndex() {
  4626. return ClassIndex.ARRAY;
  4627. }
  4628. private final void concurrentModification() {
  4629. throw getRuntime().newConcurrencyError("Detected invalid array contents due to unsynchronized modifications with concurrent users");
  4630. }
  4631. /** rb_ary_s_create
  4632. *
  4633. */
  4634. @JRubyMethod(name = "[]", rest = true, frame = true, meta = true)
  4635. public static IRubyObject create(IRubyObject klass, IRubyObject[] args, Block block) {
  4636. RubyArray arr = (RubyArray) ((RubyClass) klass).allocate();
  4637. arr.callInit(IRubyObject.NULL_ARRAY, block);
  4638. if (args.length > 0) {
  4639. arr.alloc(args.length);
  4640. System.arraycopy(args, 0, arr.values, 0, args.length);
  4641. arr.realLength = args.length;
  4642. }
  4643. return arr;
  4644. }
  4645. /** rb_ary_new2
  4646. *
  4647. */
  4648. public static final RubyArray newArray(final Ruby runtime, final long len) {
  4649. return new RubyArray(runtime, len);
  4650. }
  4651. public static final RubyArray newArrayLight(final Ruby runtime, final long len) {
  4652. return new RubyArray(runtime, len, false);
  4653. }
  4654. /** rb_ary_new
  4655. *
  4656. */
  4657. public static final RubyArray newArray(final Ruby runtime) {
  4658. return new RubyArray(runtime, ARRAY_DEFAULT_SIZE);
  4659. }
  4660. /** rb_ary_new
  4661. *
  4662. */
  4663. public static final RubyArray newArrayLight(final Ruby runtime) {
  4664. /* Ruby arrays default to holding 16 elements, so we create an
  4665. * ArrayList of the same size if we're not told otherwise
  4666. */
  4667. RubyArray arr = new RubyArray(runtime, false);
  4668. arr.alloc(ARRAY_DEFAULT_SIZE);
  4669. return arr;
  4670. }
  4671. public static RubyArray newArray(Ruby runtime, IRubyObject obj) {
  4672. return new RubyArray(runtime, new IRubyObject[] { obj });
  4673. }
  4674. public static RubyArray newArrayLight(Ruby runtime, IRubyObject obj) {
  4675. return new RubyArray(runtime, new IRubyObject[] { obj }, false);
  4676. }
  4677. /** rb_assoc_new
  4678. *
  4679. */
  4680. public static RubyArray newArray(Ruby runtime, IRubyObject car, IRubyObject cdr) {
  4681. return new RubyArray(runtime, new IRubyObject[] { car, cdr });
  4682. }
  4683. public static RubyArray newEmptyArray(Ruby runtime) {
  4684. return new RubyArray(runtime, NULL_ARRAY);
  4685. }
  4686. /** rb_ary_new4, rb_ary_new3
  4687. *
  4688. */
  4689. public static RubyArray newArray(Ruby runtime, IRubyObject[] args) {
  4690. RubyArray arr = new RubyArray(runtime, args.length);
  4691. System.arraycopy(args, 0, arr.values, 0, args.length);
  4692. arr.realLength = args.length;
  4693. return arr;
  4694. }
  4695. public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args) {
  4696. return new RubyArray(runtime, args);
  4697. }
  4698. public static RubyArray newArrayNoCopy(Ruby runtime, IRubyObject[] args, int begin) {
  4699. return new RubyArray(runtime, args, begin);
  4700. }
  4701. public static RubyArray newArrayNoCopyLight(Ruby runtime, IRubyObject[] args) {
  4702. RubyArray arr = new RubyArray(runtime, false);
  4703. arr.values = args;
  4704. arr.realLength = args.length;
  4705. return arr;
  4706. }
  4707. public static RubyArray newArray(Ruby runtime, Collection collection) {
  4708. RubyArray arr = new RubyArray(runtime, collection.size());
  4709. collection.toArray(arr.values);
  4710. arr.realLength = arr.values.length;
  4711. return arr;
  4712. }
  4713. public static final int ARRAY_DEFAULT_SIZE = 16;
  4714. // volatile to ensure that initial nil-fill is visible to other threads
  4715. private volatile IRubyObject[] values;
  4716. private static final int TMPLOCK_ARR_F = 1 << 9;
  4717. private static final int TMPLOCK_OR_FROZEN_ARR_F = TMPLOCK_ARR_F | FROZEN_F;
  4718. private volatile boolean isShared = false;
  4719. private int begin = 0;
  4720. private int realLength = 0;
  4721. /*
  4722. * plain internal array assignment
  4723. */
  4724. private RubyArray(Ruby runtime, IRubyObject[] vals) {
  4725. super(runtime, runtime.getArray());
  4726. values = vals;
  4727. realLength = vals.length;
  4728. }
  4729. /*
  4730. * plain internal array assignment
  4731. */
  4732. private RubyArray(Ruby runtime, IRubyObject[] vals, boolean objectSpace) {
  4733. super(runtime, runtime.getArray(), objectSpace);
  4734. values = vals;
  4735. realLength = vals.length;
  4736. }
  4737. /*
  4738. * plain internal array assignment
  4739. */
  4740. private RubyArray(Ruby runtime, IRubyObject[] vals, int begin) {
  4741. super(runtime, runtime.getArray());
  4742. this.values = vals;
  4743. this.begin = begin;
  4744. this.realLength = vals.length - begin;
  4745. this.isShared = true;
  4746. }
  4747. /* rb_ary_new2
  4748. * just allocates the internal array
  4749. */
  4750. private RubyArray(Ruby runtime, long length) {
  4751. super(runtime, runtime.getArray());
  4752. checkLength(length);
  4753. alloc((int) length);
  4754. }
  4755. private RubyArray(Ruby runtime, long length, boolean objectspace) {
  4756. super(runtime, runtime.getArray(), objectspace);
  4757. checkLength(length);
  4758. alloc((int)length);
  4759. }
  4760. /* rb_ary_new3, rb_ary_new4
  4761. * allocates the internal array of size length and copies the 'length' elements
  4762. */
  4763. public RubyArray(Ruby runtime, long length, IRubyObject[] vals) {
  4764. super(runtime, runtime.getArray());
  4765. checkLength(length);
  4766. int ilength = (int) length;
  4767. alloc(ilength);
  4768. if (ilength > 0 && vals.length > 0) System.arraycopy(vals, 0, values, 0, ilength);
  4769. realLength = ilength;
  4770. }
  4771. /* NEWOBJ and OBJSETUP equivalent
  4772. * fastest one, for shared arrays, optional objectspace
  4773. */
  4774. private RubyArray(Ruby runtime, boolean objectSpace) {
  4775. super(runtime, runtime.getArray(), objectSpace);
  4776. }
  4777. private RubyArray(Ruby runtime) {
  4778. super(runtime, runtime.getArray());
  4779. alloc(ARRAY_DEFAULT_SIZE);
  4780. }
  4781. public RubyArray(Ruby runtime, RubyClass klass) {
  4782. super(runtime, klass);
  4783. alloc(ARRAY_DEFAULT_SIZE);
  4784. }
  4785. /* Array constructors taking the MetaClass to fulfil MRI Array subclass behaviour
  4786. *
  4787. */
  4788. private RubyArray(Ruby runtime, RubyClass klass, int length) {
  4789. super(runtime, klass);
  4790. alloc(length);
  4791. }
  4792. private RubyArray(Ruby runtime, RubyClass klass, long length) {
  4793. super(runtime, klass);
  4794. checkLength(length);
  4795. alloc((int)length);
  4796. }
  4797. private RubyArray(Ruby runtime, RubyClass klass, long length, boolean objectspace) {
  4798. super(runtime, klass, objectspace);
  4799. checkLength(length);
  4800. alloc((int)length);
  4801. }
  4802. private RubyArray(Ruby runtime, RubyClass klass, boolean objectSpace) {
  4803. super(runtime, klass, objectSpace);
  4804. }
  4805. private RubyArray(Ruby runtime, RubyClass klass, RubyArray original) {
  4806. super(runtime, klass);
  4807. realLength = original.realLength;
  4808. alloc(realLength);
  4809. try {
  4810. System.arraycopy(original.values, original.begin, values, 0, realLength);
  4811. } catch (ArrayIndexOutOfBoundsException e) {
  4812. concurrentModification();
  4813. }
  4814. }
  4815. private final IRubyObject[] reserve(int length) {
  4816. final IRubyObject[] arr = new IRubyObject[length];
  4817. Arrays.fill(arr, getRuntime().getNil());
  4818. return arr;
  4819. }
  4820. private final void alloc(int length) {
  4821. final IRubyObject[] newValues = new IRubyObject[length];
  4822. Arrays.fill(newValues, getRuntime().getNil());
  4823. values = newValues;
  4824. }
  4825. private final void realloc(int newLength) {
  4826. IRubyObject[] reallocated = new IRubyObject[newLength];
  4827. Arrays.fill(reallocated, getRuntime().getNil());
  4828. try {
  4829. System.arraycopy(values, 0, reallocated, 0, newLength > realLength ? realLength : newLength);
  4830. } catch (ArrayIndexOutOfBoundsException e) {
  4831. concurrentModification();
  4832. }
  4833. values = reallocated;
  4834. }
  4835. private final void checkLength(long length) {
  4836. if (length < 0) {
  4837. throw getRuntime().newArgumentError("negative array size (or size too big)");
  4838. }
  4839. if (length >= Integer.MAX_VALUE) {
  4840. throw getRuntime().newArgumentError("array size too big");
  4841. }
  4842. }
  4843. /** Getter for property list.
  4844. * @return Value of property list.
  4845. */
  4846. public List getList() {
  4847. return Arrays.asList(toJavaArray());
  4848. }
  4849. public int getLength() {
  4850. return realLength;
  4851. }
  4852. public IRubyObject[] toJavaArray() {
  4853. IRubyObject[] copy = reserve(realLength);
  4854. try {
  4855. System.arraycopy(values, begin, copy, 0, realLength);
  4856. } catch (ArrayIndexOutOfBoundsException e) {
  4857. concurrentModification();
  4858. }
  4859. return copy;
  4860. }
  4861. public IRubyObject[] toJavaArrayUnsafe() {
  4862. return !isShared ? values : toJavaArray();
  4863. }
  4864. public IRubyObject[] toJavaArrayMaybeUnsafe() {
  4865. return (!isShared && begin == 0 && values.length == realLength) ? values : toJavaArray();
  4866. }
  4867. /** rb_ary_make_shared
  4868. *
  4869. */
  4870. private final RubyArray makeShared(int beg, int len, RubyClass klass) {
  4871. return makeShared(beg, len, klass, klass.getRuntime().isObjectSpaceEnabled());
  4872. }
  4873. /** rb_ary_make_shared
  4874. *
  4875. */
  4876. private final RubyArray makeShared(int beg, int len, RubyClass klass, boolean objectSpace) {
  4877. RubyArray sharedArray = new RubyArray(getRuntime(), klass, objectSpace);
  4878. isShared = true;
  4879. sharedArray.values = values;
  4880. sharedArray.isShared = true;
  4881. sharedArray.begin = beg;
  4882. sharedArray.realLength = len;
  4883. return sharedArray;
  4884. }
  4885. /** rb_ary_modify_check
  4886. *
  4887. */
  4888. private final void modifyCheck() {
  4889. if ((flags & TMPLOCK_OR_FROZEN_ARR_F) != 0) {
  4890. if ((flags & FROZEN_F) != 0) throw getRuntime().newFrozenError("array");
  4891. if ((flags & TMPLOCK_ARR_F) != 0) throw getRuntime().newTypeError("can't modify array during iteration");
  4892. }
  4893. if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
  4894. throw getRuntime().newSecurityError("Insecure: can't modify array");
  4895. }
  4896. }
  4897. /** rb_ary_modify
  4898. *
  4899. */
  4900. private final void modify() {
  4901. modifyCheck();
  4902. if (isShared) {
  4903. IRubyObject[] vals = reserve(realLength);
  4904. isShared = false;
  4905. try {
  4906. System.arraycopy(values, begin, vals, 0, realLength);
  4907. } catch (ArrayIndexOutOfBoundsException e) {
  4908. concurrentModification();
  4909. }
  4910. begin = 0;
  4911. values = vals;
  4912. }
  4913. }
  4914. /* ================
  4915. * Instance Methods
  4916. * ================
  4917. */
  4918. /** rb_ary_initialize
  4919. *
  4920. */
  4921. @JRubyMethod(name = "initialize", required = 0, optional = 2, frame = true, visibility = Visibility.PRIVATE)
  4922. public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) {
  4923. int argc = args.length;
  4924. Ruby runtime = getRuntime();
  4925. if (argc == 0) {
  4926. modifyCheck();
  4927. realLength = 0;
  4928. if (block.isGiven()) runtime.getWarnings().warn(ID.BLOCK_UNUSED, "given block not used");
  4929. return this;
  4930. }
  4931. if (argc == 1 && !(args[0] instanceof RubyFixnum)) {
  4932. IRubyObject val = args[0].checkArrayType();
  4933. if (!val.isNil()) {
  4934. replace(val);
  4935. return this;
  4936. }
  4937. }
  4938. long len = RubyNumeric.num2long(args[0]);
  4939. if (len < 0) throw runtime.newArgumentError("negative array size");
  4940. if (len >= Integer.MAX_VALUE) throw runtime.newArgumentError("array size too big");
  4941. int ilen = (int) len;
  4942. modify();
  4943. if (ilen > values.length) values = reserve(ilen);
  4944. if (block.isGiven()) {
  4945. if (argc == 2) {
  4946. runtime.getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
  4947. }
  4948. for (int i = 0; i < ilen; i++) {
  4949. store(i, block.yield(context, new RubyFixnum(runtime, i)));
  4950. realLength = i + 1;
  4951. }
  4952. } else {
  4953. try {
  4954. Arrays.fill(values, 0, ilen, (argc == 2) ? args[1] : runtime.getNil());
  4955. } catch (ArrayIndexOutOfBoundsException e) {
  4956. concurrentModification();
  4957. }
  4958. realLength = ilen;
  4959. }
  4960. return this;
  4961. }
  4962. /** rb_ary_initialize_copy
  4963. *
  4964. */
  4965. @JRubyMethod(name = {"initialize_copy"}, required = 1, visibility=Visibility.PRIVATE)
  4966. @Override
  4967. public IRubyObject initialize_copy(IRubyObject orig) {
  4968. return this.replace(orig);
  4969. }
  4970. /** rb_ary_replace
  4971. *
  4972. */
  4973. @JRubyMethod(name = {"replace"}, required = 1)
  4974. public IRubyObject replace(IRubyObject orig) {
  4975. modifyCheck();
  4976. RubyArray origArr = orig.convertToArray();
  4977. if (this == orig) return this;
  4978. origArr.isShared = true;
  4979. isShared = true;
  4980. values = origArr.values;
  4981. realLength = origArr.realLength;
  4982. begin = origArr.begin;
  4983. return this;
  4984. }
  4985. /** rb_ary_to_s
  4986. *
  4987. */
  4988. @JRubyMethod(name = "to_s")
  4989. @Override
  4990. public IRubyObject to_s() {
  4991. if (realLength == 0) return RubyString.newEmptyString(getRuntime());
  4992. return join(getRuntime().getCurrentContext(), getRuntime().getGlobalVariables().get("$,"));
  4993. }
  4994. public boolean includes(ThreadContext context, IRubyObject item) {
  4995. int begin = this.begin;
  4996. for (int i = begin; i < begin + realLength; i++) {
  4997. final IRubyObject value;
  4998. try {
  4999. value = values[i];
  5000. } catch (ArrayIndexOutOfBoundsException e) {
  5001. concurrentModification();
  5002. continue;
  5003. }
  5004. if (equalInternal(context, value, item)) return true;
  5005. }
  5006. return false;
  5007. }
  5008. /** rb_ary_hash
  5009. *
  5010. */
  5011. @JRubyMethod(name = "hash")
  5012. public RubyFixnum hash(ThreadContext context) {
  5013. int h = realLength;
  5014. Ruby runtime = getRuntime();
  5015. int begin = this.begin;
  5016. for (int i = begin; i < begin + realLength; i++) {
  5017. h = (h << 1) | (h < 0 ? 1 : 0);
  5018. final IRubyObject value;
  5019. try {
  5020. value = values[i];
  5021. } catch (ArrayIndexOutOfBoundsException e) {
  5022. concurrentModification();
  5023. continue;
  5024. }
  5025. h ^= RubyNumeric.num2long(value.callMethod(context, MethodIndex.HASH, "hash"));
  5026. }
  5027. return runtime.newFixnum(h);
  5028. }
  5029. /** rb_ary_store
  5030. *
  5031. */
  5032. public final IRubyObject store(long index, IRubyObject value) {
  5033. if (index < 0) {
  5034. index += realLength;
  5035. if (index < 0) {
  5036. throw getRuntime().newIndexError("index " + (index - realLength) + " out of array");
  5037. }
  5038. }
  5039. modify();
  5040. if (index >= realLength) {
  5041. if (index >= values.length) {
  5042. long newLength = values.length >> 1;
  5043. if (newLength < ARRAY_DEFAULT_SIZE) newLength = ARRAY_DEFAULT_SIZE;
  5044. newLength += index;
  5045. if (index >= Integer.MAX_VALUE || newLength >= Integer.MAX_VALUE) {
  5046. throw getRuntime().newArgumentError("index too big");
  5047. }
  5048. realloc((int) newLength);
  5049. }
  5050. realLength = (int) index + 1;
  5051. }
  5052. try {
  5053. values[(int) index] = value;
  5054. } catch (ArrayIndexOutOfBoundsException e) {
  5055. concurrentModification();
  5056. }
  5057. return value;
  5058. }
  5059. /** rb_ary_elt
  5060. *
  5061. */
  5062. private final IRubyObject elt(long offset) {
  5063. if (offset < 0 || offset >= realLength) {
  5064. return getRuntime().getNil();
  5065. }
  5066. try {
  5067. return values[begin + (int)offset];
  5068. } catch (ArrayIndexOutOfBoundsException e) {
  5069. concurrentModification();
  5070. return getRuntime().getNil();
  5071. }
  5072. }
  5073. /** rb_ary_entry
  5074. *
  5075. */
  5076. public final IRubyObject entry(long offset) {
  5077. return (offset < 0 ) ? elt(offset + realLength) : elt(offset);
  5078. }
  5079. /** rb_ary_entry
  5080. *
  5081. */
  5082. public final IRubyObject entry(int offset) {
  5083. return (offset < 0 ) ? elt(offset + realLength) : elt(offset);
  5084. }
  5085. public final IRubyObject eltInternal(int offset) {
  5086. return values[begin + offset];
  5087. }
  5088. public final IRubyObject eltInternalSet(int offset, IRubyObject item) {
  5089. return values[begin + offset] = item;
  5090. }
  5091. /**
  5092. * Variable arity version for compatibility. Not bound to a Ruby method.
  5093. * @deprecated Use the versions with zero, one, or two args.
  5094. */
  5095. public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
  5096. switch (args.length) {
  5097. case 1:
  5098. return fetch(context, args[0], block);
  5099. case 2:
  5100. return fetch(context, args[0], args[1], block);
  5101. default:
  5102. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  5103. return null; // not reached
  5104. }
  5105. }
  5106. /** rb_ary_fetch
  5107. *
  5108. */
  5109. @JRubyMethod(name = "fetch", frame = true)
  5110. public IRubyObject fetch(ThreadContext context, IRubyObject arg0, Block block) {
  5111. long index = RubyNumeric.num2long(arg0);
  5112. if (index < 0) index += realLength;
  5113. if (index < 0 || index >= realLength) {
  5114. if (block.isGiven()) return block.yield(context, arg0);
  5115. throw getRuntime().newIndexError("index " + index + " out of array");
  5116. }
  5117. try {
  5118. return values[begin + (int) index];
  5119. } catch (ArrayIndexOutOfBoundsException e) {
  5120. concurrentModification();
  5121. return getRuntime().getNil();
  5122. }
  5123. }
  5124. /** rb_ary_fetch
  5125. *
  5126. */
  5127. @JRubyMethod(name = "fetch", frame = true)
  5128. public IRubyObject fetch(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  5129. if (block.isGiven()) getRuntime().getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
  5130. long index = RubyNumeric.num2long(arg0);
  5131. if (index < 0) index += realLength;
  5132. if (index < 0 || index >= realLength) {
  5133. if (block.isGiven()) return block.yield(context, arg0);
  5134. return arg1;
  5135. }
  5136. try {
  5137. return values[begin + (int) index];
  5138. } catch (ArrayIndexOutOfBoundsException e) {
  5139. concurrentModification();
  5140. return getRuntime().getNil();
  5141. }
  5142. }
  5143. /** rb_ary_to_ary
  5144. *
  5145. */
  5146. private static RubyArray aryToAry(IRubyObject obj) {
  5147. if (obj instanceof RubyArray) return (RubyArray) obj;
  5148. if (obj.respondsTo("to_ary")) return obj.convertToArray();
  5149. RubyArray arr = new RubyArray(obj.getRuntime(), false); // possibly should not in object space
  5150. arr.alloc(1);
  5151. arr.values[0] = obj;
  5152. arr.realLength = 1;
  5153. return arr;
  5154. }
  5155. /** rb_ary_splice
  5156. *
  5157. */
  5158. private final void splice(long beg, long len, IRubyObject rpl) {
  5159. if (len < 0) throw getRuntime().newIndexError("negative length (" + len + ")");
  5160. if (beg < 0) {
  5161. beg += realLength;
  5162. if (beg < 0) {
  5163. beg -= realLength;
  5164. throw getRuntime().newIndexError("index " + beg + " out of array");
  5165. }
  5166. }
  5167. final RubyArray rplArr;
  5168. final int rlen;
  5169. if (rpl == null || rpl.isNil()) {
  5170. rplArr = null;
  5171. rlen = 0;
  5172. } else {
  5173. rplArr = aryToAry(rpl);
  5174. rlen = rplArr.realLength;
  5175. }
  5176. modify();
  5177. if (beg >= realLength) {
  5178. len = beg + rlen;
  5179. if (len >= values.length) {
  5180. int tryNewLength = values.length + (values.length >> 1);
  5181. realloc(len > tryNewLength ? (int)len : tryNewLength);
  5182. }
  5183. realLength = (int) len;
  5184. } else {
  5185. if (beg + len > realLength) len = realLength - beg;
  5186. long alen = realLength + rlen - len;
  5187. if (alen >= values.length) {
  5188. int tryNewLength = values.length + (values.length >> 1);
  5189. realloc(alen > tryNewLength ? (int)alen : tryNewLength);
  5190. }
  5191. if (len != rlen) {
  5192. try {
  5193. System.arraycopy(values, (int) (beg + len), values, (int) beg + rlen, realLength - (int) (beg + len));
  5194. } catch (ArrayIndexOutOfBoundsException e) {
  5195. concurrentModification();
  5196. }
  5197. realLength = (int) alen;
  5198. }
  5199. }
  5200. if (rlen > 0) {
  5201. try {
  5202. System.arraycopy(rplArr.values, rplArr.begin, values, (int) beg, rlen);
  5203. } catch (ArrayIndexOutOfBoundsException e) {
  5204. concurrentModification();
  5205. }
  5206. }
  5207. }
  5208. /** rb_ary_splice
  5209. *
  5210. */
  5211. private final void spliceOne(long beg, long len, IRubyObject rpl) {
  5212. if (len < 0) throw getRuntime().newIndexError("negative length (" + len + ")");
  5213. if (beg < 0) {
  5214. beg += realLength;
  5215. if (beg < 0) {
  5216. beg -= realLength;
  5217. throw getRuntime().newIndexError("index " + beg + " out of array");
  5218. }
  5219. }
  5220. modify();
  5221. if (beg >= realLength) {
  5222. len = beg + 1;
  5223. if (len >= values.length) {
  5224. int tryNewLength = values.length + (values.length >> 1);
  5225. realloc(len > tryNewLength ? (int)len : tryNewLength);
  5226. }
  5227. realLength = (int) len;
  5228. } else {
  5229. if (beg + len > realLength) len = realLength - beg;
  5230. int alen = realLength + 1 - (int)len;
  5231. if (alen >= values.length) {
  5232. int tryNewLength = values.length + (values.length >> 1);
  5233. realloc(alen > tryNewLength ? alen : tryNewLength);
  5234. }
  5235. if (len != 1) {
  5236. try {
  5237. System.arraycopy(values, (int) (beg + len), values, (int) beg + 1, realLength - (int) (beg + len));
  5238. } catch (ArrayIndexOutOfBoundsException e) {
  5239. concurrentModification();
  5240. }
  5241. realLength = alen;
  5242. }
  5243. }
  5244. try {
  5245. values[(int)beg] = rpl;
  5246. } catch (ArrayIndexOutOfBoundsException e) {
  5247. concurrentModification();
  5248. }
  5249. }
  5250. @JRubyMethod
  5251. public IRubyObject insert() {
  5252. throw getRuntime().newArgumentError(0, 1);
  5253. }
  5254. /** rb_ary_insert
  5255. *
  5256. */
  5257. @JRubyMethod
  5258. public IRubyObject insert(IRubyObject arg) {
  5259. return this;
  5260. }
  5261. /** rb_ary_insert
  5262. *
  5263. */
  5264. @JRubyMethod
  5265. public IRubyObject insert(IRubyObject arg1, IRubyObject arg2) {
  5266. long pos = RubyNumeric.num2long(arg1);
  5267. if (pos == -1) pos = realLength;
  5268. if (pos < 0) pos++;
  5269. spliceOne(pos, 0, arg2); // rb_ary_new4
  5270. return this;
  5271. }
  5272. /** rb_ary_insert
  5273. *
  5274. */
  5275. @JRubyMethod(name = "insert", required = 1, rest = true)
  5276. public IRubyObject insert(IRubyObject[] args) {
  5277. if (args.length == 1) return this;
  5278. long pos = RubyNumeric.num2long(args[0]);
  5279. if (pos == -1) pos = realLength;
  5280. if (pos < 0) pos++;
  5281. RubyArray inserted = new RubyArray(getRuntime(), false);
  5282. inserted.values = args;
  5283. inserted.begin = 1;
  5284. inserted.realLength = args.length - 1;
  5285. splice(pos, 0, inserted); // rb_ary_new4
  5286. return this;
  5287. }
  5288. /** rb_ary_dup
  5289. *
  5290. */
  5291. public final RubyArray aryDup() {
  5292. RubyArray dup = new RubyArray(getRuntime(), getMetaClass(), this);
  5293. dup.flags |= flags & TAINTED_F; // from DUP_SETUP
  5294. // rb_copy_generic_ivar from DUP_SETUP here ...unlikely..
  5295. return dup;
  5296. }
  5297. /** rb_ary_transpose
  5298. *
  5299. */
  5300. @JRubyMethod(name = "transpose")
  5301. public RubyArray transpose() {
  5302. RubyArray tmp, result = null;
  5303. int alen = realLength;
  5304. if (alen == 0) return aryDup();
  5305. Ruby runtime = getRuntime();
  5306. int elen = -1;
  5307. int end = begin + alen;
  5308. for (int i = begin; i < end; i++) {
  5309. tmp = elt(i).convertToArray();
  5310. if (elen < 0) {
  5311. elen = tmp.realLength;
  5312. result = new RubyArray(runtime, elen);
  5313. for (int j = 0; j < elen; j++) {
  5314. result.store(j, new RubyArray(runtime, alen));
  5315. }
  5316. } else if (elen != tmp.realLength) {
  5317. throw runtime.newIndexError("element size differs (" + tmp.realLength
  5318. + " should be " + elen + ")");
  5319. }
  5320. for (int j = 0; j < elen; j++) {
  5321. ((RubyArray) result.elt(j)).store(i - begin, tmp.elt(j));
  5322. }
  5323. }
  5324. return result;
  5325. }
  5326. /** rb_values_at (internal)
  5327. *
  5328. */
  5329. private final IRubyObject values_at(long olen, IRubyObject[] args) {
  5330. RubyArray result = new RubyArray(getRuntime(), args.length);
  5331. for (int i = 0; i < args.length; i++) {
  5332. if (args[i] instanceof RubyFixnum) {
  5333. result.append(entry(((RubyFixnum)args[i]).getLongValue()));
  5334. continue;
  5335. }
  5336. long beglen[];
  5337. if (!(args[i] instanceof RubyRange)) {
  5338. } else if ((beglen = ((RubyRange) args[i]).begLen(olen, 0)) == null) {
  5339. continue;
  5340. } else {
  5341. int beg = (int) beglen[0];
  5342. int len = (int) beglen[1];
  5343. int end = begin + len;
  5344. for (int j = begin; j < end; j++) {
  5345. result.append(entry(j + beg));
  5346. }
  5347. continue;
  5348. }
  5349. result.append(entry(RubyNumeric.num2long(args[i])));
  5350. }
  5351. return result;
  5352. }
  5353. /** rb_values_at
  5354. *
  5355. */
  5356. @JRubyMethod(name = "values_at", rest = true)
  5357. public IRubyObject values_at(IRubyObject[] args) {
  5358. return values_at(realLength, args);
  5359. }
  5360. /** rb_ary_subseq
  5361. *
  5362. */
  5363. public IRubyObject subseq(long beg, long len) {
  5364. if (beg > realLength || beg < 0 || len < 0) return getRuntime().getNil();
  5365. if (beg + len > realLength) {
  5366. len = realLength - beg;
  5367. if (len < 0) len = 0;
  5368. }
  5369. if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0);
  5370. return makeShared(begin + (int) beg, (int) len, getMetaClass());
  5371. }
  5372. /** rb_ary_subseq
  5373. *
  5374. */
  5375. public IRubyObject subseqLight(long beg, long len) {
  5376. if (beg > realLength || beg < 0 || len < 0) return getRuntime().getNil();
  5377. if (beg + len > realLength) {
  5378. len = realLength - beg;
  5379. if (len < 0) len = 0;
  5380. }
  5381. if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0, false);
  5382. return makeShared(begin + (int) beg, (int) len, getMetaClass(), false);
  5383. }
  5384. /** rb_ary_length
  5385. *
  5386. */
  5387. @JRubyMethod(name = "length", alias = "size")
  5388. public RubyFixnum length() {
  5389. return getRuntime().newFixnum(realLength);
  5390. }
  5391. /** rb_ary_push - specialized rb_ary_store
  5392. *
  5393. */
  5394. @JRubyMethod(name = "<<", required = 1)
  5395. public RubyArray append(IRubyObject item) {
  5396. modify();
  5397. if (realLength == values.length) {
  5398. if (realLength == Integer.MAX_VALUE) throw getRuntime().newArgumentError("index too big");
  5399. long newLength = values.length + (values.length >> 1);
  5400. if ( newLength > Integer.MAX_VALUE ) {
  5401. newLength = Integer.MAX_VALUE;
  5402. }else if ( newLength < ARRAY_DEFAULT_SIZE ) {
  5403. newLength = ARRAY_DEFAULT_SIZE;
  5404. }
  5405. realloc((int) newLength);
  5406. }
  5407. try {
  5408. values[realLength++] = item;
  5409. } catch (ArrayIndexOutOfBoundsException e) {
  5410. concurrentModification();
  5411. }
  5412. return this;
  5413. }
  5414. /** rb_ary_push_m
  5415. * FIXME: Whis is this named "push_m"?
  5416. */
  5417. @JRubyMethod(name = "push", rest = true)
  5418. public RubyArray push_m(IRubyObject[] items) {
  5419. for (int i = 0; i < items.length; i++) {
  5420. append(items[i]);
  5421. }
  5422. return this;
  5423. }
  5424. /** rb_ary_pop
  5425. *
  5426. */
  5427. @JRubyMethod(name = "pop")
  5428. public IRubyObject pop() {
  5429. modifyCheck();
  5430. if (realLength == 0) return getRuntime().getNil();
  5431. if (isShared) {
  5432. try {
  5433. return values[begin + --realLength];
  5434. } catch (ArrayIndexOutOfBoundsException e) {
  5435. concurrentModification();
  5436. return getRuntime().getNil();
  5437. }
  5438. } else {
  5439. int index = begin + --realLength;
  5440. try {
  5441. final IRubyObject obj = values[index];
  5442. values[index] = getRuntime().getNil();
  5443. return obj;
  5444. } catch (ArrayIndexOutOfBoundsException e) {
  5445. concurrentModification();
  5446. return getRuntime().getNil();
  5447. }
  5448. }
  5449. }
  5450. /** rb_ary_shift
  5451. *
  5452. */
  5453. @JRubyMethod(name = "shift")
  5454. public IRubyObject shift() {
  5455. modify();
  5456. if (realLength == 0) return getRuntime().getNil();
  5457. final IRubyObject obj;
  5458. try {
  5459. obj = values[begin];
  5460. values[begin] = getRuntime().getNil();
  5461. } catch (ArrayIndexOutOfBoundsException e) {
  5462. concurrentModification();
  5463. return getRuntime().getNil();
  5464. }
  5465. isShared = true;
  5466. begin++;
  5467. realLength--;
  5468. return obj;
  5469. }
  5470. /** rb_ary_unshift
  5471. *
  5472. */
  5473. public RubyArray unshift(IRubyObject item) {
  5474. modify();
  5475. if (realLength == values.length) {
  5476. int newLength = values.length >> 1;
  5477. if (newLength < ARRAY_DEFAULT_SIZE) newLength = ARRAY_DEFAULT_SIZE;
  5478. newLength += values.length;
  5479. realloc(newLength);
  5480. }
  5481. try {
  5482. System.arraycopy(values, 0, values, 1, realLength);
  5483. } catch (ArrayIndexOutOfBoundsException e) {
  5484. concurrentModification();
  5485. }
  5486. realLength++;
  5487. values[0] = item;
  5488. return this;
  5489. }
  5490. /** rb_ary_unshift_m
  5491. *
  5492. */
  5493. @JRubyMethod(name = "unshift", rest = true)
  5494. public RubyArray unshift_m(IRubyObject[] items) {
  5495. long len = realLength;
  5496. if (items.length == 0) return this;
  5497. store(len + items.length - 1, getRuntime().getNil());
  5498. try {
  5499. // it's safe to use zeroes here since modified by store()
  5500. System.arraycopy(values, 0, values, items.length, (int) len);
  5501. System.arraycopy(items, 0, values, 0, items.length);
  5502. } catch (ArrayIndexOutOfBoundsException e) {
  5503. concurrentModification();
  5504. }
  5505. return this;
  5506. }
  5507. /** rb_ary_includes
  5508. *
  5509. */
  5510. @JRubyMethod(name = "include?", required = 1)
  5511. public RubyBoolean include_p(ThreadContext context, IRubyObject item) {
  5512. return context.getRuntime().newBoolean(includes(context, item));
  5513. }
  5514. /** rb_ary_frozen_p
  5515. *
  5516. */
  5517. @JRubyMethod(name = "frozen?")
  5518. @Override
  5519. public RubyBoolean frozen_p(ThreadContext context) {
  5520. return context.getRuntime().newBoolean(isFrozen() || (flags & TMPLOCK_ARR_F) != 0);
  5521. }
  5522. /**
  5523. * Variable arity version for compatibility. Not bound to a Ruby method.
  5524. * @deprecated Use the versions with zero, one, or two args.
  5525. */
  5526. public IRubyObject aref(IRubyObject[] args) {
  5527. switch (args.length) {
  5528. case 1:
  5529. return aref(args[0]);
  5530. case 2:
  5531. return aref(args[0], args[1]);
  5532. default:
  5533. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  5534. return null; // not reached
  5535. }
  5536. }
  5537. /** rb_ary_aref
  5538. */
  5539. @JRubyMethod(name = {"[]", "slice"})
  5540. public IRubyObject aref(IRubyObject arg0) {
  5541. if (arg0 instanceof RubyFixnum) return entry(((RubyFixnum)arg0).getLongValue());
  5542. if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
  5543. long[] beglen;
  5544. if (!(arg0 instanceof RubyRange)) {
  5545. } else if ((beglen = ((RubyRange) arg0).begLen(realLength, 0)) == null) {
  5546. return getRuntime().getNil();
  5547. } else {
  5548. return subseq(beglen[0], beglen[1]);
  5549. }
  5550. return entry(RubyNumeric.num2long(arg0));
  5551. }
  5552. /** rb_ary_aref
  5553. */
  5554. @JRubyMethod(name = {"[]", "slice"})
  5555. public IRubyObject aref(IRubyObject arg0, IRubyObject arg1) {
  5556. if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
  5557. long beg = RubyNumeric.num2long(arg0);
  5558. if (beg < 0) beg += realLength;
  5559. return subseq(beg, RubyNumeric.num2long(arg1));
  5560. }
  5561. /**
  5562. * Variable arity version for compatibility. Not bound to a Ruby method.
  5563. * @deprecated Use the versions with zero, one, or two args.
  5564. */
  5565. public IRubyObject aset(IRubyObject[] args) {
  5566. switch (args.length) {
  5567. case 2:
  5568. return aset(args[0], args[1]);
  5569. case 3:
  5570. return aset(args[0], args[1], args[2]);
  5571. default:
  5572. throw getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 2)");
  5573. }
  5574. }
  5575. /** rb_ary_aset
  5576. *
  5577. */
  5578. @JRubyMethod(name = "[]=")
  5579. public IRubyObject aset(IRubyObject arg0, IRubyObject arg1) {
  5580. if (arg0 instanceof RubyFixnum) {
  5581. store(((RubyFixnum)arg0).getLongValue(), arg1);
  5582. return arg1;
  5583. }
  5584. if (arg0 instanceof RubyRange) {
  5585. long[] beglen = ((RubyRange) arg0).begLen(realLength, 1);
  5586. splice(beglen[0], beglen[1], arg1);
  5587. return arg1;
  5588. }
  5589. if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
  5590. store(RubyNumeric.num2long(arg0), arg1);
  5591. return arg1;
  5592. }
  5593. /** rb_ary_aset
  5594. *
  5595. */
  5596. @JRubyMethod(name = "[]=")
  5597. public IRubyObject aset(IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  5598. if (arg0 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as array index");
  5599. if (arg1 instanceof RubySymbol) throw getRuntime().newTypeError("Symbol as subarray length");
  5600. splice(RubyNumeric.num2long(arg0), RubyNumeric.num2long(arg1), arg2);
  5601. return arg2;
  5602. }
  5603. /** rb_ary_at
  5604. *
  5605. */
  5606. @JRubyMethod(name = "at", required = 1)
  5607. public IRubyObject at(IRubyObject pos) {
  5608. return entry(RubyNumeric.num2long(pos));
  5609. }
  5610. /** rb_ary_concat
  5611. *
  5612. */
  5613. @JRubyMethod(name = "concat", required = 1)
  5614. public RubyArray concat(IRubyObject obj) {
  5615. RubyArray ary = obj.convertToArray();
  5616. if (ary.realLength > 0) splice(realLength, 0, ary);
  5617. return this;
  5618. }
  5619. /** inspect_ary
  5620. *
  5621. */
  5622. private IRubyObject inspectAry(ThreadContext context) {
  5623. ByteList buffer = new ByteList();
  5624. buffer.append('[');
  5625. boolean tainted = isTaint();
  5626. for (int i = 0; i < realLength; i++) {
  5627. if (i > 0) buffer.append(',').append(' ');
  5628. RubyString str = inspect(context, values[begin + i]);
  5629. if (str.isTaint()) tainted = true;
  5630. buffer.append(str.getByteList());
  5631. }
  5632. buffer.append(']');
  5633. RubyString str = getRuntime().newString(buffer);
  5634. if (tainted) str.setTaint(true);
  5635. return str;
  5636. }
  5637. /** rb_ary_inspect
  5638. *
  5639. */
  5640. @JRubyMethod(name = "inspect")
  5641. @Override
  5642. public IRubyObject inspect() {
  5643. if (realLength == 0) return getRuntime().newString("[]");
  5644. if (getRuntime().isInspecting(this)) return getRuntime().newString("[...]");
  5645. try {
  5646. getRuntime().registerInspecting(this);
  5647. return inspectAry(getRuntime().getCurrentContext());
  5648. } finally {
  5649. getRuntime().unregisterInspecting(this);
  5650. }
  5651. }
  5652. /**
  5653. * Variable arity version for compatibility. Not bound to a Ruby method.
  5654. * @deprecated Use the versions with zero, one, or two args.
  5655. */
  5656. public IRubyObject first(IRubyObject[] args) {
  5657. switch (args.length) {
  5658. case 0:
  5659. return first();
  5660. case 1:
  5661. return first(args[0]);
  5662. default:
  5663. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  5664. return null; // not reached
  5665. }
  5666. }
  5667. /** rb_ary_first
  5668. *
  5669. */
  5670. @JRubyMethod(name = "first")
  5671. public IRubyObject first() {
  5672. if (realLength == 0) return getRuntime().getNil();
  5673. return values[begin];
  5674. }
  5675. /** rb_ary_first
  5676. *
  5677. */
  5678. @JRubyMethod(name = "first")
  5679. public IRubyObject first(IRubyObject arg0) {
  5680. long n = RubyNumeric.num2long(arg0);
  5681. if (n > realLength) {
  5682. n = realLength;
  5683. } else if (n < 0) {
  5684. throw getRuntime().newArgumentError("negative array size (or size too big)");
  5685. }
  5686. return makeShared(begin, (int) n, getRuntime().getArray());
  5687. }
  5688. /**
  5689. * Variable arity version for compatibility. Not bound to a Ruby method.
  5690. * @deprecated Use the versions with zero, one, or two args.
  5691. */
  5692. public IRubyObject last(IRubyObject[] args) {
  5693. switch (args.length) {
  5694. case 0:
  5695. return last();
  5696. case 1:
  5697. return last(args[0]);
  5698. default:
  5699. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  5700. return null; // not reached
  5701. }
  5702. }
  5703. /** rb_ary_last
  5704. *
  5705. */
  5706. @JRubyMethod(name = "last")
  5707. public IRubyObject last() {
  5708. if (realLength == 0) return getRuntime().getNil();
  5709. return values[begin + realLength - 1];
  5710. }
  5711. /** rb_ary_last
  5712. *
  5713. */
  5714. @JRubyMethod(name = "last")
  5715. public IRubyObject last(IRubyObject arg0) {
  5716. long n = RubyNumeric.num2long(arg0);
  5717. if (n > realLength) {
  5718. n = realLength;
  5719. } else if (n < 0) {
  5720. throw getRuntime().newArgumentError("negative array size (or size too big)");
  5721. }
  5722. return makeShared(begin + realLength - (int) n, (int) n, getRuntime().getArray());
  5723. }
  5724. /** rb_ary_each
  5725. *
  5726. */
  5727. @JRubyMethod(name = "each", frame = true)
  5728. public IRubyObject each(ThreadContext context, Block block) {
  5729. for (int i = 0; i < realLength; i++) {
  5730. block.yield(context, values[begin + i]);
  5731. }
  5732. return this;
  5733. }
  5734. /** rb_ary_each_index
  5735. *
  5736. */
  5737. @JRubyMethod(name = "each_index", frame = true)
  5738. public IRubyObject each_index(ThreadContext context, Block block) {
  5739. Ruby runtime = getRuntime();
  5740. for (int i = 0; i < realLength; i++) {
  5741. block.yield(context, runtime.newFixnum(i));
  5742. }
  5743. return this;
  5744. }
  5745. /** rb_ary_reverse_each
  5746. *
  5747. */
  5748. @JRubyMethod(name = "reverse_each", frame = true)
  5749. public IRubyObject reverse_each(ThreadContext context, Block block) {
  5750. int len = realLength;
  5751. while(len-- > 0) {
  5752. block.yield(context, values[begin + len]);
  5753. if (realLength < len) len = realLength;
  5754. }
  5755. return this;
  5756. }
  5757. private IRubyObject inspectJoin(ThreadContext context, RubyArray tmp, IRubyObject sep) {
  5758. Ruby runtime = getRuntime();
  5759. // If already inspecting, there is no need to register/unregister again.
  5760. if (runtime.isInspecting(this)) {
  5761. return tmp.join(context, sep);
  5762. }
  5763. try {
  5764. runtime.registerInspecting(this);
  5765. return tmp.join(context, sep);
  5766. } finally {
  5767. runtime.unregisterInspecting(this);
  5768. }
  5769. }
  5770. /** rb_ary_join
  5771. *
  5772. */
  5773. public RubyString join(ThreadContext context, IRubyObject sep) {
  5774. final Ruby runtime = getRuntime();
  5775. if (realLength == 0) return RubyString.newEmptyString(getRuntime());
  5776. boolean taint = isTaint() || sep.isTaint();
  5777. long len = 1;
  5778. for (int i = begin; i < begin + realLength; i++) {
  5779. IRubyObject value;
  5780. try {
  5781. value = values[i];
  5782. } catch (ArrayIndexOutOfBoundsException e) {
  5783. concurrentModification();
  5784. return runtime.newString("");
  5785. }
  5786. IRubyObject tmp = value.checkStringType();
  5787. len += tmp.isNil() ? 10 : ((RubyString) tmp).getByteList().length();
  5788. }
  5789. RubyString strSep = null;
  5790. if (!sep.isNil()) {
  5791. sep = strSep = sep.convertToString();
  5792. len += strSep.getByteList().length() * (realLength - 1);
  5793. }
  5794. ByteList buf = new ByteList((int)len);
  5795. for (int i = begin; i < begin + realLength; i++) {
  5796. IRubyObject tmp;
  5797. try {
  5798. tmp = values[i];
  5799. } catch (ArrayIndexOutOfBoundsException e) {
  5800. concurrentModification();
  5801. return runtime.newString("");
  5802. }
  5803. if (tmp instanceof RubyString) {
  5804. // do nothing
  5805. } else if (tmp instanceof RubyArray) {
  5806. if (runtime.isInspecting(tmp)) {
  5807. tmp = runtime.newString("[...]");
  5808. } else {
  5809. tmp = inspectJoin(context, (RubyArray)tmp, sep);
  5810. }
  5811. } else {
  5812. tmp = RubyString.objAsString(context, tmp);
  5813. }
  5814. if (i > begin && !sep.isNil()) buf.append(strSep.getByteList());
  5815. buf.append(tmp.asString().getByteList());
  5816. if (tmp.isTaint()) taint = true;
  5817. }
  5818. RubyString result = runtime.newString(buf);
  5819. if (taint) result.setTaint(true);
  5820. return result;
  5821. }
  5822. /** rb_ary_join_m
  5823. *
  5824. */
  5825. @JRubyMethod(name = "join", optional = 1)
  5826. public RubyString join_m(ThreadContext context, IRubyObject[] args) {
  5827. int argc = args.length;
  5828. IRubyObject sep = (argc == 1) ? args[0] : getRuntime().getGlobalVariables().get("$,");
  5829. return join(context, sep);
  5830. }
  5831. /** rb_ary_to_a
  5832. *
  5833. */
  5834. @JRubyMethod(name = "to_a")
  5835. @Override
  5836. public RubyArray to_a() {
  5837. if(getMetaClass() != getRuntime().getArray()) {
  5838. RubyArray dup = new RubyArray(getRuntime(), getRuntime().isObjectSpaceEnabled());
  5839. isShared = true;
  5840. dup.isShared = true;
  5841. dup.values = values;
  5842. dup.realLength = realLength;
  5843. dup.begin = begin;
  5844. return dup;
  5845. }
  5846. return this;
  5847. }
  5848. @JRubyMethod(name = "to_ary")
  5849. public IRubyObject to_ary() {
  5850. return this;
  5851. }
  5852. @Override
  5853. public RubyArray convertToArray() {
  5854. return this;
  5855. }
  5856. @Override
  5857. public IRubyObject checkArrayType(){
  5858. return this;
  5859. }
  5860. /** rb_ary_equal
  5861. *
  5862. */
  5863. @JRubyMethod(name = "==", required = 1)
  5864. @Override
  5865. public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
  5866. if (this == obj) return getRuntime().getTrue();
  5867. if (!(obj instanceof RubyArray)) {
  5868. if (!obj.respondsTo("to_ary")) {
  5869. return getRuntime().getFalse();
  5870. } else {
  5871. if (equalInternal(context, obj.callMethod(context, "to_ary"), this)) return getRuntime().getTrue();
  5872. return getRuntime().getFalse();
  5873. }
  5874. }
  5875. RubyArray ary = (RubyArray) obj;
  5876. if (realLength != ary.realLength) return getRuntime().getFalse();
  5877. Ruby runtime = getRuntime();
  5878. for (long i = 0; i < realLength; i++) {
  5879. if (!equalInternal(context, elt(i), ary.elt(i))) return runtime.getFalse();
  5880. }
  5881. return runtime.getTrue();
  5882. }
  5883. /** rb_ary_eql
  5884. *
  5885. */
  5886. @JRubyMethod(name = "eql?", required = 1)
  5887. public RubyBoolean eql_p(ThreadContext context, IRubyObject obj) {
  5888. if (this == obj) return getRuntime().getTrue();
  5889. if (!(obj instanceof RubyArray)) return getRuntime().getFalse();
  5890. RubyArray ary = (RubyArray) obj;
  5891. if (realLength != ary.realLength) return getRuntime().getFalse();
  5892. Ruby runtime = getRuntime();
  5893. for (int i = 0; i < realLength; i++) {
  5894. if (!eqlInternal(context, elt(i), ary.elt(i))) return runtime.getFalse();
  5895. }
  5896. return runtime.getTrue();
  5897. }
  5898. /** rb_ary_compact_bang
  5899. *
  5900. */
  5901. @JRubyMethod(name = "compact!")
  5902. public IRubyObject compact_bang() {
  5903. modify();
  5904. int p = 0;
  5905. int t = 0;
  5906. int end = p + realLength;
  5907. while (t < end) {
  5908. if (values[t].isNil()) {
  5909. t++;
  5910. } else {
  5911. values[p++] = values[t++];
  5912. }
  5913. }
  5914. if (realLength == p) return getRuntime().getNil();
  5915. realloc(p);
  5916. realLength = p;
  5917. return this;
  5918. }
  5919. /** rb_ary_compact
  5920. *
  5921. */
  5922. @JRubyMethod(name = "compact")
  5923. public IRubyObject compact() {
  5924. RubyArray ary = aryDup();
  5925. ary.compact_bang();
  5926. return ary;
  5927. }
  5928. /** rb_ary_empty_p
  5929. *
  5930. */
  5931. @JRubyMethod(name = "empty?")
  5932. public IRubyObject empty_p() {
  5933. return realLength == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
  5934. }
  5935. /** rb_ary_clear
  5936. *
  5937. */
  5938. @JRubyMethod(name = "clear")
  5939. public IRubyObject rb_clear() {
  5940. modifyCheck();
  5941. if(isShared) {
  5942. alloc(ARRAY_DEFAULT_SIZE);
  5943. isShared = true;
  5944. } else if (values.length > ARRAY_DEFAULT_SIZE << 1){
  5945. alloc(ARRAY_DEFAULT_SIZE << 1);
  5946. } else {
  5947. final int begin = this.begin;
  5948. try {
  5949. Arrays.fill(values, begin, begin + realLength, getRuntime().getNil());
  5950. } catch (ArrayIndexOutOfBoundsException e) {
  5951. concurrentModification();
  5952. }
  5953. }
  5954. begin = 0;
  5955. realLength = 0;
  5956. return this;
  5957. }
  5958. /** rb_ary_fill
  5959. *
  5960. */
  5961. @JRubyMethod(name = "fill", optional = 3, frame = true)
  5962. public IRubyObject fill(ThreadContext context, IRubyObject[] args, Block block) {
  5963. IRubyObject item = null;
  5964. IRubyObject begObj = null;
  5965. IRubyObject lenObj = null;
  5966. int argc = args.length;
  5967. if (block.isGiven()) {
  5968. Arity.checkArgumentCount(getRuntime(), args, 0, 2);
  5969. item = null;
  5970. begObj = argc > 0 ? args[0] : null;
  5971. lenObj = argc > 1 ? args[1] : null;
  5972. argc++;
  5973. } else {
  5974. Arity.checkArgumentCount(getRuntime(), args, 1, 3);
  5975. item = args[0];
  5976. begObj = argc > 1 ? args[1] : null;
  5977. lenObj = argc > 2 ? args[2] : null;
  5978. }
  5979. int beg = 0, end = 0, len = 0;
  5980. switch (argc) {
  5981. case 1:
  5982. beg = 0;
  5983. len = realLength;
  5984. break;
  5985. case 2:
  5986. if (begObj instanceof RubyRange) {
  5987. long[] beglen = ((RubyRange) begObj).begLen(realLength, 1);
  5988. beg = (int) beglen[0];
  5989. len = (int) beglen[1];
  5990. break;
  5991. }
  5992. /* fall through */
  5993. case 3:
  5994. beg = begObj.isNil() ? 0 : RubyNumeric.num2int(begObj);
  5995. if (beg < 0) {
  5996. beg = realLength + beg;
  5997. if (beg < 0) beg = 0;
  5998. }
  5999. len = (lenObj == null || lenObj.isNil()) ? realLength - beg : RubyNumeric.num2int(lenObj);
  6000. // TODO: In MRI 1.9, an explicit check for negative length is
  6001. // added here. IndexError is raised when length is negative.
  6002. // See [ruby-core:12953] for more details.
  6003. //
  6004. // New note: This is actually under re-evaluation,
  6005. // see [ruby-core:17483].
  6006. break;
  6007. }
  6008. modify();
  6009. // See [ruby-core:17483]
  6010. if (len < 0) {
  6011. return this;
  6012. }
  6013. if (len > Integer.MAX_VALUE - beg) {
  6014. throw getRuntime().newArgumentError("argument too big");
  6015. }
  6016. end = beg + len;
  6017. if (end > realLength) {
  6018. if (end >= values.length) realloc(end);
  6019. realLength = end;
  6020. }
  6021. if (block.isGiven()) {
  6022. Ruby runtime = getRuntime();
  6023. for (int i = beg; i < end; i++) {
  6024. IRubyObject v = block.yield(context, runtime.newFixnum(i));
  6025. if (i >= realLength) break;
  6026. try {
  6027. values[i] = v;
  6028. } catch (ArrayIndexOutOfBoundsException e) {
  6029. concurrentModification();
  6030. }
  6031. }
  6032. } else {
  6033. if (len > 0) {
  6034. try {
  6035. Arrays.fill(values, beg, beg + len, item);
  6036. } catch (ArrayIndexOutOfBoundsException e) {
  6037. concurrentModification();
  6038. }
  6039. }
  6040. }
  6041. return this;
  6042. }
  6043. /** rb_ary_index
  6044. *
  6045. */
  6046. @JRubyMethod(name = "index", required = 1)
  6047. public IRubyObject index(ThreadContext context, IRubyObject obj) {
  6048. Ruby runtime = getRuntime();
  6049. for (int i = begin; i < begin + realLength; i++) {
  6050. if (equalInternal(context, values[i], obj)) return runtime.newFixnum(i - begin);
  6051. }
  6052. return runtime.getNil();
  6053. }
  6054. /** rb_ary_rindex
  6055. *
  6056. */
  6057. @JRubyMethod(name = "rindex", required = 1)
  6058. public IRubyObject rindex(ThreadContext context, IRubyObject obj) {
  6059. Ruby runtime = getRuntime();
  6060. int i = realLength;
  6061. while (i-- > 0) {
  6062. if (i > realLength) {
  6063. i = realLength;
  6064. continue;
  6065. }
  6066. if (equalInternal(context, values[begin + i], obj)) return getRuntime().newFixnum(i);
  6067. }
  6068. return runtime.getNil();
  6069. }
  6070. /** rb_ary_indexes
  6071. *
  6072. */
  6073. @JRubyMethod(name = {"indexes", "indices"}, required = 1, rest = true)
  6074. public IRubyObject indexes(IRubyObject[] args) {
  6075. getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Array#indexes is deprecated; use Array#values_at", "Array#indexes", "Array#values_at");
  6076. RubyArray ary = new RubyArray(getRuntime(), args.length);
  6077. IRubyObject[] arefArgs = new IRubyObject[1];
  6078. for (int i = 0; i < args.length; i++) {
  6079. arefArgs[0] = args[i];
  6080. ary.append(aref(arefArgs));
  6081. }
  6082. return ary;
  6083. }
  6084. /** rb_ary_reverse_bang
  6085. *
  6086. */
  6087. @JRubyMethod(name = "reverse!")
  6088. public IRubyObject reverse_bang() {
  6089. modify();
  6090. final int realLength = this.realLength;
  6091. final IRubyObject[] values = this.values;
  6092. try {
  6093. if (realLength > 1) {
  6094. int p1 = 0;
  6095. int p2 = p1 + realLength - 1;
  6096. while (p1 < p2) {
  6097. final IRubyObject tmp = values[p1];
  6098. values[p1++] = values[p2];
  6099. values[p2--] = tmp;
  6100. }
  6101. }
  6102. } catch (ArrayIndexOutOfBoundsException e) {
  6103. concurrentModification();
  6104. }
  6105. return this;
  6106. }
  6107. /** rb_ary_reverse_m
  6108. *
  6109. */
  6110. @JRubyMethod(name = "reverse")
  6111. public IRubyObject reverse() {
  6112. return aryDup().reverse_bang();
  6113. }
  6114. /** rb_ary_collect
  6115. *
  6116. */
  6117. @JRubyMethod(name = {"collect", "map"}, frame = true)
  6118. public RubyArray collect(ThreadContext context, Block block) {
  6119. Ruby runtime = getRuntime();
  6120. if (!block.isGiven()) return new RubyArray(getRuntime(), runtime.getArray(), this);
  6121. RubyArray collect = new RubyArray(runtime, realLength);
  6122. for (int i = begin; i < begin + realLength; i++) {
  6123. collect.append(block.yield(context, values[i]));
  6124. }
  6125. return collect;
  6126. }
  6127. /** rb_ary_collect_bang
  6128. *
  6129. */
  6130. @JRubyMethod(name = {"collect!", "map!"}, frame = true)
  6131. public RubyArray collect_bang(ThreadContext context, Block block) {
  6132. modify();
  6133. for (int i = 0, len = realLength; i < len; i++) {
  6134. store(i, block.yield(context, values[begin + i]));
  6135. }
  6136. return this;
  6137. }
  6138. /** rb_ary_select
  6139. *
  6140. */
  6141. @JRubyMethod(name = "select", frame = true)
  6142. public RubyArray select(ThreadContext context, Block block) {
  6143. Ruby runtime = getRuntime();
  6144. RubyArray result = new RubyArray(runtime, realLength);
  6145. if (isShared) {
  6146. for (int i = begin; i < begin + realLength; i++) {
  6147. if (block.yield(context, values[i]).isTrue()) result.append(elt(i - begin));
  6148. }
  6149. } else {
  6150. for (int i = 0; i < realLength; i++) {
  6151. if (block.yield(context, values[i]).isTrue()) result.append(elt(i));
  6152. }
  6153. }
  6154. return result;
  6155. }
  6156. /** rb_ary_delete
  6157. *
  6158. */
  6159. @JRubyMethod(name = "delete", required = 1, frame = true)
  6160. public IRubyObject delete(ThreadContext context, IRubyObject item, Block block) {
  6161. int i2 = 0;
  6162. Ruby runtime = getRuntime();
  6163. for (int i1 = 0; i1 < realLength; i1++) {
  6164. IRubyObject e = values[begin + i1];
  6165. if (equalInternal(context, e, item)) continue;
  6166. if (i1 != i2) store(i2, e);
  6167. i2++;
  6168. }
  6169. if (realLength == i2) {
  6170. if (block.isGiven()) return block.yield(context, item);
  6171. return runtime.getNil();
  6172. }
  6173. modify();
  6174. final int realLength = this.realLength;
  6175. final int begin = this.begin;
  6176. final IRubyObject[] values = this.values;
  6177. if (realLength > i2) {
  6178. try {
  6179. Arrays.fill(values, begin + i2, begin + realLength, getRuntime().getNil());
  6180. } catch (ArrayIndexOutOfBoundsException e) {
  6181. concurrentModification();
  6182. }
  6183. this.realLength = i2;
  6184. if (i2 << 1 < values.length && values.length > ARRAY_DEFAULT_SIZE) realloc(i2 << 1);
  6185. }
  6186. return item;
  6187. }
  6188. /** rb_ary_delete_at
  6189. *
  6190. */
  6191. private final IRubyObject delete_at(int pos) {
  6192. int len = realLength;
  6193. if (pos >= len) return getRuntime().getNil();
  6194. if (pos < 0) pos += len;
  6195. if (pos < 0) return getRuntime().getNil();
  6196. modify();
  6197. IRubyObject obj = values[pos];
  6198. try {
  6199. System.arraycopy(values, pos + 1, values, pos, len - (pos + 1));
  6200. values[realLength-1] = getRuntime().getNil();
  6201. } catch (ArrayIndexOutOfBoundsException e) {
  6202. concurrentModification();
  6203. }
  6204. realLength--;
  6205. return obj;
  6206. }
  6207. /** rb_ary_delete_at_m
  6208. *
  6209. */
  6210. @JRubyMethod(name = "delete_at", required = 1)
  6211. public IRubyObject delete_at(IRubyObject obj) {
  6212. return delete_at((int) RubyNumeric.num2long(obj));
  6213. }
  6214. /** rb_ary_reject_bang
  6215. *
  6216. */
  6217. @JRubyMethod(name = "reject", frame = true)
  6218. public IRubyObject reject(ThreadContext context, Block block) {
  6219. RubyArray ary = aryDup();
  6220. ary.reject_bang(context, block);
  6221. return ary;
  6222. }
  6223. /** rb_ary_reject_bang
  6224. *
  6225. */
  6226. @JRubyMethod(name = "reject!", frame = true)
  6227. public IRubyObject reject_bang(ThreadContext context, Block block) {
  6228. int i2 = 0;
  6229. modify();
  6230. for (int i1 = 0; i1 < realLength; i1++) {
  6231. IRubyObject v = values[i1];
  6232. if (block.yield(context, v).isTrue()) continue;
  6233. if (i1 != i2) store(i2, v);
  6234. i2++;
  6235. }
  6236. if (realLength == i2) return getRuntime().getNil();
  6237. if (i2 < realLength) {
  6238. try {
  6239. Arrays.fill(values, i2, realLength, getRuntime().getNil());
  6240. } catch (ArrayIndexOutOfBoundsException e) {
  6241. concurrentModification();
  6242. }
  6243. realLength = i2;
  6244. }
  6245. return this;
  6246. }
  6247. /** rb_ary_delete_if
  6248. *
  6249. */
  6250. @JRubyMethod(name = "delete_if", frame = true)
  6251. public IRubyObject delete_if(ThreadContext context, Block block) {
  6252. reject_bang(context, block);
  6253. return this;
  6254. }
  6255. /** rb_ary_zip
  6256. *
  6257. */
  6258. @JRubyMethod(name = "zip", optional = 1, rest = true, frame = true)
  6259. public IRubyObject zip(ThreadContext context, IRubyObject[] args, Block block) {
  6260. for (int i = 0; i < args.length; i++) {
  6261. args[i] = args[i].convertToArray();
  6262. }
  6263. Ruby runtime = getRuntime();
  6264. if (block.isGiven()) {
  6265. for (int i = 0; i < realLength; i++) {
  6266. RubyArray tmp = new RubyArray(runtime, args.length + 1);
  6267. tmp.append(elt(i));
  6268. for (int j = 0; j < args.length; j++) {
  6269. tmp.append(((RubyArray) args[j]).elt(i));
  6270. }
  6271. block.yield(context, tmp);
  6272. }
  6273. return runtime.getNil();
  6274. }
  6275. int len = realLength;
  6276. RubyArray result = new RubyArray(runtime, len);
  6277. for (int i = 0; i < len; i++) {
  6278. RubyArray tmp = new RubyArray(runtime, args.length + 1);
  6279. tmp.append(elt(i));
  6280. for (int j = 0; j < args.length; j++) {
  6281. tmp.append(((RubyArray) args[j]).elt(i));
  6282. }
  6283. result.append(tmp);
  6284. }
  6285. return result;
  6286. }
  6287. /** rb_ary_cmp
  6288. *
  6289. */
  6290. @JRubyMethod(name = "<=>", required = 1)
  6291. public IRubyObject op_cmp(ThreadContext context, IRubyObject obj) {
  6292. RubyArray ary2 = obj.convertToArray();
  6293. int len = realLength;
  6294. if (len > ary2.realLength) len = ary2.realLength;
  6295. Ruby runtime = getRuntime();
  6296. for (int i = 0; i < len; i++) {
  6297. IRubyObject v = elt(i).callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", ary2.elt(i));
  6298. if (!(v instanceof RubyFixnum) || ((RubyFixnum) v).getLongValue() != 0) return v;
  6299. }
  6300. len = realLength - ary2.realLength;
  6301. if (len == 0) return RubyFixnum.zero(runtime);
  6302. if (len > 0) return RubyFixnum.one(runtime);
  6303. return RubyFixnum.minus_one(runtime);
  6304. }
  6305. /**
  6306. * Variable arity version for compatibility. Not bound to a Ruby method.
  6307. * @deprecated Use the versions with zero, one, or two args.
  6308. */
  6309. public IRubyObject slice_bang(IRubyObject[] args) {
  6310. switch (args.length) {
  6311. case 1:
  6312. return slice_bang(args[0]);
  6313. case 2:
  6314. return slice_bang(args[0], args[1]);
  6315. default:
  6316. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  6317. return null; // not reached
  6318. }
  6319. }
  6320. /** rb_ary_slice_bang
  6321. *
  6322. */
  6323. @JRubyMethod(name = "slice!")
  6324. public IRubyObject slice_bang(IRubyObject arg0) {
  6325. if (arg0 instanceof RubyRange) {
  6326. long[] beglen = ((RubyRange) arg0).begLen(realLength, 1);
  6327. long pos = beglen[0];
  6328. long len = beglen[1];
  6329. if (pos < 0) pos = realLength + pos;
  6330. arg0 = subseq(pos, len);
  6331. splice(pos, len, null);
  6332. return arg0;
  6333. }
  6334. return delete_at((int) RubyNumeric.num2long(arg0));
  6335. }
  6336. /** rb_ary_slice_bang
  6337. *
  6338. */
  6339. @JRubyMethod(name = "slice!")
  6340. public IRubyObject slice_bang(IRubyObject arg0, IRubyObject arg1) {
  6341. long pos = RubyNumeric.num2long(arg0);
  6342. long len = RubyNumeric.num2long(arg1);
  6343. if (pos < 0) pos = realLength + pos;
  6344. arg1 = subseq(pos, len);
  6345. splice(pos, len, null);
  6346. return arg1;
  6347. }
  6348. /** rb_ary_assoc
  6349. *
  6350. */
  6351. @JRubyMethod(name = "assoc", required = 1)
  6352. public IRubyObject assoc(ThreadContext context, IRubyObject key) {
  6353. Ruby runtime = getRuntime();
  6354. for (int i = begin; i < begin + realLength; i++) {
  6355. IRubyObject v = values[i];
  6356. if (v instanceof RubyArray) {
  6357. RubyArray arr = (RubyArray)v;
  6358. if (arr.realLength > 0 && equalInternal(context, arr.values[arr.begin], key)) return arr;
  6359. }
  6360. }
  6361. return runtime.getNil();
  6362. }
  6363. /** rb_ary_rassoc
  6364. *
  6365. */
  6366. @JRubyMethod(name = "rassoc", required = 1)
  6367. public IRubyObject rassoc(ThreadContext context, IRubyObject value) {
  6368. Ruby runtime = getRuntime();
  6369. for (int i = begin; i < begin + realLength; i++) {
  6370. IRubyObject v = values[i];
  6371. if (v instanceof RubyArray) {
  6372. RubyArray arr = (RubyArray)v;
  6373. if (arr.realLength > 1 && equalInternal(context, arr.values[arr.begin + 1], value)) return arr;
  6374. }
  6375. }
  6376. return runtime.getNil();
  6377. }
  6378. /** flatten
  6379. *
  6380. */
  6381. private final int flatten(ThreadContext context, int index, RubyArray ary2, RubyArray memo) {
  6382. int i = index;
  6383. int n;
  6384. int lim = index + ary2.realLength;
  6385. IRubyObject id = ary2.id();
  6386. if (memo.includes(context, id)) throw getRuntime().newArgumentError("tried to flatten recursive array");
  6387. memo.append(id);
  6388. splice(index, 1, ary2);
  6389. while (i < lim) {
  6390. IRubyObject tmp = elt(i).checkArrayType();
  6391. if (!tmp.isNil()) {
  6392. n = flatten(context, i, (RubyArray) tmp, memo);
  6393. i += n;
  6394. lim += n;
  6395. }
  6396. i++;
  6397. }
  6398. memo.pop();
  6399. return lim - index - 1; /* returns number of increased items */
  6400. }
  6401. /** rb_ary_flatten_bang
  6402. *
  6403. */
  6404. @JRubyMethod(name = "flatten!")
  6405. public IRubyObject flatten_bang(ThreadContext context) {
  6406. int i = 0;
  6407. RubyArray memo = null;
  6408. while (i < realLength) {
  6409. IRubyObject ary2 = values[begin + i];
  6410. IRubyObject tmp = ary2.checkArrayType();
  6411. if (!tmp.isNil()) {
  6412. if (memo == null) {
  6413. memo = new RubyArray(getRuntime(), false);
  6414. memo.values = reserve(ARRAY_DEFAULT_SIZE);
  6415. }
  6416. i += flatten(context, i, (RubyArray) tmp, memo);
  6417. }
  6418. i++;
  6419. }
  6420. if (memo == null) return getRuntime().getNil();
  6421. return this;
  6422. }
  6423. /** rb_ary_flatten
  6424. *
  6425. */
  6426. @JRubyMethod(name = "flatten")
  6427. public IRubyObject flatten(ThreadContext context) {
  6428. RubyArray ary = aryDup();
  6429. ary.flatten_bang(context);
  6430. return ary;
  6431. }
  6432. /** rb_ary_nitems
  6433. *
  6434. */
  6435. @JRubyMethod(name = "nitems")
  6436. public IRubyObject nitems() {
  6437. int n = 0;
  6438. for (int i = begin; i < begin + realLength; i++) {
  6439. if (!values[i].isNil()) n++;
  6440. }
  6441. return getRuntime().newFixnum(n);
  6442. }
  6443. /** rb_ary_plus
  6444. *
  6445. */
  6446. @JRubyMethod(name = "+", required = 1)
  6447. public IRubyObject op_plus(IRubyObject obj) {
  6448. RubyArray y = obj.convertToArray();
  6449. int len = realLength + y.realLength;
  6450. RubyArray z = new RubyArray(getRuntime(), len);
  6451. try {
  6452. System.arraycopy(values, begin, z.values, 0, realLength);
  6453. System.arraycopy(y.values, y.begin, z.values, realLength, y.realLength);
  6454. } catch (ArrayIndexOutOfBoundsException e) {
  6455. concurrentModification();
  6456. }
  6457. z.realLength = len;
  6458. return z;
  6459. }
  6460. /** rb_ary_times
  6461. *
  6462. */
  6463. @JRubyMethod(name = "*", required = 1)
  6464. public IRubyObject op_times(ThreadContext context, IRubyObject times) {
  6465. IRubyObject tmp = times.checkStringType();
  6466. if (!tmp.isNil()) return join(context, tmp);
  6467. long len = RubyNumeric.num2long(times);
  6468. if (len == 0) return new RubyArray(getRuntime(), getMetaClass(), 0);
  6469. if (len < 0) throw getRuntime().newArgumentError("negative argument");
  6470. if (Long.MAX_VALUE / len < realLength) {
  6471. throw getRuntime().newArgumentError("argument too big");
  6472. }
  6473. len *= realLength;
  6474. RubyArray ary2 = new RubyArray(getRuntime(), getMetaClass(), len);
  6475. ary2.realLength = (int) len;
  6476. try {
  6477. for (int i = 0; i < len; i += realLength) {
  6478. System.arraycopy(values, begin, ary2.values, i, realLength);
  6479. }
  6480. } catch (ArrayIndexOutOfBoundsException e) {
  6481. concurrentModification();
  6482. }
  6483. ary2.infectBy(this);
  6484. return ary2;
  6485. }
  6486. /** ary_make_hash
  6487. *
  6488. */
  6489. private final RubyHash makeHash(RubyArray ary2) {
  6490. RubyHash hash = new RubyHash(getRuntime(), false);
  6491. int begin = this.begin;
  6492. for (int i = begin; i < begin + realLength; i++) {
  6493. hash.fastASet(values[i], NEVER);
  6494. }
  6495. if (ary2 != null) {
  6496. begin = ary2.begin;
  6497. for (int i = begin; i < begin + ary2.realLength; i++) {
  6498. hash.fastASet(ary2.values[i], NEVER);
  6499. }
  6500. }
  6501. return hash;
  6502. }
  6503. /** rb_ary_uniq_bang
  6504. *
  6505. */
  6506. @JRubyMethod(name = "uniq!")
  6507. public IRubyObject uniq_bang() {
  6508. RubyHash hash = makeHash(null);
  6509. if (realLength == hash.size()) return getRuntime().getNil();
  6510. int j = 0;
  6511. for (int i = 0; i < realLength; i++) {
  6512. IRubyObject v = elt(i);
  6513. if (hash.fastDelete(v)) store(j++, v);
  6514. }
  6515. realLength = j;
  6516. return this;
  6517. }
  6518. /** rb_ary_uniq
  6519. *
  6520. */
  6521. @JRubyMethod(name = "uniq")
  6522. public IRubyObject uniq() {
  6523. RubyArray ary = aryDup();
  6524. ary.uniq_bang();
  6525. return ary;
  6526. }
  6527. /** rb_ary_diff
  6528. *
  6529. */
  6530. @JRubyMethod(name = "-", required = 1)
  6531. public IRubyObject op_diff(IRubyObject other) {
  6532. RubyHash hash = other.convertToArray().makeHash(null);
  6533. RubyArray ary3 = new RubyArray(getRuntime());
  6534. int begin = this.begin;
  6535. for (int i = begin; i < begin + realLength; i++) {
  6536. if (hash.fastARef(values[i]) != null) continue;
  6537. ary3.append(elt(i - begin));
  6538. }
  6539. return ary3;
  6540. }
  6541. /** rb_ary_and
  6542. *
  6543. */
  6544. @JRubyMethod(name = "&", required = 1)
  6545. public IRubyObject op_and(IRubyObject other) {
  6546. RubyArray ary2 = other.convertToArray();
  6547. RubyHash hash = ary2.makeHash(null);
  6548. RubyArray ary3 = new RubyArray(getRuntime(),
  6549. realLength < ary2.realLength ? realLength : ary2.realLength);
  6550. for (int i = 0; i < realLength; i++) {
  6551. IRubyObject v = elt(i);
  6552. if (hash.fastDelete(v)) ary3.append(v);
  6553. }
  6554. return ary3;
  6555. }
  6556. /** rb_ary_or
  6557. *
  6558. */
  6559. @JRubyMethod(name = "|", required = 1)
  6560. public IRubyObject op_or(IRubyObject other) {
  6561. RubyArray ary2 = other.convertToArray();
  6562. RubyHash set = makeHash(ary2);
  6563. RubyArray ary3 = new RubyArray(getRuntime(), realLength + ary2.realLength);
  6564. for (int i = 0; i < realLength; i++) {
  6565. IRubyObject v = elt(i);
  6566. if (set.fastDelete(v)) ary3.append(v);
  6567. }
  6568. for (int i = 0; i < ary2.realLength; i++) {
  6569. IRubyObject v = ary2.elt(i);
  6570. if (set.fastDelete(v)) ary3.append(v);
  6571. }
  6572. return ary3;
  6573. }
  6574. /** rb_ary_sort
  6575. *
  6576. */
  6577. @JRubyMethod(name = "sort", frame = true)
  6578. public RubyArray sort(Block block) {
  6579. RubyArray ary = aryDup();
  6580. ary.sort_bang(block);
  6581. return ary;
  6582. }
  6583. /** rb_ary_sort_bang
  6584. *
  6585. */
  6586. @JRubyMethod(name = "sort!", frame = true)
  6587. public RubyArray sort_bang(Block block) {
  6588. modify();
  6589. if (realLength > 1) {
  6590. flags |= TMPLOCK_ARR_F;
  6591. try {
  6592. if (block.isGiven()) {
  6593. Arrays.sort(values, 0, realLength, new BlockComparator(block));
  6594. } else {
  6595. Arrays.sort(values, 0, realLength, new DefaultComparator());
  6596. }
  6597. } finally {
  6598. flags &= ~TMPLOCK_ARR_F;
  6599. }
  6600. }
  6601. return this;
  6602. }
  6603. final class BlockComparator implements Comparator {
  6604. private Block block;
  6605. public BlockComparator(Block block) {
  6606. this.block = block;
  6607. }
  6608. public int compare(Object o1, Object o2) {
  6609. ThreadContext context = getRuntime().getCurrentContext();
  6610. IRubyObject obj1 = (IRubyObject) o1;
  6611. IRubyObject obj2 = (IRubyObject) o2;
  6612. IRubyObject ret = block.yield(context, getRuntime().newArray(obj1, obj2), null, null, true);
  6613. int n = RubyComparable.cmpint(context, ret, obj1, obj2);
  6614. //TODO: ary_sort_check should be done here
  6615. return n;
  6616. }
  6617. }
  6618. static final class DefaultComparator implements Comparator {
  6619. public int compare(Object o1, Object o2) {
  6620. if (o1 instanceof RubyFixnum && o2 instanceof RubyFixnum) {
  6621. return compareFixnums(o1, o2);
  6622. }
  6623. if (o1 instanceof RubyString && o2 instanceof RubyString) {
  6624. return ((RubyString) o1).op_cmp((RubyString) o2);
  6625. }
  6626. //TODO: ary_sort_check should be done here
  6627. return compareOthers((IRubyObject)o1, (IRubyObject)o2);
  6628. }
  6629. private int compareFixnums(Object o1, Object o2) {
  6630. long a = ((RubyFixnum) o1).getLongValue();
  6631. long b = ((RubyFixnum) o2).getLongValue();
  6632. if (a > b) {
  6633. return 1;
  6634. }
  6635. if (a < b) {
  6636. return -1;
  6637. }
  6638. return 0;
  6639. }
  6640. private int compareOthers(IRubyObject o1, IRubyObject o2) {
  6641. ThreadContext context = o1.getRuntime().getCurrentContext();
  6642. IRubyObject ret = o1.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", o2);
  6643. int n = RubyComparable.cmpint(context, ret, o1, o2);
  6644. //TODO: ary_sort_check should be done here
  6645. return n;
  6646. }
  6647. }
  6648. public static void marshalTo(RubyArray array, MarshalStream output) throws IOException {
  6649. output.registerLinkTarget(array);
  6650. output.writeInt(array.getList().size());
  6651. for (Iterator iter = array.getList().iterator(); iter.hasNext();) {
  6652. output.dumpObject((IRubyObject) iter.next());
  6653. }
  6654. }
  6655. public static RubyArray unmarshalFrom(UnmarshalStream input) throws IOException {
  6656. RubyArray result = input.getRuntime().newArray();
  6657. input.registerLinkTarget(result);
  6658. int size = input.unmarshalInt();
  6659. for (int i = 0; i < size; i++) {
  6660. result.append(input.unmarshalObject());
  6661. }
  6662. return result;
  6663. }
  6664. /**
  6665. * @see org.jruby.util.Pack#pack
  6666. */
  6667. @JRubyMethod(name = "pack", required = 1)
  6668. public RubyString pack(ThreadContext context, IRubyObject obj) {
  6669. RubyString iFmt = RubyString.objAsString(context, obj);
  6670. return Pack.pack(getRuntime(), this, iFmt.getByteList());
  6671. }
  6672. @Override
  6673. public Class getJavaClass() {
  6674. return List.class;
  6675. }
  6676. // Satisfy java.util.List interface (for Java integration)
  6677. public int size() {
  6678. return realLength;
  6679. }
  6680. public boolean isEmpty() {
  6681. return realLength == 0;
  6682. }
  6683. public boolean contains(Object element) {
  6684. return indexOf(element) != -1;
  6685. }
  6686. public Object[] toArray() {
  6687. Object[] array = new Object[realLength];
  6688. for (int i = begin; i < realLength; i++) {
  6689. array[i - begin] = JavaUtil.convertRubyToJava(values[i]);
  6690. }
  6691. return array;
  6692. }
  6693. public Object[] toArray(final Object[] arg) {
  6694. Object[] array = arg;
  6695. if (array.length < realLength) {
  6696. Class type = array.getClass().getComponentType();
  6697. array = (Object[]) Array.newInstance(type, realLength);
  6698. }
  6699. int length = realLength - begin;
  6700. for (int i = 0; i < length; i++) {
  6701. array[i] = JavaUtil.convertRubyToJava(values[i + begin]);
  6702. }
  6703. return array;
  6704. }
  6705. public boolean add(Object element) {
  6706. append(JavaUtil.convertJavaToRuby(getRuntime(), element));
  6707. return true;
  6708. }
  6709. public boolean remove(Object element) {
  6710. IRubyObject deleted = delete(getRuntime().getCurrentContext(), JavaUtil.convertJavaToRuby(getRuntime(), element), Block.NULL_BLOCK);
  6711. return deleted.isNil() ? false : true; // TODO: is this correct ?
  6712. }
  6713. public boolean containsAll(Collection c) {
  6714. for (Iterator iter = c.iterator(); iter.hasNext();) {
  6715. if (indexOf(iter.next()) == -1) {
  6716. return false;
  6717. }
  6718. }
  6719. return true;
  6720. }
  6721. public boolean addAll(Collection c) {
  6722. for (Iterator iter = c.iterator(); iter.hasNext();) {
  6723. add(iter.next());
  6724. }
  6725. return !c.isEmpty();
  6726. }
  6727. public boolean addAll(int index, Collection c) {
  6728. Iterator iter = c.iterator();
  6729. for (int i = index; iter.hasNext(); i++) {
  6730. add(i, iter.next());
  6731. }
  6732. return !c.isEmpty();
  6733. }
  6734. public boolean removeAll(Collection c) {
  6735. boolean listChanged = false;
  6736. for (Iterator iter = c.iterator(); iter.hasNext();) {
  6737. if (remove(iter.next())) {
  6738. listChanged = true;
  6739. }
  6740. }
  6741. return listChanged;
  6742. }
  6743. public boolean retainAll(Collection c) {
  6744. boolean listChanged = false;
  6745. for (Iterator iter = iterator(); iter.hasNext();) {
  6746. Object element = iter.next();
  6747. if (!c.contains(element)) {
  6748. remove(element);
  6749. listChanged = true;
  6750. }
  6751. }
  6752. return listChanged;
  6753. }
  6754. public Object get(int index) {
  6755. return JavaUtil.convertRubyToJava((IRubyObject) elt(index), Object.class);
  6756. }
  6757. public Object set(int index, Object element) {
  6758. return store(index, JavaUtil.convertJavaToRuby(getRuntime(), element));
  6759. }
  6760. // TODO: make more efficient by not creating IRubyArray[]
  6761. public void add(int index, Object element) {
  6762. insert(new IRubyObject[]{RubyFixnum.newFixnum(getRuntime(), index), JavaUtil.convertJavaToRuby(getRuntime(), element)});
  6763. }
  6764. public Object remove(int index) {
  6765. return JavaUtil.convertRubyToJava(delete_at(index), Object.class);
  6766. }
  6767. public int indexOf(Object element) {
  6768. int begin = this.begin;
  6769. if (element != null) {
  6770. IRubyObject convertedElement = JavaUtil.convertJavaToRuby(getRuntime(), element);
  6771. for (int i = begin; i < begin + realLength; i++) {
  6772. if (convertedElement.equals(values[i])) {
  6773. return i;
  6774. }
  6775. }
  6776. }
  6777. return -1;
  6778. }
  6779. public int lastIndexOf(Object element) {
  6780. int begin = this.begin;
  6781. if (element != null) {
  6782. IRubyObject convertedElement = JavaUtil.convertJavaToRuby(getRuntime(), element);
  6783. for (int i = begin + realLength - 1; i >= begin; i--) {
  6784. if (convertedElement.equals(values[i])) {
  6785. return i;
  6786. }
  6787. }
  6788. }
  6789. return -1;
  6790. }
  6791. public class RubyArrayConversionIterator implements Iterator {
  6792. protected int index = 0;
  6793. protected int last = -1;
  6794. public boolean hasNext() {
  6795. return index < realLength;
  6796. }
  6797. public Object next() {
  6798. IRubyObject element = elt(index);
  6799. last = index++;
  6800. return JavaUtil.convertRubyToJava(element, Object.class);
  6801. }
  6802. public void remove() {
  6803. if (last == -1) throw new IllegalStateException();
  6804. delete_at(last);
  6805. if (last < index) index--;
  6806. last = -1;
  6807. }
  6808. }
  6809. public Iterator iterator() {
  6810. return new RubyArrayConversionIterator();
  6811. }
  6812. final class RubyArrayConversionListIterator extends RubyArrayConversionIterator implements ListIterator {
  6813. public RubyArrayConversionListIterator() {
  6814. }
  6815. public RubyArrayConversionListIterator(int index) {
  6816. this.index = index;
  6817. }
  6818. public boolean hasPrevious() {
  6819. return index >= 0;
  6820. }
  6821. public Object previous() {
  6822. return JavaUtil.convertRubyToJava((IRubyObject) elt(last = --index), Object.class);
  6823. }
  6824. public int nextIndex() {
  6825. return index;
  6826. }
  6827. public int previousIndex() {
  6828. return index - 1;
  6829. }
  6830. public void set(Object obj) {
  6831. if (last == -1) throw new IllegalStateException();
  6832. store(last, JavaUtil.convertJavaToRuby(getRuntime(), obj));
  6833. }
  6834. public void add(Object obj) {
  6835. insert(new IRubyObject[] { RubyFixnum.newFixnum(getRuntime(), index++), JavaUtil.convertJavaToRuby(getRuntime(), obj) });
  6836. last = -1;
  6837. }
  6838. }
  6839. public ListIterator listIterator() {
  6840. return new RubyArrayConversionListIterator();
  6841. }
  6842. public ListIterator listIterator(int index) {
  6843. return new RubyArrayConversionListIterator(index);
  6844. }
  6845. // TODO: list.subList(from, to).clear() is supposed to clear the sublist from the list.
  6846. // How can we support this operation?
  6847. public List subList(int fromIndex, int toIndex) {
  6848. if (fromIndex < 0 || toIndex > size() || fromIndex > toIndex) {
  6849. throw new IndexOutOfBoundsException();
  6850. }
  6851. IRubyObject subList = subseq(fromIndex, toIndex - fromIndex + 1);
  6852. return subList.isNil() ? null : (List) subList;
  6853. }
  6854. public void clear() {
  6855. rb_clear();
  6856. }
  6857. }
  6858. /***** BEGIN LICENSE BLOCK *****
  6859. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  6860. *
  6861. * The contents of this file are subject to the Common Public
  6862. * License Version 1.0 (the "License"); you may not use this file
  6863. * except in compliance with the License. You may obtain a copy of
  6864. * the License at http://www.eclipse.org/legal/cpl-v10.html
  6865. *
  6866. * Software distributed under the License is distributed on an "AS
  6867. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  6868. * implied. See the License for the specific language governing
  6869. * rights and limitations under the License.
  6870. *
  6871. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  6872. *
  6873. * Alternatively, the contents of this file may be used under the terms of
  6874. * either of the GNU General Public License Version 2 or later (the "GPL"),
  6875. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  6876. * in which case the provisions of the GPL or the LGPL are applicable instead
  6877. * of those above. If you wish to allow use of your version of this file only
  6878. * under the terms of either the GPL or the LGPL, and not to allow others to
  6879. * use your version of this file under the terms of the CPL, indicate your
  6880. * decision by deleting the provisions above and replace them with the notice
  6881. * and other provisions required by the GPL or the LGPL. If you do not delete
  6882. * the provisions above, a recipient may use your version of this file under
  6883. * the terms of any one of the CPL, the GPL or the LGPL.
  6884. ***** END LICENSE BLOCK *****/
  6885. package org.jruby;
  6886. import java.math.BigDecimal;
  6887. import java.math.BigInteger;
  6888. import java.math.MathContext;
  6889. import java.math.RoundingMode;
  6890. import java.util.ArrayList;
  6891. import java.util.List;
  6892. import java.util.regex.Matcher;
  6893. import java.util.regex.Pattern;
  6894. import org.jruby.anno.JRubyClass;
  6895. import org.jruby.anno.JRubyConstant;
  6896. import org.jruby.anno.JRubyMethod;
  6897. import org.jruby.runtime.Arity;
  6898. import org.jruby.runtime.Block;
  6899. import org.jruby.runtime.CallbackFactory;
  6900. import org.jruby.runtime.MethodIndex;
  6901. import org.jruby.runtime.ObjectAllocator;
  6902. import org.jruby.runtime.ThreadContext;
  6903. import org.jruby.runtime.Visibility;
  6904. import org.jruby.runtime.builtin.IRubyObject;
  6905. /**
  6906. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  6907. */
  6908. @JRubyClass(name="BigDecimal", parent="Numeric")
  6909. public class RubyBigDecimal extends RubyNumeric {
  6910. private static final ObjectAllocator BIGDECIMAL_ALLOCATOR = new ObjectAllocator() {
  6911. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  6912. return new RubyBigDecimal(runtime, klass);
  6913. }
  6914. };
  6915. @JRubyConstant
  6916. public final static int ROUND_DOWN = BigDecimal.ROUND_DOWN;
  6917. @JRubyConstant
  6918. public final static int ROUND_CEILING = BigDecimal.ROUND_CEILING;
  6919. @JRubyConstant
  6920. public final static int ROUND_UP = BigDecimal.ROUND_UP;
  6921. @JRubyConstant
  6922. public final static int ROUND_HALF_DOWN = BigDecimal.ROUND_HALF_DOWN;
  6923. @JRubyConstant
  6924. public final static int ROUND_HALF_EVEN = BigDecimal.ROUND_HALF_EVEN;
  6925. @JRubyConstant
  6926. public final static int ROUND_HALF_UP = BigDecimal.ROUND_HALF_UP;
  6927. @JRubyConstant
  6928. public final static int ROUND_FLOOR = BigDecimal.ROUND_FLOOR;
  6929. @JRubyConstant
  6930. public final static int SIGN_POSITIVE_INFINITE=3;
  6931. @JRubyConstant
  6932. public final static int EXCEPTION_OVERFLOW=1;
  6933. @JRubyConstant
  6934. public final static int SIGN_POSITIVE_ZERO=1;
  6935. @JRubyConstant
  6936. public final static int EXCEPTION_ALL=255;
  6937. @JRubyConstant
  6938. public final static int SIGN_NEGATIVE_FINITE=-2;
  6939. @JRubyConstant
  6940. public final static int EXCEPTION_UNDERFLOW=4;
  6941. @JRubyConstant
  6942. public final static int SIGN_NaN=0;
  6943. @JRubyConstant
  6944. public final static int BASE=10000;
  6945. @JRubyConstant
  6946. public final static int ROUND_MODE=256;
  6947. @JRubyConstant
  6948. public final static int SIGN_POSITIVE_FINITE=2;
  6949. @JRubyConstant
  6950. public final static int EXCEPTION_INFINITY=1;
  6951. @JRubyConstant
  6952. public final static int SIGN_NEGATIVE_INFINITE=-3;
  6953. @JRubyConstant
  6954. public final static int EXCEPTION_ZERODIVIDE=1;
  6955. @JRubyConstant
  6956. public final static int SIGN_NEGATIVE_ZERO=-1;
  6957. @JRubyConstant
  6958. public final static int EXCEPTION_NaN=2;
  6959. // Static constants
  6960. private static final BigDecimal TWO = new BigDecimal(2);
  6961. private static final double SQRT_10 = 3.162277660168379332;
  6962. public static RubyClass createBigDecimal(Ruby runtime) {
  6963. RubyClass result = runtime.defineClass("BigDecimal",runtime.getNumeric(), BIGDECIMAL_ALLOCATOR);
  6964. CallbackFactory callbackFactory = runtime.callbackFactory(RubyBigDecimal.class);
  6965. runtime.getKernel().defineAnnotatedMethods(BigDecimalKernelMethods.class);
  6966. result.setInternalModuleVariable("vpPrecLimit", RubyFixnum.zero(runtime));
  6967. result.setInternalModuleVariable("vpExceptionMode", RubyFixnum.zero(runtime));
  6968. result.setInternalModuleVariable("vpRoundingMode", runtime.newFixnum(ROUND_HALF_UP));
  6969. result.defineAnnotatedMethods(RubyBigDecimal.class);
  6970. result.defineAnnotatedConstants(RubyBigDecimal.class);
  6971. return result;
  6972. }
  6973. private boolean isNaN = false;
  6974. private int infinitySign = 0;
  6975. private int zeroSign = 0;
  6976. private BigDecimal value;
  6977. public BigDecimal getValue() {
  6978. return value;
  6979. }
  6980. public RubyBigDecimal(Ruby runtime, RubyClass klass) {
  6981. super(runtime, klass);
  6982. }
  6983. public RubyBigDecimal(Ruby runtime, BigDecimal value) {
  6984. super(runtime, runtime.fastGetClass("BigDecimal"));
  6985. this.value = value;
  6986. }
  6987. public static class BigDecimalKernelMethods {
  6988. @JRubyMethod(name = "BigDecimal", rest = true, module = true, visibility = Visibility.PRIVATE)
  6989. public static IRubyObject newBigDecimal(IRubyObject recv, IRubyObject[] args) {
  6990. return RubyBigDecimal.newBigDecimal(recv, args, Block.NULL_BLOCK);
  6991. }
  6992. }
  6993. public static RubyBigDecimal newBigDecimal(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  6994. return newInstance(recv.getRuntime().fastGetClass("BigDecimal"), args);
  6995. }
  6996. @JRubyMethod(name = "ver", meta = true)
  6997. public static IRubyObject ver(IRubyObject recv) {
  6998. return recv.getRuntime().newString("1.0.1");
  6999. }
  7000. @JRubyMethod(name = "_dump", optional = 1, frame = true)
  7001. public IRubyObject dump(IRubyObject[] args, Block unusedBlock) {
  7002. RubyString precision = RubyString.newUnicodeString(args[0].getRuntime(), "0:");
  7003. RubyString str = this.asString();
  7004. return precision.append(str);
  7005. }
  7006. @JRubyMethod(name = "_load", required = 1, frame = true, meta = true)
  7007. public static RubyBigDecimal load(IRubyObject recv, IRubyObject from, Block block) {
  7008. RubyBigDecimal rubyBigDecimal = (RubyBigDecimal) (((RubyClass)recv).allocate());
  7009. String precisionAndValue = from.convertToString().asJavaString();
  7010. String value = precisionAndValue.substring(precisionAndValue.indexOf(":")+1);
  7011. rubyBigDecimal.value = new BigDecimal(value);
  7012. return rubyBigDecimal;
  7013. }
  7014. @JRubyMethod(name = "double_fig", meta = true)
  7015. public static IRubyObject double_fig(IRubyObject recv) {
  7016. return recv.getRuntime().newFixnum(20);
  7017. }
  7018. @JRubyMethod(name = "limit", optional = 1, meta = true)
  7019. public static IRubyObject limit(IRubyObject recv, IRubyObject[] args) {
  7020. Ruby runtime = recv.getRuntime();
  7021. RubyModule c = (RubyModule)recv;
  7022. IRubyObject nCur = c.searchInternalModuleVariable("vpPrecLimit");
  7023. if (args.length > 0) {
  7024. IRubyObject arg = args[0];
  7025. if (!arg.isNil()) {
  7026. if (!(arg instanceof RubyFixnum)) {
  7027. throw runtime.newTypeError(arg, runtime.getFixnum());
  7028. }
  7029. if (0 > ((RubyFixnum)arg).getLongValue()) {
  7030. throw runtime.newArgumentError("argument must be positive");
  7031. }
  7032. c.setInternalModuleVariable("vpPrecLimit", arg);
  7033. }
  7034. }
  7035. return nCur;
  7036. }
  7037. @JRubyMethod(name = "mode", required = 1, optional = 1, meta = true)
  7038. public static IRubyObject mode(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  7039. // FIXME: I doubt any of the constants referenced in this method
  7040. // are ever redefined -- should compare to the known values, rather
  7041. // than do an expensive constant lookup.
  7042. Ruby runtime = recv.getRuntime();
  7043. RubyClass clazz = runtime.fastGetClass("BigDecimal");
  7044. RubyModule c = (RubyModule)recv;
  7045. args = Arity.scanArgs(runtime, args, 1, 1);
  7046. IRubyObject mode = args[0];
  7047. IRubyObject value = args[1];
  7048. if (!(mode instanceof RubyFixnum)) {
  7049. throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
  7050. }
  7051. long longMode = ((RubyFixnum)mode).getLongValue();
  7052. long EXCEPTION_ALL = ((RubyFixnum)clazz.fastGetConstant("EXCEPTION_ALL")).getLongValue();
  7053. if ((longMode & EXCEPTION_ALL) != 0) {
  7054. if (value.isNil()) {
  7055. return c.searchInternalModuleVariable("vpExceptionMode");
  7056. }
  7057. if (!(value.isNil()) && !(value instanceof RubyBoolean)) {
  7058. throw runtime.newTypeError("second argument must be true or false");
  7059. }
  7060. RubyFixnum currentExceptionMode = (RubyFixnum)c.searchInternalModuleVariable("vpExceptionMode");
  7061. RubyFixnum newExceptionMode = new RubyFixnum(runtime, currentExceptionMode.getLongValue());
  7062. RubyFixnum EXCEPTION_INFINITY = (RubyFixnum)clazz.fastGetConstant("EXCEPTION_INFINITY");
  7063. if ((longMode & EXCEPTION_INFINITY.getLongValue()) != 0) {
  7064. newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context, "|", EXCEPTION_INFINITY)
  7065. : (RubyFixnum)currentExceptionMode.callCoerced(context, "&", new RubyFixnum(runtime, ~(EXCEPTION_INFINITY).getLongValue()));
  7066. }
  7067. RubyFixnum EXCEPTION_NaN = (RubyFixnum)clazz.fastGetConstant("EXCEPTION_NaN");
  7068. if ((longMode & EXCEPTION_NaN.getLongValue()) != 0) {
  7069. newExceptionMode = (value.isTrue()) ? (RubyFixnum)currentExceptionMode.callCoerced(context, "|", EXCEPTION_NaN)
  7070. : (RubyFixnum)currentExceptionMode.callCoerced(context, "&", new RubyFixnum(runtime, ~(EXCEPTION_NaN).getLongValue()));
  7071. }
  7072. c.setInternalModuleVariable("vpExceptionMode", newExceptionMode);
  7073. return newExceptionMode;
  7074. }
  7075. long ROUND_MODE = ((RubyFixnum)clazz.fastGetConstant("ROUND_MODE")).getLongValue();
  7076. if (longMode == ROUND_MODE) {
  7077. if (value.isNil()) {
  7078. return c.searchInternalModuleVariable("vpRoundingMode");
  7079. }
  7080. if (!(value instanceof RubyFixnum)) {
  7081. throw runtime.newTypeError("wrong argument type " + mode.getMetaClass() + " (expected Fixnum)");
  7082. }
  7083. RubyFixnum roundingMode = (RubyFixnum)value;
  7084. if (roundingMode == clazz.fastGetConstant("ROUND_UP") ||
  7085. roundingMode == clazz.fastGetConstant("ROUND_DOWN") ||
  7086. roundingMode == clazz.fastGetConstant("ROUND_FLOOR") ||
  7087. roundingMode == clazz.fastGetConstant("ROUND_CEILING") ||
  7088. roundingMode == clazz.fastGetConstant("ROUND_HALF_UP") ||
  7089. roundingMode == clazz.fastGetConstant("ROUND_HALF_DOWN") ||
  7090. roundingMode == clazz.fastGetConstant("ROUND_HALF_EVEN")) {
  7091. c.setInternalModuleVariable("vpRoundingMode", roundingMode);
  7092. } else {
  7093. throw runtime.newTypeError("invalid rounding mode");
  7094. }
  7095. return c.searchInternalModuleVariable("vpRoundingMode");
  7096. }
  7097. throw runtime.newTypeError("first argument for BigDecimal#mode invalid");
  7098. }
  7099. private RoundingMode getRoundingMode(Ruby runtime) {
  7100. RubyFixnum roundingMode = (RubyFixnum)runtime.fastGetClass("BigDecimal")
  7101. .searchInternalModuleVariable("vpRoundingMode");
  7102. return RoundingMode.valueOf((int)roundingMode.getLongValue());
  7103. }
  7104. private RubyBigDecimal getVpValue(IRubyObject v, boolean must) {
  7105. if(v instanceof RubyBigDecimal) {
  7106. return (RubyBigDecimal)v;
  7107. } else if(v instanceof RubyFixnum || v instanceof RubyBignum) {
  7108. String s = v.toString();
  7109. return newInstance(getRuntime().fastGetClass("BigDecimal"),new IRubyObject[]{getRuntime().newString(s)});
  7110. }
  7111. if(must) {
  7112. String err;
  7113. if (isImmediate()) {
  7114. ThreadContext context = getRuntime().getCurrentContext();
  7115. err = inspect(context, this).toString();
  7116. } else {
  7117. err = getMetaClass().getBaseName();
  7118. }
  7119. throw getRuntime().newTypeError(err + " can't be coerced into BigDecimal");
  7120. }
  7121. return null;
  7122. }
  7123. private final static Pattern INFINITY_PATTERN = Pattern.compile("^([+-])?Infinity$");
  7124. private final static Pattern NUMBER_PATTERN
  7125. = Pattern.compile("^([+-]?\\d*\\.?\\d*([eE][+-]?)?\\d*).*");
  7126. @JRubyMethod(name = "new", required = 1, optional = 1, meta = true)
  7127. public static RubyBigDecimal newInstance(IRubyObject recv, IRubyObject[] args) {
  7128. BigDecimal decimal;
  7129. if (args.length == 0) {
  7130. decimal = new BigDecimal(0);
  7131. } else {
  7132. String strValue = args[0].convertToString().toString();
  7133. strValue = strValue.trim();
  7134. if ("NaN".equals(strValue)) {
  7135. return newNaN(recv.getRuntime());
  7136. }
  7137. Matcher m = INFINITY_PATTERN.matcher(strValue);
  7138. if (m.matches()) {
  7139. int sign = 1;
  7140. String signGroup = m.group(1);
  7141. if ("-".equals(signGroup)) {
  7142. sign = -1;
  7143. }
  7144. return newInfinity(recv.getRuntime(), sign);
  7145. }
  7146. // Clean-up string representation so that it could be understood
  7147. // by Java's BigDecimal. Not terribly efficient for now.
  7148. // 1. MRI allows d and D as exponent separators
  7149. strValue = strValue.replaceFirst("[dD]", "E");
  7150. // 2. MRI allows underscores anywhere
  7151. strValue = strValue.replaceAll("_", "");
  7152. // 3. MRI ignores the trailing junk
  7153. strValue = NUMBER_PATTERN.matcher(strValue).replaceFirst("$1");
  7154. try {
  7155. decimal = new BigDecimal(strValue);
  7156. } catch(NumberFormatException e) {
  7157. decimal = new BigDecimal(0);
  7158. }
  7159. if (decimal.signum() == 0) {
  7160. // MRI behavior: -0 and +0 are two different things
  7161. if (strValue.matches("^\\s*-.*")) {
  7162. return newZero(recv.getRuntime(), -1);
  7163. } else {
  7164. return newZero(recv.getRuntime(), 1);
  7165. }
  7166. }
  7167. }
  7168. return new RubyBigDecimal(recv.getRuntime(), decimal);
  7169. }
  7170. private static RubyBigDecimal newZero(Ruby runtime, int sign) {
  7171. RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
  7172. if (sign < 0) {
  7173. rbd.zeroSign = -1;
  7174. } else {
  7175. rbd.zeroSign = 1;
  7176. }
  7177. return rbd;
  7178. }
  7179. private static RubyBigDecimal newNaN(Ruby runtime) {
  7180. RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
  7181. rbd.isNaN = true;
  7182. return rbd;
  7183. }
  7184. private static RubyBigDecimal newInfinity(Ruby runtime, int sign) {
  7185. RubyBigDecimal rbd = new RubyBigDecimal(runtime, BigDecimal.ZERO);
  7186. if (sign < 0) {
  7187. rbd.infinitySign = -1;
  7188. } else {
  7189. rbd.infinitySign = 1;
  7190. }
  7191. return rbd;
  7192. }
  7193. private RubyBigDecimal setResult() {
  7194. return setResult(0);
  7195. }
  7196. private RubyBigDecimal setResult(int scale) {
  7197. int prec = RubyFixnum.fix2int(getRuntime().fastGetClass("BigDecimal").searchInternalModuleVariable("vpPrecLimit"));
  7198. int prec2 = Math.max(scale,prec);
  7199. if(prec2 > 0 && this.value.scale() > (prec2-getExponent())) {
  7200. this.value = this.value.setScale(prec2-getExponent(),BigDecimal.ROUND_HALF_UP);
  7201. }
  7202. return this;
  7203. }
  7204. @JRubyMethod(name = "hash")
  7205. public RubyFixnum hash() {
  7206. return getRuntime().newFixnum(value.hashCode());
  7207. }
  7208. @JRubyMethod(name = {"%", "modulo"}, required = 1)
  7209. public IRubyObject op_mod(ThreadContext context, IRubyObject arg) {
  7210. // TODO: full-precision remainder is 1000x slower than MRI!
  7211. Ruby runtime = context.getRuntime();
  7212. if (isInfinity() || isNaN()) {
  7213. return newNaN(runtime);
  7214. }
  7215. RubyBigDecimal val = getVpValue(arg, false);
  7216. if (val == null) {
  7217. return callCoerced(context, "%", arg, true);
  7218. }
  7219. if (val.isInfinity() || val.isNaN() || val.isZero()) {
  7220. return newNaN(runtime);
  7221. }
  7222. // Java and MRI definitions of modulo are different.
  7223. BigDecimal modulo = value.remainder(val.value);
  7224. if (modulo.signum() * val.value.signum() < 0) {
  7225. modulo = modulo.add(val.value);
  7226. }
  7227. return new RubyBigDecimal(runtime, modulo).setResult();
  7228. }
  7229. @JRubyMethod(name = "remainder", required = 1)
  7230. public IRubyObject remainder(ThreadContext context, IRubyObject arg) {
  7231. // TODO: full-precision remainder is 1000x slower than MRI!
  7232. Ruby runtime = context.getRuntime();
  7233. if (isInfinity() || isNaN()) {
  7234. return newNaN(runtime);
  7235. }
  7236. RubyBigDecimal val = getVpValue(arg,false);
  7237. if (val == null) {
  7238. return callCoerced(context, "remainder", arg, true);
  7239. }
  7240. if (val.isInfinity() || val.isNaN() || val.isZero()) {
  7241. return newNaN(runtime);
  7242. }
  7243. // Java and MRI definitions of remainder are the same.
  7244. return new RubyBigDecimal(runtime, value.remainder(val.value)).setResult();
  7245. }
  7246. @JRubyMethod(name = "*", required = 1)
  7247. public IRubyObject op_mul(ThreadContext context, IRubyObject arg) {
  7248. return mult2(context, arg, RubyFixnum.zero(context.getRuntime()));
  7249. }
  7250. @JRubyMethod(name = "mult", required = 2)
  7251. public IRubyObject mult2(ThreadContext context, IRubyObject b, IRubyObject n) {
  7252. Ruby runtime = context.getRuntime();
  7253. RubyBigDecimal val = getVpValue(b,false);
  7254. if(val == null) {
  7255. // TODO: what about n arg?
  7256. return callCoerced(context, "*", b);
  7257. }
  7258. int digits = RubyNumeric.fix2int(n);
  7259. if (isNaN() || val.isNaN()) {
  7260. return newNaN(runtime);
  7261. }
  7262. if ((isInfinity() && val.isZero()) || (isZero() && val.isInfinity())) {
  7263. return newNaN(runtime);
  7264. }
  7265. if (isZero() || val.isZero()) {
  7266. int sign1 = isZero()? zeroSign : value.signum();
  7267. int sign2 = val.isZero() ? val.zeroSign : val.value.signum();
  7268. return newZero(runtime, sign1 * sign2);
  7269. }
  7270. if (isInfinity() || val.isInfinity()) {
  7271. int sign1 = isInfinity() ? infinitySign : value.signum();
  7272. int sign2 = val.isInfinity() ? val.infinitySign : val.value.signum();
  7273. return newInfinity(runtime, sign1 * sign2);
  7274. }
  7275. BigDecimal res = value.multiply(val.value);
  7276. if (res.precision() > digits) {
  7277. // TODO: rounding mode should not be hard-coded. See #mode.
  7278. res = res.round(new MathContext(digits, RoundingMode.HALF_UP));
  7279. }
  7280. return new RubyBigDecimal(runtime, res).setResult();
  7281. }
  7282. @JRubyMethod(name = {"**", "power"}, required = 1)
  7283. public IRubyObject op_pow(IRubyObject arg) {
  7284. if (!(arg instanceof RubyFixnum)) {
  7285. throw getRuntime().newTypeError("wrong argument type " + arg.getMetaClass() + " (expected Fixnum)");
  7286. }
  7287. if (isNaN() || isInfinity()) {
  7288. return newNaN(getRuntime());
  7289. }
  7290. int times = RubyNumeric.fix2int(arg.convertToInteger());
  7291. if (times < 0) {
  7292. if (isZero()) {
  7293. return newInfinity(getRuntime(), value.signum());
  7294. }
  7295. // Note: MRI has a very non-trivial way of calculating the precision,
  7296. // so we use very simple approximation here:
  7297. int precision = (-times + 4) * (getAllDigits().length() + 4);
  7298. return new RubyBigDecimal(getRuntime(),
  7299. value.pow(times, new MathContext(precision, RoundingMode.HALF_UP)));
  7300. } else {
  7301. return new RubyBigDecimal(getRuntime(), value.pow(times));
  7302. }
  7303. }
  7304. @JRubyMethod(name = "+", required = 1, frame=true)
  7305. public IRubyObject op_plus(ThreadContext context, IRubyObject b) {
  7306. return addInternal(context, b, "add", RubyFixnum.zero(context.getRuntime()));
  7307. }
  7308. @JRubyMethod(name = "add", required = 2, frame=true)
  7309. public IRubyObject add2(ThreadContext context, IRubyObject b, IRubyObject digits) {
  7310. return addInternal(context, b, "add", digits);
  7311. }
  7312. private IRubyObject addInternal(ThreadContext context, IRubyObject b, String op, IRubyObject digits) {
  7313. Ruby runtime = context.getRuntime();
  7314. int prec = getPositiveInt(context, digits);
  7315. RubyBigDecimal val = getVpValue(b, false);
  7316. if (val == null) {
  7317. // TODO:
  7318. // MRI behavior: Call "+" or "add", depending on the call.
  7319. // But this leads to exceptions when Floats are added. See:
  7320. // http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/17374
  7321. // return callCoerced(context, op, b, true); -- this is MRI behavior.
  7322. // We'll use ours for now, thus providing an ability to add Floats.
  7323. return callCoerced(context, "+", b, true);
  7324. }
  7325. IRubyObject res = handleAddSpecialValues(val);
  7326. if (res != null) {
  7327. return res;
  7328. }
  7329. RoundingMode roundMode = getRoundingMode(runtime);
  7330. return new RubyBigDecimal(runtime, value.add(
  7331. val.value, new MathContext(prec, roundMode))); // TODO: why this: .setResult();
  7332. }
  7333. private int getPositiveInt(ThreadContext context, IRubyObject arg) {
  7334. Ruby runtime = context.getRuntime();
  7335. if (arg instanceof RubyFixnum) {
  7336. int value = RubyNumeric.fix2int(arg);
  7337. if (value < 0) {
  7338. throw runtime.newArgumentError("argument must be positive");
  7339. }
  7340. return value;
  7341. } else {
  7342. throw runtime.newTypeError(arg, runtime.getFixnum());
  7343. }
  7344. }
  7345. private IRubyObject handleAddSpecialValues(RubyBigDecimal val) {
  7346. if (isNaN() || val.isNaN) {
  7347. return newNaN(getRuntime());
  7348. }
  7349. // TODO: don't calculate the same value 3 times
  7350. if (infinitySign * val.infinitySign > 0) {
  7351. return isInfinity() ? this : val;
  7352. }
  7353. if (infinitySign * val.infinitySign < 0) {
  7354. return newNaN(getRuntime());
  7355. }
  7356. if (infinitySign * val.infinitySign == 0) {
  7357. int sign = infinitySign + val.infinitySign;
  7358. if (sign != 0) {
  7359. return newInfinity(getRuntime(), sign);
  7360. }
  7361. }
  7362. return null;
  7363. }
  7364. @JRubyMethod(name = "+@")
  7365. public IRubyObject op_uplus() {
  7366. return this;
  7367. }
  7368. @JRubyMethod(name = "-", required = 1)
  7369. public IRubyObject op_minus(ThreadContext context, IRubyObject arg) {
  7370. RubyBigDecimal val = getVpValue(arg, false);
  7371. if(val == null) {
  7372. return callCoerced(context, "-", arg);
  7373. }
  7374. RubyBigDecimal res = handleMinusSpecialValues(val);
  7375. if (res != null) {
  7376. return res;
  7377. }
  7378. return new RubyBigDecimal(getRuntime(),value.subtract(val.value)).setResult();
  7379. }
  7380. @JRubyMethod(name = "sub", required = 2)
  7381. public IRubyObject sub2(ThreadContext context, IRubyObject b, IRubyObject n) {
  7382. RubyBigDecimal val = getVpValue(b, false);
  7383. if(val == null) {
  7384. return callCoerced(context, "-", b);
  7385. }
  7386. RubyBigDecimal res = handleMinusSpecialValues(val);
  7387. if (res != null) {
  7388. return res;
  7389. }
  7390. return new RubyBigDecimal(getRuntime(),value.subtract(val.value)).setResult();
  7391. }
  7392. private RubyBigDecimal handleMinusSpecialValues(RubyBigDecimal val) {
  7393. if (isNaN() || val.isNaN()) {
  7394. return newNaN(getRuntime());
  7395. }
  7396. // TODO: 3 times calculate the same value below
  7397. if (infinitySign * val.infinitySign > 0) {
  7398. return newNaN(getRuntime());
  7399. }
  7400. if (infinitySign * val.infinitySign < 0) {
  7401. return this;
  7402. }
  7403. if (infinitySign * val.infinitySign == 0) {
  7404. if (isInfinity()) {
  7405. return this;
  7406. }
  7407. if (val.isInfinity()) {
  7408. return newInfinity(getRuntime(), val.infinitySign * -1);
  7409. }
  7410. int sign = infinitySign + val.infinitySign;
  7411. if (sign != 0) {
  7412. return newInfinity(getRuntime(), sign);
  7413. }
  7414. }
  7415. return null;
  7416. }
  7417. @JRubyMethod(name = "-@")
  7418. public IRubyObject op_uminus() {
  7419. Ruby runtime = getRuntime();
  7420. if (isNaN()) {
  7421. return newNaN(runtime);
  7422. }
  7423. if (isInfinity()) {
  7424. return newInfinity(runtime, -infinitySign);
  7425. }
  7426. if (isZero()) {
  7427. return newZero(runtime, -zeroSign);
  7428. }
  7429. return new RubyBigDecimal(getRuntime(), value.negate());
  7430. }
  7431. @JRubyMethod(name = {"/", "quo"})
  7432. public IRubyObject op_quo(ThreadContext context, IRubyObject other) {
  7433. // regular division with some default precision
  7434. // TODO: proper algorithm to set the precision
  7435. return op_div(context, other, getRuntime().newFixnum(200));
  7436. }
  7437. @JRubyMethod(name = "div")
  7438. public IRubyObject op_div(ThreadContext context, IRubyObject other) {
  7439. // integer division
  7440. RubyBigDecimal val = getVpValue(other, false);
  7441. if (val == null) {
  7442. return callCoerced(context, "div", other);
  7443. }
  7444. if (isNaN() || val.isZero() || val.isNaN()) {
  7445. return newNaN(getRuntime());
  7446. }
  7447. if (isInfinity() || val.isInfinity()) {
  7448. return newNaN(getRuntime());
  7449. }
  7450. return new RubyBigDecimal(getRuntime(),
  7451. this.value.divideToIntegralValue(val.value)).setResult();
  7452. }
  7453. @JRubyMethod(name = "div")
  7454. public IRubyObject op_div(ThreadContext context, IRubyObject other, IRubyObject digits) {
  7455. // TODO: take BigDecimal.mode into account.
  7456. int scale = RubyNumeric.fix2int(digits);
  7457. RubyBigDecimal val = getVpValue(other, false);
  7458. if (val == null) {
  7459. return callCoerced(context, "/", other);
  7460. }
  7461. if (isNaN() || (isZero() && val.isZero()) || val.isNaN()) {
  7462. return newNaN(getRuntime());
  7463. }
  7464. if (val.isZero()) {
  7465. int sign1 = isInfinity() ? infinitySign : value.signum();
  7466. return newInfinity(getRuntime(), sign1 * val.zeroSign);
  7467. }
  7468. if (isInfinity() && !val.isInfinity()) {
  7469. return newInfinity(getRuntime(), infinitySign * val.value.signum());
  7470. }
  7471. if (!isInfinity() && val.isInfinity()) {
  7472. return newZero(getRuntime(), value.signum() * val.infinitySign);
  7473. }
  7474. if (isInfinity() && val.isInfinity()) {
  7475. return newNaN(getRuntime());
  7476. }
  7477. if (scale == 0) {
  7478. // MRI behavior: "If digits is 0, the result is the same as the / operator."
  7479. return op_quo(context, other);
  7480. } else {
  7481. // TODO: better algorithm to set precision needed
  7482. int prec = Math.max(200, scale);
  7483. return new RubyBigDecimal(getRuntime(),
  7484. value.divide(val.value, new MathContext(prec, RoundingMode.HALF_UP))).setResult(scale);
  7485. }
  7486. }
  7487. private IRubyObject cmp(ThreadContext context, IRubyObject r, char op) {
  7488. int e = 0;
  7489. RubyBigDecimal rb = getVpValue(r,false);
  7490. if(rb == null) {
  7491. IRubyObject ee = callCoerced(context, "<=>",r);
  7492. if(ee.isNil()) {
  7493. return getRuntime().getNil();
  7494. }
  7495. e = RubyNumeric.fix2int(ee);
  7496. } else {
  7497. if (isNaN() | rb.isNaN()) {
  7498. return getRuntime().getNil();
  7499. }
  7500. if (infinitySign != 0 || rb.infinitySign != 0) {
  7501. e = infinitySign - rb.infinitySign;
  7502. } else {
  7503. e = value.compareTo(rb.value);
  7504. }
  7505. }
  7506. switch(op) {
  7507. case '*': return getRuntime().newFixnum(e);
  7508. case '=': return (e==0)?getRuntime().getTrue():getRuntime().getFalse();
  7509. case '!': return (e!=0)?getRuntime().getTrue():getRuntime().getFalse();
  7510. case 'G': return (e>=0)?getRuntime().getTrue():getRuntime().getFalse();
  7511. case '>': return (e> 0)?getRuntime().getTrue():getRuntime().getFalse();
  7512. case 'L': return (e<=0)?getRuntime().getTrue():getRuntime().getFalse();
  7513. case '<': return (e< 0)?getRuntime().getTrue():getRuntime().getFalse();
  7514. }
  7515. return getRuntime().getNil();
  7516. }
  7517. @JRubyMethod(name = "<=>", required = 1)
  7518. public IRubyObject op_cmp(ThreadContext context, IRubyObject arg) {
  7519. return cmp(context, arg,'*');
  7520. }
  7521. @JRubyMethod(name = {"eql?", "==", "==="}, required = 1)
  7522. public IRubyObject eql_p(ThreadContext context, IRubyObject arg) {
  7523. return cmp(context, arg,'=');
  7524. }
  7525. @JRubyMethod(name = "<", required = 1)
  7526. public IRubyObject op_lt(ThreadContext context, IRubyObject arg) {
  7527. return cmp(context, arg,'<');
  7528. }
  7529. @JRubyMethod(name = "<=", required = 1)
  7530. public IRubyObject op_le(ThreadContext context, IRubyObject arg) {
  7531. return cmp(context, arg,'L');
  7532. }
  7533. @JRubyMethod(name = ">", required = 1)
  7534. public IRubyObject op_gt(ThreadContext context, IRubyObject arg) {
  7535. return cmp(context, arg,'>');
  7536. }
  7537. @JRubyMethod(name = ">=", required = 1)
  7538. public IRubyObject op_ge(ThreadContext context, IRubyObject arg) {
  7539. return cmp(context, arg,'G');
  7540. }
  7541. @JRubyMethod(name = "abs")
  7542. public IRubyObject abs() {
  7543. Ruby runtime = getRuntime();
  7544. if (isNaN) {
  7545. return newNaN(runtime);
  7546. }
  7547. if (isInfinity()) {
  7548. return newInfinity(runtime, 1);
  7549. }
  7550. return new RubyBigDecimal(getRuntime(), value.abs()).setResult();
  7551. }
  7552. @JRubyMethod(name = "ceil", optional = 1)
  7553. public IRubyObject ceil(IRubyObject[] args) {
  7554. if (isNaN) {
  7555. return newNaN(getRuntime());
  7556. }
  7557. if (isInfinity()) {
  7558. return newInfinity(getRuntime(), infinitySign);
  7559. }
  7560. int n = 0;
  7561. if (args.length > 0) {
  7562. n = RubyNumeric.fix2int(args[0]);
  7563. }
  7564. if (value.scale() > n) { // rounding neccessary
  7565. return new RubyBigDecimal(getRuntime(),
  7566. value.setScale(n, RoundingMode.CEILING));
  7567. } else {
  7568. return this;
  7569. }
  7570. }
  7571. @JRubyMethod(name = "coerce", required = 1)
  7572. public IRubyObject coerce(IRubyObject other) {
  7573. IRubyObject obj;
  7574. if(other instanceof RubyFloat) {
  7575. obj = getRuntime().newArray(other,to_f());
  7576. } else {
  7577. obj = getRuntime().newArray(getVpValue(other, true),this);
  7578. }
  7579. return obj;
  7580. }
  7581. public double getDoubleValue() { return value.doubleValue(); }
  7582. public long getLongValue() { return value.longValue(); }
  7583. public RubyNumeric multiplyWith(ThreadContext context, RubyInteger value) {
  7584. return (RubyNumeric)op_mul(context, value);
  7585. }
  7586. public RubyNumeric multiplyWith(ThreadContext context, RubyFloat value) {
  7587. return (RubyNumeric)op_mul(context, value);
  7588. }
  7589. public RubyNumeric multiplyWith(ThreadContext context, RubyBignum value) {
  7590. return (RubyNumeric)op_mul(context, value);
  7591. }
  7592. @JRubyMethod(name = "divmod", required = 1)
  7593. public IRubyObject divmod(ThreadContext context, IRubyObject other) {
  7594. // TODO: full-precision divmod is 1000x slower than MRI!
  7595. Ruby runtime = context.getRuntime();
  7596. if (isInfinity() || isNaN()) {
  7597. return RubyArray.newArray(runtime, newNaN(runtime), newNaN(runtime));
  7598. }
  7599. RubyBigDecimal val = getVpValue(other, false);
  7600. if (val == null) {
  7601. return callCoerced(context, "divmod", other, true);
  7602. }
  7603. if (val.isInfinity() || val.isNaN() || val.isZero()) {
  7604. return RubyArray.newArray(runtime, newNaN(runtime), newNaN(runtime));
  7605. }
  7606. // Java and MRI definitions of divmod are different.
  7607. BigDecimal[] divmod = value.divideAndRemainder(val.value);
  7608. BigDecimal div = divmod[0];
  7609. BigDecimal mod = divmod[1];
  7610. if (mod.signum() * val.value.signum() < 0) {
  7611. div = div.subtract(BigDecimal.ONE);
  7612. mod = mod.add(val.value);
  7613. }
  7614. return RubyArray.newArray(runtime,
  7615. new RubyBigDecimal(runtime, div),
  7616. new RubyBigDecimal(runtime, mod));
  7617. }
  7618. @JRubyMethod(name = "exponent")
  7619. public IRubyObject exponent() {
  7620. return getRuntime().newFixnum(getExponent());
  7621. }
  7622. @JRubyMethod(name = "finite?")
  7623. public IRubyObject finite_p() {
  7624. if (isNaN()) {
  7625. return getRuntime().getFalse();
  7626. }
  7627. return getRuntime().newBoolean(!isInfinity());
  7628. }
  7629. @JRubyMethod(name = "floor", optional = 1)
  7630. public IRubyObject floor(IRubyObject[]args) {
  7631. if (isNaN) {
  7632. return newNaN(getRuntime());
  7633. }
  7634. if (isInfinity()) {
  7635. return newInfinity(getRuntime(), infinitySign);
  7636. }
  7637. int n = 0;
  7638. if (args.length > 0) {
  7639. n = RubyNumeric.fix2int(args[0]);
  7640. }
  7641. if (value.scale() > n) { // rounding neccessary
  7642. return new RubyBigDecimal(getRuntime(),
  7643. value.setScale(n, RoundingMode.FLOOR));
  7644. } else {
  7645. return this;
  7646. }
  7647. }
  7648. @JRubyMethod(name = "frac")
  7649. public IRubyObject frac() {
  7650. if (isNaN) {
  7651. return newNaN(getRuntime());
  7652. }
  7653. if (isInfinity()) {
  7654. return newInfinity(getRuntime(), infinitySign);
  7655. }
  7656. if (value.scale() > 0 && value.precision() < value.scale()) {
  7657. return new RubyBigDecimal(getRuntime(), value);
  7658. }
  7659. BigDecimal val = value.subtract(((RubyBigDecimal)fix()).value);
  7660. return new RubyBigDecimal(getRuntime(), val);
  7661. }
  7662. @JRubyMethod(name = "infinite?")
  7663. public IRubyObject infinite_p() {
  7664. if (infinitySign == 0) {
  7665. return getRuntime().getNil();
  7666. }
  7667. return getRuntime().newFixnum(infinitySign);
  7668. }
  7669. @JRubyMethod(name = "inspect")
  7670. public IRubyObject inspect(ThreadContext context) {
  7671. StringBuilder val = new StringBuilder("#<BigDecimal:").append(Integer.toHexString(System.identityHashCode(this))).append(",");
  7672. val.append("'").append(this.callMethod(context, MethodIndex.TO_S, "to_s")).append("'").append(",");
  7673. val.append(getSignificantDigits().length()).append("(");
  7674. int len = getAllDigits().length();
  7675. int pow = len / 4;
  7676. val.append((pow + 1) * 4).append(")").append(">");
  7677. return getRuntime().newString(val.toString());
  7678. }
  7679. @JRubyMethod(name = "nan?")
  7680. public IRubyObject nan_p() {
  7681. return getRuntime().newBoolean(isNaN);
  7682. }
  7683. @JRubyMethod(name = "nonzero?")
  7684. public IRubyObject nonzero_p() {
  7685. return isZero() ? getRuntime().getNil() : this;
  7686. }
  7687. @JRubyMethod(name = "precs")
  7688. public IRubyObject precs() {
  7689. final Ruby runtime = getRuntime();
  7690. final IRubyObject[] array = new IRubyObject[2];
  7691. array[0] = runtime.newFixnum(getSignificantDigits().length());
  7692. int len = getAllDigits().length();
  7693. int pow = len / 4;
  7694. array[1] = runtime.newFixnum((pow + 1) * 4);
  7695. return RubyArray.newArray(runtime, array);
  7696. }
  7697. @JRubyMethod(name = "round", optional = 2)
  7698. public IRubyObject round(IRubyObject[] args) {
  7699. int scale = args.length > 0 ? num2int(args[0]) : 0;
  7700. int mode = (args.length > 1) ? javaRoundingModeFromRubyRoundingMode(args[1]) : BigDecimal.ROUND_HALF_UP;
  7701. // JRUBY-914: Java 1.4 BigDecimal does not allow a negative scale, so we have to simulate it
  7702. if (scale < 0) {
  7703. // shift the decimal point just to the right of the digit to be rounded to (divide by 10**(abs(scale)))
  7704. // -1 -> 10's digit, -2 -> 100's digit, etc.
  7705. BigDecimal normalized = value.movePointRight(scale);
  7706. // ...round to that digit
  7707. BigDecimal rounded = normalized.setScale(0,mode);
  7708. // ...and shift the result back to the left (multiply by 10**(abs(scale)))
  7709. return new RubyBigDecimal(getRuntime(), rounded.movePointLeft(scale));
  7710. } else {
  7711. return new RubyBigDecimal(getRuntime(), value.setScale(scale, mode));
  7712. }
  7713. }
  7714. //this relies on the Ruby rounding enumerations == Java ones, which they (currently) all are
  7715. private int javaRoundingModeFromRubyRoundingMode(IRubyObject arg) {
  7716. return num2int(arg);
  7717. }
  7718. @JRubyMethod(name = "sign")
  7719. public IRubyObject sign() {
  7720. if (isNaN()) {
  7721. return getMetaClass().fastGetConstant("SIGN_NaN");
  7722. }
  7723. if (isInfinity()) {
  7724. if (infinitySign < 0) {
  7725. return getMetaClass().fastGetConstant("SIGN_NEGATIVE_INFINITE");
  7726. } else {
  7727. return getMetaClass().fastGetConstant("SIGN_POSITIVE_INFINITE");
  7728. }
  7729. }
  7730. if (isZero()) {
  7731. if (zeroSign < 0) {
  7732. return getMetaClass().fastGetConstant("SIGN_NEGATIVE_ZERO");
  7733. } else {
  7734. return getMetaClass().fastGetConstant("SIGN_POSITIVE_ZERO");
  7735. }
  7736. }
  7737. if (value.signum() < 0) {
  7738. return getMetaClass().fastGetConstant("SIGN_NEGATIVE_FINITE");
  7739. } else {
  7740. return getMetaClass().fastGetConstant("SIGN_POSITIVE_FINITE");
  7741. }
  7742. }
  7743. @JRubyMethod(name = "split")
  7744. public RubyArray split() {
  7745. final Ruby runtime = getRuntime();
  7746. final IRubyObject[] array = new IRubyObject[4];
  7747. // sign
  7748. final RubyFixnum sign;
  7749. if (isNaN) {
  7750. sign = RubyFixnum.zero(runtime);
  7751. } else if (isInfinity()) {
  7752. sign = runtime.newFixnum(infinitySign);
  7753. } else if (isZero()){
  7754. sign = runtime.newFixnum(zeroSign);
  7755. } else {
  7756. sign = runtime.newFixnum(value.signum());
  7757. }
  7758. array[0] = sign;
  7759. // significant digits and exponent
  7760. final RubyString digits;
  7761. final RubyFixnum exp;
  7762. if (isNaN()) {
  7763. digits = runtime.newString("NaN");
  7764. exp = RubyFixnum.zero(runtime);
  7765. } else if (isInfinity()) {
  7766. digits = runtime.newString("Infinity");
  7767. exp = RubyFixnum.zero(runtime);
  7768. } else if (isZero()){
  7769. digits = runtime.newString("0");
  7770. exp = RubyFixnum.zero(runtime);
  7771. } else {
  7772. // normalize the value
  7773. digits = runtime.newString(getSignificantDigits());
  7774. exp = runtime.newFixnum(getExponent());
  7775. }
  7776. array[1] = digits;
  7777. array[3] = exp;
  7778. // base
  7779. array[2] = runtime.newFixnum(10);
  7780. return RubyArray.newArray(runtime, array);
  7781. }
  7782. // it doesn't handle special cases
  7783. private String getSignificantDigits() {
  7784. // TODO: no need to calculate every time.
  7785. BigDecimal val = value.abs().stripTrailingZeros();
  7786. return val.unscaledValue().toString();
  7787. }
  7788. private String getAllDigits() {
  7789. // TODO: no need to calculate every time.
  7790. BigDecimal val = value.abs();
  7791. return val.unscaledValue().toString();
  7792. }
  7793. // it doesn't handle special cases
  7794. private int getExponent() {
  7795. // TODO: no need to calculate every time.
  7796. if (isZero()) {
  7797. return 0;
  7798. }
  7799. BigDecimal val = value.abs().stripTrailingZeros();
  7800. return val.precision() - val.scale();
  7801. }
  7802. @JRubyMethod(name = "sqrt", required = 1)
  7803. public IRubyObject sqrt(IRubyObject arg) {
  7804. Ruby runtime = getRuntime();
  7805. if (isNaN()) {
  7806. throw runtime.newFloatDomainError("(VpSqrt) SQRT(NaN value)");
  7807. }
  7808. if ((isInfinity() && infinitySign < 0) || value.signum() < 0) {
  7809. throw runtime.newFloatDomainError("(VpSqrt) SQRT(negative value)");
  7810. }
  7811. if (isInfinity() && infinitySign > 0) {
  7812. return newInfinity(runtime, 1);
  7813. }
  7814. // NOTE: MRI's sqrt precision is limited by 100,
  7815. // but we allow values more than 100.
  7816. int n = RubyNumeric.fix2int(arg);
  7817. if (n < 0) {
  7818. throw runtime.newArgumentError("argument must be positive");
  7819. }
  7820. n += 4; // just in case, add a bit of extra precision
  7821. return new RubyBigDecimal(getRuntime(),
  7822. bigSqrt(this.value, new MathContext(n, RoundingMode.HALF_UP))).setResult();
  7823. }
  7824. @JRubyMethod(name = "to_f")
  7825. public IRubyObject to_f() {
  7826. if (isNaN()) {
  7827. return RubyFloat.newFloat(getRuntime(), Double.NaN);
  7828. }
  7829. if (isInfinity()) {
  7830. return RubyFloat.newFloat(getRuntime(),
  7831. infinitySign < 0 ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
  7832. }
  7833. if (isZero()) {
  7834. return RubyFloat.newFloat(getRuntime(),
  7835. zeroSign < 0 ? -0.0 : 0.0);
  7836. }
  7837. return RubyFloat.newFloat(getRuntime(), value.doubleValue());
  7838. }
  7839. @JRubyMethod(name = {"to_i", "to_int"})
  7840. public IRubyObject to_int() {
  7841. if (isNaN() || infinitySign != 0) {
  7842. return getRuntime().getNil();
  7843. }
  7844. try {
  7845. return RubyNumeric.int2fix(getRuntime(), value.longValueExact());
  7846. } catch (ArithmeticException ae) {
  7847. return RubyBignum.bignorm(getRuntime(), value.toBigInteger());
  7848. }
  7849. }
  7850. private String removeTrailingZeroes(String in) {
  7851. while(in.length() > 0 && in.charAt(in.length()-1)=='0') {
  7852. in = in.substring(0,in.length()-1);
  7853. }
  7854. return in;
  7855. }
  7856. public static boolean formatHasLeadingPlus(String format) {
  7857. return format.startsWith("+");
  7858. }
  7859. public static boolean formatHasLeadingSpace(String format) {
  7860. return format.startsWith(" ");
  7861. }
  7862. public static boolean formatHasFloatingPointNotation(String format) {
  7863. return format.endsWith("F");
  7864. }
  7865. public static int formatFractionalDigitGroups(String format) {
  7866. int groups = 0;
  7867. Pattern p = Pattern.compile("(\\+| )?(\\d+)(E|F)?");
  7868. Matcher m = p.matcher(format);
  7869. if (m.matches()) {
  7870. groups = Integer.parseInt(m.group(2));
  7871. }
  7872. return groups;
  7873. }
  7874. private boolean hasArg(IRubyObject[] args) {
  7875. return args.length != 0 && !args[0].isNil();
  7876. }
  7877. private String format(IRubyObject[] args) {
  7878. return args[0].toString();
  7879. }
  7880. private String firstArgument(IRubyObject[] args) {
  7881. if (hasArg(args)) {
  7882. return format(args);
  7883. }
  7884. return null;
  7885. }
  7886. private boolean posSpace(String arg) {
  7887. if (null != arg) {
  7888. return formatHasLeadingSpace(arg);
  7889. }
  7890. return false;
  7891. }
  7892. private boolean posSign(String arg) {
  7893. if (null != arg) {
  7894. return formatHasLeadingPlus(arg) || posSpace(arg);
  7895. }
  7896. return false;
  7897. }
  7898. private boolean asEngineering(String arg) {
  7899. if (null != arg) {
  7900. return !formatHasFloatingPointNotation(arg);
  7901. }
  7902. return true;
  7903. }
  7904. private int groups(String arg) {
  7905. if (null != arg) {
  7906. return formatFractionalDigitGroups(arg);
  7907. }
  7908. return 0;
  7909. }
  7910. private boolean isZero() {
  7911. return !isNaN() && !isInfinity() && (value.signum() == 0);
  7912. }
  7913. private boolean isNaN() {
  7914. return isNaN;
  7915. }
  7916. private boolean isInfinity() {
  7917. return infinitySign != 0;
  7918. }
  7919. private String unscaledValue() {
  7920. return value.abs().unscaledValue().toString();
  7921. }
  7922. private IRubyObject engineeringValue(String arg) {
  7923. int exponent = getExponent();
  7924. int signum = value.signum();
  7925. StringBuilder build = new StringBuilder();
  7926. build.append(signum == -1 ? "-" : (signum == 1 ? (posSign(arg) ? (posSpace(arg) ? " " : "+") : "") : ""));
  7927. build.append("0.");
  7928. if (0 == groups(arg)) {
  7929. String s = removeTrailingZeroes(unscaledValue());
  7930. if ("".equals(s)) {
  7931. build.append("0");
  7932. } else {
  7933. build.append(s);
  7934. }
  7935. } else {
  7936. int index = 0;
  7937. String sep = "";
  7938. while (index < unscaledValue().length()) {
  7939. int next = index + groups(arg);
  7940. if (next > unscaledValue().length()) {
  7941. next = unscaledValue().length();
  7942. }
  7943. build.append(sep).append(unscaledValue().substring(index, next));
  7944. sep = " ";
  7945. index += groups(arg);
  7946. }
  7947. }
  7948. build.append("E").append(exponent);
  7949. return getRuntime().newString(build.toString());
  7950. }
  7951. private IRubyObject floatingPointValue(String arg) {
  7952. String values[] = value.abs().stripTrailingZeros().toPlainString().split("\\.");
  7953. String whole = "0";
  7954. if (values.length > 0) {
  7955. whole = values[0];
  7956. }
  7957. String after = "0";
  7958. if (values.length > 1) {
  7959. after = values[1];
  7960. }
  7961. int signum = value.signum();
  7962. StringBuilder build = new StringBuilder();
  7963. build.append(signum == -1 ? "-" : (signum == 1 ? (posSign(arg) ? (posSpace(arg) ? " " : "+") : "") : ""));
  7964. if (groups(arg) == 0) {
  7965. build.append(whole);
  7966. if (null != after) {
  7967. build.append(".").append(after);
  7968. }
  7969. } else {
  7970. int index = 0;
  7971. String sep = "";
  7972. while (index < whole.length()) {
  7973. int next = index + groups(arg);
  7974. if (next > whole.length()) {
  7975. next = whole.length();
  7976. }
  7977. build.append(sep).append(whole.substring(index, next));
  7978. sep = " ";
  7979. index += groups(arg);
  7980. }
  7981. if (null != after) {
  7982. build.append(".");
  7983. index = 0;
  7984. sep = "";
  7985. while (index < after.length()) {
  7986. int next = index + groups(arg);
  7987. if (next > after.length()) {
  7988. next = after.length();
  7989. }
  7990. build.append(sep).append(after.substring(index, next));
  7991. sep = " ";
  7992. index += groups(arg);
  7993. }
  7994. }
  7995. }
  7996. return getRuntime().newString(build.toString());
  7997. }
  7998. @JRubyMethod(name = "to_s", optional = 1)
  7999. public IRubyObject to_s(IRubyObject[] args) {
  8000. String arg = firstArgument(args);
  8001. if (isNaN()) {
  8002. return getRuntime().newString("NaN");
  8003. }
  8004. if (infinitySign != 0) {
  8005. if (infinitySign == -1) {
  8006. return getRuntime().newString("-Infinity");
  8007. } else {
  8008. return getRuntime().newString("Infinity");
  8009. }
  8010. }
  8011. if (isZero()) {
  8012. String zero = "0.0";
  8013. if (zeroSign < 0) {
  8014. zero = "-" + zero;
  8015. }
  8016. return getRuntime().newString(zero);
  8017. }
  8018. if(asEngineering(arg)) {
  8019. return engineeringValue(arg);
  8020. } else {
  8021. return floatingPointValue(arg);
  8022. }
  8023. }
  8024. // Note: #fix has only no-arg form, but truncate allows optional parameter.
  8025. @JRubyMethod
  8026. public IRubyObject fix() {
  8027. return truncate(RubyFixnum.zero(getRuntime()));
  8028. }
  8029. @JRubyMethod
  8030. public IRubyObject truncate() {
  8031. return truncate(RubyFixnum.zero(getRuntime()));
  8032. }
  8033. @JRubyMethod
  8034. public IRubyObject truncate(IRubyObject arg) {
  8035. if (isNaN) {
  8036. return newNaN(getRuntime());
  8037. }
  8038. if (isInfinity()) {
  8039. return newInfinity(getRuntime(), infinitySign);
  8040. }
  8041. int n = RubyNumeric.fix2int(arg);
  8042. int precision = value.precision() - value.scale() + n;
  8043. if (precision > 0) {
  8044. return new RubyBigDecimal(getRuntime(),
  8045. value.round(new MathContext(precision, RoundingMode.DOWN)));
  8046. } else {
  8047. // TODO: proper sign
  8048. return new RubyBigDecimal(getRuntime(), BigDecimal.ZERO);
  8049. }
  8050. }
  8051. @JRubyMethod(name = "zero?")
  8052. public IRubyObject zero_p() {
  8053. return getRuntime().newBoolean(isZero());
  8054. }
  8055. /**
  8056. * Returns the correctly rounded square root of a positive
  8057. * BigDecimal. This method performs the fast <i>Square Root by
  8058. * Coupled Newton Iteration</i> algorithm by Timm Ahrendt, from
  8059. * the book "Pi, unleashed" by Jรถrg Arndt in a neat loop.
  8060. * <p>
  8061. * The code is based on Frans Lelieveld's code , used here with
  8062. * permission.
  8063. *
  8064. * @param squarD The number to get the root from.
  8065. * @param rootMC Precision and rounding mode.
  8066. * @return the root of the argument number
  8067. * @throws ArithmeticException
  8068. * if the argument number is negative
  8069. * @throws IllegalArgumentException
  8070. * if rootMC has precision 0
  8071. */
  8072. public static BigDecimal bigSqrt(BigDecimal squarD, MathContext rootMC) {
  8073. // General number and precision checking
  8074. int sign = squarD.signum();
  8075. if (sign == -1) {
  8076. throw new ArithmeticException("Square root of a negative number: " + squarD);
  8077. } else if(sign == 0) {
  8078. return squarD.round(rootMC);
  8079. }
  8080. int prec = rootMC.getPrecision(); // the requested precision
  8081. if (prec == 0) {
  8082. throw new IllegalArgumentException("Most roots won't have infinite precision = 0");
  8083. }
  8084. // Initial precision is that of double numbers 2^63/2 ~ 4E18
  8085. int BITS = 62; // 63-1 an even number of number bits
  8086. int nInit = 16; // precision seems 16 to 18 digits
  8087. MathContext nMC = new MathContext(18, RoundingMode.HALF_DOWN);
  8088. // Iteration variables, for the square root x and the reciprocal v
  8089. BigDecimal x = null, e = null; // initial x: x0 ~ sqrt()
  8090. BigDecimal v = null, g = null; // initial v: v0 = 1/(2*x)
  8091. // Estimate the square root with the foremost 62 bits of squarD
  8092. BigInteger bi = squarD.unscaledValue(); // bi and scale are a tandem
  8093. int biLen = bi.bitLength();
  8094. int shift = Math.max(0, biLen - BITS + (biLen%2 == 0 ? 0 : 1)); // even shift..
  8095. bi = bi.shiftRight(shift); // ..floors to 62 or 63 bit BigInteger
  8096. double root = Math.sqrt(bi.doubleValue());
  8097. BigDecimal halfBack = new BigDecimal(BigInteger.ONE.shiftLeft(shift/2));
  8098. int scale = squarD.scale();
  8099. if (scale % 2 == 1) {
  8100. root *= SQRT_10; // 5 -> 2, -5 -> -3 need half a scale more..
  8101. }
  8102. scale = (int) Math.floor(scale/2.); // ..where 100 -> 10 shifts the scale
  8103. // Initial x - use double root - multiply by halfBack to unshift - set new scale
  8104. x = new BigDecimal(root, nMC);
  8105. x = x.multiply(halfBack, nMC); // x0 ~ sqrt()
  8106. if (scale != 0) {
  8107. x = x.movePointLeft(scale);
  8108. }
  8109. if (prec < nInit) { // for prec 15 root x0 must surely be OK
  8110. return x.round(rootMC); // return small prec roots without iterations
  8111. }
  8112. // Initial v - the reciprocal
  8113. v = BigDecimal.ONE.divide(TWO.multiply(x), nMC); // v0 = 1/(2*x)
  8114. // Collect iteration precisions beforehand
  8115. List<Integer> nPrecs = new ArrayList<Integer>();
  8116. assert nInit > 3 : "Never ending loop!"; // assume nInit = 16 <= prec
  8117. // Let m be the exact digits precision in an earlier! loop
  8118. for (int m = prec + 1; m > nInit; m = m/2 + (m > 100 ? 1 : 2)) {
  8119. nPrecs.add(m);
  8120. }
  8121. // The loop of "Square Root by Coupled Newton Iteration"
  8122. for (int i = nPrecs.size() - 1; i > -1; i--) {
  8123. // Increase precision - next iteration supplies n exact digits
  8124. nMC = new MathContext(nPrecs.get(i), (i%2 == 1) ? RoundingMode.HALF_UP :
  8125. RoundingMode.HALF_DOWN);
  8126. // Next x // e = d - x^2
  8127. e = squarD.subtract(x.multiply(x, nMC), nMC);
  8128. if (i != 0) {
  8129. x = x.add(e.multiply(v, nMC)); // x += e*v ~ sqrt()
  8130. } else {
  8131. x = x.add(e.multiply(v, rootMC), rootMC); // root x is ready!
  8132. break;
  8133. }
  8134. // Next v // g = 1 - 2*x*v
  8135. g = BigDecimal.ONE.subtract(TWO.multiply(x).multiply(v, nMC));
  8136. v = v.add(g.multiply(v, nMC)); // v += g*v ~ 1/2/sqrt()
  8137. }
  8138. return x; // return sqrt(squarD) with precision of rootMC
  8139. }
  8140. }// RubyBigdecimal
  8141. /***** BEGIN LICENSE BLOCK *****
  8142. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  8143. *
  8144. * The contents of this file are subject to the Common Public
  8145. * License Version 1.0 (the "License"); you may not use this file
  8146. * except in compliance with the License. You may obtain a copy of
  8147. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8148. *
  8149. * Software distributed under the License is distributed on an "AS
  8150. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  8151. * implied. See the License for the specific language governing
  8152. * rights and limitations under the License.
  8153. *
  8154. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  8155. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  8156. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  8157. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  8158. * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  8159. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  8160. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  8161. *
  8162. * Alternatively, the contents of this file may be used under the terms of
  8163. * either of the GNU General Public License Version 2 or later (the "GPL"),
  8164. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  8165. * in which case the provisions of the GPL or the LGPL are applicable instead
  8166. * of those above. If you wish to allow use of your version of this file only
  8167. * under the terms of either the GPL or the LGPL, and not to allow others to
  8168. * use your version of this file under the terms of the CPL, indicate your
  8169. * decision by deleting the provisions above and replace them with the notice
  8170. * and other provisions required by the GPL or the LGPL. If you do not delete
  8171. * the provisions above, a recipient may use your version of this file under
  8172. * the terms of any one of the CPL, the GPL or the LGPL.
  8173. ***** END LICENSE BLOCK *****/
  8174. package org.jruby;
  8175. import java.io.IOException;
  8176. import java.math.BigDecimal;
  8177. import java.math.BigInteger;
  8178. import org.jruby.anno.JRubyClass;
  8179. import org.jruby.anno.JRubyMethod;
  8180. import org.jruby.common.IRubyWarnings.ID;
  8181. import org.jruby.runtime.ClassIndex;
  8182. import org.jruby.runtime.ObjectAllocator;
  8183. import org.jruby.runtime.ThreadContext;
  8184. import org.jruby.runtime.builtin.IRubyObject;
  8185. import org.jruby.runtime.marshal.MarshalStream;
  8186. import org.jruby.runtime.marshal.UnmarshalStream;
  8187. /**
  8188. *
  8189. * @author jpetersen
  8190. */
  8191. @JRubyClass(name="Bignum", parent="Integer")
  8192. public class RubyBignum extends RubyInteger {
  8193. public static RubyClass createBignumClass(Ruby runtime) {
  8194. RubyClass bignum = runtime.defineClass("Bignum", runtime.getInteger(),
  8195. ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  8196. runtime.setBignum(bignum);
  8197. bignum.index = ClassIndex.BIGNUM;
  8198. bignum.defineAnnotatedMethods(RubyBignum.class);
  8199. return bignum;
  8200. }
  8201. private static final int BIT_SIZE = 64;
  8202. private static final long MAX = (1L << (BIT_SIZE - 1)) - 1;
  8203. private static final BigInteger LONG_MAX = BigInteger.valueOf(MAX);
  8204. private static final BigInteger LONG_MIN = BigInteger.valueOf(-MAX - 1);
  8205. private final BigInteger value;
  8206. public RubyBignum(Ruby runtime, BigInteger value) {
  8207. super(runtime, runtime.getBignum());
  8208. this.value = value;
  8209. }
  8210. public int getNativeTypeIndex() {
  8211. return ClassIndex.BIGNUM;
  8212. }
  8213. public Class<?> getJavaClass() {
  8214. return BigInteger.class;
  8215. }
  8216. public static RubyBignum newBignum(Ruby runtime, long value) {
  8217. return newBignum(runtime, BigInteger.valueOf(value));
  8218. }
  8219. public static RubyBignum newBignum(Ruby runtime, double value) {
  8220. return newBignum(runtime, new BigDecimal(value).toBigInteger());
  8221. }
  8222. public static RubyBignum newBignum(Ruby runtime, BigInteger value) {
  8223. return new RubyBignum(runtime, value);
  8224. }
  8225. public static RubyBignum newBignum(Ruby runtime, String value) {
  8226. return new RubyBignum(runtime, new BigInteger(value));
  8227. }
  8228. public double getDoubleValue() {
  8229. return big2dbl(this);
  8230. }
  8231. public long getLongValue() {
  8232. return big2long(this);
  8233. }
  8234. /** Getter for property value.
  8235. * @return Value of property value.
  8236. */
  8237. public BigInteger getValue() {
  8238. return value;
  8239. }
  8240. /* ================
  8241. * Utility Methods
  8242. * ================
  8243. */
  8244. /* If the value will fit in a Fixnum, return one of those. */
  8245. /** rb_big_norm
  8246. *
  8247. */
  8248. public static RubyInteger bignorm(Ruby runtime, BigInteger bi) {
  8249. if (bi.compareTo(LONG_MIN) < 0 || bi.compareTo(LONG_MAX) > 0) {
  8250. return newBignum(runtime, bi);
  8251. }
  8252. return runtime.newFixnum(bi.longValue());
  8253. }
  8254. /** rb_big2long
  8255. *
  8256. */
  8257. public static long big2long(RubyBignum value) {
  8258. BigInteger big = value.getValue();
  8259. if (big.compareTo(LONG_MIN) < 0 || big.compareTo(LONG_MAX) > 0) {
  8260. throw value.getRuntime().newRangeError("bignum too big to convert into `long'");
  8261. }
  8262. return big.longValue();
  8263. }
  8264. /** rb_big2dbl
  8265. *
  8266. */
  8267. public static double big2dbl(RubyBignum value) {
  8268. BigInteger big = value.getValue();
  8269. double dbl = convertToDouble(big);
  8270. if (dbl == Double.NEGATIVE_INFINITY || dbl == Double.POSITIVE_INFINITY) {
  8271. value.getRuntime().getWarnings().warn(ID.BIGNUM_FROM_FLOAT_RANGE, "Bignum out of Float range");
  8272. }
  8273. return dbl;
  8274. }
  8275. private IRubyObject checkShiftDown(RubyBignum other) {
  8276. if (other.value.signum() == 0) return RubyFixnum.zero(getRuntime());
  8277. if (value.compareTo(LONG_MIN) < 0 || value.compareTo(LONG_MAX) > 0) {
  8278. return other.value.signum() >= 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.minus_one(getRuntime());
  8279. }
  8280. return getRuntime().getNil();
  8281. }
  8282. /**
  8283. * BigInteger#doubleValue is _really_ slow currently.
  8284. * This is faster, and mostly correct (?)
  8285. */
  8286. static double convertToDouble(BigInteger bigint) {
  8287. byte[] arr = bigint.toByteArray();
  8288. double res = 0;
  8289. double acc = 1;
  8290. for (int i = arr.length - 1; i > 0 ; i--)
  8291. {
  8292. res += (double) (arr[i] & 0xff) * acc;
  8293. acc *= 256;
  8294. }
  8295. res += (double) arr[0] * acc; // final byte sign is significant
  8296. return res;
  8297. }
  8298. /** rb_int2big
  8299. *
  8300. */
  8301. public static BigInteger fix2big(RubyFixnum arg) {
  8302. return BigInteger.valueOf(arg.getLongValue());
  8303. }
  8304. /* ================
  8305. * Instance Methods
  8306. * ================
  8307. */
  8308. /** rb_big_to_s
  8309. *
  8310. */
  8311. @JRubyMethod(name = "to_s", optional = 1)
  8312. public IRubyObject to_s(IRubyObject[] args) {
  8313. int base = args.length == 0 ? 10 : num2int(args[0]);
  8314. if (base < 2 || base > 36) {
  8315. throw getRuntime().newArgumentError("illegal radix " + base);
  8316. }
  8317. return getRuntime().newString(getValue().toString(base));
  8318. }
  8319. /** rb_big_coerce
  8320. *
  8321. */
  8322. @JRubyMethod(name = "coerce", required = 1)
  8323. public IRubyObject coerce(IRubyObject other) {
  8324. if (other instanceof RubyFixnum) {
  8325. return getRuntime().newArray(newBignum(getRuntime(), ((RubyFixnum) other).getLongValue()), this);
  8326. } else if (other instanceof RubyBignum) {
  8327. return getRuntime().newArray(newBignum(getRuntime(), ((RubyBignum) other).getValue()), this);
  8328. }
  8329. throw getRuntime().newTypeError("Can't coerce " + other.getMetaClass().getName() + " to Bignum");
  8330. }
  8331. /** rb_big_uminus
  8332. *
  8333. */
  8334. @JRubyMethod(name = "-@")
  8335. public IRubyObject op_uminus() {
  8336. return bignorm(getRuntime(), value.negate());
  8337. }
  8338. /** rb_big_plus
  8339. *
  8340. */
  8341. @JRubyMethod(name = "+", required = 1)
  8342. public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
  8343. if (other instanceof RubyFixnum) {
  8344. return addFixnum((RubyFixnum)other);
  8345. } else if (other instanceof RubyBignum) {
  8346. return addBignum((RubyBignum)other);
  8347. } else if (other instanceof RubyFloat) {
  8348. return addFloat((RubyFloat)other);
  8349. }
  8350. return addOther(context, other);
  8351. }
  8352. private IRubyObject addFixnum(RubyFixnum other) {
  8353. return bignorm(getRuntime(), value.add(fix2big(other)));
  8354. }
  8355. private IRubyObject addBignum(RubyBignum other) {
  8356. return bignorm(getRuntime(), value.add(other.value));
  8357. }
  8358. private IRubyObject addFloat(RubyFloat other) {
  8359. return RubyFloat.newFloat(getRuntime(), big2dbl(this) + other.getDoubleValue());
  8360. }
  8361. private IRubyObject addOther(ThreadContext context, IRubyObject other) {
  8362. return coerceBin(context, "+", other);
  8363. }
  8364. /** rb_big_minus
  8365. *
  8366. */
  8367. @JRubyMethod(name = "-", required = 1)
  8368. public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
  8369. if (other instanceof RubyFixnum) {
  8370. return subtractFixnum((RubyFixnum)other);
  8371. } else if (other instanceof RubyBignum) {
  8372. return subtractBignum((RubyBignum)other);
  8373. } else if (other instanceof RubyFloat) {
  8374. return subtractFloat((RubyFloat)other);
  8375. }
  8376. return subtractOther(context, other);
  8377. }
  8378. private IRubyObject subtractFixnum(RubyFixnum other) {
  8379. return bignorm(getRuntime(), value.subtract(fix2big(((RubyFixnum) other))));
  8380. }
  8381. private IRubyObject subtractBignum(RubyBignum other) {
  8382. return bignorm(getRuntime(), value.subtract(((RubyBignum) other).value));
  8383. }
  8384. private IRubyObject subtractFloat(RubyFloat other) {
  8385. return RubyFloat.newFloat(getRuntime(), big2dbl(this) - ((RubyFloat) other).getDoubleValue());
  8386. }
  8387. private IRubyObject subtractOther(ThreadContext context, IRubyObject other) {
  8388. return coerceBin(context, "-", other);
  8389. }
  8390. /** rb_big_mul
  8391. *
  8392. */
  8393. @JRubyMethod(name = "*", required = 1)
  8394. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  8395. if (other instanceof RubyFixnum) {
  8396. return bignorm(getRuntime(), value.multiply(fix2big(((RubyFixnum) other))));
  8397. }
  8398. if (other instanceof RubyBignum) {
  8399. return bignorm(getRuntime(), value.multiply(((RubyBignum) other).value));
  8400. } else if (other instanceof RubyFloat) {
  8401. return RubyFloat.newFloat(getRuntime(), big2dbl(this) * ((RubyFloat) other).getDoubleValue());
  8402. }
  8403. return coerceBin(context, "*", other);
  8404. }
  8405. /**
  8406. * rb_big_divide. Shared part for both "/" and "div" operations.
  8407. */
  8408. private IRubyObject op_divide(ThreadContext context, IRubyObject other, String op) {
  8409. assert ("/".equals(op) || "div".equals(op));
  8410. final BigInteger otherValue;
  8411. if (other instanceof RubyFixnum) {
  8412. otherValue = fix2big((RubyFixnum) other);
  8413. } else if (other instanceof RubyBignum) {
  8414. otherValue = ((RubyBignum) other).value;
  8415. } else if (other instanceof RubyFloat) {
  8416. double div = big2dbl(this) / ((RubyFloat) other).getDoubleValue();
  8417. if ("/".equals(op)) {
  8418. return RubyFloat.newFloat(getRuntime(),
  8419. big2dbl(this) / ((RubyFloat) other).getDoubleValue());
  8420. } else {
  8421. return RubyNumeric.dbl2num(getRuntime(), div);
  8422. }
  8423. } else {
  8424. return coerceBin(context, op, other);
  8425. }
  8426. if (otherValue.equals(BigInteger.ZERO)) {
  8427. throw getRuntime().newZeroDivisionError();
  8428. }
  8429. BigInteger[] results = value.divideAndRemainder(otherValue);
  8430. if ((value.signum() * otherValue.signum()) == -1 && results[1].signum() != 0) {
  8431. return bignorm(getRuntime(), results[0].subtract(BigInteger.ONE));
  8432. }
  8433. return bignorm(getRuntime(), results[0]);
  8434. }
  8435. /** rb_big_div
  8436. *
  8437. */
  8438. @JRubyMethod(name = {"/"}, required = 1)
  8439. public IRubyObject op_div(ThreadContext context, IRubyObject other) {
  8440. return op_divide(context, other, "/");
  8441. }
  8442. /** rb_big_idiv
  8443. *
  8444. */
  8445. @JRubyMethod(name = {"div"}, required = 1)
  8446. public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
  8447. return op_divide(context, other, "div");
  8448. }
  8449. /** rb_big_divmod
  8450. *
  8451. */
  8452. @JRubyMethod(name = "divmod", required = 1)
  8453. public IRubyObject divmod(ThreadContext context, IRubyObject other) {
  8454. final BigInteger otherValue;
  8455. if (other instanceof RubyFixnum) {
  8456. otherValue = fix2big((RubyFixnum) other);
  8457. } else if (other instanceof RubyBignum) {
  8458. otherValue = ((RubyBignum) other).value;
  8459. } else {
  8460. return coerceBin(context, "divmod", other);
  8461. }
  8462. if (otherValue.equals(BigInteger.ZERO)) {
  8463. throw getRuntime().newZeroDivisionError();
  8464. }
  8465. BigInteger[] results = value.divideAndRemainder(otherValue);
  8466. if ((value.signum() * otherValue.signum()) == -1 && results[1].signum() != 0) {
  8467. results[0] = results[0].subtract(BigInteger.ONE);
  8468. results[1] = otherValue.add(results[1]);
  8469. }
  8470. final Ruby runtime = getRuntime();
  8471. return RubyArray.newArray(getRuntime(), bignorm(runtime, results[0]), bignorm(runtime, results[1]));
  8472. }
  8473. /** rb_big_modulo
  8474. *
  8475. */
  8476. @JRubyMethod(name = {"%", "modulo"}, required = 1)
  8477. public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
  8478. final BigInteger otherValue;
  8479. if (other instanceof RubyFixnum) {
  8480. otherValue = fix2big((RubyFixnum) other);
  8481. } else if (other instanceof RubyBignum) {
  8482. otherValue = ((RubyBignum) other).value;
  8483. } else {
  8484. return coerceBin(context, "%", other);
  8485. }
  8486. if (otherValue.equals(BigInteger.ZERO)) {
  8487. throw getRuntime().newZeroDivisionError();
  8488. }
  8489. BigInteger result = value.mod(otherValue.abs());
  8490. if (otherValue.signum() == -1 && result.signum() != 0) {
  8491. result = otherValue.add(result);
  8492. }
  8493. return bignorm(getRuntime(), result);
  8494. }
  8495. /** rb_big_remainder
  8496. *
  8497. */
  8498. @JRubyMethod(name = "remainder", required = 1)
  8499. public IRubyObject remainder(ThreadContext context, IRubyObject other) {
  8500. final BigInteger otherValue;
  8501. if (other instanceof RubyFixnum) {
  8502. otherValue = fix2big(((RubyFixnum) other));
  8503. } else if (other instanceof RubyBignum) {
  8504. otherValue = ((RubyBignum) other).value;
  8505. } else {
  8506. return coerceBin(context, "remainder", other);
  8507. }
  8508. if (otherValue.equals(BigInteger.ZERO)) {
  8509. throw getRuntime().newZeroDivisionError();
  8510. }
  8511. return bignorm(getRuntime(), value.remainder(otherValue));
  8512. }
  8513. /** rb_big_quo
  8514. *
  8515. */
  8516. @JRubyMethod(name = "quo", required = 1)
  8517. public IRubyObject quo(ThreadContext context, IRubyObject other) {
  8518. if (other instanceof RubyNumeric) {
  8519. return RubyFloat.newFloat(getRuntime(), big2dbl(this) / ((RubyNumeric) other).getDoubleValue());
  8520. } else {
  8521. return coerceBin(context, "quo", other);
  8522. }
  8523. }
  8524. /** rb_big_pow
  8525. *
  8526. */
  8527. @JRubyMethod(name = {"**", "power"}, required = 1)
  8528. public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
  8529. double d;
  8530. if (other instanceof RubyFixnum) {
  8531. RubyFixnum fix = (RubyFixnum) other;
  8532. long fixValue = fix.getLongValue();
  8533. // MRI issuses warning here on (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024)
  8534. if (((value.bitLength() + 7) / 8) * 4 * Math.abs(fixValue) > 1024 * 1024) {
  8535. getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", fixValue);
  8536. }
  8537. if (fixValue >= 0) {
  8538. return bignorm(getRuntime(), value.pow((int) fixValue)); // num2int is also implemented
  8539. } else {
  8540. return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), (double)fixValue));
  8541. }
  8542. } else if (other instanceof RubyBignum) {
  8543. d = ((RubyBignum) other).getDoubleValue();
  8544. getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", d);
  8545. } else if (other instanceof RubyFloat) {
  8546. d = ((RubyFloat) other).getDoubleValue();
  8547. } else {
  8548. return coerceBin(context, "**", other);
  8549. }
  8550. return RubyFloat.newFloat(getRuntime(), Math.pow(big2dbl(this), d));
  8551. }
  8552. /** rb_big_pow
  8553. *
  8554. */
  8555. @JRubyMethod(name = {"**", "power"}, required = 1, compat = CompatVersion.RUBY1_9)
  8556. public IRubyObject op_pow_19(ThreadContext context, IRubyObject other) {
  8557. Ruby runtime = context.getRuntime();
  8558. if (other == RubyFixnum.zero(runtime)) return RubyFixnum.one(runtime);
  8559. double d;
  8560. if (other instanceof RubyFixnum) {
  8561. RubyFixnum fix = (RubyFixnum) other;
  8562. long fixValue = fix.getLongValue();
  8563. if (fixValue < 0) {
  8564. return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
  8565. }
  8566. // MRI issuses warning here on (RBIGNUM(x)->len * SIZEOF_BDIGITS * yy > 1024*1024)
  8567. if (((value.bitLength() + 7) / 8) * 4 * Math.abs(fixValue) > 1024 * 1024) {
  8568. getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", fixValue);
  8569. }
  8570. if (fixValue >= 0) {
  8571. return bignorm(runtime, value.pow((int) fixValue)); // num2int is also implemented
  8572. } else {
  8573. return RubyFloat.newFloat(runtime, Math.pow(big2dbl(this), (double)fixValue));
  8574. }
  8575. } else if (other instanceof RubyBignum) {
  8576. if (other.callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue()) {
  8577. return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
  8578. }
  8579. d = ((RubyBignum) other).getDoubleValue();
  8580. getRuntime().getWarnings().warn(ID.MAY_BE_TOO_BIG, "in a**b, b may be too big", d);
  8581. } else if (other instanceof RubyFloat) {
  8582. d = ((RubyFloat) other).getDoubleValue();
  8583. } else {
  8584. return coerceBin(context, "**", other);
  8585. }
  8586. return RubyNumeric.dbl2num(runtime, Math.pow(big2dbl(this), d));
  8587. }
  8588. /** rb_big_and
  8589. *
  8590. */
  8591. @JRubyMethod(name = "&", required = 1)
  8592. public IRubyObject op_and(ThreadContext context, IRubyObject other) {
  8593. other = other.convertToInteger();
  8594. if (other instanceof RubyBignum) {
  8595. return bignorm(getRuntime(), value.and(((RubyBignum) other).value));
  8596. } else if(other instanceof RubyFixnum) {
  8597. return bignorm(getRuntime(), value.and(fix2big((RubyFixnum)other)));
  8598. }
  8599. return coerceBin(context, "&", other);
  8600. }
  8601. /** rb_big_or
  8602. *
  8603. */
  8604. @JRubyMethod(name = "|", required = 1)
  8605. public IRubyObject op_or(ThreadContext context, IRubyObject other) {
  8606. other = other.convertToInteger();
  8607. if (other instanceof RubyBignum) {
  8608. return bignorm(getRuntime(), value.or(((RubyBignum) other).value));
  8609. }
  8610. if (other instanceof RubyFixnum) { // no bignorm here needed
  8611. return bignorm(getRuntime(), value.or(fix2big((RubyFixnum)other)));
  8612. }
  8613. return coerceBin(context, "|", other);
  8614. }
  8615. /** rb_big_xor
  8616. *
  8617. */
  8618. @JRubyMethod(name = "^", required = 1)
  8619. public IRubyObject op_xor(ThreadContext context, IRubyObject other) {
  8620. other = other.convertToInteger();
  8621. if (other instanceof RubyBignum) {
  8622. return bignorm(getRuntime(), value.xor(((RubyBignum) other).value));
  8623. }
  8624. if (other instanceof RubyFixnum) {
  8625. return bignorm(getRuntime(), value.xor(BigInteger.valueOf(((RubyFixnum) other).getLongValue())));
  8626. }
  8627. return coerceBin(context, "^", other);
  8628. }
  8629. /** rb_big_neg
  8630. *
  8631. */
  8632. @JRubyMethod(name = "~")
  8633. public IRubyObject op_neg() {
  8634. return RubyBignum.newBignum(getRuntime(), value.not());
  8635. }
  8636. /** rb_big_lshift
  8637. *
  8638. */
  8639. @JRubyMethod(name = "<<", required = 1)
  8640. public IRubyObject op_lshift(IRubyObject other) {
  8641. long shift;
  8642. boolean neg = false;
  8643. for (;;) {
  8644. if (other instanceof RubyFixnum) {
  8645. shift = ((RubyFixnum)other).getLongValue();
  8646. if (shift < 0) {
  8647. neg = true;
  8648. shift = -shift;
  8649. }
  8650. break;
  8651. } else if (other instanceof RubyBignum) {
  8652. RubyBignum otherBignum = (RubyBignum)other;
  8653. if (otherBignum.value.signum() < 0) {
  8654. IRubyObject tmp = otherBignum.checkShiftDown(this);
  8655. if (!tmp.isNil()) return tmp;
  8656. neg = true;
  8657. }
  8658. shift = big2long(otherBignum);
  8659. break;
  8660. }
  8661. other = other.convertToInteger();
  8662. }
  8663. return bignorm(getRuntime(), neg ? value.shiftRight((int)shift) : value.shiftLeft((int)shift));
  8664. }
  8665. /** rb_big_rshift
  8666. *
  8667. */
  8668. @JRubyMethod(name = ">>", required = 1)
  8669. public IRubyObject op_rshift(IRubyObject other) {
  8670. long shift;
  8671. boolean neg = false;
  8672. for (;;) {
  8673. if (other instanceof RubyFixnum) {
  8674. shift = ((RubyFixnum)other).getLongValue();
  8675. if (shift < 0) {
  8676. neg = true;
  8677. shift = -shift;
  8678. }
  8679. break;
  8680. } else if (other instanceof RubyBignum) {
  8681. RubyBignum otherBignum = (RubyBignum)other;
  8682. if (otherBignum.value.signum() >= 0) {
  8683. IRubyObject tmp = otherBignum.checkShiftDown(this);
  8684. if (!tmp.isNil()) return tmp;
  8685. } else {
  8686. neg = true;
  8687. }
  8688. shift = big2long(otherBignum);
  8689. break;
  8690. }
  8691. other = other.convertToInteger();
  8692. }
  8693. return bignorm(getRuntime(), neg ? value.shiftLeft((int)shift) : value.shiftRight((int)shift));
  8694. }
  8695. /** rb_big_aref
  8696. *
  8697. */
  8698. @JRubyMethod(name = "[]", required = 1)
  8699. public RubyFixnum op_aref(IRubyObject other) {
  8700. if (other instanceof RubyBignum) {
  8701. if (((RubyBignum) other).value.signum() >= 0 || value.signum() == -1) {
  8702. return RubyFixnum.zero(getRuntime());
  8703. }
  8704. return RubyFixnum.one(getRuntime());
  8705. }
  8706. long position = num2long(other);
  8707. if (position < 0 || position > Integer.MAX_VALUE) {
  8708. return RubyFixnum.zero(getRuntime());
  8709. }
  8710. return value.testBit((int)position) ? RubyFixnum.one(getRuntime()) : RubyFixnum.zero(getRuntime());
  8711. }
  8712. /** rb_big_cmp
  8713. *
  8714. */
  8715. @JRubyMethod(name = "<=>", required = 1)
  8716. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  8717. final BigInteger otherValue;
  8718. if (other instanceof RubyFixnum) {
  8719. otherValue = fix2big((RubyFixnum) other);
  8720. } else if (other instanceof RubyBignum) {
  8721. otherValue = ((RubyBignum) other).value;
  8722. } else if (other instanceof RubyFloat) {
  8723. return dbl_cmp(getRuntime(), big2dbl(this), ((RubyFloat) other).getDoubleValue());
  8724. } else {
  8725. return coerceCmp(context, "<=>", other);
  8726. }
  8727. // wow, the only time we can use the java protocol ;)
  8728. return RubyFixnum.newFixnum(getRuntime(), value.compareTo(otherValue));
  8729. }
  8730. /** rb_big_eq
  8731. *
  8732. */
  8733. @JRubyMethod(name = "==", required = 1)
  8734. public IRubyObject op_equal(IRubyObject other) {
  8735. final BigInteger otherValue;
  8736. if (other instanceof RubyFixnum) {
  8737. otherValue = fix2big((RubyFixnum) other);
  8738. } else if (other instanceof RubyBignum) {
  8739. otherValue = ((RubyBignum) other).value;
  8740. } else if (other instanceof RubyFloat) {
  8741. double a = ((RubyFloat) other).getDoubleValue();
  8742. if (Double.isNaN(a)) {
  8743. return getRuntime().getFalse();
  8744. }
  8745. return RubyBoolean.newBoolean(getRuntime(), a == big2dbl(this));
  8746. } else {
  8747. return other.op_eqq(getRuntime().getCurrentContext(), this);
  8748. }
  8749. return RubyBoolean.newBoolean(getRuntime(), value.compareTo(otherValue) == 0);
  8750. }
  8751. /** rb_big_eql
  8752. *
  8753. */
  8754. @JRubyMethod(name = {"eql?", "==="}, required = 1)
  8755. public IRubyObject eql_p(IRubyObject other) {
  8756. if (other instanceof RubyBignum) {
  8757. return value.compareTo(((RubyBignum)other).value) == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
  8758. }
  8759. return getRuntime().getFalse();
  8760. }
  8761. /** rb_big_hash
  8762. *
  8763. */
  8764. @JRubyMethod(name = "hash")
  8765. public RubyFixnum hash() {
  8766. return getRuntime().newFixnum(value.hashCode());
  8767. }
  8768. /** rb_big_to_f
  8769. *
  8770. */
  8771. @JRubyMethod(name = "to_f")
  8772. public IRubyObject to_f() {
  8773. return RubyFloat.newFloat(getRuntime(), getDoubleValue());
  8774. }
  8775. /** rb_big_abs
  8776. *
  8777. */
  8778. @JRubyMethod(name = "abs")
  8779. public IRubyObject abs() {
  8780. return RubyBignum.newBignum(getRuntime(), value.abs());
  8781. }
  8782. /** rb_big_size
  8783. *
  8784. */
  8785. @JRubyMethod(name = "size")
  8786. public IRubyObject size() {
  8787. return getRuntime().newFixnum((value.bitLength() + 7) / 8);
  8788. }
  8789. public static void marshalTo(RubyBignum bignum, MarshalStream output) throws IOException {
  8790. output.registerLinkTarget(bignum);
  8791. output.write(bignum.value.signum() >= 0 ? '+' : '-');
  8792. BigInteger absValue = bignum.value.abs();
  8793. byte[] digits = absValue.toByteArray();
  8794. boolean oddLengthNonzeroStart = (digits.length % 2 != 0 && digits[0] != 0);
  8795. int shortLength = digits.length / 2;
  8796. if (oddLengthNonzeroStart) {
  8797. shortLength++;
  8798. }
  8799. output.writeInt(shortLength);
  8800. for (int i = 1; i <= shortLength * 2 && i <= digits.length; i++) {
  8801. output.write(digits[digits.length - i]);
  8802. }
  8803. if (oddLengthNonzeroStart) {
  8804. // Pad with a 0
  8805. output.write(0);
  8806. }
  8807. }
  8808. public static RubyNumeric unmarshalFrom(UnmarshalStream input) throws IOException {
  8809. boolean positive = input.readUnsignedByte() == '+';
  8810. int shortLength = input.unmarshalInt();
  8811. // BigInteger required a sign byte in incoming array
  8812. byte[] digits = new byte[shortLength * 2 + 1];
  8813. for (int i = digits.length - 1; i >= 1; i--) {
  8814. digits[i] = input.readSignedByte();
  8815. }
  8816. BigInteger value = new BigInteger(digits);
  8817. if (!positive) {
  8818. value = value.negate();
  8819. }
  8820. RubyNumeric result = bignorm(input.getRuntime(), value);
  8821. input.registerLinkTarget(result);
  8822. return result;
  8823. }
  8824. }
  8825. /***** BEGIN LICENSE BLOCK *****
  8826. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  8827. *
  8828. * The contents of this file are subject to the Common Public
  8829. * License Version 1.0 (the "License"); you may not use this file
  8830. * except in compliance with the License. You may obtain a copy of
  8831. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8832. *
  8833. * Software distributed under the License is distributed on an "AS
  8834. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  8835. * implied. See the License for the specific language governing
  8836. * rights and limitations under the License.
  8837. *
  8838. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  8839. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  8840. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  8841. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  8842. * Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
  8843. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  8844. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  8845. *
  8846. * Alternatively, the contents of this file may be used under the terms of
  8847. * either of the GNU General Public License Version 2 or later (the "GPL"),
  8848. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  8849. * in which case the provisions of the GPL or the LGPL are applicable instead
  8850. * of those above. If you wish to allow use of your version of this file only
  8851. * under the terms of either the GPL or the LGPL, and not to allow others to
  8852. * use your version of this file under the terms of the CPL, indicate your
  8853. * decision by deleting the provisions above and replace them with the notice
  8854. * and other provisions required by the GPL or the LGPL. If you do not delete
  8855. * the provisions above, a recipient may use your version of this file under
  8856. * the terms of any one of the CPL, the GPL or the LGPL.
  8857. ***** END LICENSE BLOCK *****/
  8858. package org.jruby;
  8859. import org.jruby.anno.JRubyClass;
  8860. import org.jruby.anno.JRubyMethod;
  8861. import org.jruby.runtime.Binding;
  8862. import org.jruby.runtime.Frame;
  8863. import org.jruby.runtime.ObjectAllocator;
  8864. import org.jruby.runtime.ThreadContext;
  8865. import org.jruby.runtime.Visibility;
  8866. import org.jruby.runtime.builtin.IRubyObject;
  8867. /**
  8868. * @author jpetersen
  8869. */
  8870. @JRubyClass(name="Binding")
  8871. public class RubyBinding extends RubyObject {
  8872. private Binding binding;
  8873. public RubyBinding(Ruby runtime, RubyClass rubyClass, Binding binding) {
  8874. super(runtime, rubyClass);
  8875. this.binding = binding;
  8876. }
  8877. private RubyBinding(Ruby runtime, RubyClass rubyClass) {
  8878. super(runtime, rubyClass);
  8879. }
  8880. private static ObjectAllocator BINDING_ALLOCATOR = new ObjectAllocator() {
  8881. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  8882. RubyBinding instance = new RubyBinding(runtime, klass);
  8883. return instance;
  8884. }
  8885. };
  8886. public static RubyClass createBindingClass(Ruby runtime) {
  8887. RubyClass bindingClass = runtime.defineClass("Binding", runtime.getObject(), BINDING_ALLOCATOR);
  8888. runtime.setBinding(bindingClass);
  8889. bindingClass.defineAnnotatedMethods(RubyBinding.class);
  8890. return bindingClass;
  8891. }
  8892. public Binding getBinding() {
  8893. return binding;
  8894. }
  8895. // Proc class
  8896. public static RubyBinding newBinding(Ruby runtime, Binding binding) {
  8897. return new RubyBinding(runtime, runtime.getBinding(), binding);
  8898. }
  8899. public static RubyBinding newBinding(Ruby runtime) {
  8900. ThreadContext context = runtime.getCurrentContext();
  8901. // FIXME: We should be cloning, not reusing: frame, scope, dynvars, and potentially iter/block info
  8902. Frame frame = context.getCurrentFrame();
  8903. Binding binding = new Binding(frame, context.getBindingRubyClass(), context.getCurrentScope());
  8904. return new RubyBinding(runtime, runtime.getBinding(), binding);
  8905. }
  8906. /**
  8907. * Create a binding appropriate for a bare "eval", by using the previous (caller's) frame and current
  8908. * scope.
  8909. */
  8910. public static RubyBinding newBindingForEval(ThreadContext context) {
  8911. // This requires some explaining. We use Frame values when executing blocks to fill in
  8912. // various values in ThreadContext and EvalState.eval like rubyClass, cref, and self.
  8913. // Largely, for an eval that is using the logical binding at a place where the eval is
  8914. // called we mostly want to use the current frames value for this. Most importantly,
  8915. // we need that self (JRUBY-858) at this point. We also need to make sure that returns
  8916. // jump to the right place (which happens to be the previous frame). Lastly, we do not
  8917. // want the current frames klazz since that will be the klazz represented of self. We
  8918. // want the class right before the eval (well we could use cref class for this too I think).
  8919. // Once we end up having Frames created earlier I think the logic of stuff like this will
  8920. // be better since we won't be worried about setting Frame to setup other variables/stacks
  8921. // but just making sure Frame itself is correct...
  8922. Frame previousFrame = context.getPreviousFrame();
  8923. Frame currentFrame = context.getCurrentFrame();
  8924. currentFrame.setKlazz(previousFrame.getKlazz());
  8925. // Set jump target to whatever the previousTarget thinks is good.
  8926. // currentFrame.setJumpTarget(previousFrame.getJumpTarget() != null ? previousFrame.getJumpTarget() : previousFrame);
  8927. Binding binding = new Binding(previousFrame, context.getBindingRubyClass(), context.getCurrentScope());
  8928. Ruby runtime = context.getRuntime();
  8929. return new RubyBinding(runtime, runtime.getBinding(), binding);
  8930. }
  8931. @JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
  8932. public IRubyObject initialize(ThreadContext context) {
  8933. // FIXME: We should be cloning, not reusing: frame, scope, dynvars, and potentially iter/block info
  8934. Frame frame = context.getCurrentFrame();
  8935. binding = new Binding(frame, context.getBindingRubyClass(), context.getCurrentScope());
  8936. return this;
  8937. }
  8938. @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
  8939. @Override
  8940. public IRubyObject initialize_copy(IRubyObject other) {
  8941. RubyBinding otherBinding = (RubyBinding)other;
  8942. binding = otherBinding.binding;
  8943. return this;
  8944. }
  8945. }
  8946. /*
  8947. ***** BEGIN LICENSE BLOCK *****
  8948. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  8949. *
  8950. * The contents of this file are subject to the Common Public
  8951. * License Version 1.0 (the "License"); you may not use this file
  8952. * except in compliance with the License. You may obtain a copy of
  8953. * the License at http://www.eclipse.org/legal/cpl-v10.html
  8954. *
  8955. * Software distributed under the License is distributed on an "AS
  8956. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  8957. * implied. See the License for the specific language governing
  8958. * rights and limitations under the License.
  8959. *
  8960. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  8961. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  8962. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  8963. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  8964. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  8965. *
  8966. * Alternatively, the contents of this file may be used under the terms of
  8967. * either of the GNU General Public License Version 2 or later (the "GPL"),
  8968. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  8969. * in which case the provisions of the GPL or the LGPL are applicable instead
  8970. * of those above. If you wish to allow use of your version of this file only
  8971. * under the terms of either the GPL or the LGPL, and not to allow others to
  8972. * use your version of this file under the terms of the CPL, indicate your
  8973. * decision by deleting the provisions above and replace them with the notice
  8974. * and other provisions required by the GPL or the LGPL. If you do not delete
  8975. * the provisions above, a recipient may use your version of this file under
  8976. * the terms of any one of the CPL, the GPL or the LGPL.
  8977. ***** END LICENSE BLOCK *****/
  8978. package org.jruby;
  8979. import org.jruby.anno.JRubyClass;
  8980. import org.jruby.anno.JRubyMethod;
  8981. import org.jruby.runtime.ClassIndex;
  8982. import org.jruby.runtime.ObjectAllocator;
  8983. import org.jruby.runtime.ThreadContext;
  8984. import org.jruby.runtime.builtin.IRubyObject;
  8985. import org.jruby.runtime.marshal.MarshalStream;
  8986. /**
  8987. *
  8988. * @author jpetersen
  8989. */
  8990. @JRubyClass(name={"TrueClass", "FalseClass"})
  8991. public class RubyBoolean extends RubyObject {
  8992. public RubyBoolean(Ruby runtime, boolean value) {
  8993. super(runtime, (value ? runtime.getTrueClass() : runtime.getFalseClass()), // Don't initialize with class
  8994. false); // Don't put in object space
  8995. if (!value) flags = FALSE_F;
  8996. }
  8997. @Override
  8998. public int getNativeTypeIndex() {
  8999. return (flags & FALSE_F) == 0 ? ClassIndex.TRUE : ClassIndex.FALSE;
  9000. }
  9001. @Override
  9002. public boolean isImmediate() {
  9003. return true;
  9004. }
  9005. @Override
  9006. public RubyClass getSingletonClass() {
  9007. return metaClass;
  9008. }
  9009. @Override
  9010. public Class<?> getJavaClass() {
  9011. return boolean.class;
  9012. }
  9013. public static RubyClass createFalseClass(Ruby runtime) {
  9014. RubyClass falseClass = runtime.defineClass("FalseClass", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  9015. runtime.setFalseClass(falseClass);
  9016. falseClass.index = ClassIndex.FALSE;
  9017. falseClass.defineAnnotatedMethods(False.class);
  9018. falseClass.getMetaClass().undefineMethod("new");
  9019. return falseClass;
  9020. }
  9021. public static RubyClass createTrueClass(Ruby runtime) {
  9022. RubyClass trueClass = runtime.defineClass("TrueClass", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  9023. runtime.setTrueClass(trueClass);
  9024. trueClass.index = ClassIndex.TRUE;
  9025. trueClass.defineAnnotatedMethods(True.class);
  9026. trueClass.getMetaClass().undefineMethod("new");
  9027. return trueClass;
  9028. }
  9029. public static RubyBoolean newBoolean(Ruby runtime, boolean value) {
  9030. return value ? runtime.getTrue() : runtime.getFalse();
  9031. }
  9032. public static class False {
  9033. @JRubyMethod(name = "&")
  9034. public static IRubyObject false_and(IRubyObject f, IRubyObject oth) {
  9035. return f;
  9036. }
  9037. @JRubyMethod(name = "|")
  9038. public static IRubyObject false_or(IRubyObject f, IRubyObject oth) {
  9039. return oth.isTrue() ? f.getRuntime().getTrue() : f;
  9040. }
  9041. @JRubyMethod(name = "^")
  9042. public static IRubyObject false_xor(IRubyObject f, IRubyObject oth) {
  9043. return oth.isTrue() ? f.getRuntime().getTrue() : f;
  9044. }
  9045. @JRubyMethod(name = "to_s")
  9046. public static IRubyObject false_to_s(IRubyObject f) {
  9047. return f.getRuntime().newString("false");
  9048. }
  9049. }
  9050. public static class True {
  9051. @JRubyMethod(name = "&")
  9052. public static IRubyObject true_and(IRubyObject t, IRubyObject oth) {
  9053. return oth.isTrue() ? t : t.getRuntime().getFalse();
  9054. }
  9055. @JRubyMethod(name = "|")
  9056. public static IRubyObject true_or(IRubyObject t, IRubyObject oth) {
  9057. return t;
  9058. }
  9059. @JRubyMethod(name = "^")
  9060. public static IRubyObject true_xor(IRubyObject t, IRubyObject oth) {
  9061. return oth.isTrue() ? t.getRuntime().getFalse() : t;
  9062. }
  9063. @JRubyMethod(name = "to_s")
  9064. public static IRubyObject true_to_s(IRubyObject t) {
  9065. return t.getRuntime().newString("true");
  9066. }
  9067. }
  9068. @Override
  9069. public RubyFixnum id() {
  9070. if ((flags & FALSE_F) == 0) {
  9071. return RubyFixnum.newFixnum(getRuntime(), 2);
  9072. } else {
  9073. return RubyFixnum.zero(getRuntime());
  9074. }
  9075. }
  9076. @Override
  9077. public IRubyObject taint(ThreadContext context) {
  9078. return this;
  9079. }
  9080. @Override
  9081. public IRubyObject freeze(ThreadContext context) {
  9082. return this;
  9083. }
  9084. public void marshalTo(MarshalStream output) throws java.io.IOException {
  9085. output.write(isTrue() ? 'T' : 'F');
  9086. }
  9087. }
  9088. /***** BEGIN LICENSE BLOCK *****
  9089. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  9090. *
  9091. * The contents of this file are subject to the Common Public
  9092. * License Version 1.0 (the "License"); you may not use this file
  9093. * except in compliance with the License. You may obtain a copy of
  9094. * the License at http://www.eclipse.org/legal/cpl-v10.html
  9095. *
  9096. * Software distributed under the License is distributed on an "AS
  9097. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9098. * implied. See the License for the specific language governing
  9099. * rights and limitations under the License.
  9100. *
  9101. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  9102. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  9103. * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
  9104. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  9105. *
  9106. * Alternatively, the contents of this file may be used under the terms of
  9107. * either of the GNU General Public License Version 2 or later (the "GPL"),
  9108. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  9109. * in which case the provisions of the GPL or the LGPL are applicable instead
  9110. * of those above. If you wish to allow use of your version of this file only
  9111. * under the terms of either the GPL or the LGPL, and not to allow others to
  9112. * use your version of this file under the terms of the CPL, indicate your
  9113. * decision by deleting the provisions above and replace them with the notice
  9114. * and other provisions required by the GPL or the LGPL. If you do not delete
  9115. * the provisions above, a recipient may use your version of this file under
  9116. * the terms of any one of the CPL, the GPL or the LGPL.
  9117. ***** END LICENSE BLOCK *****/
  9118. package org.jruby;
  9119. import java.io.IOException;
  9120. import java.util.ArrayList;
  9121. import java.util.Collection;
  9122. import java.util.Collections;
  9123. import java.util.Set;
  9124. import org.jruby.anno.JRubyMethod;
  9125. import org.jruby.anno.JRubyClass;
  9126. import org.jruby.internal.runtime.methods.DynamicMethod;
  9127. import org.jruby.internal.runtime.methods.JavaMethod;
  9128. import org.jruby.javasupport.util.RuntimeHelpers;
  9129. import org.jruby.runtime.Block;
  9130. import org.jruby.runtime.CallSite;
  9131. import org.jruby.runtime.CallSite.InlineCachingCallSite;
  9132. import org.jruby.runtime.CallType;
  9133. import org.jruby.runtime.ClassIndex;
  9134. import org.jruby.runtime.ObjectAllocator;
  9135. import org.jruby.runtime.ObjectMarshal;
  9136. import org.jruby.runtime.ThreadContext;
  9137. import org.jruby.runtime.Visibility;
  9138. import org.jruby.runtime.builtin.IRubyObject;
  9139. import org.jruby.runtime.marshal.MarshalStream;
  9140. import org.jruby.runtime.marshal.UnmarshalStream;
  9141. import org.jruby.util.collections.WeakHashSet;
  9142. /**
  9143. *
  9144. * @author jpetersen
  9145. */
  9146. @JRubyClass(name="Class", parent="Module")
  9147. public class RubyClass extends RubyModule {
  9148. public static final int CS_IDX_INITIALIZE = 0;
  9149. public static final String[] CS_NAMES = {
  9150. "initialize"
  9151. };
  9152. private final CallSite[] baseCallSites = new CallSite[CS_NAMES.length];
  9153. {
  9154. for(int i = 0; i < CS_NAMES.length; i++) {
  9155. baseCallSites[i] = new InlineCachingCallSite(CS_NAMES[i], CallType.FUNCTIONAL);
  9156. }
  9157. }
  9158. private CallSite[] extraCallSites;
  9159. public static void createClassClass(Ruby runtime, RubyClass classClass) {
  9160. classClass.index = ClassIndex.CLASS;
  9161. classClass.kindOf = new RubyModule.KindOf() {
  9162. @Override
  9163. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  9164. return obj instanceof RubyClass;
  9165. }
  9166. };
  9167. classClass.undefineMethod("module_function");
  9168. classClass.undefineMethod("append_features");
  9169. classClass.undefineMethod("extend_object");
  9170. classClass.defineAnnotatedMethods(RubyClass.class);
  9171. classClass.addMethod("new", new SpecificArityNew(classClass, Visibility.PUBLIC));
  9172. // This is a non-standard method; have we decided to start extending Ruby?
  9173. //classClass.defineFastMethod("subclasses", callbackFactory.getFastOptMethod("subclasses"));
  9174. // FIXME: for some reason this dispatcher causes a VerifyError...
  9175. //classClass.dispatcher = callbackFactory.createDispatcher(classClass);
  9176. }
  9177. public static final ObjectAllocator CLASS_ALLOCATOR = new ObjectAllocator() {
  9178. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  9179. RubyClass clazz = new RubyClass(runtime);
  9180. clazz.allocator = ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR; // Class.allocate object is not allocatable before it is initialized
  9181. return clazz;
  9182. }
  9183. };
  9184. public ObjectAllocator getAllocator() {
  9185. return allocator;
  9186. }
  9187. public void setAllocator(ObjectAllocator allocator) {
  9188. this.allocator = allocator;
  9189. }
  9190. @JRubyMethod(name = "allocate")
  9191. public IRubyObject allocate() {
  9192. if (superClass == null) throw runtime.newTypeError("can't instantiate uninitialized class");
  9193. IRubyObject obj = allocator.allocate(runtime, this);
  9194. if (obj.getMetaClass().getRealClass() != getRealClass()) throw runtime.newTypeError("wrong instance allocation");
  9195. return obj;
  9196. }
  9197. public CallSite[] getBaseCallSites() {
  9198. return baseCallSites;
  9199. }
  9200. public CallSite[] getExtraCallSites() {
  9201. return extraCallSites;
  9202. }
  9203. @Override
  9204. public int getNativeTypeIndex() {
  9205. return ClassIndex.CLASS;
  9206. }
  9207. @Override
  9208. public boolean isModule() {
  9209. return false;
  9210. }
  9211. @Override
  9212. public boolean isClass() {
  9213. return true;
  9214. }
  9215. @Override
  9216. public boolean isSingleton() {
  9217. return false;
  9218. }
  9219. /** boot_defclass
  9220. * Create an initial Object meta class before Module and Kernel dependencies have
  9221. * squirreled themselves together.
  9222. *
  9223. * @param runtime we need it
  9224. * @return a half-baked meta class for object
  9225. */
  9226. public static RubyClass createBootstrapClass(Ruby runtime, String name, RubyClass superClass, ObjectAllocator allocator) {
  9227. RubyClass obj;
  9228. if (superClass == null ) { // boot the Object class
  9229. obj = new RubyClass(runtime);
  9230. obj.marshal = DEFAULT_OBJECT_MARSHAL;
  9231. } else { // boot the Module and Class classes
  9232. obj = new RubyClass(runtime, superClass);
  9233. }
  9234. obj.setAllocator(allocator);
  9235. obj.setBaseName(name);
  9236. return obj;
  9237. }
  9238. private final Ruby runtime;
  9239. private ObjectAllocator allocator; // the default allocator
  9240. protected ObjectMarshal marshal;
  9241. private Set<RubyClass> subclasses;
  9242. /** separate path for MetaClass and IncludedModuleWrapper construction
  9243. * (rb_class_boot version for MetaClasses)
  9244. * no marshal, allocator initialization and addSubclass(this) here!
  9245. */
  9246. protected RubyClass(Ruby runtime, RubyClass superClass, boolean objectSpace) {
  9247. super(runtime, runtime.getClassClass(), objectSpace);
  9248. this.runtime = runtime;
  9249. this.superClass = superClass; // this is the only case it might be null here (in MetaClass construction)
  9250. }
  9251. /** used by CLASS_ALLOCATOR (any Class' class will be a Class!)
  9252. * also used to bootstrap Object class
  9253. */
  9254. protected RubyClass(Ruby runtime) {
  9255. super(runtime, runtime.getClassClass());
  9256. this.runtime = runtime;
  9257. index = ClassIndex.CLASS;
  9258. }
  9259. /** rb_class_boot (for plain Classes)
  9260. * also used to bootstrap Module and Class classes
  9261. */
  9262. protected RubyClass(Ruby runtime, RubyClass superClazz) {
  9263. this(runtime);
  9264. superClass = superClazz;
  9265. marshal = superClazz.marshal; // use parent's marshal
  9266. superClazz.addSubclass(this);
  9267. infectBy(superClass);
  9268. }
  9269. /**
  9270. * A constructor which allows passing in an array of supplementary call sites.
  9271. */
  9272. protected RubyClass(Ruby runtime, RubyClass superClazz, CallSite[] extraCallSites) {
  9273. this(runtime);
  9274. this.superClass = superClazz;
  9275. this.marshal = superClazz.marshal; // use parent's marshal
  9276. superClazz.addSubclass(this);
  9277. this.extraCallSites = extraCallSites;
  9278. infectBy(superClass);
  9279. }
  9280. /**
  9281. * Construct a new class with the given name scoped under Object (global)
  9282. * and with Object as its immediate superclass.
  9283. * Corresponds to rb_class_new in MRI.
  9284. */
  9285. public static RubyClass newClass(Ruby runtime, RubyClass superClass) {
  9286. if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
  9287. if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
  9288. return new RubyClass(runtime, superClass);
  9289. }
  9290. /**
  9291. * A variation on newClass that allow passing in an array of supplementary
  9292. * call sites to improve dynamic invocation.
  9293. */
  9294. public static RubyClass newClass(Ruby runtime, RubyClass superClass, CallSite[] extraCallSites) {
  9295. if (superClass == runtime.getClassClass()) throw runtime.newTypeError("can't make subclass of Class");
  9296. if (superClass.isSingleton()) throw runtime.newTypeError("can't make subclass of virtual class");
  9297. return new RubyClass(runtime, superClass, extraCallSites);
  9298. }
  9299. /**
  9300. * Construct a new class with the given name, allocator, parent class,
  9301. * and containing class. If setParent is true, the class's parent will be
  9302. * explicitly set to the provided parent (rather than the new class just
  9303. * being assigned to a constant in that parent).
  9304. * Corresponds to rb_class_new/rb_define_class_id/rb_name_class/rb_set_class_path
  9305. * in MRI.
  9306. */
  9307. public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent) {
  9308. RubyClass clazz = newClass(runtime, superClass);
  9309. clazz.setBaseName(name);
  9310. clazz.setAllocator(allocator);
  9311. clazz.makeMetaClass(superClass.getMetaClass());
  9312. if (setParent) clazz.setParent(parent);
  9313. parent.setConstant(name, clazz);
  9314. clazz.inherit(superClass);
  9315. return clazz;
  9316. }
  9317. /**
  9318. * A variation on newClass that allows passing in an array of supplementary
  9319. * call sites to improve dynamic invocation performance.
  9320. */
  9321. public static RubyClass newClass(Ruby runtime, RubyClass superClass, String name, ObjectAllocator allocator, RubyModule parent, boolean setParent, CallSite[] extraCallSites) {
  9322. RubyClass clazz = newClass(runtime, superClass, extraCallSites);
  9323. clazz.setBaseName(name);
  9324. clazz.setAllocator(allocator);
  9325. clazz.makeMetaClass(superClass.getMetaClass());
  9326. if (setParent) clazz.setParent(parent);
  9327. parent.setConstant(name, clazz);
  9328. clazz.inherit(superClass);
  9329. return clazz;
  9330. }
  9331. /** rb_make_metaclass
  9332. *
  9333. */
  9334. @Override
  9335. public RubyClass makeMetaClass(RubyClass superClass) {
  9336. if (isSingleton()) { // could be pulled down to RubyClass in future
  9337. MetaClass klass = new MetaClass(getRuntime(), superClass); // rb_class_boot
  9338. setMetaClass(klass);
  9339. klass.setAttached(this);
  9340. klass.setMetaClass(klass);
  9341. klass.setSuperClass(getSuperClass().getRealClass().getMetaClass());
  9342. return klass;
  9343. } else {
  9344. return super.makeMetaClass(superClass);
  9345. }
  9346. }
  9347. @Deprecated
  9348. public IRubyObject invoke(ThreadContext context, IRubyObject self, int methodIndex, String name, IRubyObject[] args, CallType callType, Block block) {
  9349. return invoke(context, self, name, args, callType, block);
  9350. }
  9351. public boolean notVisibleAndNotMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
  9352. return !method.isCallableFrom(caller, callType) && !name.equals("method_missing");
  9353. }
  9354. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9355. CallType callType, Block block) {
  9356. DynamicMethod method = searchMethod(name);
  9357. IRubyObject caller = context.getFrameSelf();
  9358. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9359. return RuntimeHelpers.callMethodMissing(context, self, method, name, caller, callType, block);
  9360. }
  9361. return method.call(context, self, this, name, block);
  9362. }
  9363. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name, Block block) {
  9364. DynamicMethod method = searchMethod(name);
  9365. if (shouldCallMethodMissing(method)) {
  9366. return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, block);
  9367. }
  9368. return method.call(context, self, this, name, block);
  9369. }
  9370. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9371. IRubyObject[] args, CallType callType, Block block) {
  9372. assert args != null;
  9373. DynamicMethod method = searchMethod(name);
  9374. IRubyObject caller = context.getFrameSelf();
  9375. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9376. return RuntimeHelpers.callMethodMissing(context, self, method, name, args, caller, callType, block);
  9377. }
  9378. return method.call(context, self, this, name, args, block);
  9379. }
  9380. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9381. IRubyObject[] args, Block block) {
  9382. assert args != null;
  9383. DynamicMethod method = searchMethod(name);
  9384. if (shouldCallMethodMissing(method)) {
  9385. return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, block);
  9386. }
  9387. return method.call(context, self, this, name, args, block);
  9388. }
  9389. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9390. IRubyObject arg, CallType callType, Block block) {
  9391. DynamicMethod method = searchMethod(name);
  9392. IRubyObject caller = context.getFrameSelf();
  9393. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9394. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, caller, callType, block);
  9395. }
  9396. return method.call(context, self, this, name, arg, block);
  9397. }
  9398. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9399. IRubyObject arg, Block block) {
  9400. DynamicMethod method = searchMethod(name);
  9401. if (shouldCallMethodMissing(method)) {
  9402. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, block);
  9403. }
  9404. return method.call(context, self, this, name, arg, block);
  9405. }
  9406. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9407. IRubyObject arg0, IRubyObject arg1, CallType callType, Block block) {
  9408. DynamicMethod method = searchMethod(name);
  9409. IRubyObject caller = context.getFrameSelf();
  9410. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9411. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, caller, callType, block);
  9412. }
  9413. return method.call(context, self, this, name, arg0, arg1, block);
  9414. }
  9415. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9416. IRubyObject arg0, IRubyObject arg1, Block block) {
  9417. DynamicMethod method = searchMethod(name);
  9418. if (shouldCallMethodMissing(method)) {
  9419. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, block);
  9420. }
  9421. return method.call(context, self, this, name, arg0, arg1, block);
  9422. }
  9423. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9424. IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType, Block block) {
  9425. DynamicMethod method = searchMethod(name);
  9426. IRubyObject caller = context.getFrameSelf();
  9427. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9428. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, caller, callType, block);
  9429. }
  9430. return method.call(context, self, this, name, arg0, arg1, arg2, block);
  9431. }
  9432. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9433. IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  9434. DynamicMethod method = searchMethod(name);
  9435. if (shouldCallMethodMissing(method)) {
  9436. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, block);
  9437. }
  9438. return method.call(context, self, this, name, arg0, arg1, arg2, block);
  9439. }
  9440. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9441. CallType callType) {
  9442. DynamicMethod method = searchMethod(name);
  9443. IRubyObject caller = context.getFrameSelf();
  9444. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9445. return RuntimeHelpers.callMethodMissing(context, self, method, name, caller, callType, Block.NULL_BLOCK);
  9446. }
  9447. return method.call(context, self, this, name);
  9448. }
  9449. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name) {
  9450. DynamicMethod method = searchMethod(name);
  9451. if (shouldCallMethodMissing(method)) {
  9452. return RuntimeHelpers.callMethodMissing(context, self, method, name, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9453. }
  9454. return method.call(context, self, this, name);
  9455. }
  9456. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9457. IRubyObject[] args, CallType callType) {
  9458. assert args != null;
  9459. DynamicMethod method = searchMethod(name);
  9460. IRubyObject caller = context.getFrameSelf();
  9461. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9462. return RuntimeHelpers.callMethodMissing(context, self, method, name, args, caller, callType, Block.NULL_BLOCK);
  9463. }
  9464. return method.call(context, self, this, name, args);
  9465. }
  9466. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9467. IRubyObject[] args) {
  9468. assert args != null;
  9469. DynamicMethod method = searchMethod(name);
  9470. if (shouldCallMethodMissing(method)) {
  9471. return RuntimeHelpers.callMethodMissing(context, self, method, name, args, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9472. }
  9473. return method.call(context, self, this, name, args);
  9474. }
  9475. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9476. IRubyObject arg, CallType callType) {
  9477. DynamicMethod method = searchMethod(name);
  9478. IRubyObject caller = context.getFrameSelf();
  9479. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9480. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, caller, callType, Block.NULL_BLOCK);
  9481. }
  9482. return method.call(context, self, this, name, arg);
  9483. }
  9484. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9485. IRubyObject arg) {
  9486. DynamicMethod method = searchMethod(name);
  9487. if (shouldCallMethodMissing(method)) {
  9488. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9489. }
  9490. return method.call(context, self, this, name, arg);
  9491. }
  9492. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9493. IRubyObject arg0, IRubyObject arg1, CallType callType) {
  9494. DynamicMethod method = searchMethod(name);
  9495. IRubyObject caller = context.getFrameSelf();
  9496. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9497. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, caller, callType, Block.NULL_BLOCK);
  9498. }
  9499. return method.call(context, self, this, name, arg0, arg1);
  9500. }
  9501. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9502. IRubyObject arg0, IRubyObject arg1) {
  9503. DynamicMethod method = searchMethod(name);
  9504. if (shouldCallMethodMissing(method)) {
  9505. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9506. }
  9507. return method.call(context, self, this, name, arg0, arg1);
  9508. }
  9509. public IRubyObject invoke(ThreadContext context, IRubyObject self, String name,
  9510. IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, CallType callType) {
  9511. DynamicMethod method = searchMethod(name);
  9512. IRubyObject caller = context.getFrameSelf();
  9513. if (shouldCallMethodMissing(method, name, caller, callType)) {
  9514. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, caller, callType, Block.NULL_BLOCK);
  9515. }
  9516. return method.call(context, self, this, name, arg0, arg1, arg2);
  9517. }
  9518. public IRubyObject finvoke(ThreadContext context, IRubyObject self, String name,
  9519. IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  9520. DynamicMethod method = searchMethod(name);
  9521. if (shouldCallMethodMissing(method)) {
  9522. return RuntimeHelpers.callMethodMissing(context, self, method, name, arg0, arg1, arg2, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9523. }
  9524. return method.call(context, self, this, name, arg0, arg1, arg2);
  9525. }
  9526. private boolean shouldCallMethodMissing(DynamicMethod method) {
  9527. return method.isUndefined();
  9528. }
  9529. private boolean shouldCallMethodMissing(DynamicMethod method, String name, IRubyObject caller, CallType callType) {
  9530. return method.isUndefined() || notVisibleAndNotMethodMissing(method, name, caller, callType);
  9531. }
  9532. public IRubyObject invokeInherited(ThreadContext context, IRubyObject self, IRubyObject subclass) {
  9533. DynamicMethod method = getMetaClass().searchMethod("inherited");
  9534. if (method.isUndefined()) {
  9535. return RuntimeHelpers.callMethodMissing(context, self, method, "inherited", subclass, context.getFrameSelf(), CallType.FUNCTIONAL, Block.NULL_BLOCK);
  9536. }
  9537. return method.call(context, self, getMetaClass(), "inherited", subclass, Block.NULL_BLOCK);
  9538. }
  9539. /** rb_class_new_instance
  9540. *
  9541. */
  9542. public IRubyObject newInstance(ThreadContext context, IRubyObject[] args, Block block) {
  9543. IRubyObject obj = allocate();
  9544. baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
  9545. return obj;
  9546. }
  9547. // TODO: replace this with a smarter generated invoker that can handle 0-N args
  9548. public static class SpecificArityNew extends JavaMethod {
  9549. public SpecificArityNew(RubyModule implClass, Visibility visibility) {
  9550. super(implClass, visibility);
  9551. }
  9552. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  9553. RubyClass cls = (RubyClass)self;
  9554. IRubyObject obj = cls.allocate();
  9555. cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, args, block);
  9556. return obj;
  9557. }
  9558. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
  9559. RubyClass cls = (RubyClass)self;
  9560. IRubyObject obj = cls.allocate();
  9561. cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, block);
  9562. return obj;
  9563. }
  9564. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
  9565. RubyClass cls = (RubyClass)self;
  9566. IRubyObject obj = cls.allocate();
  9567. cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, block);
  9568. return obj;
  9569. }
  9570. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
  9571. RubyClass cls = (RubyClass)self;
  9572. IRubyObject obj = cls.allocate();
  9573. cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, block);
  9574. return obj;
  9575. }
  9576. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  9577. RubyClass cls = (RubyClass)self;
  9578. IRubyObject obj = cls.allocate();
  9579. cls.baseCallSites[CS_IDX_INITIALIZE].call(context, obj, arg0, arg1, arg2, block);
  9580. return obj;
  9581. }
  9582. }
  9583. /** rb_class_initialize
  9584. *
  9585. */
  9586. @JRubyMethod(name = "initialize", optional = 1, frame = true, visibility = Visibility.PRIVATE)
  9587. public IRubyObject initialize(IRubyObject[] args, Block block) {
  9588. if (superClass != null) {
  9589. throw getRuntime().newTypeError("already initialized class");
  9590. }
  9591. IRubyObject superObject;
  9592. if (args.length == 0) {
  9593. superObject = getRuntime().getObject();
  9594. } else {
  9595. superObject = args[0];
  9596. checkInheritable(superObject);
  9597. }
  9598. RubyClass superClazz = (RubyClass) superObject;
  9599. superClass = superClazz;
  9600. allocator = superClazz.allocator;
  9601. makeMetaClass(superClazz.getMetaClass());
  9602. marshal = superClazz.marshal;
  9603. superClazz.addSubclass(this);
  9604. super.initialize(block);
  9605. inherit(superClazz);
  9606. return this;
  9607. }
  9608. /** rb_class_init_copy
  9609. *
  9610. */
  9611. @JRubyMethod(name = "initialize_copy", required = 1)
  9612. @Override
  9613. public IRubyObject initialize_copy(IRubyObject original){
  9614. if (superClass != null) throw runtime.newTypeError("already initialized class");
  9615. if (original instanceof MetaClass) throw getRuntime().newTypeError("can't copy singleton class");
  9616. super.initialize_copy(original);
  9617. allocator = ((RubyClass)original).allocator;
  9618. return this;
  9619. }
  9620. // TODO: Someday, enable.
  9621. // @JRubyMethod(name = "subclasses", optional = 1)
  9622. public IRubyObject subclasses(ThreadContext context, IRubyObject[] args) {
  9623. boolean recursive = false;
  9624. if (args.length == 1) {
  9625. if (args[0] instanceof RubyBoolean) {
  9626. recursive = args[0].isTrue();
  9627. } else {
  9628. context.getRuntime().newTypeError(args[0], context.getRuntime().fastGetClass("Boolean"));
  9629. }
  9630. }
  9631. return RubyArray.newArray(context.getRuntime(), subclasses(recursive)).freeze(context);
  9632. }
  9633. public Collection subclasses(boolean includeDescendants) {
  9634. if (subclasses != null) {
  9635. Collection<RubyClass> mine = new ArrayList<RubyClass>(subclasses);
  9636. if (includeDescendants) {
  9637. for (RubyClass i: subclasses) {
  9638. mine.addAll(i.subclasses(includeDescendants));
  9639. }
  9640. }
  9641. return mine;
  9642. } else {
  9643. return Collections.EMPTY_LIST;
  9644. }
  9645. }
  9646. public synchronized void addSubclass(RubyClass subclass) {
  9647. if (subclasses == null) subclasses = new WeakHashSet<RubyClass>();
  9648. subclasses.add(subclass);
  9649. }
  9650. public Ruby getClassRuntime() {
  9651. return runtime;
  9652. }
  9653. public RubyClass getRealClass() {
  9654. return this;
  9655. }
  9656. @JRubyMethod(name = "inherited", required = 1)
  9657. public IRubyObject inherited(ThreadContext context, IRubyObject arg) {
  9658. return context.getRuntime().getNil();
  9659. }
  9660. /** rb_class_inherited (reversed semantics!)
  9661. *
  9662. */
  9663. public void inherit(RubyClass superClazz) {
  9664. if (superClazz == null) superClazz = getRuntime().getObject();
  9665. superClazz.invokeInherited(getRuntime().getCurrentContext(), superClazz, this);
  9666. }
  9667. /** Return the real super class of this class.
  9668. *
  9669. * rb_class_superclass
  9670. *
  9671. */
  9672. @JRubyMethod(name = "superclass")
  9673. public IRubyObject superclass(ThreadContext context) {
  9674. RubyClass superClazz = superClass;
  9675. if (superClazz == null) throw context.getRuntime().newTypeError("uninitialized class");
  9676. if (isSingleton()) superClazz = metaClass;
  9677. while (superClazz != null && superClazz.isIncluded()) superClazz = superClazz.superClass;
  9678. return superClazz != null ? superClazz : context.getRuntime().getNil();
  9679. }
  9680. /** rb_check_inheritable
  9681. *
  9682. */
  9683. public static void checkInheritable(IRubyObject superClass) {
  9684. if (!(superClass instanceof RubyClass)) {
  9685. throw superClass.getRuntime().newTypeError("superclass must be a Class (" + superClass.getMetaClass() + " given)");
  9686. }
  9687. if (((RubyClass)superClass).isSingleton()) {
  9688. throw superClass.getRuntime().newTypeError("can't make subclass of virtual class");
  9689. }
  9690. }
  9691. public final ObjectMarshal getMarshal() {
  9692. return marshal;
  9693. }
  9694. public final void setMarshal(ObjectMarshal marshal) {
  9695. this.marshal = marshal;
  9696. }
  9697. public final void marshal(Object obj, MarshalStream marshalStream) throws IOException {
  9698. getMarshal().marshalTo(getRuntime(), obj, this, marshalStream);
  9699. }
  9700. public final Object unmarshal(UnmarshalStream unmarshalStream) throws IOException {
  9701. return getMarshal().unmarshalFrom(getRuntime(), this, unmarshalStream);
  9702. }
  9703. public static void marshalTo(RubyClass clazz, MarshalStream output) throws java.io.IOException {
  9704. output.registerLinkTarget(clazz);
  9705. output.writeString(MarshalStream.getPathFromClass(clazz));
  9706. }
  9707. public static RubyClass unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  9708. String name = RubyString.byteListToString(input.unmarshalString());
  9709. RubyClass result = UnmarshalStream.getClassFromPath(input.getRuntime(), name);
  9710. input.registerLinkTarget(result);
  9711. return result;
  9712. }
  9713. protected static final ObjectMarshal DEFAULT_OBJECT_MARSHAL = new ObjectMarshal() {
  9714. public void marshalTo(Ruby runtime, Object obj, RubyClass type,
  9715. MarshalStream marshalStream) throws IOException {
  9716. IRubyObject object = (IRubyObject)obj;
  9717. marshalStream.registerLinkTarget(object);
  9718. marshalStream.dumpVariables(object.getVariableList());
  9719. }
  9720. public Object unmarshalFrom(Ruby runtime, RubyClass type,
  9721. UnmarshalStream unmarshalStream) throws IOException {
  9722. IRubyObject result = type.allocate();
  9723. unmarshalStream.registerLinkTarget(result);
  9724. unmarshalStream.defaultVariablesUnmarshal(result);
  9725. return result;
  9726. }
  9727. };
  9728. }
  9729. /***** BEGIN LICENSE BLOCK *****
  9730. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  9731. *
  9732. * The contents of this file are subject to the Common Public
  9733. * License Version 1.0 (the "License"); you may not use this file
  9734. * except in compliance with the License. You may obtain a copy of
  9735. * the License at http://www.eclipse.org/legal/cpl-v10.html
  9736. *
  9737. * Software distributed under the License is distributed on an "AS
  9738. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9739. * implied. See the License for the specific language governing
  9740. * rights and limitations under the License.
  9741. *
  9742. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  9743. *
  9744. * Alternatively, the contents of this file may be used under the terms of
  9745. * either of the GNU General Public License Version 2 or later (the "GPL"),
  9746. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  9747. * in which case the provisions of the GPL or the LGPL are applicable instead
  9748. * of those above. If you wish to allow use of your version of this file only
  9749. * under the terms of either the GPL or the LGPL, and not to allow others to
  9750. * use your version of this file under the terms of the CPL, indicate your
  9751. * decision by deleting the provisions above and replace them with the notice
  9752. * and other provisions required by the GPL or the LGPL. If you do not delete
  9753. * the provisions above, a recipient may use your version of this file under
  9754. * the terms of any one of the CPL, the GPL or the LGPL.
  9755. ***** END LICENSE BLOCK *****/
  9756. package org.jruby;
  9757. import java.io.File;
  9758. import java.net.MalformedURLException;
  9759. import java.net.URL;
  9760. import org.jruby.anno.JRubyMethod;
  9761. import org.jruby.runtime.Block;
  9762. import org.jruby.runtime.ThreadContext;
  9763. import org.jruby.runtime.builtin.IRubyObject;
  9764. /**
  9765. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  9766. */
  9767. public class RubyClassPathVariable extends RubyObject {
  9768. public static void createClassPathVariable(Ruby runtime) {
  9769. RubyClassPathVariable self = new RubyClassPathVariable(runtime);
  9770. runtime.getEnumerable().extend_object(self);
  9771. runtime.defineReadonlyVariable("$CLASSPATH", self);
  9772. self.getMetaClass().defineAnnotatedMethods(RubyClassPathVariable.class);
  9773. }
  9774. private RubyClassPathVariable(Ruby runtime) {
  9775. super(runtime, runtime.getObject());
  9776. }
  9777. @JRubyMethod(name = {"append", "<<"}, required = 1)
  9778. public IRubyObject append(IRubyObject obj) throws Exception {
  9779. String ss = obj.convertToString().toString();
  9780. URL url = getURL(ss);
  9781. getRuntime().getJRubyClassLoader().addURL(url);
  9782. return this;
  9783. }
  9784. private URL getURL(String target) throws MalformedURLException {
  9785. if(target.indexOf("://") == -1) {
  9786. return new File(target).toURI().toURL();
  9787. } else {
  9788. return new URL(target);
  9789. }
  9790. }
  9791. @JRubyMethod(name = {"size", "length"})
  9792. public IRubyObject size() {
  9793. return getRuntime().newFixnum(getRuntime().getJRubyClassLoader().getURLs().length);
  9794. }
  9795. @JRubyMethod(name = "each", frame = true)
  9796. public IRubyObject each(Block block) {
  9797. URL[] urls = getRuntime().getJRubyClassLoader().getURLs();
  9798. ThreadContext ctx = getRuntime().getCurrentContext();
  9799. for(int i=0,j=urls.length;i<j;i++) {
  9800. block.yield(ctx, getRuntime().newString(urls[i].toString()));
  9801. }
  9802. return getRuntime().getNil();
  9803. }
  9804. @JRubyMethod(name = "to_s")
  9805. public IRubyObject to_s() {
  9806. return callMethod(getRuntime().getCurrentContext(), "to_a").callMethod(getRuntime().getCurrentContext(), "to_s");
  9807. }
  9808. @JRubyMethod(name = "inspect")
  9809. public IRubyObject inspect() {
  9810. return callMethod(getRuntime().getCurrentContext(), "to_a").callMethod(getRuntime().getCurrentContext(), "inspect");
  9811. }
  9812. }// RubyClassPathVariable
  9813. /***** BEGIN LICENSE BLOCK *****
  9814. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  9815. *
  9816. * The contents of this file are subject to the Common Public
  9817. * License Version 1.0 (the "License"); you may not use this file
  9818. * except in compliance with the License. You may obtain a copy of
  9819. * the License at http://www.eclipse.org/legal/cpl-v10.html
  9820. *
  9821. * Software distributed under the License is distributed on an "AS
  9822. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9823. * implied. See the License for the specific language governing
  9824. * rights and limitations under the License.
  9825. *
  9826. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  9827. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  9828. * Copyright (C) 2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  9829. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  9830. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  9831. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  9832. * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
  9833. *
  9834. * Alternatively, the contents of this file may be used under the terms of
  9835. * either of the GNU General Public License Version 2 or later (the "GPL"),
  9836. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  9837. * in which case the provisions of the GPL or the LGPL are applicable instead
  9838. * of those above. If you wish to allow use of your version of this file only
  9839. * under the terms of either the GPL or the LGPL, and not to allow others to
  9840. * use your version of this file under the terms of the CPL, indicate your
  9841. * decision by deleting the provisions above and replace them with the notice
  9842. * and other provisions required by the GPL or the LGPL. If you do not delete
  9843. * the provisions above, a recipient may use your version of this file under
  9844. * the terms of any one of the CPL, the GPL or the LGPL.
  9845. ***** END LICENSE BLOCK *****/
  9846. package org.jruby;
  9847. import org.jruby.anno.JRubyMethod;
  9848. import org.jruby.anno.JRubyModule;
  9849. import org.jruby.exceptions.RaiseException;
  9850. import org.jruby.runtime.MethodIndex;
  9851. import org.jruby.runtime.ThreadContext;
  9852. import org.jruby.runtime.builtin.IRubyObject;
  9853. /** Implementation of the Comparable module.
  9854. *
  9855. */
  9856. @JRubyModule(name="Comparable")
  9857. public class RubyComparable {
  9858. public static RubyModule createComparable(Ruby runtime) {
  9859. RubyModule comparableModule = runtime.defineModule("Comparable");
  9860. runtime.setComparable(comparableModule);
  9861. comparableModule.defineAnnotatedMethods(RubyComparable.class);
  9862. return comparableModule;
  9863. }
  9864. /* ================
  9865. * Utility Methods
  9866. * ================
  9867. */
  9868. /** rb_cmpint
  9869. *
  9870. */
  9871. public static int cmpint(ThreadContext context, IRubyObject val, IRubyObject a, IRubyObject b) {
  9872. if (val.isNil()) cmperr(a, b);
  9873. if (val instanceof RubyFixnum) return RubyNumeric.fix2int((RubyFixnum) val);
  9874. if (val instanceof RubyBignum) return ((RubyBignum) val).getValue().signum() == -1 ? 1 : -1;
  9875. RubyFixnum zero = RubyFixnum.zero(context.getRuntime());
  9876. if (val.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue()) return 1;
  9877. if (val.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue()) return -1;
  9878. return 0;
  9879. }
  9880. /** rb_cmperr
  9881. *
  9882. */
  9883. public static IRubyObject cmperr(IRubyObject recv, IRubyObject other) {
  9884. IRubyObject target;
  9885. if (other.isImmediate() || !(other.isNil() || other.isTrue() || other == recv.getRuntime().getFalse())) {
  9886. target = other.inspect();
  9887. } else {
  9888. target = other.getType();
  9889. }
  9890. throw recv.getRuntime().newArgumentError("comparison of " + recv.getType() + " with " + target + " failed");
  9891. }
  9892. /* ================
  9893. * Module Methods
  9894. * ================
  9895. */
  9896. /** cmp_equal (cmp_eq inlined here)
  9897. *
  9898. */
  9899. @JRubyMethod(name = "==", required = 1)
  9900. public static IRubyObject op_equal(ThreadContext context, IRubyObject recv, IRubyObject other) {
  9901. Ruby runtime = context.getRuntime();
  9902. if (recv == other) return runtime.getTrue();
  9903. try {
  9904. IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
  9905. return RubyBoolean.newBoolean(runtime, cmpint(context, result, recv, other) == 0);
  9906. } catch (RaiseException e) {
  9907. if (e.getException().kind_of_p(context, runtime.getStandardError()).isTrue()) {
  9908. return runtime.getNil();
  9909. } else {
  9910. throw e;
  9911. }
  9912. }
  9913. }
  9914. /** cmp_gt
  9915. *
  9916. */
  9917. // <=> may return nil in many circumstances, e.g. 3 <=> NaN
  9918. @JRubyMethod(name = ">", required = 1)
  9919. public static RubyBoolean op_gt(ThreadContext context, IRubyObject recv, IRubyObject other) {
  9920. IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
  9921. if (result.isNil()) cmperr(recv, other);
  9922. return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) > 0);
  9923. }
  9924. /** cmp_ge
  9925. *
  9926. */
  9927. @JRubyMethod(name = ">=", required = 1)
  9928. public static RubyBoolean op_ge(ThreadContext context, IRubyObject recv, IRubyObject other) {
  9929. IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
  9930. if (result.isNil()) cmperr(recv, other);
  9931. return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) >= 0);
  9932. }
  9933. /** cmp_lt
  9934. *
  9935. */
  9936. @JRubyMethod(name = "<", required = 1)
  9937. public static RubyBoolean op_lt(ThreadContext context, IRubyObject recv, IRubyObject other) {
  9938. IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
  9939. if (result.isNil()) cmperr(recv, other);
  9940. return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) < 0);
  9941. }
  9942. /** cmp_le
  9943. *
  9944. */
  9945. @JRubyMethod(name = "<=", required = 1)
  9946. public static RubyBoolean op_le(ThreadContext context, IRubyObject recv, IRubyObject other) {
  9947. IRubyObject result = recv.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other);
  9948. if (result.isNil()) cmperr(recv, other);
  9949. return RubyBoolean.newBoolean(context.getRuntime(), cmpint(context, result, recv, other) <= 0);
  9950. }
  9951. /** cmp_between
  9952. *
  9953. */
  9954. @JRubyMethod(name = "between?", required = 2)
  9955. public static RubyBoolean between_p(ThreadContext context, IRubyObject recv, IRubyObject first, IRubyObject second) {
  9956. return context.getRuntime().newBoolean(op_lt(context, recv, first).isFalse() && op_gt(context, recv, second).isFalse());
  9957. }
  9958. }
  9959. /***** BEGIN LICENSE BLOCK *****
  9960. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  9961. *
  9962. * The contents of this file are subject to the Common Public
  9963. * License Version 1.0 (the "License"); you may not use this file
  9964. * except in compliance with the License. You may obtain a copy of
  9965. * the License at http://www.eclipse.org/legal/cpl-v10.html
  9966. *
  9967. * Software distributed under the License is distributed on an "AS
  9968. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9969. * implied. See the License for the specific language governing
  9970. * rights and limitations under the License.
  9971. *
  9972. * Alternatively, the contents of this file may be used under the terms of
  9973. * either of the GNU General Public License Version 2 or later (the "GPL"),
  9974. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  9975. * in which case the provisions of the GPL or the LGPL are applicable instead
  9976. * of those above. If you wish to allow use of your version of this file only
  9977. * under the terms of either the GPL or the LGPL, and not to allow others to
  9978. * use your version of this file under the terms of the CPL, indicate your
  9979. * decision by deleting the provisions above and replace them with the notice
  9980. * and other provisions required by the GPL or the LGPL. If you do not delete
  9981. * the provisions above, a recipient may use your version of this file under
  9982. * the terms of any one of the CPL, the GPL or the LGPL.
  9983. ***** END LICENSE BLOCK *****/
  9984. package org.jruby;
  9985. import static org.jruby.util.Numeric.f_abs;
  9986. import static org.jruby.util.Numeric.f_abs2;
  9987. import static org.jruby.util.Numeric.f_add;
  9988. import static org.jruby.util.Numeric.f_arg;
  9989. import static org.jruby.util.Numeric.f_conjugate;
  9990. import static org.jruby.util.Numeric.f_denominator;
  9991. import static org.jruby.util.Numeric.f_div;
  9992. import static org.jruby.util.Numeric.f_divmod;
  9993. import static org.jruby.util.Numeric.f_equal_p;
  9994. import static org.jruby.util.Numeric.f_exact_p;
  9995. import static org.jruby.util.Numeric.f_expt;
  9996. import static org.jruby.util.Numeric.f_gt_p;
  9997. import static org.jruby.util.Numeric.f_inspect;
  9998. import static org.jruby.util.Numeric.f_lcm;
  9999. import static org.jruby.util.Numeric.f_mul;
  10000. import static org.jruby.util.Numeric.f_negate;
  10001. import static org.jruby.util.Numeric.f_negative_p;
  10002. import static org.jruby.util.Numeric.f_numerator;
  10003. import static org.jruby.util.Numeric.f_one_p;
  10004. import static org.jruby.util.Numeric.f_polar;
  10005. import static org.jruby.util.Numeric.f_quo;
  10006. import static org.jruby.util.Numeric.f_scalar_p;
  10007. import static org.jruby.util.Numeric.f_sub;
  10008. import static org.jruby.util.Numeric.f_to_f;
  10009. import static org.jruby.util.Numeric.f_to_i;
  10010. import static org.jruby.util.Numeric.f_to_r;
  10011. import static org.jruby.util.Numeric.f_to_s;
  10012. import static org.jruby.util.Numeric.f_xor;
  10013. import static org.jruby.util.Numeric.f_zero_p;
  10014. import org.jruby.anno.JRubyClass;
  10015. import org.jruby.anno.JRubyMethod;
  10016. import org.jruby.runtime.Arity;
  10017. import org.jruby.runtime.ClassIndex;
  10018. import org.jruby.runtime.Frame;
  10019. import org.jruby.runtime.ObjectAllocator;
  10020. import org.jruby.runtime.ThreadContext;
  10021. import org.jruby.runtime.Visibility;
  10022. import org.jruby.runtime.builtin.IRubyObject;
  10023. import org.jruby.util.ByteList;
  10024. import org.jruby.util.Numeric;
  10025. /**
  10026. * 1.9 complex.c as of revision: 18876
  10027. */
  10028. @JRubyClass(name = "Complex", parent = "Numeric")
  10029. public class RubyComplex extends RubyNumeric {
  10030. public static RubyClass createComplexClass(Ruby runtime) {
  10031. RubyClass complexc = runtime.defineClass("Complex", runtime.getNumeric(), COMPLEX_ALLOCATOR); // because one can Complex.send(:allocate)
  10032. runtime.setComplex(complexc);
  10033. complexc.index = ClassIndex.COMPLEX;
  10034. complexc.kindOf = new RubyModule.KindOf() {
  10035. @Override
  10036. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  10037. return obj instanceof RubyComplex;
  10038. }
  10039. };
  10040. ThreadContext context = runtime.getCurrentContext();
  10041. complexc.callMethod(context, "private_class_method", runtime.newSymbol("allocate"));
  10042. complexc.defineAnnotatedMethods(RubyComplex.class);
  10043. String[]undefined = {"<", "<=", "<=>", ">", ">=", "between?", "divmod",
  10044. "floor", "ceil", "modulo", "round", "step", "truncate"};
  10045. for (String undef : undefined) {
  10046. complexc.undefineMethod(undef);
  10047. }
  10048. complexc.defineConstant("I", RubyComplex.newComplexConvert(context, RubyFixnum.zero(runtime), RubyFixnum.one(runtime)));
  10049. return complexc;
  10050. }
  10051. private static ObjectAllocator COMPLEX_ALLOCATOR = new ObjectAllocator() {
  10052. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  10053. return new RubyComplex(runtime, klass, RubyFixnum.zero(runtime), RubyFixnum.zero(runtime));
  10054. }
  10055. };
  10056. /** internal
  10057. *
  10058. */
  10059. private RubyComplex(Ruby runtime, IRubyObject clazz, IRubyObject real, IRubyObject image) {
  10060. super(runtime, (RubyClass)clazz);
  10061. this.real = real;
  10062. this.image = image;
  10063. }
  10064. /** rb_complex_raw
  10065. *
  10066. */
  10067. static RubyComplex newComplexRaw(Ruby runtime, IRubyObject x, RubyObject y) {
  10068. return new RubyComplex(runtime, runtime.getComplex(), x, y);
  10069. }
  10070. /** rb_complex_raw1
  10071. *
  10072. */
  10073. static RubyComplex newComplexRaw(Ruby runtime, IRubyObject x) {
  10074. return new RubyComplex(runtime, runtime.getComplex(), x, RubyFixnum.zero(runtime));
  10075. }
  10076. /** rb_complex_new1
  10077. *
  10078. */
  10079. public static IRubyObject newComplexCanonicalize(ThreadContext context, IRubyObject x) {
  10080. return newComplexCanonicalize(context, x, RubyFixnum.zero(context.getRuntime()));
  10081. }
  10082. /** rb_complex_new
  10083. *
  10084. */
  10085. public static IRubyObject newComplexCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
  10086. return canonicalizeInternal(context, context.getRuntime().getComplex(), x, y);
  10087. }
  10088. /** rb_complex_polar
  10089. *
  10090. */
  10091. static IRubyObject newComplexPolar(ThreadContext context, IRubyObject x, IRubyObject y) {
  10092. return polar(context, context.getRuntime().getComplex(), x, y);
  10093. }
  10094. /** f_complex_new1
  10095. *
  10096. */
  10097. static IRubyObject newComplex(ThreadContext context, IRubyObject clazz, IRubyObject x) {
  10098. return newComplex(context, clazz, x, RubyFixnum.zero(context.getRuntime()));
  10099. }
  10100. /** f_complex_new2
  10101. *
  10102. */
  10103. static IRubyObject newComplex(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  10104. assert !(x instanceof RubyComplex);
  10105. return canonicalizeInternal(context, clazz, x, y);
  10106. }
  10107. /** f_complex_new_bang2
  10108. *
  10109. */
  10110. static RubyComplex newComplexBang(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  10111. assert x instanceof RubyComplex && y instanceof RubyComplex;
  10112. return new RubyComplex(context.getRuntime(), clazz, x, y);
  10113. }
  10114. /** f_complex_new_bang1
  10115. *
  10116. */
  10117. public static RubyComplex newComplexBang(ThreadContext context, IRubyObject clazz, IRubyObject x) {
  10118. assert x instanceof RubyComplex;
  10119. return newComplexBang(context, clazz, x, RubyFixnum.zero(context.getRuntime()));
  10120. }
  10121. private IRubyObject real;
  10122. private IRubyObject image;
  10123. IRubyObject getImage() {
  10124. return image;
  10125. }
  10126. IRubyObject getReal() {
  10127. return real;
  10128. }
  10129. /** m_cos
  10130. *
  10131. */
  10132. private static IRubyObject m_cos(ThreadContext context, IRubyObject x) {
  10133. if (f_scalar_p(context, x).isTrue()) return RubyMath.cos(x, x);
  10134. RubyComplex complex = (RubyComplex)x;
  10135. return newComplex(context, context.getRuntime().getComplex(),
  10136. f_mul(context, RubyMath.cos(x, complex.real), RubyMath.cosh(x, complex.image)),
  10137. f_mul(context, f_negate(context, RubyMath.sin(x, complex.real)), RubyMath.sinh(x, complex.image)));
  10138. }
  10139. /** m_sin
  10140. *
  10141. */
  10142. private static IRubyObject m_sin(ThreadContext context, IRubyObject x) {
  10143. if (f_scalar_p(context, x).isTrue()) return RubyMath.sin(x, x);
  10144. RubyComplex complex = (RubyComplex)x;
  10145. return newComplex(context, context.getRuntime().getComplex(),
  10146. f_mul(context, RubyMath.sin(x, complex.real), RubyMath.cosh(x, complex.image)),
  10147. f_mul(context, RubyMath.cos(x, complex.real), RubyMath.sinh(x, complex.image)));
  10148. }
  10149. /** m_sqrt
  10150. *
  10151. */
  10152. private static IRubyObject m_sqrt(ThreadContext context, IRubyObject x) {
  10153. if (f_scalar_p(context, x).isTrue()) {
  10154. if (!f_negative_p(context, x)) return RubyMath.sqrt(x, x);
  10155. return newComplex(context, context.getRuntime().getComplex(),
  10156. RubyFixnum.zero(context.getRuntime()),
  10157. RubyMath.sqrt(x, f_negate(context, x)));
  10158. } else {
  10159. RubyComplex complex = (RubyComplex)x;
  10160. if (f_negative_p(context, complex.image)) {
  10161. return f_conjugate(context, m_sqrt(context, f_conjugate(context, x)));
  10162. } else {
  10163. IRubyObject a = f_abs(context, x);
  10164. IRubyObject two = RubyFixnum.two(context.getRuntime());
  10165. return newComplex(context, context.getRuntime().getComplex(),
  10166. RubyMath.sqrt(x, f_div(context, f_add(context, a, complex.real), two)),
  10167. RubyMath.sqrt(x, f_div(context, f_sub(context, a, complex.real), two)));
  10168. }
  10169. }
  10170. }
  10171. /** nucomp_s_new_bang
  10172. *
  10173. */
  10174. @Deprecated
  10175. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject[]args) {
  10176. switch (args.length) {
  10177. case 1: return newInstanceBang(context, recv, args[0]);
  10178. case 2: return newInstanceBang(context, recv, args[0], args[1]);
  10179. }
  10180. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
  10181. return null;
  10182. }
  10183. @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
  10184. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject real) {
  10185. if (!(real instanceof RubyNumeric)) real = f_to_i(context, real);
  10186. return new RubyComplex(context.getRuntime(), recv, real, RubyFixnum.zero(context.getRuntime()));
  10187. }
  10188. @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
  10189. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject real, IRubyObject image) {
  10190. if (!(real instanceof RubyNumeric)) real = f_to_i(context, real);
  10191. if (!(image instanceof RubyNumeric)) image = f_to_i(context, image);
  10192. return new RubyComplex(context.getRuntime(), recv, real, image);
  10193. }
  10194. /** nucomp_real_check (might go to bimorphic)
  10195. *
  10196. */
  10197. private static void realCheck(ThreadContext context, IRubyObject num) {
  10198. switch (num.getMetaClass().index) {
  10199. case ClassIndex.FIXNUM:
  10200. case ClassIndex.BIGNUM:
  10201. case ClassIndex.FLOAT:
  10202. case ClassIndex.RATIONAL:
  10203. break;
  10204. default:
  10205. if (!(num instanceof RubyNumeric ) || !f_scalar_p(context, num).isTrue()) {
  10206. throw context.getRuntime().newArgumentError("not a real");
  10207. }
  10208. }
  10209. }
  10210. /** nucomp_s_canonicalize_internal
  10211. *
  10212. */
  10213. private static final boolean CL_CANNON = true;
  10214. private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObject clazz, IRubyObject real, IRubyObject image) {
  10215. if (f_zero_p(context, image) &&
  10216. ((RubyModule)clazz).fastHasConstant("Unify") &&
  10217. (!CL_CANNON ||
  10218. (!(real instanceof RubyFloat) &&
  10219. !(image instanceof RubyFloat)))) {
  10220. return real;
  10221. } else if (f_scalar_p(context, real).isTrue() &&
  10222. f_scalar_p(context, image).isTrue()) {
  10223. return new RubyComplex(context.getRuntime(), clazz, real, image);
  10224. } else if (f_scalar_p(context, real).isTrue()) {
  10225. RubyComplex complex = (RubyComplex)image;
  10226. return new RubyComplex(context.getRuntime(), clazz,
  10227. f_sub(context, real, complex.image),
  10228. f_add(context, RubyFixnum.zero(context.getRuntime()), complex.real));
  10229. } else if (f_scalar_p(context, image).isTrue()) {
  10230. RubyComplex complex = (RubyComplex)real;
  10231. return new RubyComplex(context.getRuntime(), clazz,
  10232. complex.real,
  10233. f_add(context, complex.image, image));
  10234. } else {
  10235. RubyComplex complex1 = (RubyComplex)real;
  10236. RubyComplex complex2 = (RubyComplex)image;
  10237. return new RubyComplex(context.getRuntime(), clazz,
  10238. f_sub(context, complex1.real, complex2.image),
  10239. f_add(context, complex1.image, complex2.real));
  10240. }
  10241. }
  10242. /** nucomp_s_new
  10243. *
  10244. */
  10245. @Deprecated
  10246. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[]args) {
  10247. switch (args.length) {
  10248. case 1: return newInstance(context, recv, args[0]);
  10249. case 2: return newInstance(context, recv, args[0], args[1]);
  10250. }
  10251. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
  10252. return null;
  10253. }
  10254. @JRubyMethod(name = {"new", "rect", "rectangular"}, meta = true)
  10255. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject real) {
  10256. realCheck(context, real);
  10257. return canonicalizeInternal(context, recv, real, RubyFixnum.zero(context.getRuntime()));
  10258. }
  10259. @JRubyMethod(name = {"new", "rect", "rectangular"}, meta = true)
  10260. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject real, IRubyObject image) {
  10261. realCheck(context, real);
  10262. realCheck(context, image);
  10263. return canonicalizeInternal(context, recv, real, image);
  10264. }
  10265. /** f_complex_polar
  10266. *
  10267. */
  10268. private static IRubyObject f_complex_polar(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  10269. assert !(x instanceof RubyComplex) && !(y instanceof RubyComplex);
  10270. return canonicalizeInternal(context, clazz,
  10271. f_mul(context, x, m_cos(context, y)),
  10272. f_mul(context, x, m_sin(context, y)));
  10273. }
  10274. /** nucomp_s_polar
  10275. *
  10276. */
  10277. @JRubyMethod(name = "polar", meta = true)
  10278. public static IRubyObject polar(ThreadContext context, IRubyObject clazz, IRubyObject abs, IRubyObject arg) {
  10279. return f_complex_polar(context, clazz, abs, arg);
  10280. }
  10281. /** rb_Complex1
  10282. *
  10283. */
  10284. public static IRubyObject newComplexConvert(ThreadContext context, IRubyObject x) {
  10285. return newComplexConvert(context, x, RubyFixnum.zero(context.getRuntime()));
  10286. }
  10287. /** rb_Complex/rb_Complex2
  10288. *
  10289. */
  10290. public static IRubyObject newComplexConvert(ThreadContext context, IRubyObject x, IRubyObject y) {
  10291. return convert(context, context.getRuntime().getComplex(), x, y);
  10292. }
  10293. @Deprecated
  10294. public static IRubyObject convert(ThreadContext context, IRubyObject clazz, IRubyObject[]args) {
  10295. switch (args.length) {
  10296. case 0: return convert(context, clazz);
  10297. case 1: return convert(context, clazz, args[0]);
  10298. case 2: return convert(context, clazz, args[0], args[1]);
  10299. }
  10300. Arity.raiseArgumentError(context.getRuntime(), args.length, 0, 2);
  10301. return null;
  10302. }
  10303. /** nucomp_s_convert
  10304. *
  10305. */
  10306. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  10307. public static IRubyObject convert(ThreadContext context, IRubyObject recv) {
  10308. IRubyObject nil = context.getRuntime().getNil();
  10309. return convertCommon(context, recv, nil, nil);
  10310. }
  10311. /** nucomp_s_convert
  10312. *
  10313. */
  10314. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  10315. public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1) {
  10316. return convertCommon(context, recv, a1, context.getRuntime().getNil());
  10317. }
  10318. /** nucomp_s_convert
  10319. *
  10320. */
  10321. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  10322. public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
  10323. return convertCommon(context, recv, a1, a2);
  10324. }
  10325. private static IRubyObject convertCommon(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
  10326. Frame frame = context.getCurrentFrame();
  10327. IRubyObject backref = frame.getBackRef();
  10328. if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
  10329. if (a1 instanceof RubyString) a1 = str_to_c_strict(context, a1);
  10330. if (a2 instanceof RubyString) a2 = str_to_c_strict(context, a2);
  10331. frame.setBackRef(backref);
  10332. if (a1 instanceof RubyComplex) {
  10333. RubyComplex a1Complex = (RubyComplex)a1;
  10334. if (!(a1Complex.image instanceof RubyFloat) && f_zero_p(context, a1Complex.image)) {
  10335. a1 = a1Complex.real;
  10336. }
  10337. }
  10338. if (a2 instanceof RubyComplex) {
  10339. RubyComplex a2Complex = (RubyComplex)a2;
  10340. if (!(a2Complex.image instanceof RubyFloat) && f_zero_p(context, a2Complex.image)) {
  10341. a2 = a2Complex.real;
  10342. }
  10343. }
  10344. if (a1 instanceof RubyComplex) {
  10345. if (a2.isNil() || f_zero_p(context, a2)) return a1;
  10346. }
  10347. return a2.isNil() ? newInstance(context, recv, a1) : newInstance(context, recv, a1, a2);
  10348. }
  10349. /** nucomp_real
  10350. *
  10351. */
  10352. @JRubyMethod(name = "real")
  10353. public IRubyObject real() {
  10354. return real;
  10355. }
  10356. /** nucomp_image
  10357. *
  10358. */
  10359. @JRubyMethod(name = {"image", "imag"})
  10360. public IRubyObject image() {
  10361. return image;
  10362. }
  10363. /** nucomp_add
  10364. *
  10365. */
  10366. @JRubyMethod(name = "+")
  10367. public IRubyObject op_add(ThreadContext context, IRubyObject other) {
  10368. if (other instanceof RubyComplex) {
  10369. RubyComplex otherComplex = (RubyComplex)other;
  10370. return newComplex(context, getMetaClass(),
  10371. f_add(context, real, otherComplex.real),
  10372. f_add(context, image, otherComplex.image));
  10373. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10374. return newComplex(context, getMetaClass(), f_add(context, real, other), image);
  10375. }
  10376. return coerceBin(context, "+", other);
  10377. }
  10378. /** nucomp_sub
  10379. *
  10380. */
  10381. @JRubyMethod(name = "-")
  10382. public IRubyObject op_sub(ThreadContext context, IRubyObject other) {
  10383. if (other instanceof RubyComplex) {
  10384. RubyComplex otherComplex = (RubyComplex)other;
  10385. return newComplex(context, getMetaClass(),
  10386. f_sub(context, real, otherComplex.real),
  10387. f_sub(context, image, otherComplex.image));
  10388. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10389. return newComplex(context, getMetaClass(), f_sub(context, real, other), image);
  10390. }
  10391. return coerceBin(context, "-", other);
  10392. }
  10393. /** nucomp_mul
  10394. *
  10395. */
  10396. @JRubyMethod(name = "*")
  10397. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  10398. if (other instanceof RubyComplex) {
  10399. RubyComplex otherComplex = (RubyComplex)other;
  10400. IRubyObject realp = f_sub(context,
  10401. f_mul(context, real, otherComplex.real),
  10402. f_mul(context, image, otherComplex.image));
  10403. IRubyObject imagep = f_add(context,
  10404. f_mul(context, real, otherComplex.image),
  10405. f_mul(context, image, otherComplex.real));
  10406. return newComplex(context, getMetaClass(), realp, imagep);
  10407. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10408. return newComplex(context, getMetaClass(),
  10409. f_mul(context, real, other),
  10410. f_mul(context, image, other));
  10411. }
  10412. return coerceBin(context, "*", other);
  10413. }
  10414. /** nucomp_div
  10415. *
  10416. */
  10417. @JRubyMethod(name = "/")
  10418. public IRubyObject op_div(ThreadContext context, IRubyObject other) {
  10419. if (other instanceof RubyComplex) {
  10420. RubyComplex otherComplex = (RubyComplex)other;
  10421. if (real instanceof RubyFloat || image instanceof RubyFloat ||
  10422. otherComplex.real instanceof RubyFloat || otherComplex.image instanceof RubyFloat) {
  10423. IRubyObject magn = RubyMath.hypot(this, otherComplex.real, otherComplex.image);
  10424. IRubyObject tmp = newComplexBang(context, getMetaClass(),
  10425. f_quo(context, otherComplex.real, magn),
  10426. f_quo(context, otherComplex.image, magn));
  10427. return f_quo(context, f_mul(context, this, f_conjugate(context, tmp)), magn);
  10428. }
  10429. return f_quo(context, f_mul(context, this, f_conjugate(context, other)), f_abs2(context, other));
  10430. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10431. return newComplex(context, getMetaClass(),
  10432. f_quo(context, real, other),
  10433. f_quo(context, image, other));
  10434. }
  10435. return coerceBin(context, "/", other);
  10436. }
  10437. /** nucomp_fdiv / nucomp_quo
  10438. *
  10439. */
  10440. @JRubyMethod(name = {"fdiv", "quo"})
  10441. public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
  10442. IRubyObject complex = newComplex(context, getMetaClass(),
  10443. f_to_f(context, real),
  10444. f_to_f(context, image));
  10445. return f_div(context, complex, other);
  10446. }
  10447. /** nucomp_expt
  10448. *
  10449. */
  10450. @JRubyMethod(name = "**")
  10451. public IRubyObject op_expt(ThreadContext context, IRubyObject other) {
  10452. if (f_zero_p(context, other)) {
  10453. return newComplexBang(context, getMetaClass(), RubyFixnum.one(context.getRuntime()));
  10454. } else if (other instanceof RubyRational && f_one_p(context, f_denominator(context, other))) {
  10455. other = f_numerator(context, other);
  10456. }
  10457. if (other instanceof RubyComplex) {
  10458. RubyArray a = f_polar(context, this).convertToArray();
  10459. IRubyObject r = a.eltInternal(0);
  10460. IRubyObject theta = a.eltInternal(1);
  10461. RubyComplex otherComplex = (RubyComplex)other;
  10462. IRubyObject nr = RubyMath.exp(this, f_sub(context,
  10463. f_mul(context, otherComplex.real, RubyMath.log(this, r)),
  10464. f_mul(context, otherComplex.image, theta)));
  10465. IRubyObject ntheta = f_add(context,
  10466. f_mul(context, theta, otherComplex.real),
  10467. f_mul(context, otherComplex.image, RubyMath.log(this, r)));
  10468. return polar(context, getMetaClass(), nr, ntheta);
  10469. } else if (other instanceof RubyInteger) {
  10470. IRubyObject one = RubyFixnum.one(context.getRuntime());
  10471. if (f_gt_p(context, other, RubyFixnum.zero(context.getRuntime())).isTrue()) {
  10472. IRubyObject x = this;
  10473. IRubyObject z = x;
  10474. IRubyObject n = f_sub(context, other, one);
  10475. IRubyObject two = RubyFixnum.two(context.getRuntime());
  10476. while (!f_zero_p(context, n)) {
  10477. RubyArray a = f_divmod(context, n, two).convertToArray();
  10478. while (f_zero_p(context, a.eltInternal(1))) {
  10479. RubyComplex xComplex = (RubyComplex)x;
  10480. x = newComplex(context, getMetaClass(),
  10481. f_sub(context, f_mul(context, xComplex.real, xComplex.real),
  10482. f_mul(context, xComplex.image, xComplex.image)),
  10483. f_mul(context, f_mul(context, two, xComplex.real), xComplex.image));
  10484. n = a.eltInternal(0);
  10485. a = f_divmod(context, n, two).convertToArray();
  10486. }
  10487. z = f_mul(context, z, x);
  10488. n = f_sub(context, n, one);
  10489. }
  10490. return z;
  10491. }
  10492. return f_expt(context, f_div(context, f_to_r(context, one), this), f_negate(context, other));
  10493. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10494. RubyArray a = f_polar(context, this).convertToArray();
  10495. IRubyObject r = a.eltInternal(0);
  10496. IRubyObject theta = a.eltInternal(1);
  10497. return f_complex_polar(context, getMetaClass(), f_expt(context, r, other), f_mul(context, theta, other));
  10498. }
  10499. return coerceBin(context, "**", other);
  10500. }
  10501. /** nucomp_equal_p
  10502. *
  10503. */
  10504. @JRubyMethod(name = "==")
  10505. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  10506. if (other instanceof RubyComplex) {
  10507. RubyComplex otherComplex = (RubyComplex)other;
  10508. if (f_equal_p(context, real, otherComplex.real) && f_equal_p(context, image, otherComplex.image)) return context.getRuntime().getTrue();
  10509. return context.getRuntime().getFalse();
  10510. } else if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10511. if (f_equal_p(context, real, other) && f_zero_p(context, image)) return context.getRuntime().getTrue();
  10512. return context.getRuntime().getFalse();
  10513. }
  10514. return f_equal_p(context, other, this) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  10515. }
  10516. /** nucomp_coerce
  10517. *
  10518. */
  10519. @JRubyMethod(name = "coerce")
  10520. public IRubyObject coerce(ThreadContext context, IRubyObject other) {
  10521. if (other instanceof RubyNumeric && f_scalar_p(context, other).isTrue()) {
  10522. return context.getRuntime().newArray(newComplexBang(context, getMetaClass(), other), this);
  10523. }
  10524. throw context.getRuntime().newTypeError(other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
  10525. }
  10526. /** nucomp_abs
  10527. *
  10528. */
  10529. @JRubyMethod(name = {"abs", "magnitude"})
  10530. public IRubyObject abs(ThreadContext context) {
  10531. return RubyMath.hypot(this, real, image);
  10532. }
  10533. /** nucomp_abs2
  10534. *
  10535. */
  10536. @JRubyMethod(name = "abs2")
  10537. public IRubyObject abs2(ThreadContext context) {
  10538. return f_add(context,
  10539. f_mul(context, real, real),
  10540. f_mul(context, image, image));
  10541. }
  10542. /** nucomp_arg
  10543. *
  10544. */
  10545. @JRubyMethod(name = {"arg", "angle"})
  10546. public IRubyObject arg(ThreadContext context) {
  10547. return RubyMath.atan2(this, image, real);
  10548. }
  10549. /** nucomp_polar
  10550. *
  10551. */
  10552. @JRubyMethod(name = "polar")
  10553. public IRubyObject polar(ThreadContext context) {
  10554. return context.getRuntime().newArray(f_abs(context, this), f_arg(context, this));
  10555. }
  10556. /** nucomp_conjugate
  10557. *
  10558. */
  10559. @JRubyMethod(name = {"conjugate", "conj", "~"})
  10560. public IRubyObject conjugate(ThreadContext context) {
  10561. return newComplex(context, getMetaClass(), real, f_negate(context, image));
  10562. }
  10563. /** nucomp_real_p
  10564. *
  10565. */
  10566. //@JRubyMethod(name = "real?")
  10567. public IRubyObject real_p(ThreadContext context) {
  10568. return context.getRuntime().getFalse();
  10569. }
  10570. /** nucomp_complex_p
  10571. *
  10572. */
  10573. // @JRubyMethod(name = "complex?")
  10574. public IRubyObject complex_p(ThreadContext context) {
  10575. return context.getRuntime().getTrue();
  10576. }
  10577. /** nucomp_exact_p
  10578. *
  10579. */
  10580. // @JRubyMethod(name = "exact?")
  10581. public IRubyObject exact_p(ThreadContext context) {
  10582. return (f_exact_p(context, real).isTrue() && f_exact_p(context, image).isTrue()) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  10583. }
  10584. /** nucomp_exact_p
  10585. *
  10586. */
  10587. // @JRubyMethod(name = "inexact?")
  10588. public IRubyObject inexact_p(ThreadContext context) {
  10589. return exact_p(context).isTrue() ? context.getRuntime().getFalse() : context.getRuntime().getTrue();
  10590. }
  10591. /** nucomp_denominator
  10592. *
  10593. */
  10594. @JRubyMethod(name = "denominator")
  10595. public IRubyObject demoninator(ThreadContext context) {
  10596. return f_lcm(context, f_denominator(context, real), f_denominator(context, image));
  10597. }
  10598. /** nucomp_numerator
  10599. *
  10600. */
  10601. @JRubyMethod(name = "numerator")
  10602. public IRubyObject numerator(ThreadContext context) {
  10603. IRubyObject cd = callMethod(context, "denominator");
  10604. return newComplex(context, getMetaClass(),
  10605. f_mul(context,
  10606. f_numerator(context, real),
  10607. f_div(context, cd, f_denominator(context, real))),
  10608. f_mul(context,
  10609. f_numerator(context, image),
  10610. f_div(context, cd, f_denominator(context, image))));
  10611. }
  10612. /** nucomp_hash
  10613. *
  10614. */
  10615. @JRubyMethod(name = "hash")
  10616. public IRubyObject hash(ThreadContext context) {
  10617. return f_xor(context, real, image);
  10618. }
  10619. /** f_signbit
  10620. *
  10621. */
  10622. private static boolean signbit(ThreadContext context, IRubyObject x) {
  10623. if (x instanceof RubyFloat) {
  10624. return Double.doubleToLongBits(((RubyFloat)x).getDoubleValue()) < 0;
  10625. }
  10626. return f_negative_p(context, x);
  10627. }
  10628. /** f_tpositive_p
  10629. *
  10630. */
  10631. private static boolean tpositive_p(ThreadContext context, IRubyObject x) {
  10632. return !signbit(context, x);
  10633. }
  10634. /** nucomp_to_s
  10635. *
  10636. */
  10637. @JRubyMethod(name = "to_s")
  10638. public IRubyObject to_s(ThreadContext context) {
  10639. boolean impos = tpositive_p(context, image);
  10640. RubyString str = f_to_s(context, real).convertToString();
  10641. str.cat(impos ? (byte)'+' : (byte)'-');
  10642. str.cat(f_to_s(context, f_abs(context, image)).convertToString().getByteList());
  10643. str.cat((byte)'i');
  10644. return str;
  10645. }
  10646. /** nucomp_inspect
  10647. *
  10648. */
  10649. @JRubyMethod(name = "inspect")
  10650. public IRubyObject inspect(ThreadContext context) {
  10651. boolean impos = tpositive_p(context, image);
  10652. RubyString str = context.getRuntime().newString();
  10653. str.cat((byte)'(');
  10654. str.cat(f_inspect(context, real).convertToString().getByteList());
  10655. str.cat(impos ? (byte)'+' : (byte)'-');
  10656. str.cat(f_inspect(context, f_abs(context, image)).convertToString().getByteList());
  10657. str.cat((byte)'i');
  10658. str.cat((byte)')');
  10659. return str;
  10660. }
  10661. /** nucomp_marshal_dump
  10662. *
  10663. */
  10664. @JRubyMethod(name = "marshal_dump")
  10665. public IRubyObject marshal_dump(ThreadContext context) {
  10666. return context.getRuntime().newArray(real, image);
  10667. }
  10668. /** nucomp_marshal_load
  10669. *
  10670. */
  10671. @JRubyMethod(name = "marshal_load")
  10672. public IRubyObject marshal_load(ThreadContext context, IRubyObject arg) {
  10673. RubyArray a = arg.convertToArray();
  10674. real = a.size() > 0 ? a.eltInternal(0) : context.getRuntime().getNil();
  10675. image = a.size() > 1 ? a.eltInternal(1) : context.getRuntime().getNil();
  10676. return this;
  10677. }
  10678. /** nucomp_scalar_p
  10679. *
  10680. */
  10681. @JRubyMethod(name = "scalar?")
  10682. public IRubyObject scalar_p(ThreadContext context) {
  10683. return context.getRuntime().getFalse();
  10684. }
  10685. /** nucomp_to_i
  10686. *
  10687. */
  10688. @JRubyMethod(name = "to_i")
  10689. public IRubyObject to_i(ThreadContext context) {
  10690. if (image instanceof RubyFloat || !f_zero_p(context, image)) {
  10691. throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Integer");
  10692. }
  10693. return f_to_i(context, real);
  10694. }
  10695. /** nucomp_to_f
  10696. *
  10697. */
  10698. @JRubyMethod(name = "to_f")
  10699. public IRubyObject to_f(ThreadContext context) {
  10700. if (image instanceof RubyFloat || !f_zero_p(context, image)) {
  10701. throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Float");
  10702. }
  10703. return f_to_f(context, real);
  10704. }
  10705. /** nucomp_to_f
  10706. *
  10707. */
  10708. @JRubyMethod(name = "to_r")
  10709. public IRubyObject to_r(ThreadContext context) {
  10710. if (image instanceof RubyFloat || !f_zero_p(context, image)) {
  10711. throw context.getRuntime().newRangeError("can't convert " + f_to_s(context, this).convertToString() + " into Rational");
  10712. }
  10713. return f_to_r(context, real);
  10714. }
  10715. static RubyArray str_to_c_internal(ThreadContext context, IRubyObject recv) {
  10716. RubyString s = recv.callMethod(context, "strip").convertToString();
  10717. ByteList bytes = s.getByteList();
  10718. Ruby runtime = context.getRuntime();
  10719. if (bytes.realSize == 0) return runtime.newArray(runtime.getNil(), recv);
  10720. IRubyObject sr, si, re;
  10721. sr = si = re = runtime.getNil();
  10722. boolean po = false;
  10723. IRubyObject m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat0).callMethod(context, "match", s);
  10724. if (!m.isNil()) {
  10725. sr = m.callMethod(context, "[]", RubyFixnum.one(runtime));
  10726. si = m.callMethod(context, "[]", RubyFixnum.two(runtime));
  10727. re = m.callMethod(context, "post_match");
  10728. po = true;
  10729. }
  10730. if (m.isNil()) {
  10731. m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat1).callMethod(context, "match", s);
  10732. if (!m.isNil()) {
  10733. sr = runtime.getNil();
  10734. si = m.callMethod(context, "[]", RubyFixnum.one(runtime));
  10735. if (si.isNil()) si = runtime.newString();
  10736. IRubyObject t = m.callMethod(context, "[]", RubyFixnum.two(runtime));
  10737. if (t.isNil()) t = runtime.newString(new ByteList(new byte[]{'1'}));
  10738. si.convertToString().cat(t.convertToString().getByteList());
  10739. re = m.callMethod(context, "post_match");
  10740. po = false;
  10741. }
  10742. }
  10743. if (m.isNil()) {
  10744. m = RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.comp_pat2).callMethod(context, "match", s);
  10745. if (m.isNil()) return runtime.newArray(runtime.getNil(), recv);
  10746. sr = m.callMethod(context, "[]", RubyFixnum.one(runtime));
  10747. if (m.callMethod(context, "[]", RubyFixnum.two(runtime)).isNil()) {
  10748. si = runtime.getNil();
  10749. } else {
  10750. si = m.callMethod(context, "[]", RubyFixnum.three(runtime));
  10751. IRubyObject t = m.callMethod(context, "[]", RubyFixnum.four(runtime));
  10752. if (t.isNil()) t = runtime.newString(new ByteList(new byte[]{'1'}));
  10753. si.convertToString().cat(t.convertToString().getByteList());
  10754. }
  10755. re = m.callMethod(context, "post_match");
  10756. po = false;
  10757. }
  10758. IRubyObject r = RubyFixnum.zero(runtime);
  10759. IRubyObject i = r;
  10760. if (!sr.isNil()) {
  10761. if (sr.callMethod(context, "include?", runtime.newString(new ByteList(new byte[]{'/'}))).isTrue()) {
  10762. r = f_to_r(context, sr);
  10763. } else if (f_gt_p(context, sr.callMethod(context, "count", runtime.newString(".eE")), RubyFixnum.zero(runtime)).isTrue()) {
  10764. r = f_to_f(context, sr);
  10765. } else {
  10766. r = f_to_i(context, sr);
  10767. }
  10768. }
  10769. if (!si.isNil()) {
  10770. if (si.callMethod(context, "include?", runtime.newString(new ByteList(new byte[]{'/'}))).isTrue()) {
  10771. i = f_to_r(context, si);
  10772. } else if (f_gt_p(context, si.callMethod(context, "count", runtime.newString(".eE")), RubyFixnum.zero(runtime)).isTrue()) {
  10773. i = f_to_f(context, si);
  10774. } else {
  10775. i = f_to_i(context, si);
  10776. }
  10777. }
  10778. return runtime.newArray(po ? newComplexPolar(context, r, i) : newComplexCanonicalize(context, r, i), re);
  10779. }
  10780. private static IRubyObject str_to_c_strict(ThreadContext context, IRubyObject recv) {
  10781. RubyArray a = str_to_c_internal(context, recv);
  10782. if (a.eltInternal(0).isNil() || a.eltInternal(1).convertToString().getByteList().length() > 0) {
  10783. IRubyObject s = recv.callMethod(context, "inspect");
  10784. throw context.getRuntime().newArgumentError("invalid value for Complex: " + s.convertToString());
  10785. }
  10786. return a.eltInternal(0);
  10787. }
  10788. }
  10789. /***** BEGIN LICENSE BLOCK *****
  10790. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  10791. *
  10792. * The contents of this file are subject to the Common Public
  10793. * License Version 1.0 (the "License"); you may not use this file
  10794. * except in compliance with the License. You may obtain a copy of
  10795. * the License at http://www.eclipse.org/legal/cpl-v10.html
  10796. *
  10797. * Software distributed under the License is distributed on an "AS
  10798. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10799. * implied. See the License for the specific language governing
  10800. * rights and limitations under the License.
  10801. *
  10802. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  10803. *
  10804. * Alternatively, the contents of this file may be used under the terms of
  10805. * either of the GNU General Public License Version 2 or later (the "GPL"),
  10806. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  10807. * in which case the provisions of the GPL or the LGPL are applicable instead
  10808. * of those above. If you wish to allow use of your version of this file only
  10809. * under the terms of either the GPL or the LGPL, and not to allow others to
  10810. * use your version of this file under the terms of the CPL, indicate your
  10811. * decision by deleting the provisions above and replace them with the notice
  10812. * and other provisions required by the GPL or the LGPL. If you do not delete
  10813. * the provisions above, a recipient may use your version of this file under
  10814. * the terms of any one of the CPL, the GPL or the LGPL.
  10815. ***** END LICENSE BLOCK *****/
  10816. package org.jruby;
  10817. import org.jruby.anno.JRubyMethod;
  10818. import org.jruby.anno.JRubyClass;
  10819. import org.jruby.runtime.Block;
  10820. import org.jruby.runtime.builtin.IRubyObject;
  10821. /**
  10822. * Placeholder until/if we can support this
  10823. *
  10824. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  10825. */
  10826. @JRubyClass(name="Continuation")
  10827. public class RubyContinuation {
  10828. public static void createContinuation(Ruby runtime) {
  10829. RubyClass cContinuation = runtime.defineClass("Continuation",runtime.getObject(),runtime.getObject().getAllocator());
  10830. cContinuation.defineAnnotatedMethods(RubyContinuation.class);
  10831. runtime.setContinuation(cContinuation);
  10832. }
  10833. @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
  10834. public static IRubyObject call(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  10835. throw recv.getRuntime().newNotImplementedError("Continuations are not implemented in JRuby and will not work");
  10836. }
  10837. }// RubyContinuation
  10838. /*
  10839. ***** BEGIN LICENSE BLOCK *****
  10840. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  10841. *
  10842. * The contents of this file are subject to the Common Public
  10843. * License Version 1.0 (the "License"); you may not use this file
  10844. * except in compliance with the License. You may obtain a copy of
  10845. * the License at http://www.eclipse.org/legal/cpl-v10.html
  10846. *
  10847. * Software distributed under the License is distributed on an "AS
  10848. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  10849. * implied. See the License for the specific language governing
  10850. * rights and limitations under the License.
  10851. *
  10852. * Copyright (C) 2006, 2007 Ola Bini <ola@ologix.com>
  10853. * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  10854. * Copyright (C) 2008 Vladimir Sizikov <vsizikov@gmail.com>
  10855. *
  10856. * Alternatively, the contents of this file may be used under the terms of
  10857. * either of the GNU General Public License Version 2 or later (the "GPL"),
  10858. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  10859. * in which case the provisions of the GPL or the LGPL are applicable instead
  10860. * of those above. If you wish to allow use of your version of this file only
  10861. * under the terms of either the GPL or the LGPL, and not to allow others to
  10862. * use your version of this file under the terms of the CPL, indicate your
  10863. * decision by deleting the provisions above and replace them with the notice
  10864. * and other provisions required by the GPL or the LGPL. If you do not delete
  10865. * the provisions above, a recipient may use your version of this file under
  10866. * the terms of any one of the CPL, the GPL or the LGPL.
  10867. ***** END LICENSE BLOCK *****/
  10868. package org.jruby;
  10869. import java.security.Provider;
  10870. import java.security.MessageDigest;
  10871. import java.security.NoSuchAlgorithmException;
  10872. import org.jruby.anno.JRubyMethod;
  10873. import org.jruby.anno.JRubyModule;
  10874. import org.jruby.anno.JRubyClass;
  10875. import org.jruby.runtime.Arity;
  10876. import org.jruby.runtime.Block;
  10877. import org.jruby.runtime.ObjectAllocator;
  10878. import org.jruby.runtime.builtin.IRubyObject;
  10879. import org.jruby.runtime.callback.Callback;
  10880. import org.jruby.util.ByteList;
  10881. /**
  10882. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  10883. */
  10884. @JRubyModule(name="Digest")
  10885. public class RubyDigest {
  10886. private static Provider provider = null;
  10887. public static void createDigest(Ruby runtime) {
  10888. try {
  10889. provider = (Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance();
  10890. } catch(Exception e) {
  10891. // provider is not available
  10892. }
  10893. RubyModule mDigest = runtime.defineModule("Digest");
  10894. RubyClass cDigestBase = mDigest.defineClassUnder("Base",runtime.getObject(), Base.BASE_ALLOCATOR);
  10895. cDigestBase.defineAnnotatedMethods(Base.class);
  10896. }
  10897. private static MessageDigest createMessageDigest(Ruby runtime, String providerName) throws NoSuchAlgorithmException {
  10898. if(provider != null) {
  10899. try {
  10900. return MessageDigest.getInstance(providerName, provider);
  10901. } catch(NoSuchAlgorithmException e) {
  10902. // bouncy castle doesn't support algorithm
  10903. }
  10904. }
  10905. // fall back to system JCA providers
  10906. return MessageDigest.getInstance(providerName);
  10907. }
  10908. @JRubyClass(name="Digest::MD5", parent="Digest::Base")
  10909. public static class MD5 {}
  10910. @JRubyClass(name="Digest::RMD160", parent="Digest::Base")
  10911. public static class RMD160 {}
  10912. @JRubyClass(name="Digest::SHA1", parent="Digest::Base")
  10913. public static class SHA1 {}
  10914. @JRubyClass(name="Digest::SHA256", parent="Digest::Base")
  10915. public static class SHA256 {}
  10916. @JRubyClass(name="Digest::SHA384", parent="Digest::Base")
  10917. public static class SHA384 {}
  10918. @JRubyClass(name="Digest::SHA512", parent="Digest::Base")
  10919. public static class SHA512 {}
  10920. public static void createDigestMD5(Ruby runtime) {
  10921. runtime.getLoadService().require("digest.so");
  10922. RubyModule mDigest = runtime.fastGetModule("Digest");
  10923. RubyClass cDigestBase = mDigest.fastGetClass("Base");
  10924. RubyClass cDigest_MD5 = mDigest.defineClassUnder("MD5",cDigestBase,cDigestBase.getAllocator());
  10925. cDigest_MD5.defineFastMethod("block_length", new Callback() {
  10926. public Arity getArity() {
  10927. return Arity.NO_ARGUMENTS;
  10928. }
  10929. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
  10930. return RubyFixnum.newFixnum(recv.getRuntime(), 64);
  10931. }
  10932. });
  10933. cDigest_MD5.setInternalModuleVariable("metadata",runtime.newString("MD5"));
  10934. }
  10935. public static void createDigestRMD160(Ruby runtime) {
  10936. runtime.getLoadService().require("digest.so");
  10937. if(provider == null) {
  10938. throw runtime.newLoadError("RMD160 not supported without BouncyCastle");
  10939. }
  10940. RubyModule mDigest = runtime.fastGetModule("Digest");
  10941. RubyClass cDigestBase = mDigest.fastGetClass("Base");
  10942. RubyClass cDigest_RMD160 = mDigest.defineClassUnder("RMD160",cDigestBase,cDigestBase.getAllocator());
  10943. cDigest_RMD160.setInternalModuleVariable("metadata",runtime.newString("RIPEMD160"));
  10944. }
  10945. public static void createDigestSHA1(Ruby runtime) {
  10946. runtime.getLoadService().require("digest.so");
  10947. RubyModule mDigest = runtime.fastGetModule("Digest");
  10948. RubyClass cDigestBase = mDigest.fastGetClass("Base");
  10949. RubyClass cDigest_SHA1 = mDigest.defineClassUnder("SHA1",cDigestBase,cDigestBase.getAllocator());
  10950. cDigest_SHA1.setInternalModuleVariable("metadata",runtime.newString("SHA1"));
  10951. }
  10952. public static void createDigestSHA2(Ruby runtime) {
  10953. runtime.getLoadService().require("digest.so");
  10954. try {
  10955. createMessageDigest(runtime, "SHA-256");
  10956. } catch(NoSuchAlgorithmException e) {
  10957. throw runtime.newLoadError("SHA2 not supported");
  10958. }
  10959. RubyModule mDigest = runtime.fastGetModule("Digest");
  10960. RubyClass cDigestBase = mDigest.fastGetClass("Base");
  10961. RubyClass cDigest_SHA2_256 = mDigest.defineClassUnder("SHA256",cDigestBase,cDigestBase.getAllocator());
  10962. cDigest_SHA2_256.setInternalModuleVariable("metadata",runtime.newString("SHA-256"));
  10963. RubyClass cDigest_SHA2_384 = mDigest.defineClassUnder("SHA384",cDigestBase,cDigestBase.getAllocator());
  10964. cDigest_SHA2_384.setInternalModuleVariable("metadata",runtime.newString("SHA-384"));
  10965. RubyClass cDigest_SHA2_512 = mDigest.defineClassUnder("SHA512",cDigestBase,cDigestBase.getAllocator());
  10966. cDigest_SHA2_512.setInternalModuleVariable("metadata",runtime.newString("SHA-512"));
  10967. }
  10968. @JRubyClass(name="Digest::Base")
  10969. public static class Base extends RubyObject {
  10970. protected static final ObjectAllocator BASE_ALLOCATOR = new ObjectAllocator() {
  10971. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  10972. return new Base(runtime, klass);
  10973. }
  10974. };
  10975. @JRubyMethod(name = "digest", required = 1, meta = true)
  10976. public static IRubyObject s_digest(IRubyObject recv, IRubyObject str) {
  10977. Ruby runtime = recv.getRuntime();
  10978. String name = ((RubyClass)recv).searchInternalModuleVariable("metadata").toString();
  10979. try {
  10980. MessageDigest md = createMessageDigest(runtime, name);
  10981. return RubyString.newString(runtime, md.digest(str.convertToString().getBytes()));
  10982. } catch(NoSuchAlgorithmException e) {
  10983. throw recv.getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
  10984. }
  10985. }
  10986. @JRubyMethod(name = "hexdigest", required = 1, meta = true)
  10987. public static IRubyObject s_hexdigest(IRubyObject recv, IRubyObject str) {
  10988. Ruby runtime = recv.getRuntime();
  10989. String name = ((RubyClass)recv).searchInternalModuleVariable("metadata").toString();
  10990. try {
  10991. MessageDigest md = createMessageDigest(runtime, name);
  10992. return RubyString.newString(runtime, ByteList.plain(toHex(md.digest(str.convertToString().getBytes()))));
  10993. } catch(NoSuchAlgorithmException e) {
  10994. throw recv.getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
  10995. }
  10996. }
  10997. private MessageDigest algo;
  10998. private StringBuffer data;
  10999. public Base(Ruby runtime, RubyClass type) {
  11000. super(runtime,type);
  11001. data = new StringBuffer();
  11002. if(type == runtime.fastGetModule("Digest").fastGetClass("Base")) {
  11003. throw runtime.newNotImplementedError("Digest::Base is an abstract class");
  11004. }
  11005. if(!type.hasInternalModuleVariable("metadata")) {
  11006. throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
  11007. }
  11008. try {
  11009. setAlgorithm(type.searchInternalModuleVariable("metadata"));
  11010. } catch(NoSuchAlgorithmException e) {
  11011. throw runtime.newNotImplementedError("the " + type + "() function is unimplemented on this machine");
  11012. }
  11013. }
  11014. @JRubyMethod(name = "initialize", optional = 1, frame = true)
  11015. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  11016. if(args.length > 0 && !args[0].isNil()) {
  11017. update(args[0]);
  11018. }
  11019. return this;
  11020. }
  11021. @JRubyMethod(name = "initialize_copy", required = 1)
  11022. public IRubyObject initialize_copy(IRubyObject obj) {
  11023. if(this == obj) {
  11024. return this;
  11025. }
  11026. ((RubyObject)obj).checkFrozen();
  11027. data = new StringBuffer(((Base)obj).data.toString());
  11028. String name = ((Base)obj).algo.getAlgorithm();
  11029. try {
  11030. algo = createMessageDigest(getRuntime(), name);
  11031. } catch(NoSuchAlgorithmException e) {
  11032. throw getRuntime().newNotImplementedError("Unsupported digest algorithm (" + name + ")");
  11033. }
  11034. return this;
  11035. }
  11036. @JRubyMethod(name = {"update", "<<"}, required = 1)
  11037. public IRubyObject update(IRubyObject obj) {
  11038. data.append(obj);
  11039. return this;
  11040. }
  11041. @JRubyMethod(name = "digest", optional = 1)
  11042. public IRubyObject digest(IRubyObject[] args) {
  11043. if (args.length == 1) {
  11044. reset();
  11045. data.append(args[0]);
  11046. }
  11047. IRubyObject digest = getDigest();
  11048. if (args.length == 1) {
  11049. reset();
  11050. }
  11051. return digest;
  11052. }
  11053. private IRubyObject getDigest() {
  11054. algo.reset();
  11055. return RubyString.newString(getRuntime(), algo.digest(ByteList.plain(data)));
  11056. }
  11057. @JRubyMethod(name = "digest!")
  11058. public IRubyObject digest_bang() {
  11059. algo.reset();
  11060. byte[] digest = algo.digest(ByteList.plain(data));
  11061. reset();
  11062. return RubyString.newString(getRuntime(), digest);
  11063. }
  11064. @JRubyMethod(name = {"hexdigest"}, optional = 1)
  11065. public IRubyObject hexdigest(IRubyObject[] args) {
  11066. algo.reset();
  11067. if (args.length == 1) {
  11068. reset();
  11069. data.append(args[0]);
  11070. }
  11071. byte[] digest = ByteList.plain(toHex(algo.digest(ByteList.plain(data))));
  11072. if (args.length == 1) {
  11073. reset();
  11074. }
  11075. return RubyString.newString(getRuntime(), digest);
  11076. }
  11077. @JRubyMethod(name = {"to_s"})
  11078. public IRubyObject to_s() {
  11079. algo.reset();
  11080. return RubyString.newString(getRuntime(), ByteList.plain(toHex(algo.digest(ByteList.plain(data)))));
  11081. }
  11082. @JRubyMethod(name = {"hexdigest!"})
  11083. public IRubyObject hexdigest_bang() {
  11084. algo.reset();
  11085. byte[] digest = ByteList.plain(toHex(algo.digest(ByteList.plain(data))));
  11086. reset();
  11087. return RubyString.newString(getRuntime(), digest);
  11088. }
  11089. @JRubyMethod(name = "inspect")
  11090. public IRubyObject inspect() {
  11091. algo.reset();
  11092. return RubyString.newString(getRuntime(), ByteList.plain("#<" + getMetaClass().getRealClass().getName() + ": " + toHex(algo.digest(ByteList.plain(data))) + ">"));
  11093. }
  11094. @JRubyMethod(name = "==", required = 1)
  11095. public IRubyObject op_equal(IRubyObject oth) {
  11096. boolean ret = this == oth;
  11097. if(!ret) {
  11098. if (oth instanceof Base) {
  11099. Base b = (Base)oth;
  11100. ret = this.algo.getAlgorithm().equals(b.algo.getAlgorithm()) &&
  11101. this.getDigest().equals(b.getDigest());
  11102. } else {
  11103. IRubyObject str = oth.convertToString();
  11104. ret = this.to_s().equals(str);
  11105. }
  11106. }
  11107. return ret ? getRuntime().getTrue() : getRuntime().getFalse();
  11108. }
  11109. @JRubyMethod(name = {"length", "size", "digest_length"})
  11110. public IRubyObject length() {
  11111. return RubyFixnum.newFixnum(getRuntime(), algo.getDigestLength());
  11112. }
  11113. @JRubyMethod(name = {"block_length"})
  11114. public IRubyObject block_length() {
  11115. throw getRuntime().newRuntimeError(
  11116. this.getMetaClass() + " doesn't implement block_length()");
  11117. }
  11118. @JRubyMethod(name = {"reset"})
  11119. public IRubyObject reset() {
  11120. algo.reset();
  11121. data = new StringBuffer();
  11122. return getRuntime().getNil();
  11123. }
  11124. private void setAlgorithm(IRubyObject algo) throws NoSuchAlgorithmException {
  11125. this.algo = createMessageDigest(getRuntime(), algo.toString());
  11126. }
  11127. private static String toHex(byte[] val) {
  11128. StringBuilder out = new StringBuilder();
  11129. for(int i=0,j=val.length;i<j;i++) {
  11130. String ve = Integer.toString((((int)((char)val[i])) & 0xFF),16);
  11131. if(ve.length() == 1) {
  11132. ve = "0" + ve;
  11133. }
  11134. out.append(ve);
  11135. }
  11136. return out.toString();
  11137. }
  11138. }
  11139. }// RubyDigest
  11140. /***** BEGIN LICENSE BLOCK *****
  11141. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  11142. *
  11143. * The contents of this file are subject to the Common Public
  11144. * License Version 1.0 (the "License"); you may not use this file
  11145. * except in compliance with the License. You may obtain a copy of
  11146. * the License at http://www.eclipse.org/legal/cpl-v10.html
  11147. *
  11148. * Software distributed under the License is distributed on an "AS
  11149. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11150. * implied. See the License for the specific language governing
  11151. * rights and limitations under the License.
  11152. *
  11153. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  11154. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  11155. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  11156. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  11157. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  11158. *
  11159. * Alternatively, the contents of this file may be used under the terms of
  11160. * either of the GNU General Public License Version 2 or later (the "GPL"),
  11161. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  11162. * in which case the provisions of the GPL or the LGPL are applicable instead
  11163. * of those above. If you wish to allow use of your version of this file only
  11164. * under the terms of either the GPL or the LGPL, and not to allow others to
  11165. * use your version of this file under the terms of the CPL, indicate your
  11166. * decision by deleting the provisions above and replace them with the notice
  11167. * and other provisions required by the GPL or the LGPL. If you do not delete
  11168. * the provisions above, a recipient may use your version of this file under
  11169. * the terms of any one of the CPL, the GPL or the LGPL.
  11170. ***** END LICENSE BLOCK *****/
  11171. package org.jruby;
  11172. import java.io.File;
  11173. import java.io.FileInputStream;
  11174. import java.io.IOException;
  11175. import java.util.ArrayList;
  11176. import java.util.List;
  11177. import org.jruby.anno.JRubyMethod;
  11178. import org.jruby.anno.JRubyClass;
  11179. import org.jruby.ext.posix.util.Platform;
  11180. import org.jruby.javasupport.JavaUtil;
  11181. import org.jruby.runtime.Block;
  11182. import org.jruby.runtime.ObjectAllocator;
  11183. import org.jruby.runtime.ThreadContext;
  11184. import org.jruby.runtime.builtin.IRubyObject;
  11185. import org.jruby.util.Dir;
  11186. import org.jruby.util.JRubyFile;
  11187. import org.jruby.util.ByteList;
  11188. /**
  11189. * .The Ruby built-in class Dir.
  11190. *
  11191. * @author jvoegele
  11192. */
  11193. @JRubyClass(name="Dir", include="Enumerable")
  11194. public class RubyDir extends RubyObject {
  11195. // What we passed to the constructor for method 'path'
  11196. private RubyString path;
  11197. protected JRubyFile dir;
  11198. private String[] snapshot; // snapshot of contents of directory
  11199. private int pos; // current position in directory
  11200. private boolean isOpen = true;
  11201. public RubyDir(Ruby runtime, RubyClass type) {
  11202. super(runtime, type);
  11203. }
  11204. private static final ObjectAllocator DIR_ALLOCATOR = new ObjectAllocator() {
  11205. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  11206. return new RubyDir(runtime, klass);
  11207. }
  11208. };
  11209. public static RubyClass createDirClass(Ruby runtime) {
  11210. RubyClass dirClass = runtime.defineClass("Dir", runtime.getObject(), DIR_ALLOCATOR);
  11211. runtime.setDir(dirClass);
  11212. dirClass.includeModule(runtime.getEnumerable());
  11213. dirClass.defineAnnotatedMethods(RubyDir.class);
  11214. return dirClass;
  11215. }
  11216. private final void checkDir() {
  11217. if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: operation on untainted Dir");
  11218. testFrozen("");
  11219. if (!isOpen) throw getRuntime().newIOError("closed directory");
  11220. }
  11221. /**
  11222. * Creates a new <code>Dir</code>. This method takes a snapshot of the
  11223. * contents of the directory at creation time, so changes to the contents
  11224. * of the directory will not be reflected during the lifetime of the
  11225. * <code>Dir</code> object returned, so a new <code>Dir</code> instance
  11226. * must be created to reflect changes to the underlying file system.
  11227. */
  11228. @JRubyMethod(name = "initialize", required = 1, frame = true)
  11229. public IRubyObject initialize(IRubyObject _newPath, Block unusedBlock) {
  11230. RubyString newPath = _newPath.convertToString();
  11231. getRuntime().checkSafeString(newPath);
  11232. String adjustedPath = RubyFile.adjustRootPathOnWindows(getRuntime(), newPath.toString(), null);
  11233. checkDirIsTwoSlashesOnWindows(getRuntime(), adjustedPath);
  11234. dir = JRubyFile.create(getRuntime().getCurrentDirectory(), adjustedPath);
  11235. if (!dir.isDirectory()) {
  11236. dir = null;
  11237. throw getRuntime().newErrnoENOENTError(newPath.toString() + " is not a directory");
  11238. }
  11239. path = newPath;
  11240. List<String> snapshotList = new ArrayList<String>();
  11241. snapshotList.add(".");
  11242. snapshotList.add("..");
  11243. snapshotList.addAll(getContents(dir));
  11244. snapshot = (String[]) snapshotList.toArray(new String[snapshotList.size()]);
  11245. pos = 0;
  11246. return this;
  11247. }
  11248. // ----- Ruby Class Methods ----------------------------------------------------
  11249. private static List<ByteList> dirGlobs(String cwd, IRubyObject[] args, int flags) {
  11250. List<ByteList> dirs = new ArrayList<ByteList>();
  11251. for (int i = 0; i < args.length; i++) {
  11252. ByteList globPattern = args[i].convertToString().getByteList();
  11253. dirs.addAll(Dir.push_glob(cwd, globPattern, flags));
  11254. }
  11255. return dirs;
  11256. }
  11257. private static IRubyObject asRubyStringList(Ruby runtime, List<ByteList> dirs) {
  11258. List<RubyString> allFiles = new ArrayList<RubyString>();
  11259. for (ByteList dir: dirs) {
  11260. allFiles.add(RubyString.newString(runtime, dir));
  11261. }
  11262. IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
  11263. allFiles.toArray(tempFileList);
  11264. return runtime.newArrayNoCopy(tempFileList);
  11265. }
  11266. private static String getCWD(Ruby runtime) {
  11267. try {
  11268. return new org.jruby.util.NormalizedFile(runtime.getCurrentDirectory()).getCanonicalPath();
  11269. } catch(Exception e) {
  11270. return runtime.getCurrentDirectory();
  11271. }
  11272. }
  11273. @JRubyMethod(name = "[]", required = 1, rest=true, meta = true)
  11274. public static IRubyObject aref(IRubyObject recv, IRubyObject[] args) {
  11275. List<ByteList> dirs;
  11276. if (args.length == 1) {
  11277. ByteList globPattern = args[0].convertToString().getByteList();
  11278. dirs = Dir.push_glob(getCWD(recv.getRuntime()), globPattern, 0);
  11279. } else {
  11280. dirs = dirGlobs(getCWD(recv.getRuntime()), args, 0);
  11281. }
  11282. return asRubyStringList(recv.getRuntime(), dirs);
  11283. }
  11284. /**
  11285. * Returns an array of filenames matching the specified wildcard pattern
  11286. * <code>pat</code>. If a block is given, the array is iterated internally
  11287. * with each filename is passed to the block in turn. In this case, Nil is
  11288. * returned.
  11289. */
  11290. @JRubyMethod(name = "glob", required = 1, optional = 1, frame = true, meta = true)
  11291. public static IRubyObject glob(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  11292. Ruby runtime = recv.getRuntime();
  11293. int flags = args.length == 2 ? RubyNumeric.num2int(args[1]) : 0;
  11294. List<ByteList> dirs;
  11295. IRubyObject tmp = args[0].checkArrayType();
  11296. if (tmp.isNil()) {
  11297. ByteList globPattern = args[0].convertToString().getByteList();
  11298. dirs = Dir.push_glob(recv.getRuntime().getCurrentDirectory(), globPattern, flags);
  11299. } else {
  11300. dirs = dirGlobs(getCWD(runtime), ((RubyArray) tmp).toJavaArray(), flags);
  11301. }
  11302. if (block.isGiven()) {
  11303. for (int i = 0; i < dirs.size(); i++) {
  11304. block.yield(context, RubyString.newString(runtime, dirs.get(i)));
  11305. }
  11306. return recv.getRuntime().getNil();
  11307. }
  11308. return asRubyStringList(recv.getRuntime(), dirs);
  11309. }
  11310. /**
  11311. * @return all entries for this Dir
  11312. */
  11313. @JRubyMethod(name = "entries")
  11314. public RubyArray entries() {
  11315. return getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(getRuntime(), snapshot));
  11316. }
  11317. /**
  11318. * Returns an array containing all of the filenames in the given directory.
  11319. */
  11320. @JRubyMethod(name = "entries", required = 1, meta = true)
  11321. public static RubyArray entries(IRubyObject recv, IRubyObject path) {
  11322. Ruby runtime = recv.getRuntime();
  11323. String adjustedPath = RubyFile.adjustRootPathOnWindows(
  11324. runtime, path.convertToString().toString(), null);
  11325. checkDirIsTwoSlashesOnWindows(runtime, adjustedPath);
  11326. final JRubyFile directory = JRubyFile.create(
  11327. recv.getRuntime().getCurrentDirectory(), adjustedPath);
  11328. if (!directory.isDirectory()) {
  11329. throw recv.getRuntime().newErrnoENOENTError("No such directory");
  11330. }
  11331. List<String> fileList = getContents(directory);
  11332. fileList.add(0, ".");
  11333. fileList.add(1, "..");
  11334. Object[] files = fileList.toArray();
  11335. return recv.getRuntime().newArrayNoCopy(JavaUtil.convertJavaArrayToRuby(recv.getRuntime(), files));
  11336. }
  11337. // MRI behavior: just plain '//' or '\\\\' are considered illegal on Windows.
  11338. private static void checkDirIsTwoSlashesOnWindows(Ruby runtime, String path) {
  11339. if (Platform.IS_WINDOWS && ("//".equals(path) || "\\\\".equals(path))) {
  11340. throw runtime.newErrnoEINVALError("Invalid argument - " + path);
  11341. }
  11342. }
  11343. /** Changes the current directory to <code>path</code> */
  11344. @JRubyMethod(name = "chdir", optional = 1, frame = true, meta = true)
  11345. public static IRubyObject chdir(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  11346. RubyString path = args.length == 1 ?
  11347. (RubyString) args[0].convertToString() : getHomeDirectoryPath(context);
  11348. String adjustedPath = RubyFile.adjustRootPathOnWindows(
  11349. recv.getRuntime(), path.toString(), null);
  11350. checkDirIsTwoSlashesOnWindows(recv.getRuntime(), adjustedPath);
  11351. JRubyFile dir = getDir(recv.getRuntime(), adjustedPath, true);
  11352. String realPath = null;
  11353. String oldCwd = recv.getRuntime().getCurrentDirectory();
  11354. // We get canonical path to try and flatten the path out.
  11355. // a dir '/subdir/..' should return as '/'
  11356. // cnutter: Do we want to flatten path out?
  11357. try {
  11358. realPath = dir.getCanonicalPath();
  11359. } catch (IOException e) {
  11360. realPath = dir.getAbsolutePath();
  11361. }
  11362. IRubyObject result = null;
  11363. if (block.isGiven()) {
  11364. // FIXME: Don't allow multiple threads to do this at once
  11365. recv.getRuntime().setCurrentDirectory(realPath);
  11366. try {
  11367. result = block.yield(context, path);
  11368. } finally {
  11369. dir = getDir(recv.getRuntime(), oldCwd, true);
  11370. recv.getRuntime().setCurrentDirectory(oldCwd);
  11371. }
  11372. } else {
  11373. recv.getRuntime().setCurrentDirectory(realPath);
  11374. result = recv.getRuntime().newFixnum(0);
  11375. }
  11376. return result;
  11377. }
  11378. /**
  11379. * Changes the root directory (only allowed by super user). Not available
  11380. * on all platforms.
  11381. */
  11382. @JRubyMethod(name = "chroot", required = 1, meta = true)
  11383. public static IRubyObject chroot(IRubyObject recv, IRubyObject path) {
  11384. throw recv.getRuntime().newNotImplementedError("chroot not implemented: chroot is non-portable and is not supported.");
  11385. }
  11386. /**
  11387. * Deletes the directory specified by <code>path</code>. The directory must
  11388. * be empty.
  11389. */
  11390. @JRubyMethod(name = {"rmdir", "unlink", "delete"}, required = 1, meta = true)
  11391. public static IRubyObject rmdir(IRubyObject recv, IRubyObject path) {
  11392. JRubyFile directory = getDir(recv.getRuntime(), path.convertToString().toString(), true);
  11393. if (!directory.delete()) {
  11394. throw recv.getRuntime().newSystemCallError("No such directory");
  11395. }
  11396. return recv.getRuntime().newFixnum(0);
  11397. }
  11398. /**
  11399. * Executes the block once for each file in the directory specified by
  11400. * <code>path</code>.
  11401. */
  11402. @JRubyMethod(name = "foreach", required = 1, frame = true, meta = true)
  11403. public static IRubyObject foreach(ThreadContext context, IRubyObject recv, IRubyObject _path, Block block) {
  11404. RubyString path = _path.convertToString();
  11405. recv.getRuntime().checkSafeString(path);
  11406. RubyClass dirClass = recv.getRuntime().getDir();
  11407. RubyDir dir = (RubyDir) dirClass.newInstance(context, new IRubyObject[] { path }, block);
  11408. dir.each(context, block);
  11409. return recv.getRuntime().getNil();
  11410. }
  11411. /** Returns the current directory. */
  11412. @JRubyMethod(name = {"getwd", "pwd"}, meta = true)
  11413. public static RubyString getwd(IRubyObject recv) {
  11414. Ruby ruby = recv.getRuntime();
  11415. return RubyString.newUnicodeString(ruby, ruby.getCurrentDirectory());
  11416. }
  11417. /**
  11418. * Creates the directory specified by <code>path</code>. Note that the
  11419. * <code>mode</code> parameter is provided only to support existing Ruby
  11420. * code, and is ignored.
  11421. */
  11422. @JRubyMethod(name = "mkdir", required = 1, optional = 1, meta = true)
  11423. public static IRubyObject mkdir(IRubyObject recv, IRubyObject[] args) {
  11424. Ruby runtime = recv.getRuntime();
  11425. runtime.checkSafeString(args[0]);
  11426. String path = args[0].toString();
  11427. File newDir = getDir(runtime, path, false);
  11428. if (File.separatorChar == '\\') {
  11429. newDir = new File(newDir.getPath());
  11430. }
  11431. int mode = args.length == 2 ? ((int) args[1].convertToInteger().getLongValue()) : 0777;
  11432. if (runtime.getPosix().mkdir(newDir.getAbsolutePath(), mode) < 0) {
  11433. // FIXME: This is a system error based on errno
  11434. throw recv.getRuntime().newSystemCallError("mkdir failed");
  11435. }
  11436. return RubyFixnum.zero(recv.getRuntime());
  11437. }
  11438. /**
  11439. * Returns a new directory object for <code>path</code>. If a block is
  11440. * provided, a new directory object is passed to the block, which closes the
  11441. * directory object before terminating.
  11442. */
  11443. @JRubyMethod(name = "open", required = 1, frame = true, meta = true)
  11444. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject path, Block block) {
  11445. RubyDir directory =
  11446. (RubyDir) recv.getRuntime().getDir().newInstance(context,
  11447. new IRubyObject[] { path }, Block.NULL_BLOCK);
  11448. if (!block.isGiven()) return directory;
  11449. try {
  11450. return block.yield(context, directory);
  11451. } finally {
  11452. directory.close();
  11453. }
  11454. }
  11455. // ----- Ruby Instance Methods -------------------------------------------------
  11456. /**
  11457. * Closes the directory stream.
  11458. */
  11459. @JRubyMethod(name = "close")
  11460. public IRubyObject close() {
  11461. // Make sure any read()s after close fail.
  11462. checkDir();
  11463. isOpen = false;
  11464. return getRuntime().getNil();
  11465. }
  11466. /**
  11467. * Executes the block once for each entry in the directory.
  11468. */
  11469. @JRubyMethod(name = "each", frame = true)
  11470. public IRubyObject each(ThreadContext context, Block block) {
  11471. checkDir();
  11472. String[] contents = snapshot;
  11473. for (int i=0; i<contents.length; i++) {
  11474. block.yield(context, getRuntime().newString(contents[i]));
  11475. }
  11476. return this;
  11477. }
  11478. /**
  11479. * Returns the current position in the directory.
  11480. */
  11481. @JRubyMethod(name = {"tell", "pos"})
  11482. public RubyInteger tell() {
  11483. checkDir();
  11484. return getRuntime().newFixnum(pos);
  11485. }
  11486. /**
  11487. * Moves to a position <code>d</code>. <code>pos</code> must be a value
  11488. * returned by <code>tell</code> or 0.
  11489. */
  11490. @JRubyMethod(name = "seek", required = 1)
  11491. public IRubyObject seek(IRubyObject newPos) {
  11492. checkDir();
  11493. set_pos(newPos);
  11494. return this;
  11495. }
  11496. @JRubyMethod(name = "pos=", required = 1)
  11497. public IRubyObject set_pos(IRubyObject newPos) {
  11498. this.pos = RubyNumeric.fix2int(newPos);
  11499. return newPos;
  11500. }
  11501. @JRubyMethod(name = "path")
  11502. public IRubyObject path(ThreadContext context) {
  11503. checkDir();
  11504. return path.strDup(context.getRuntime());
  11505. }
  11506. /** Returns the next entry from this directory. */
  11507. @JRubyMethod(name = "read")
  11508. public IRubyObject read() {
  11509. checkDir();
  11510. if (pos >= snapshot.length) {
  11511. return getRuntime().getNil();
  11512. }
  11513. RubyString result = getRuntime().newString(snapshot[pos]);
  11514. pos++;
  11515. return result;
  11516. }
  11517. /** Moves position in this directory to the first entry. */
  11518. @JRubyMethod(name = "rewind")
  11519. public IRubyObject rewind() {
  11520. if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: can't close");
  11521. checkDir();
  11522. pos = 0;
  11523. return this;
  11524. }
  11525. // ----- Helper Methods --------------------------------------------------------
  11526. /** Returns a Java <code>File</code> object for the specified path. If
  11527. * <code>path</code> is not a directory, throws <code>IOError</code>.
  11528. *
  11529. * @param path path for which to return the <code>File</code> object.
  11530. * @param mustExist is true the directory must exist. If false it must not.
  11531. * @throws IOError if <code>path</code> is not a directory.
  11532. */
  11533. protected static JRubyFile getDir(final Ruby runtime, final String path, final boolean mustExist) {
  11534. JRubyFile result = JRubyFile.create(runtime.getCurrentDirectory(),path);
  11535. if (mustExist && !result.exists()) {
  11536. throw runtime.newErrnoENOENTError("No such file or directory - " + path);
  11537. }
  11538. boolean isDirectory = result.isDirectory();
  11539. if (mustExist && !isDirectory) {
  11540. throw runtime.newErrnoENOTDIRError(path + " is not a directory");
  11541. } else if (!mustExist && isDirectory) {
  11542. throw runtime.newErrnoEEXISTError("File exists - " + path);
  11543. }
  11544. return result;
  11545. }
  11546. /**
  11547. * Returns the contents of the specified <code>directory</code> as an
  11548. * <code>ArrayList</code> containing the names of the files as Java Strings.
  11549. */
  11550. protected static List<String> getContents(File directory) {
  11551. String[] contents = directory.list();
  11552. List<String> result = new ArrayList<String>();
  11553. // If an IO exception occurs (something odd, but possible)
  11554. // A directory may return null.
  11555. if (contents != null) {
  11556. for (int i=0; i<contents.length; i++) {
  11557. result.add(contents[i]);
  11558. }
  11559. }
  11560. return result;
  11561. }
  11562. /**
  11563. * Returns the contents of the specified <code>directory</code> as an
  11564. * <code>ArrayList</code> containing the names of the files as Ruby Strings.
  11565. */
  11566. protected static List<RubyString> getContents(File directory, Ruby runtime) {
  11567. List<RubyString> result = new ArrayList<RubyString>();
  11568. String[] contents = directory.list();
  11569. for (int i = 0; i < contents.length; i++) {
  11570. result.add(runtime.newString(contents[i]));
  11571. }
  11572. return result;
  11573. }
  11574. /**
  11575. * Returns the home directory of the specified <code>user</code> on the
  11576. * system. If the home directory of the specified user cannot be found,
  11577. * an <code>ArgumentError it thrown</code>.
  11578. */
  11579. public static IRubyObject getHomeDirectoryPath(ThreadContext context, String user) {
  11580. /*
  11581. * TODO: This version is better than the hackish previous one. Windows
  11582. * behavior needs to be defined though. I suppose this version
  11583. * could be improved more too.
  11584. * TODO: /etc/passwd is also inadequate for MacOSX since it does not
  11585. * use /etc/passwd for regular user accounts
  11586. */
  11587. String passwd = null;
  11588. try {
  11589. FileInputStream stream = new FileInputStream("/etc/passwd");
  11590. int totalBytes = stream.available();
  11591. byte[] bytes = new byte[totalBytes];
  11592. stream.read(bytes);
  11593. stream.close();
  11594. passwd = new String(bytes);
  11595. } catch (IOException e) {
  11596. return context.getRuntime().getNil();
  11597. }
  11598. String[] rows = passwd.split("\n");
  11599. int rowCount = rows.length;
  11600. for (int i = 0; i < rowCount; i++) {
  11601. String[] fields = rows[i].split(":");
  11602. if (fields[0].equals(user)) {
  11603. return context.getRuntime().newString(fields[5]);
  11604. }
  11605. }
  11606. throw context.getRuntime().newArgumentError("user " + user + " doesn't exist");
  11607. }
  11608. public static RubyString getHomeDirectoryPath(ThreadContext context) {
  11609. Ruby runtime = context.getRuntime();
  11610. RubyHash systemHash = (RubyHash) runtime.getObject().fastGetConstant("ENV_JAVA");
  11611. RubyHash envHash = (RubyHash) runtime.getObject().fastGetConstant("ENV");
  11612. IRubyObject home = envHash.op_aref(context, runtime.newString("HOME"));
  11613. if (home == null || home.isNil()) {
  11614. home = systemHash.op_aref(context, runtime.newString("user.home"));
  11615. }
  11616. if (home == null || home.isNil()) {
  11617. home = envHash.op_aref(context, runtime.newString("LOGDIR"));
  11618. }
  11619. if (home == null || home.isNil()) {
  11620. throw runtime.newArgumentError("user.home/LOGDIR not set");
  11621. }
  11622. return (RubyString) home;
  11623. }
  11624. }
  11625. /***** BEGIN LICENSE BLOCK *****
  11626. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  11627. *
  11628. * The contents of this file are subject to the Common Public
  11629. * License Version 1.0 (the "License"); you may not use this file
  11630. * except in compliance with the License. You may obtain a copy of
  11631. * the License at http://www.eclipse.org/legal/cpl-v10.html
  11632. *
  11633. * Software distributed under the License is distributed on an "AS
  11634. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  11635. * implied. See the License for the specific language governing
  11636. * rights and limitations under the License.
  11637. *
  11638. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  11639. *
  11640. * Alternatively, the contents of this file may be used under the terms of
  11641. * either of the GNU General Public License Version 2 or later (the "GPL"),
  11642. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  11643. * in which case the provisions of the GPL or the LGPL are applicable instead
  11644. * of those above. If you wish to allow use of your version of this file only
  11645. * under the terms of either the GPL or the LGPL, and not to allow others to
  11646. * use your version of this file under the terms of the CPL, indicate your
  11647. * decision by deleting the provisions above and replace them with the notice
  11648. * and other provisions required by the GPL or the LGPL. If you do not delete
  11649. * the provisions above, a recipient may use your version of this file under
  11650. * the terms of any one of the CPL, the GPL or the LGPL.
  11651. ***** END LICENSE BLOCK *****/
  11652. package org.jruby;
  11653. import java.util.Comparator;
  11654. import java.util.Arrays;
  11655. import java.util.concurrent.atomic.AtomicInteger;
  11656. import org.jruby.anno.JRubyMethod;
  11657. import org.jruby.anno.JRubyModule;
  11658. import org.jruby.exceptions.JumpException;
  11659. import org.jruby.javasupport.util.RuntimeHelpers;
  11660. import org.jruby.runtime.Arity;
  11661. import org.jruby.runtime.Block;
  11662. import org.jruby.runtime.CallBlock;
  11663. import org.jruby.runtime.BlockCallback;
  11664. import org.jruby.runtime.MethodIndex;
  11665. import org.jruby.runtime.ThreadContext;
  11666. import org.jruby.runtime.builtin.IRubyObject;
  11667. import org.jruby.util.TypeConverter;
  11668. /**
  11669. * The implementation of Ruby's Enumerable module.
  11670. */
  11671. @JRubyModule(name="Enumerable")
  11672. public class RubyEnumerable {
  11673. public static RubyModule createEnumerableModule(Ruby runtime) {
  11674. RubyModule enumModule = runtime.defineModule("Enumerable");
  11675. runtime.setEnumerable(enumModule);
  11676. enumModule.defineAnnotatedMethods(RubyEnumerable.class);
  11677. return enumModule;
  11678. }
  11679. public static IRubyObject callEach(Ruby runtime, ThreadContext context, IRubyObject self,
  11680. BlockCallback callback) {
  11681. return RuntimeHelpers.invoke(context, self, "each", CallBlock.newCallClosure(self, runtime.getEnumerable(),
  11682. Arity.noArguments(), callback, context));
  11683. }
  11684. private static class ExitIteration extends RuntimeException {
  11685. public Throwable fillInStackTrace() {
  11686. return this;
  11687. }
  11688. }
  11689. @JRubyMethod(name = "first")
  11690. public static IRubyObject first_0(ThreadContext context, IRubyObject self) {
  11691. Ruby runtime = self.getRuntime();
  11692. final ThreadContext localContext = context;
  11693. final IRubyObject[] holder = new IRubyObject[]{runtime.getNil()};
  11694. try {
  11695. callEach(runtime, context, self, new BlockCallback() {
  11696. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11697. if (localContext != ctx) {
  11698. throw ctx.getRuntime().newThreadError("Enumerable#first cannot be parallelized");
  11699. }
  11700. holder[0] = largs[0];
  11701. throw new ExitIteration();
  11702. }
  11703. });
  11704. } catch(ExitIteration ei) {}
  11705. return holder[0];
  11706. }
  11707. @JRubyMethod(name = "first")
  11708. public static IRubyObject first_1(ThreadContext context, IRubyObject self, final IRubyObject num) {
  11709. final Ruby runtime = self.getRuntime();
  11710. final RubyArray result = runtime.newArray();
  11711. final ThreadContext localContext = context;
  11712. if(RubyNumeric.fix2int(num) < 0) {
  11713. throw runtime.newArgumentError("negative index");
  11714. }
  11715. try {
  11716. callEach(runtime, context, self, new BlockCallback() {
  11717. private int iter = RubyNumeric.fix2int(num);
  11718. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11719. if (localContext != ctx) {
  11720. throw runtime.newThreadError("Enumerable#first cannot be parallelized");
  11721. }
  11722. if(iter-- == 0) {
  11723. throw new ExitIteration();
  11724. }
  11725. result.append(largs[0]);
  11726. return runtime.getNil();
  11727. }
  11728. });
  11729. } catch(ExitIteration ei) {}
  11730. return result;
  11731. }
  11732. @JRubyMethod(name = {"to_a", "entries"})
  11733. public static IRubyObject to_a(ThreadContext context, IRubyObject self) {
  11734. Ruby runtime = self.getRuntime();
  11735. RubyArray result = runtime.newArray();
  11736. callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
  11737. return result;
  11738. }
  11739. @JRubyMethod(name = "sort", frame = true)
  11740. public static IRubyObject sort(ThreadContext context, IRubyObject self, final Block block) {
  11741. Ruby runtime = self.getRuntime();
  11742. RubyArray result = runtime.newArray();
  11743. callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
  11744. result.sort_bang(block);
  11745. return result;
  11746. }
  11747. @JRubyMethod(name = "sort_by", frame = true)
  11748. public static IRubyObject sort_by(ThreadContext context, IRubyObject self, final Block block) {
  11749. final Ruby runtime = self.getRuntime();
  11750. final ThreadContext localContext = context; // MUST NOT be used across threads
  11751. if (self instanceof RubyArray) {
  11752. RubyArray selfArray = (RubyArray) self;
  11753. final IRubyObject[][] valuesAndCriteria = new IRubyObject[selfArray.size()][2];
  11754. callEach(runtime, context, self, new BlockCallback() {
  11755. AtomicInteger i = new AtomicInteger(0);
  11756. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11757. IRubyObject[] myVandC = valuesAndCriteria[i.getAndIncrement()];
  11758. myVandC[0] = largs[0];
  11759. myVandC[1] = block.yield(ctx, largs[0]);
  11760. return runtime.getNil();
  11761. }
  11762. });
  11763. Arrays.sort(valuesAndCriteria, new Comparator<IRubyObject[]>() {
  11764. public int compare(IRubyObject[] o1, IRubyObject[] o2) {
  11765. return RubyFixnum.fix2int(o1[1].callMethod(localContext, MethodIndex.OP_SPACESHIP, "<=>", o2[1]));
  11766. }
  11767. });
  11768. IRubyObject dstArray[] = new IRubyObject[selfArray.size()];
  11769. for (int i = 0; i < dstArray.length; i++) {
  11770. dstArray[i] = valuesAndCriteria[i][0];
  11771. }
  11772. return runtime.newArrayNoCopy(dstArray);
  11773. } else {
  11774. final RubyArray result = runtime.newArray();
  11775. callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
  11776. final IRubyObject[][] valuesAndCriteria = new IRubyObject[result.size()][2];
  11777. for (int i = 0; i < valuesAndCriteria.length; i++) {
  11778. IRubyObject val = result.eltInternal(i);
  11779. valuesAndCriteria[i][0] = val;
  11780. valuesAndCriteria[i][1] = block.yield(context, val);
  11781. }
  11782. Arrays.sort(valuesAndCriteria, new Comparator<IRubyObject[]>() {
  11783. public int compare(IRubyObject[] o1, IRubyObject[] o2) {
  11784. return RubyFixnum.fix2int(o1[1].callMethod(localContext, MethodIndex.OP_SPACESHIP, "<=>", o2[1]));
  11785. }
  11786. });
  11787. for (int i = 0; i < valuesAndCriteria.length; i++) {
  11788. result.eltInternalSet(i, valuesAndCriteria[i][0]);
  11789. }
  11790. return result;
  11791. }
  11792. }
  11793. @JRubyMethod(name = "grep", required = 1, frame = true)
  11794. public static IRubyObject grep(ThreadContext context, IRubyObject self, final IRubyObject pattern, final Block block) {
  11795. final Ruby runtime = self.getRuntime();
  11796. final RubyArray result = runtime.newArray();
  11797. if (block.isGiven()) {
  11798. callEach(runtime, context, self, new BlockCallback() {
  11799. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11800. ctx.setRubyFrameDelta(ctx.getRubyFrameDelta()+2);
  11801. if (pattern.callMethod(ctx, MethodIndex.OP_EQQ, "===", largs[0]).isTrue()) {
  11802. IRubyObject value = block.yield(ctx, largs[0]);
  11803. synchronized (result) {
  11804. result.append(value);
  11805. }
  11806. }
  11807. ctx.setRubyFrameDelta(ctx.getRubyFrameDelta()-2);
  11808. return runtime.getNil();
  11809. }
  11810. });
  11811. } else {
  11812. callEach(runtime, context, self, new BlockCallback() {
  11813. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11814. if (pattern.callMethod(ctx, MethodIndex.OP_EQQ, "===", largs[0]).isTrue()) {
  11815. synchronized (result) {
  11816. result.append(largs[0]);
  11817. }
  11818. }
  11819. return runtime.getNil();
  11820. }
  11821. });
  11822. }
  11823. return result;
  11824. }
  11825. @JRubyMethod(name = {"detect", "find"}, optional = 1, frame = true)
  11826. public static IRubyObject detect(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
  11827. final Ruby runtime = self.getRuntime();
  11828. final IRubyObject result[] = new IRubyObject[] { null };
  11829. final ThreadContext localContext = context;
  11830. IRubyObject ifnone = null;
  11831. if (args.length == 1) ifnone = args[0];
  11832. try {
  11833. callEach(runtime, context, self, new BlockCallback() {
  11834. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11835. if (localContext != ctx) {
  11836. throw runtime.newThreadError("Enumerable#detect/find cannot be parallelized");
  11837. }
  11838. if (block.yield(ctx, largs[0]).isTrue()) {
  11839. result[0] = largs[0];
  11840. throw JumpException.SPECIAL_JUMP;
  11841. }
  11842. return runtime.getNil();
  11843. }
  11844. });
  11845. } catch (JumpException.SpecialJump sj) {
  11846. return result[0];
  11847. }
  11848. return ifnone != null ? ifnone.callMethod(context, "call") : runtime.getNil();
  11849. }
  11850. @JRubyMethod(name = {"select", "find_all"}, frame = true)
  11851. public static IRubyObject select(ThreadContext context, IRubyObject self, final Block block) {
  11852. final Ruby runtime = self.getRuntime();
  11853. final RubyArray result = runtime.newArray();
  11854. callEach(runtime, context, self, new BlockCallback() {
  11855. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11856. if (block.yield(ctx, largs[0]).isTrue()) {
  11857. synchronized (result) {
  11858. result.append(largs[0]);
  11859. }
  11860. }
  11861. return runtime.getNil();
  11862. }
  11863. });
  11864. return result;
  11865. }
  11866. @JRubyMethod(name = "reject", frame = true)
  11867. public static IRubyObject reject(ThreadContext context, IRubyObject self, final Block block) {
  11868. final Ruby runtime = self.getRuntime();
  11869. final RubyArray result = runtime.newArray();
  11870. callEach(runtime, context, self, new BlockCallback() {
  11871. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11872. if (!block.yield(ctx, largs[0]).isTrue()) {
  11873. synchronized (result) {
  11874. result.append(largs[0]);
  11875. }
  11876. }
  11877. return runtime.getNil();
  11878. }
  11879. });
  11880. return result;
  11881. }
  11882. @JRubyMethod(name = {"collect", "map"}, frame = true)
  11883. public static IRubyObject collect(ThreadContext context, IRubyObject self, final Block block) {
  11884. final Ruby runtime = self.getRuntime();
  11885. final RubyArray result = runtime.newArray();
  11886. if (block.isGiven()) {
  11887. callEach(runtime, context, self, new BlockCallback() {
  11888. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11889. IRubyObject value = block.yield(ctx, largs[0]);
  11890. synchronized (result) {
  11891. result.append(value);
  11892. }
  11893. return runtime.getNil();
  11894. }
  11895. });
  11896. } else {
  11897. callEach(runtime, context, self, new AppendBlockCallback(runtime, result));
  11898. }
  11899. return result;
  11900. }
  11901. @JRubyMethod(name = "inject", optional = 1, frame = true)
  11902. public static IRubyObject inject(ThreadContext context, IRubyObject self, IRubyObject[] args, final Block block) {
  11903. final Ruby runtime = self.getRuntime();
  11904. final IRubyObject result[] = new IRubyObject[] { null };
  11905. final ThreadContext localContext = context;
  11906. if (args.length == 1) result[0] = args[0];
  11907. callEach(runtime, context, self, new BlockCallback() {
  11908. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11909. if (localContext != ctx) {
  11910. throw runtime.newThreadError("Enumerable#inject cannot be parallelized");
  11911. }
  11912. result[0] = result[0] == null ?
  11913. largs[0] : block.yield(ctx, runtime.newArray(result[0], largs[0]), null, null, true);
  11914. return runtime.getNil();
  11915. }
  11916. });
  11917. return result[0] == null ? runtime.getNil() : result[0];
  11918. }
  11919. @JRubyMethod(name = "partition", frame = true)
  11920. public static IRubyObject partition(ThreadContext context, IRubyObject self, final Block block) {
  11921. final Ruby runtime = self.getRuntime();
  11922. final RubyArray arr_true = runtime.newArray();
  11923. final RubyArray arr_false = runtime.newArray();
  11924. callEach(runtime, context, self, new BlockCallback() {
  11925. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11926. if (block.yield(ctx, largs[0]).isTrue()) {
  11927. synchronized (arr_true) {
  11928. arr_true.append(largs[0]);
  11929. }
  11930. } else {
  11931. synchronized (arr_false) {
  11932. arr_false.append(largs[0]);
  11933. }
  11934. }
  11935. return runtime.getNil();
  11936. }
  11937. });
  11938. return runtime.newArray(arr_true, arr_false);
  11939. }
  11940. private static class EachWithIndex implements BlockCallback {
  11941. private int index = 0;
  11942. private final Block block;
  11943. private final Ruby runtime;
  11944. public EachWithIndex(ThreadContext ctx, Block block) {
  11945. this.block = block;
  11946. this.runtime = ctx.getRuntime();
  11947. }
  11948. public IRubyObject call(ThreadContext context, IRubyObject[] iargs, Block block) {
  11949. this.block.call(context, new IRubyObject[] { runtime.newArray(iargs[0], runtime.newFixnum(index++)) });
  11950. return runtime.getNil();
  11951. }
  11952. }
  11953. @JRubyMethod(name = "each_with_index", frame = true)
  11954. public static IRubyObject each_with_index(ThreadContext context, IRubyObject self, Block block) {
  11955. RuntimeHelpers.invoke(context, self, "each", CallBlock.newCallClosure(self, self.getRuntime().getEnumerable(),
  11956. Arity.noArguments(), new EachWithIndex(context, block), context));
  11957. return self;
  11958. }
  11959. @JRubyMethod(name = {"include?", "member?"}, required = 1, frame = true)
  11960. public static IRubyObject include_p(ThreadContext context, IRubyObject self, final IRubyObject arg) {
  11961. final Ruby runtime = context.getRuntime();
  11962. final ThreadContext localContext = context;
  11963. try {
  11964. callEach(runtime, context, self, new BlockCallback() {
  11965. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11966. if (localContext != ctx) {
  11967. throw runtime.newThreadError("Enumerable#include?/member? cannot be parallelized");
  11968. }
  11969. if (RubyObject.equalInternal(ctx, largs[0], arg)) {
  11970. throw JumpException.SPECIAL_JUMP;
  11971. }
  11972. return runtime.getNil();
  11973. }
  11974. });
  11975. } catch (JumpException.SpecialJump sj) {
  11976. return runtime.getTrue();
  11977. }
  11978. return runtime.getFalse();
  11979. }
  11980. @JRubyMethod(name = "max", frame = true)
  11981. public static IRubyObject max(ThreadContext context, IRubyObject self, final Block block) {
  11982. final Ruby runtime = self.getRuntime();
  11983. final IRubyObject result[] = new IRubyObject[] { null };
  11984. final ThreadContext localContext = context;
  11985. if (block.isGiven()) {
  11986. callEach(runtime, context, self, new BlockCallback() {
  11987. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  11988. if (localContext != ctx) {
  11989. throw runtime.newThreadError("Enumerable#max{} cannot be parallelized");
  11990. }
  11991. if (result[0] == null || RubyComparable.cmpint(ctx, block.yield(ctx,
  11992. runtime.newArray(largs[0], result[0])), largs[0], result[0]) > 0) {
  11993. result[0] = largs[0];
  11994. }
  11995. return runtime.getNil();
  11996. }
  11997. });
  11998. } else {
  11999. callEach(runtime, context, self, new BlockCallback() {
  12000. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12001. synchronized (result) {
  12002. if (result[0] == null || RubyComparable.cmpint(ctx, largs[0].callMethod(ctx,
  12003. MethodIndex.OP_SPACESHIP, "<=>", result[0]), largs[0], result[0]) > 0) {
  12004. result[0] = largs[0];
  12005. }
  12006. }
  12007. return runtime.getNil();
  12008. }
  12009. });
  12010. }
  12011. return result[0] == null ? runtime.getNil() : result[0];
  12012. }
  12013. @JRubyMethod(name = "min", frame = true)
  12014. public static IRubyObject min(ThreadContext context, IRubyObject self, final Block block) {
  12015. final Ruby runtime = self.getRuntime();
  12016. final IRubyObject result[] = new IRubyObject[] { null };
  12017. final ThreadContext localContext = context;
  12018. if (block.isGiven()) {
  12019. callEach(runtime, context, self, new BlockCallback() {
  12020. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12021. if (localContext != ctx) {
  12022. throw runtime.newThreadError("Enumerable#min{} cannot be parallelized");
  12023. }
  12024. if (result[0] == null || RubyComparable.cmpint(ctx, block.yield(ctx,
  12025. runtime.newArray(largs[0], result[0])), largs[0], result[0]) < 0) {
  12026. result[0] = largs[0];
  12027. }
  12028. return runtime.getNil();
  12029. }
  12030. });
  12031. } else {
  12032. callEach(runtime, context, self, new BlockCallback() {
  12033. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12034. synchronized (result) {
  12035. if (result[0] == null || RubyComparable.cmpint(ctx, largs[0].callMethod(ctx,
  12036. MethodIndex.OP_SPACESHIP, "<=>", result[0]), largs[0], result[0]) < 0) {
  12037. result[0] = largs[0];
  12038. }
  12039. }
  12040. return runtime.getNil();
  12041. }
  12042. });
  12043. }
  12044. return result[0] == null ? runtime.getNil() : result[0];
  12045. }
  12046. @JRubyMethod(name = "all?", frame = true)
  12047. public static IRubyObject all_p(ThreadContext context, IRubyObject self, final Block block) {
  12048. final Ruby runtime = self.getRuntime();
  12049. final ThreadContext localContext = context;
  12050. try {
  12051. if (block.isGiven()) {
  12052. callEach(runtime, context, self, new BlockCallback() {
  12053. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12054. if (localContext != ctx) {
  12055. throw runtime.newThreadError("Enumerable#all? cannot be parallelized");
  12056. }
  12057. if (!block.yield(ctx, largs[0]).isTrue()) {
  12058. throw JumpException.SPECIAL_JUMP;
  12059. }
  12060. return runtime.getNil();
  12061. }
  12062. });
  12063. } else {
  12064. callEach(runtime, context, self, new BlockCallback() {
  12065. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12066. if (localContext != ctx) {
  12067. throw runtime.newThreadError("Enumerable#all? cannot be parallelized");
  12068. }
  12069. if (!largs[0].isTrue()) {
  12070. throw JumpException.SPECIAL_JUMP;
  12071. }
  12072. return runtime.getNil();
  12073. }
  12074. });
  12075. }
  12076. } catch (JumpException.SpecialJump sj) {
  12077. return runtime.getFalse();
  12078. }
  12079. return runtime.getTrue();
  12080. }
  12081. @JRubyMethod(name = "any?", frame = true)
  12082. public static IRubyObject any_p(ThreadContext context, IRubyObject self, final Block block) {
  12083. final Ruby runtime = self.getRuntime();
  12084. final ThreadContext localContext = context;
  12085. try {
  12086. if (block.isGiven()) {
  12087. callEach(runtime, context, self, new BlockCallback() {
  12088. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12089. if (localContext != ctx) {
  12090. throw runtime.newThreadError("Enumerable#any? cannot be parallelized");
  12091. }
  12092. if (block.yield(ctx, largs[0]).isTrue()) {
  12093. throw JumpException.SPECIAL_JUMP;
  12094. }
  12095. return runtime.getNil();
  12096. }
  12097. });
  12098. } else {
  12099. callEach(runtime, context, self, new BlockCallback() {
  12100. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12101. if (localContext != ctx) {
  12102. throw runtime.newThreadError("Enumerable#any? cannot be parallelized");
  12103. }
  12104. if (largs[0].isTrue()) {
  12105. throw JumpException.SPECIAL_JUMP;
  12106. }
  12107. return runtime.getNil();
  12108. }
  12109. });
  12110. }
  12111. } catch (JumpException.SpecialJump sj) {
  12112. return runtime.getTrue();
  12113. }
  12114. return runtime.getFalse();
  12115. }
  12116. @JRubyMethod(name = "zip", rest = true, frame = true)
  12117. public static IRubyObject zip(ThreadContext context, IRubyObject self, final IRubyObject[] args, final Block block) {
  12118. final Ruby runtime = self.getRuntime();
  12119. for (int i = 0; i < args.length; i++) {
  12120. args[i] = TypeConverter.convertToType(args[i], runtime.getArray(), MethodIndex.TO_A, "to_a");
  12121. }
  12122. final int aLen = args.length + 1;
  12123. if (block.isGiven()) {
  12124. callEach(runtime, context, self, new BlockCallback() {
  12125. AtomicInteger ix = new AtomicInteger(0);
  12126. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12127. RubyArray array = runtime.newArray(aLen);
  12128. int myIx = ix.getAndIncrement();
  12129. array.append(largs[0]);
  12130. for (int i = 0, j = args.length; i < j; i++) {
  12131. array.append(((RubyArray) args[i]).entry(myIx));
  12132. }
  12133. block.yield(ctx, array);
  12134. return runtime.getNil();
  12135. }
  12136. });
  12137. return runtime.getNil();
  12138. } else {
  12139. final RubyArray zip = runtime.newArray();
  12140. callEach(runtime, context, self, new BlockCallback() {
  12141. AtomicInteger ix = new AtomicInteger(0);
  12142. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12143. RubyArray array = runtime.newArray(aLen);
  12144. array.append(largs[0]);
  12145. int myIx = ix.getAndIncrement();
  12146. for (int i = 0, j = args.length; i < j; i++) {
  12147. array.append(((RubyArray) args[i]).entry(myIx));
  12148. }
  12149. synchronized (zip) {
  12150. zip.append(array);
  12151. }
  12152. return runtime.getNil();
  12153. }
  12154. });
  12155. return zip;
  12156. }
  12157. }
  12158. @JRubyMethod(name = "group_by", frame = true)
  12159. public static IRubyObject group_by(ThreadContext context, IRubyObject self, final Block block) {
  12160. final Ruby runtime = self.getRuntime();
  12161. final RubyHash result = new RubyHash(runtime);
  12162. callEach(runtime, context, self, new BlockCallback() {
  12163. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12164. IRubyObject key = block.yield(ctx, largs[0]);
  12165. synchronized (result) {
  12166. IRubyObject curr = result.fastARef(key);
  12167. if (curr == null) {
  12168. curr = runtime.newArray();
  12169. result.fastASet(key, curr);
  12170. }
  12171. curr.callMethod(ctx, MethodIndex.OP_LSHIFT, "<<", largs[0]);
  12172. }
  12173. return runtime.getNil();
  12174. }
  12175. });
  12176. return result;
  12177. }
  12178. public static final class AppendBlockCallback implements BlockCallback {
  12179. private Ruby runtime;
  12180. private RubyArray result;
  12181. public AppendBlockCallback(Ruby runtime, RubyArray result) {
  12182. this.runtime = runtime;
  12183. this.result = result;
  12184. }
  12185. public IRubyObject call(ThreadContext context, IRubyObject[] largs, Block blk) {
  12186. result.append(largs[0]);
  12187. return runtime.getNil();
  12188. }
  12189. }
  12190. }
  12191. /***** BEGIN LICENSE BLOCK *****
  12192. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  12193. *
  12194. * The contents of this file are subject to the Common Public
  12195. * License Version 1.0 (the "License"); you may not use this file
  12196. * except in compliance with the License. You may obtain a copy of
  12197. * the License at http://www.eclipse.org/legal/cpl-v10.html
  12198. *
  12199. * Software distributed under the License is distributed on an "AS
  12200. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12201. * implied. See the License for the specific language governing
  12202. * rights and limitations under the License.
  12203. *
  12204. * Copyright (C) 2006 Michael Studman <me@michaelstudman.com>
  12205. *
  12206. * Alternatively, the contents of this file may be used under the terms of
  12207. * either of the GNU General Public License Version 2 or later (the "GPL"),
  12208. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  12209. * in which case the provisions of the GPL or the LGPL are applicable instead
  12210. * of those above. If you wish to allow use of your version of this file only
  12211. * under the terms of either the GPL or the LGPL, and not to allow others to
  12212. * use your version of this file under the terms of the CPL, indicate your
  12213. * decision by deleting the provisions above and replace them with the notice
  12214. * and other provisions required by the GPL or the LGPL. If you do not delete
  12215. * the provisions above, a recipient may use your version of this file under
  12216. * the terms of any one of the CPL, the GPL or the LGPL.
  12217. ***** END LICENSE BLOCK *****/
  12218. package org.jruby;
  12219. import org.jruby.anno.JRubyMethod;
  12220. import org.jruby.anno.JRubyModule;
  12221. import org.jruby.javasupport.util.RuntimeHelpers;
  12222. import org.jruby.runtime.Block;
  12223. import org.jruby.runtime.BlockCallback;
  12224. import org.jruby.runtime.ObjectAllocator;
  12225. import org.jruby.runtime.ThreadContext;
  12226. import org.jruby.runtime.Visibility;
  12227. import org.jruby.runtime.builtin.IRubyObject;
  12228. /**
  12229. * Implementation of Ruby's Enumerator module.
  12230. */
  12231. @JRubyModule(name="Enumerable::Enumerator", include="Enumerable")
  12232. public class RubyEnumerator extends RubyObject {
  12233. /** target for each operation */
  12234. private IRubyObject object;
  12235. /** method to invoke for each operation */
  12236. private IRubyObject method;
  12237. /** args to each method */
  12238. private IRubyObject[] methodArgs;
  12239. private static ObjectAllocator ENUMERATOR_ALLOCATOR = new ObjectAllocator() {
  12240. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  12241. return new RubyEnumerator(runtime, klass);
  12242. }
  12243. };
  12244. public static void defineEnumerator(Ruby runtime) {
  12245. RubyModule kernel = runtime.getKernel();
  12246. kernel.defineAnnotatedMethod(RubyEnumerator.class, "obj_to_enum");
  12247. RubyModule enm = runtime.getClassFromPath("Enumerable");
  12248. enm.defineAnnotatedMethod(RubyEnumerator.class, "each_with_index");
  12249. enm.defineAnnotatedMethod(RubyEnumerator.class, "each_slice");
  12250. enm.defineAnnotatedMethod(RubyEnumerator.class, "enum_slice");
  12251. enm.defineAnnotatedMethod(RubyEnumerator.class, "each_cons");
  12252. enm.defineAnnotatedMethod(RubyEnumerator.class, "enum_cons");
  12253. RubyClass enmr = enm.defineClassUnder("Enumerator", runtime.getObject(), ENUMERATOR_ALLOCATOR);
  12254. enmr.includeModule(enm);
  12255. enmr.defineAnnotatedMethod(RubyEnumerator.class, "initialize");
  12256. enmr.defineAnnotatedMethod(RubyEnumerator.class, "each");
  12257. runtime.setEnumerator(enmr);
  12258. }
  12259. @JRubyMethod(name = {"to_enum", "enum_for"}, optional = 1, rest = true, frame = true)
  12260. public static IRubyObject obj_to_enum(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
  12261. IRubyObject[] newArgs = new IRubyObject[args.length + 1];
  12262. newArgs[0] = self;
  12263. System.arraycopy(args, 0, newArgs, 1, args.length);
  12264. return self.getRuntime().getEnumerator().callMethod(context, "new", newArgs);
  12265. }
  12266. private RubyEnumerator(Ruby runtime, RubyClass type) {
  12267. super(runtime, type);
  12268. object = method = runtime.getNil();
  12269. }
  12270. @JRubyMethod(name = "initialize", required = 1, rest = true, visibility = Visibility.PRIVATE)
  12271. public IRubyObject initialize(IRubyObject[] args) {
  12272. object = args[0];
  12273. method = args.length > 1 ? args[1] : getRuntime().fastNewSymbol("each");
  12274. if (args.length > 2) {
  12275. methodArgs = new IRubyObject[Math.max(0, args.length - 2)];
  12276. System.arraycopy(args, 2, methodArgs, 0, args.length - 2);
  12277. } else {
  12278. methodArgs = new IRubyObject[0];
  12279. }
  12280. return this;
  12281. }
  12282. /**
  12283. * Send current block and supplied args to method on target. According to MRI
  12284. * Block may not be given and "each" should just ignore it and call on through to
  12285. * underlying method.
  12286. */
  12287. @JRubyMethod(name = "each", frame = true)
  12288. public IRubyObject each(ThreadContext context, Block block) {
  12289. return object.callMethod(context, method.asJavaString(), methodArgs, block);
  12290. }
  12291. @JRubyMethod(name = "enum_with_index")
  12292. public static IRubyObject each_with_index(ThreadContext context, IRubyObject self) {
  12293. IRubyObject enumerator = self.getRuntime().getEnumerator();
  12294. return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_with_index"));
  12295. }
  12296. @JRubyMethod(name = "each_slice", required = 1, frame = true)
  12297. public static IRubyObject each_slice(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
  12298. final int size = (int)RubyNumeric.num2long(arg);
  12299. if (size <= 0) throw self.getRuntime().newArgumentError("invalid slice size");
  12300. final Ruby runtime = self.getRuntime();
  12301. final RubyArray result[] = new RubyArray[]{runtime.newArray(size)};
  12302. RubyEnumerable.callEach(runtime, context, self, new BlockCallback() {
  12303. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12304. result[0].append(largs[0]);
  12305. if (result[0].size() == size) {
  12306. block.yield(ctx, result[0]);
  12307. result[0] = runtime.newArray(size);
  12308. }
  12309. return runtime.getNil();
  12310. }
  12311. });
  12312. if (result[0].size() > 0) block.yield(context, result[0]);
  12313. return self.getRuntime().getNil();
  12314. }
  12315. @JRubyMethod(name = "each_cons", required = 1, frame = true)
  12316. public static IRubyObject each_cons(ThreadContext context, IRubyObject self, IRubyObject arg, final Block block) {
  12317. final int size = (int)RubyNumeric.num2long(arg);
  12318. if (size <= 0) throw self.getRuntime().newArgumentError("invalid size");
  12319. final Ruby runtime = self.getRuntime();
  12320. final RubyArray result = runtime.newArray(size);
  12321. RubyEnumerable.callEach(runtime, context, self, new BlockCallback() {
  12322. public IRubyObject call(ThreadContext ctx, IRubyObject[] largs, Block blk) {
  12323. if (result.size() == size) result.shift();
  12324. result.append(largs[0]);
  12325. if (result.size() == size) block.yield(ctx, result.aryDup());
  12326. return runtime.getNil();
  12327. }
  12328. });
  12329. return runtime.getNil();
  12330. }
  12331. @JRubyMethod(name = "enum_slice", required = 1)
  12332. public static IRubyObject enum_slice(ThreadContext context, IRubyObject self, IRubyObject arg) {
  12333. IRubyObject enumerator = self.getRuntime().getEnumerator();
  12334. return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_slice"), arg);
  12335. }
  12336. @JRubyMethod(name = "enum_cons", required = 1)
  12337. public static IRubyObject enum_cons(ThreadContext context, IRubyObject self, IRubyObject arg) {
  12338. IRubyObject enumerator = self.getRuntime().getEnumerator();
  12339. return RuntimeHelpers.invoke(context, enumerator, "new", self, self.getRuntime().fastNewSymbol("each_cons"), arg);
  12340. }
  12341. }
  12342. package org.jruby;
  12343. import org.jruby.anno.JRubyMethod;
  12344. import org.jruby.anno.JRubyModule;
  12345. import org.jruby.ext.posix.Passwd;
  12346. import org.jruby.ext.posix.Group;
  12347. import org.jruby.ext.posix.POSIX;
  12348. import org.jruby.ext.posix.util.Platform;
  12349. import org.jruby.runtime.Block;
  12350. import org.jruby.runtime.ThreadContext;
  12351. import org.jruby.runtime.builtin.IRubyObject;
  12352. @JRubyModule(name="Etc")
  12353. public class RubyEtc {
  12354. public static RubyModule createEtcModule(Ruby runtime) {
  12355. RubyModule etcModule = runtime.defineModule("Etc");
  12356. runtime.setEtc(etcModule);
  12357. etcModule.defineAnnotatedMethods(RubyEtc.class);
  12358. definePasswdStruct(runtime);
  12359. defineGroupStruct(runtime);
  12360. return etcModule;
  12361. }
  12362. private static void definePasswdStruct(Ruby runtime) {
  12363. IRubyObject[] args = new IRubyObject[] {
  12364. runtime.newString("Passwd"),
  12365. runtime.newSymbol("name"),
  12366. runtime.newSymbol("passwd"),
  12367. runtime.newSymbol("uid"),
  12368. runtime.newSymbol("gid"),
  12369. runtime.newSymbol("gecos"),
  12370. runtime.newSymbol("dir"),
  12371. runtime.newSymbol("shell"),
  12372. runtime.newSymbol("change"),
  12373. runtime.newSymbol("uclass"),
  12374. runtime.newSymbol("expire")
  12375. };
  12376. runtime.setPasswdStruct(RubyStruct.newInstance(runtime.getStructClass(), args, Block.NULL_BLOCK));
  12377. }
  12378. private static void defineGroupStruct(Ruby runtime) {
  12379. IRubyObject[] args = new IRubyObject[] {
  12380. runtime.newString("Group"),
  12381. runtime.newSymbol("name"),
  12382. runtime.newSymbol("passwd"),
  12383. runtime.newSymbol("gid"),
  12384. runtime.newSymbol("mem")
  12385. };
  12386. runtime.setGroupStruct(RubyStruct.newInstance(runtime.getStructClass(), args, Block.NULL_BLOCK));
  12387. }
  12388. private static IRubyObject setupPasswd(Ruby runtime, Passwd passwd) {
  12389. IRubyObject[] args = new IRubyObject[] {
  12390. runtime.newString(passwd.getLoginName()),
  12391. runtime.newString(passwd.getPassword()),
  12392. runtime.newFixnum(passwd.getUID()),
  12393. runtime.newFixnum(passwd.getGID()),
  12394. runtime.newString(passwd.getGECOS()),
  12395. runtime.newString(passwd.getHome()),
  12396. runtime.newString(passwd.getShell()),
  12397. runtime.newFixnum(passwd.getPasswdChangeTime()),
  12398. runtime.newString(passwd.getAccessClass()),
  12399. runtime.newFixnum(passwd.getExpire())
  12400. };
  12401. return RubyStruct.newStruct(runtime.getPasswdStruct(), args, Block.NULL_BLOCK);
  12402. }
  12403. private static IRubyObject setupGroup(Ruby runtime, Group group) {
  12404. IRubyObject[] args = new IRubyObject[] {
  12405. runtime.newString(group.getName()),
  12406. runtime.newString(group.getPassword()),
  12407. runtime.newFixnum(group.getGID()),
  12408. intoStringArray(runtime, group.getMembers())
  12409. };
  12410. return RubyStruct.newStruct(runtime.getGroupStruct(), args, Block.NULL_BLOCK);
  12411. }
  12412. private static IRubyObject intoStringArray(Ruby runtime, String[] members) {
  12413. IRubyObject[] arr = new IRubyObject[members.length];
  12414. for(int i = 0; i<arr.length; i++) {
  12415. arr[i] = runtime.newString(members[i]);
  12416. }
  12417. return runtime.newArrayNoCopy(arr);
  12418. }
  12419. @JRubyMethod(name = "getpwuid", optional=1, module = true)
  12420. public static IRubyObject getpwuid(IRubyObject recv, IRubyObject[] args) {
  12421. Ruby runtime = recv.getRuntime();
  12422. POSIX posix = runtime.getPosix();
  12423. int uid = args.length == 0 ? posix.getuid() : RubyNumeric.fix2int(args[0]);
  12424. Passwd pwd = posix.getpwuid(uid);
  12425. if(pwd == null) {
  12426. if (Platform.IS_WINDOWS) { // MRI behavior
  12427. return recv.getRuntime().getNil();
  12428. }
  12429. throw runtime.newArgumentError("can't find user for " + uid);
  12430. }
  12431. return setupPasswd(runtime, pwd);
  12432. }
  12433. @JRubyMethod(name = "getpwnam", required=1, module = true)
  12434. public static IRubyObject getpwnam(IRubyObject recv, IRubyObject name) {
  12435. String nam = name.convertToString().toString();
  12436. Passwd pwd = recv.getRuntime().getPosix().getpwnam(nam);
  12437. if(pwd == null) {
  12438. if (Platform.IS_WINDOWS) { // MRI behavior
  12439. return recv.getRuntime().getNil();
  12440. }
  12441. throw recv.getRuntime().newArgumentError("can't find user for " + nam);
  12442. }
  12443. return setupPasswd(recv.getRuntime(), pwd);
  12444. }
  12445. @JRubyMethod(name = "passwd", module = true, frame=true)
  12446. public static IRubyObject passwd(IRubyObject recv, Block block) {
  12447. Ruby runtime = recv.getRuntime();
  12448. POSIX posix = runtime.getPosix();
  12449. if(block.isGiven()) {
  12450. ThreadContext context = runtime.getCurrentContext();
  12451. posix.setpwent();
  12452. Passwd pw;
  12453. while((pw = posix.getpwent()) != null) {
  12454. block.yield(context, setupPasswd(runtime, pw));
  12455. }
  12456. posix.endpwent();
  12457. }
  12458. Passwd pw = posix.getpwent();
  12459. if (pw != null) {
  12460. return setupPasswd(runtime, pw);
  12461. } else {
  12462. return runtime.getNil();
  12463. }
  12464. }
  12465. @JRubyMethod(name = "getlogin", module = true)
  12466. public static IRubyObject getlogin(IRubyObject recv) {
  12467. Ruby runtime = recv.getRuntime();
  12468. String login = runtime.getPosix().getlogin();
  12469. if (login != null) {
  12470. return runtime.newString(login);
  12471. } else {
  12472. return runtime.getNil();
  12473. }
  12474. }
  12475. @JRubyMethod(name = "endpwent", module = true)
  12476. public static IRubyObject endpwent(IRubyObject recv) {
  12477. Ruby runtime = recv.getRuntime();
  12478. runtime.getPosix().endpwent();
  12479. return runtime.getNil();
  12480. }
  12481. @JRubyMethod(name = "setpwent", module = true)
  12482. public static IRubyObject setpwent(IRubyObject recv) {
  12483. Ruby runtime = recv.getRuntime();
  12484. runtime.getPosix().setpwent();
  12485. return runtime.getNil();
  12486. }
  12487. @JRubyMethod(name = "getpwent", module = true)
  12488. public static IRubyObject getpwent(IRubyObject recv) {
  12489. Ruby runtime = recv.getRuntime();
  12490. Passwd passwd = runtime.getPosix().getpwent();
  12491. if (passwd != null) {
  12492. return setupPasswd(recv.getRuntime(), passwd);
  12493. } else {
  12494. return runtime.getNil();
  12495. }
  12496. }
  12497. @JRubyMethod(name = "getgrnam", required=1, module = true)
  12498. public static IRubyObject getgrnam(IRubyObject recv, IRubyObject name) {
  12499. String nam = name.convertToString().toString();
  12500. Group grp = recv.getRuntime().getPosix().getgrnam(nam);
  12501. if(grp == null) {
  12502. if (Platform.IS_WINDOWS) { // MRI behavior
  12503. return recv.getRuntime().getNil();
  12504. }
  12505. throw recv.getRuntime().newArgumentError("can't find group for " + nam);
  12506. }
  12507. return setupGroup(recv.getRuntime(), grp);
  12508. }
  12509. @JRubyMethod(name = "getgrgid", optional=1, module = true)
  12510. public static IRubyObject getgrgid(IRubyObject recv, IRubyObject[] args) {
  12511. Ruby runtime = recv.getRuntime();
  12512. POSIX posix = runtime.getPosix();
  12513. int gid = args.length == 0 ? posix.getgid() : RubyNumeric.fix2int(args[0]);
  12514. Group gr = posix.getgrgid(gid);
  12515. if(gr == null) {
  12516. if (Platform.IS_WINDOWS) { // MRI behavior
  12517. return runtime.getNil();
  12518. }
  12519. throw runtime.newArgumentError("can't find group for " + gid);
  12520. }
  12521. return setupGroup(runtime, gr);
  12522. }
  12523. @JRubyMethod(name = "endgrent", module = true)
  12524. public static IRubyObject endgrent(IRubyObject recv) {
  12525. Ruby runtime = recv.getRuntime();
  12526. runtime.getPosix().endgrent();
  12527. return runtime.getNil();
  12528. }
  12529. @JRubyMethod(name = "setgrent", module = true)
  12530. public static IRubyObject setgrent(IRubyObject recv) {
  12531. Ruby runtime = recv.getRuntime();
  12532. runtime.getPosix().setgrent();
  12533. return runtime.getNil();
  12534. }
  12535. @JRubyMethod(name = "group", module = true, frame=true)
  12536. public static IRubyObject group(IRubyObject recv, Block block) {
  12537. Ruby runtime = recv.getRuntime();
  12538. POSIX posix = runtime.getPosix();
  12539. if(block.isGiven()) {
  12540. ThreadContext context = runtime.getCurrentContext();
  12541. posix.setgrent();
  12542. Group gr;
  12543. while((gr = posix.getgrent()) != null) {
  12544. block.yield(context, setupGroup(runtime, gr));
  12545. }
  12546. posix.endgrent();
  12547. }
  12548. Group gr = posix.getgrent();
  12549. if (gr != null) {
  12550. return setupGroup(runtime, gr);
  12551. } else {
  12552. return runtime.getNil();
  12553. }
  12554. }
  12555. @JRubyMethod(name = "getgrent", module = true)
  12556. public static IRubyObject getgrent(IRubyObject recv) {
  12557. Ruby runtime = recv.getRuntime();
  12558. Group gr = runtime.getPosix().getgrent();
  12559. if (gr != null) {
  12560. return setupGroup(recv.getRuntime(), gr);
  12561. } else {
  12562. return runtime.getNil();
  12563. }
  12564. }
  12565. }
  12566. /***** BEGIN LICENSE BLOCK *****
  12567. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  12568. *
  12569. * The contents of this file are subject to the Common Public
  12570. * License Version 1.0 (the "License"); you may not use this file
  12571. * except in compliance with the License. You may obtain a copy of
  12572. * the License at http://www.eclipse.org/legal/cpl-v10.html
  12573. *
  12574. * Software distributed under the License is distributed on an "AS
  12575. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12576. * implied. See the License for the specific language governing
  12577. * rights and limitations under the License.
  12578. *
  12579. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  12580. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  12581. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  12582. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  12583. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  12584. * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
  12585. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  12586. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  12587. * Copyright (C) 2005 David Corbin <dcorbin@users.sf.net>
  12588. * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
  12589. *
  12590. * Alternatively, the contents of this file may be used under the terms of
  12591. * either of the GNU General Public License Version 2 or later (the "GPL"),
  12592. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  12593. * in which case the provisions of the GPL or the LGPL are applicable instead
  12594. * of those above. If you wish to allow use of your version of this file only
  12595. * under the terms of either the GPL or the LGPL, and not to allow others to
  12596. * use your version of this file under the terms of the CPL, indicate your
  12597. * decision by deleting the provisions above and replace them with the notice
  12598. * and other provisions required by the GPL or the LGPL. If you do not delete
  12599. * the provisions above, a recipient may use your version of this file under
  12600. * the terms of any one of the CPL, the GPL or the LGPL.
  12601. ***** END LICENSE BLOCK *****/
  12602. package org.jruby;
  12603. import java.io.IOException;
  12604. import java.io.PrintStream;
  12605. import java.util.List;
  12606. import org.jruby.anno.JRubyClass;
  12607. import org.jruby.anno.JRubyMethod;
  12608. import org.jruby.runtime.Block;
  12609. import org.jruby.runtime.Frame;
  12610. import org.jruby.runtime.MethodIndex;
  12611. import org.jruby.runtime.ObjectAllocator;
  12612. import org.jruby.runtime.ObjectMarshal;
  12613. import org.jruby.runtime.ThreadContext;
  12614. import org.jruby.runtime.Visibility;
  12615. import org.jruby.runtime.builtin.IRubyObject;
  12616. import org.jruby.runtime.builtin.Variable;
  12617. import org.jruby.runtime.component.VariableEntry;
  12618. import org.jruby.runtime.marshal.MarshalStream;
  12619. import org.jruby.runtime.marshal.UnmarshalStream;
  12620. import org.jruby.util.SafePropertyAccessor;
  12621. /**
  12622. *
  12623. * @author jpetersen
  12624. */
  12625. @JRubyClass(name="Exception")
  12626. public class RubyException extends RubyObject {
  12627. private StackTraceElement[] backtraceFrames;
  12628. private StackTraceElement[] javaStackTrace;
  12629. private IRubyObject backtrace;
  12630. public IRubyObject message;
  12631. public static final int TRACE_HEAD = 8;
  12632. public static final int TRACE_TAIL = 4;
  12633. public static final int TRACE_MAX = TRACE_HEAD + TRACE_TAIL + 6;
  12634. protected RubyException(Ruby runtime, RubyClass rubyClass) {
  12635. this(runtime, rubyClass, null);
  12636. }
  12637. public RubyException(Ruby runtime, RubyClass rubyClass, String message) {
  12638. super(runtime, rubyClass);
  12639. this.message = message == null ? runtime.getNil() : runtime.newString(message);
  12640. }
  12641. private static ObjectAllocator EXCEPTION_ALLOCATOR = new ObjectAllocator() {
  12642. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  12643. RubyException instance = new RubyException(runtime, klass);
  12644. // for future compatibility as constructors move toward not accepting metaclass?
  12645. instance.setMetaClass(klass);
  12646. return instance;
  12647. }
  12648. };
  12649. private static final ObjectMarshal EXCEPTION_MARSHAL = new ObjectMarshal() {
  12650. public void marshalTo(Ruby runtime, Object obj, RubyClass type,
  12651. MarshalStream marshalStream) throws IOException {
  12652. RubyException exc = (RubyException)obj;
  12653. marshalStream.registerLinkTarget(exc);
  12654. List<Variable<IRubyObject>> attrs = exc.getVariableList();
  12655. attrs.add(new VariableEntry<IRubyObject>(
  12656. "mesg", exc.message == null ? runtime.getNil() : exc.message));
  12657. attrs.add(new VariableEntry<IRubyObject>("bt", exc.getBacktrace()));
  12658. marshalStream.dumpVariables(attrs);
  12659. }
  12660. public Object unmarshalFrom(Ruby runtime, RubyClass type,
  12661. UnmarshalStream unmarshalStream) throws IOException {
  12662. RubyException exc = (RubyException)type.allocate();
  12663. unmarshalStream.registerLinkTarget(exc);
  12664. unmarshalStream.defaultVariablesUnmarshal(exc);
  12665. exc.message = exc.removeInternalVariable("mesg");
  12666. exc.set_backtrace(exc.removeInternalVariable("bt"));
  12667. return exc;
  12668. }
  12669. };
  12670. public static RubyClass createExceptionClass(Ruby runtime) {
  12671. RubyClass exceptionClass = runtime.defineClass("Exception", runtime.getObject(), EXCEPTION_ALLOCATOR);
  12672. runtime.setException(exceptionClass);
  12673. exceptionClass.setMarshal(EXCEPTION_MARSHAL);
  12674. exceptionClass.defineAnnotatedMethods(RubyException.class);
  12675. return exceptionClass;
  12676. }
  12677. public static RubyException newException(Ruby runtime, RubyClass excptnClass, String msg) {
  12678. return new RubyException(runtime, excptnClass, msg);
  12679. }
  12680. public void setBacktraceFrames(StackTraceElement[] backtraceFrames) {
  12681. this.backtraceFrames = backtraceFrames;
  12682. if (TRACE_TYPE == RAW ||
  12683. TRACE_TYPE == RAW_FILTERED ||
  12684. TRACE_TYPE == RUBY_COMPILED ||
  12685. TRACE_TYPE == RUBY_HYBRID) {
  12686. javaStackTrace = Thread.currentThread().getStackTrace();
  12687. }
  12688. }
  12689. public static final int RAW = 0;
  12690. public static final int RAW_FILTERED = 1;
  12691. public static final int RUBY_FRAMED = 2;
  12692. public static final int RUBY_COMPILED = 3;
  12693. public static final int RUBY_HYBRID = 4;
  12694. public static final int TRACE_TYPE;
  12695. static {
  12696. String style = SafePropertyAccessor.getProperty("jruby.backtrace.style", "ruby_framed").toLowerCase();
  12697. if (style.equals("raw")) TRACE_TYPE = RAW;
  12698. else if (style.equals("raw_filtered")) TRACE_TYPE = RAW_FILTERED;
  12699. else if (style.equals("ruby_framed")) TRACE_TYPE = RUBY_FRAMED;
  12700. else if (style.equals("ruby_compiled")) TRACE_TYPE = RUBY_COMPILED;
  12701. else if (style.equals("ruby_hybrid")) TRACE_TYPE = RUBY_HYBRID;
  12702. else TRACE_TYPE = RUBY_FRAMED;
  12703. }
  12704. public IRubyObject getBacktrace() {
  12705. if (backtrace == null) {
  12706. initBacktrace();
  12707. }
  12708. return backtrace;
  12709. }
  12710. public void initBacktrace() {
  12711. switch (TRACE_TYPE) {
  12712. case RAW:
  12713. backtrace = ThreadContext.createRawBacktrace(getRuntime(), javaStackTrace, false);
  12714. break;
  12715. case RAW_FILTERED:
  12716. backtrace = ThreadContext.createRawBacktrace(getRuntime(), javaStackTrace, true);
  12717. break;
  12718. case RUBY_FRAMED:
  12719. backtrace = backtraceFrames == null ? getRuntime().getNil() : ThreadContext.createBacktraceFromFrames(getRuntime(), backtraceFrames);
  12720. break;
  12721. case RUBY_COMPILED:
  12722. backtrace = ThreadContext.createRubyCompiledBacktrace(getRuntime(), javaStackTrace);
  12723. break;
  12724. case RUBY_HYBRID:
  12725. backtrace = ThreadContext.createRubyHybridBacktrace(getRuntime(), backtraceFrames, javaStackTrace, getRuntime().getDebug().isTrue());
  12726. break;
  12727. }
  12728. }
  12729. @JRubyMethod(optional = 2, frame = true, visibility = Visibility.PRIVATE)
  12730. public IRubyObject initialize(IRubyObject[] args, Block block) {
  12731. if (args.length == 1) message = args[0];
  12732. return this;
  12733. }
  12734. @JRubyMethod
  12735. public IRubyObject backtrace() {
  12736. return getBacktrace();
  12737. }
  12738. @JRubyMethod(required = 1)
  12739. public IRubyObject set_backtrace(IRubyObject obj) {
  12740. if (obj.isNil()) {
  12741. backtrace = null;
  12742. } else if (!isArrayOfStrings(obj)) {
  12743. throw getRuntime().newTypeError("backtrace must be Array of String");
  12744. } else {
  12745. backtrace = (RubyArray) obj;
  12746. }
  12747. return backtrace();
  12748. }
  12749. @JRubyMethod(name = "exception", optional = 1, rest = true, meta = true)
  12750. public static IRubyObject exception(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  12751. return ((RubyClass) recv).newInstance(context, args, block);
  12752. }
  12753. @JRubyMethod(optional = 1)
  12754. public RubyException exception(IRubyObject[] args) {
  12755. switch (args.length) {
  12756. case 0 :
  12757. return this;
  12758. case 1 :
  12759. if(args[0] == this) {
  12760. return this;
  12761. }
  12762. RubyException ret = (RubyException)rbClone();
  12763. ret.initialize(args, Block.NULL_BLOCK); // This looks wrong, but it's the way MRI does it.
  12764. return ret;
  12765. default :
  12766. throw getRuntime().newArgumentError("Wrong argument count");
  12767. }
  12768. }
  12769. @JRubyMethod
  12770. public IRubyObject to_s() {
  12771. if (message.isNil()) return getRuntime().newString(getMetaClass().getName());
  12772. message.setTaint(isTaint());
  12773. return message;
  12774. }
  12775. @JRubyMethod(name = {"to_str", "message"})
  12776. public IRubyObject to_str(ThreadContext context) {
  12777. return callMethod(context, MethodIndex.TO_S, "to_s");
  12778. }
  12779. /** inspects an object and return a kind of debug information
  12780. *
  12781. *@return A RubyString containing the debug information.
  12782. */
  12783. @JRubyMethod
  12784. public IRubyObject inspect(ThreadContext context) {
  12785. RubyModule rubyClass = getMetaClass();
  12786. RubyString exception = RubyString.objAsString(context, this);
  12787. if (exception.getByteList().realSize == 0) return getRuntime().newString(rubyClass.getName());
  12788. StringBuilder sb = new StringBuilder("#<");
  12789. sb.append(rubyClass.getName()).append(": ").append(exception.getByteList()).append(">");
  12790. return getRuntime().newString(sb.toString());
  12791. }
  12792. public void printBacktrace(PrintStream errorStream) {
  12793. IRubyObject backtrace = callMethod(getRuntime().getCurrentContext(), "backtrace");
  12794. boolean debug = getRuntime().getDebug().isTrue();
  12795. if (!backtrace.isNil() && backtrace instanceof RubyArray) {
  12796. IRubyObject[] elements = backtrace.convertToArray().toJavaArray();
  12797. for (int i = 1; i < elements.length; i++) {
  12798. IRubyObject stackTraceLine = elements[i];
  12799. if (stackTraceLine instanceof RubyString) {
  12800. printStackTraceLine(errorStream, stackTraceLine);
  12801. }
  12802. if (!debug && i == RubyException.TRACE_HEAD && elements.length > RubyException.TRACE_MAX) {
  12803. int hiddenLevels = elements.length - RubyException.TRACE_HEAD - RubyException.TRACE_TAIL;
  12804. errorStream.print("\t ... " + hiddenLevels + " levels...\n");
  12805. i = elements.length - RubyException.TRACE_TAIL;
  12806. }
  12807. }
  12808. }
  12809. }
  12810. private void printStackTraceLine(PrintStream errorStream, IRubyObject stackTraceLine) {
  12811. errorStream.print("\tfrom " + stackTraceLine + '\n');
  12812. }
  12813. private boolean isArrayOfStrings(IRubyObject backtrace) {
  12814. if (!(backtrace instanceof RubyArray)) return false;
  12815. IRubyObject[] elements = ((RubyArray) backtrace).toJavaArray();
  12816. for (int i = 0 ; i < elements.length ; i++) {
  12817. if (!(elements[i] instanceof RubyString)) return false;
  12818. }
  12819. return true;
  12820. }
  12821. }
  12822. /*
  12823. ***** BEGIN LICENSE BLOCK *****
  12824. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  12825. *
  12826. * The contents of this file are subject to the Common Public
  12827. * License Version 1.0 (the "License"); you may not use this file
  12828. * except in compliance with the License. You may obtain a copy of
  12829. * the License at http://www.eclipse.org/legal/cpl-v10.html
  12830. *
  12831. * Software distributed under the License is distributed on an "AS
  12832. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12833. * implied. See the License for the specific language governing
  12834. * rights and limitations under the License.
  12835. *
  12836. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  12837. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  12838. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  12839. * Copyright (C) 2003 Joey Gibson <joey@joeygibson.com>
  12840. * Copyright (C) 2004-2007 Thomas E Enebo <enebo@acm.org>
  12841. * Copyright (C) 2004-2007 Charles O Nutter <headius@headius.com>
  12842. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  12843. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  12844. *
  12845. * Alternatively, the contents of this file may be used under the terms of
  12846. * either of the GNU General Public License Version 2 or later (the "GPL"),
  12847. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  12848. * in which case the provisions of the GPL or the LGPL are applicable instead
  12849. * of those above. If you wish to allow use of your version of this file only
  12850. * under the terms of either the GPL or the LGPL, and not to allow others to
  12851. * use your version of this file under the terms of the CPL, indicate your
  12852. * decision by deleting the provisions above and replace them with the notice
  12853. * and other provisions required by the GPL or the LGPL. If you do not delete
  12854. * the provisions above, a recipient may use your version of this file under
  12855. * the terms of any one of the CPL, the GPL or the LGPL.
  12856. ***** END LICENSE BLOCK *****/
  12857. package org.jruby;
  12858. import org.jruby.util.io.OpenFile;
  12859. import org.jruby.util.io.ChannelDescriptor;
  12860. import java.io.File;
  12861. import java.io.FileDescriptor;
  12862. import java.io.FileNotFoundException;
  12863. import java.io.IOException;
  12864. import java.io.InputStream;
  12865. import java.io.Reader;
  12866. import java.nio.channels.Channels;
  12867. import java.nio.channels.FileChannel;
  12868. import java.nio.channels.FileLock;
  12869. import org.jruby.anno.JRubyClass;
  12870. import org.jruby.anno.JRubyMethod;
  12871. import org.jruby.anno.JRubyModule;
  12872. import org.jruby.ext.posix.util.Platform;
  12873. import org.jruby.runtime.Block;
  12874. import org.jruby.runtime.MethodIndex;
  12875. import org.jruby.runtime.ObjectAllocator;
  12876. import org.jruby.runtime.ThreadContext;
  12877. import org.jruby.runtime.Visibility;
  12878. import org.jruby.runtime.builtin.IRubyObject;
  12879. import org.jruby.util.ByteList;
  12880. import org.jruby.util.io.DirectoryAsFileException;
  12881. import org.jruby.util.io.Stream;
  12882. import org.jruby.util.io.ChannelStream;
  12883. import org.jruby.util.io.ModeFlags;
  12884. import org.jruby.util.JRubyFile;
  12885. import org.jruby.util.TypeConverter;
  12886. import org.jruby.util.io.BadDescriptorException;
  12887. import org.jruby.util.io.FileExistsException;
  12888. import org.jruby.util.io.InvalidValueException;
  12889. import org.jruby.util.io.PipeException;
  12890. /**
  12891. * Ruby File class equivalent in java.
  12892. **/
  12893. @JRubyClass(name="File", parent="IO", include="FileTest")
  12894. public class RubyFile extends RubyIO {
  12895. private static final long serialVersionUID = 1L;
  12896. public static final int LOCK_SH = 1;
  12897. public static final int LOCK_EX = 2;
  12898. public static final int LOCK_NB = 4;
  12899. public static final int LOCK_UN = 8;
  12900. private static final int FNM_NOESCAPE = 1;
  12901. private static final int FNM_PATHNAME = 2;
  12902. private static final int FNM_DOTMATCH = 4;
  12903. private static final int FNM_CASEFOLD = 8;
  12904. private static final int FNM_SYSCASE;
  12905. static {
  12906. if (Platform.IS_WINDOWS) {
  12907. FNM_SYSCASE = FNM_CASEFOLD;
  12908. } else {
  12909. FNM_SYSCASE = 0;
  12910. }
  12911. }
  12912. private static boolean startsWithDriveLetterOnWindows(String path) {
  12913. return (path != null)
  12914. && Platform.IS_WINDOWS &&
  12915. ((path.length()>1 && path.charAt(0) == '/') ?
  12916. (path.length() > 2
  12917. && isWindowsDriveLetter(path.charAt(1))
  12918. && path.charAt(2) == ':') :
  12919. (path.length() > 1
  12920. && isWindowsDriveLetter(path.charAt(0))
  12921. && path.charAt(1) == ':'));
  12922. }
  12923. // adjusts paths started with '/' or '\\', on windows.
  12924. static String adjustRootPathOnWindows(Ruby runtime, String path, String dir) {
  12925. if (path == null) return path;
  12926. if (Platform.IS_WINDOWS) {
  12927. // MRI behavior on Windows: it treats '/' as a root of
  12928. // a current drive (but only if SINGLE slash is present!):
  12929. // E.g., if current work directory is
  12930. // 'D:/home/directory', then '/' means 'D:/'.
  12931. //
  12932. // Basically, '/path' is treated as a *RELATIVE* path,
  12933. // relative to the current drive. '//path' is treated
  12934. // as absolute one.
  12935. if ((path.startsWith("/") && !(path.length()>2 && path.charAt(2) == ':')) || path.startsWith("\\")) {
  12936. if (path.length() > 1 && (path.charAt(1) == '/' || path.charAt(1) == '\\')) {
  12937. return path;
  12938. }
  12939. // First try to use drive letter from supplied dir value,
  12940. // then try current work dir.
  12941. if (!startsWithDriveLetterOnWindows(dir)) {
  12942. dir = runtime.getCurrentDirectory();
  12943. }
  12944. if (dir.length() >= 2) {
  12945. path = dir.substring(0, 2) + path;
  12946. }
  12947. } else if (startsWithDriveLetterOnWindows(path) && path.length() == 2) {
  12948. // compensate for missing slash after drive letter on windows
  12949. path += "/";
  12950. }
  12951. }
  12952. return path;
  12953. }
  12954. protected String path;
  12955. private FileLock currentLock;
  12956. public RubyFile(Ruby runtime, RubyClass type) {
  12957. super(runtime, type);
  12958. }
  12959. // XXX This constructor is a hack to implement the __END__ syntax.
  12960. // Converting a reader back into an InputStream doesn't generally work.
  12961. public RubyFile(Ruby runtime, String path, final Reader reader) {
  12962. this(runtime, path, new InputStream() {
  12963. public int read() throws IOException {
  12964. return reader.read();
  12965. }
  12966. });
  12967. }
  12968. public RubyFile(Ruby runtime, String path, InputStream in) {
  12969. super(runtime, runtime.getFile());
  12970. this.path = path;
  12971. try {
  12972. this.openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(in), getNewFileno(), new FileDescriptor())));
  12973. } catch (InvalidValueException ex) {
  12974. throw runtime.newErrnoEINVALError();
  12975. }
  12976. this.openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
  12977. registerDescriptor(openFile.getMainStream().getDescriptor());
  12978. }
  12979. private static ObjectAllocator FILE_ALLOCATOR = new ObjectAllocator() {
  12980. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  12981. RubyFile instance = new RubyFile(runtime, klass);
  12982. instance.setMetaClass(klass);
  12983. return instance;
  12984. }
  12985. };
  12986. @JRubyModule(name="File::Constants")
  12987. public static class Constants {}
  12988. public static RubyClass createFileClass(Ruby runtime) {
  12989. RubyClass fileClass = runtime.defineClass("File", runtime.getIO(), FILE_ALLOCATOR);
  12990. runtime.setFile(fileClass);
  12991. RubyString separator = runtime.newString("/");
  12992. ThreadContext context = runtime.getCurrentContext();
  12993. fileClass.kindOf = new RubyModule.KindOf() {
  12994. @Override
  12995. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  12996. return obj instanceof RubyFile;
  12997. }
  12998. };
  12999. separator.freeze(context);
  13000. fileClass.defineConstant("SEPARATOR", separator);
  13001. fileClass.defineConstant("Separator", separator);
  13002. if (File.separatorChar == '\\') {
  13003. RubyString altSeparator = runtime.newString("\\");
  13004. altSeparator.freeze(context);
  13005. fileClass.defineConstant("ALT_SEPARATOR", altSeparator);
  13006. } else {
  13007. fileClass.defineConstant("ALT_SEPARATOR", runtime.getNil());
  13008. }
  13009. RubyString pathSeparator = runtime.newString(File.pathSeparator);
  13010. pathSeparator.freeze(context);
  13011. fileClass.defineConstant("PATH_SEPARATOR", pathSeparator);
  13012. // TODO: why are we duplicating the constants here, and then in
  13013. // File::Constants below? File::Constants is included in IO.
  13014. // TODO: These were missing, so we're not handling them elsewhere?
  13015. // FIXME: The old value, 32786, didn't match what IOModes expected, so I reference
  13016. // the constant here. THIS MAY NOT BE THE CORRECT VALUE.
  13017. fileClass.fastSetConstant("BINARY", runtime.newFixnum(ModeFlags.BINARY));
  13018. fileClass.fastSetConstant("FNM_NOESCAPE", runtime.newFixnum(FNM_NOESCAPE));
  13019. fileClass.fastSetConstant("FNM_CASEFOLD", runtime.newFixnum(FNM_CASEFOLD));
  13020. fileClass.fastSetConstant("FNM_SYSCASE", runtime.newFixnum(FNM_SYSCASE));
  13021. fileClass.fastSetConstant("FNM_DOTMATCH", runtime.newFixnum(FNM_DOTMATCH));
  13022. fileClass.fastSetConstant("FNM_PATHNAME", runtime.newFixnum(FNM_PATHNAME));
  13023. // Create constants for open flags
  13024. fileClass.fastSetConstant("RDONLY", runtime.newFixnum(ModeFlags.RDONLY));
  13025. fileClass.fastSetConstant("WRONLY", runtime.newFixnum(ModeFlags.WRONLY));
  13026. fileClass.fastSetConstant("RDWR", runtime.newFixnum(ModeFlags.RDWR));
  13027. fileClass.fastSetConstant("CREAT", runtime.newFixnum(ModeFlags.CREAT));
  13028. fileClass.fastSetConstant("EXCL", runtime.newFixnum(ModeFlags.EXCL));
  13029. fileClass.fastSetConstant("NOCTTY", runtime.newFixnum(ModeFlags.NOCTTY));
  13030. fileClass.fastSetConstant("TRUNC", runtime.newFixnum(ModeFlags.TRUNC));
  13031. fileClass.fastSetConstant("APPEND", runtime.newFixnum(ModeFlags.APPEND));
  13032. fileClass.fastSetConstant("NONBLOCK", runtime.newFixnum(ModeFlags.NONBLOCK));
  13033. // Create constants for flock
  13034. fileClass.fastSetConstant("LOCK_SH", runtime.newFixnum(RubyFile.LOCK_SH));
  13035. fileClass.fastSetConstant("LOCK_EX", runtime.newFixnum(RubyFile.LOCK_EX));
  13036. fileClass.fastSetConstant("LOCK_NB", runtime.newFixnum(RubyFile.LOCK_NB));
  13037. fileClass.fastSetConstant("LOCK_UN", runtime.newFixnum(RubyFile.LOCK_UN));
  13038. // Create Constants class
  13039. RubyModule constants = fileClass.defineModuleUnder("Constants");
  13040. // TODO: These were missing, so we're not handling them elsewhere?
  13041. constants.fastSetConstant("BINARY", runtime.newFixnum(ModeFlags.BINARY));
  13042. constants.fastSetConstant("SYNC", runtime.newFixnum(0x1000));
  13043. constants.fastSetConstant("FNM_NOESCAPE", runtime.newFixnum(FNM_NOESCAPE));
  13044. constants.fastSetConstant("FNM_CASEFOLD", runtime.newFixnum(FNM_CASEFOLD));
  13045. constants.fastSetConstant("FNM_SYSCASE", runtime.newFixnum(FNM_SYSCASE));
  13046. constants.fastSetConstant("FNM_DOTMATCH", runtime.newFixnum(FNM_DOTMATCH));
  13047. constants.fastSetConstant("FNM_PATHNAME", runtime.newFixnum(FNM_PATHNAME));
  13048. // Create constants for open flags
  13049. constants.fastSetConstant("RDONLY", runtime.newFixnum(ModeFlags.RDONLY));
  13050. constants.fastSetConstant("WRONLY", runtime.newFixnum(ModeFlags.WRONLY));
  13051. constants.fastSetConstant("RDWR", runtime.newFixnum(ModeFlags.RDWR));
  13052. constants.fastSetConstant("CREAT", runtime.newFixnum(ModeFlags.CREAT));
  13053. constants.fastSetConstant("EXCL", runtime.newFixnum(ModeFlags.EXCL));
  13054. constants.fastSetConstant("NOCTTY", runtime.newFixnum(ModeFlags.NOCTTY));
  13055. constants.fastSetConstant("TRUNC", runtime.newFixnum(ModeFlags.TRUNC));
  13056. constants.fastSetConstant("APPEND", runtime.newFixnum(ModeFlags.APPEND));
  13057. constants.fastSetConstant("NONBLOCK", runtime.newFixnum(ModeFlags.NONBLOCK));
  13058. // Create constants for flock
  13059. constants.fastSetConstant("LOCK_SH", runtime.newFixnum(RubyFile.LOCK_SH));
  13060. constants.fastSetConstant("LOCK_EX", runtime.newFixnum(RubyFile.LOCK_EX));
  13061. constants.fastSetConstant("LOCK_NB", runtime.newFixnum(RubyFile.LOCK_NB));
  13062. constants.fastSetConstant("LOCK_UN", runtime.newFixnum(RubyFile.LOCK_UN));
  13063. // File::Constants module is included in IO.
  13064. runtime.getIO().includeModule(constants);
  13065. runtime.getFileTest().extend_object(fileClass);
  13066. fileClass.defineAnnotatedMethods(RubyFile.class);
  13067. return fileClass;
  13068. }
  13069. @JRubyMethod
  13070. @Override
  13071. public IRubyObject close() {
  13072. // Make sure any existing lock is released before we try and close the file
  13073. if (currentLock != null) {
  13074. try {
  13075. currentLock.release();
  13076. } catch (IOException e) {
  13077. throw getRuntime().newIOError(e.getMessage());
  13078. }
  13079. }
  13080. return super.close();
  13081. }
  13082. @JRubyMethod(required = 1)
  13083. public IRubyObject flock(ThreadContext context, IRubyObject lockingConstant) {
  13084. // TODO: port exact behavior from MRI, and move most locking logic into ChannelDescriptor
  13085. // TODO: for all LOCK_NB cases, return false if they would block
  13086. ChannelDescriptor descriptor = openFile.getMainStream().getDescriptor();
  13087. // null channel always succeeds for all locking operations
  13088. if (descriptor.isNull()) return RubyFixnum.zero(context.getRuntime());
  13089. FileChannel fileChannel = (FileChannel)descriptor.getChannel();
  13090. int lockMode = RubyNumeric.num2int(lockingConstant);
  13091. // Exclusive locks in Java require the channel to be writable, otherwise
  13092. // an exception is thrown (terminating JRuby execution).
  13093. // But flock behavior of MRI is that it allows
  13094. // exclusive locks even on non-writable file. So we convert exclusive
  13095. // lock to shared lock if the channel is not writable, to better match
  13096. // the MRI behavior.
  13097. if (!openFile.isWritable() && (lockMode & LOCK_EX) > 0) {
  13098. lockMode = (lockMode ^ LOCK_EX) | LOCK_SH;
  13099. }
  13100. try {
  13101. switch (lockMode) {
  13102. case LOCK_UN:
  13103. case LOCK_UN | LOCK_NB:
  13104. if (currentLock != null) {
  13105. currentLock.release();
  13106. currentLock = null;
  13107. return RubyFixnum.zero(context.getRuntime());
  13108. }
  13109. break;
  13110. case LOCK_EX:
  13111. if (currentLock != null) {
  13112. currentLock.release();
  13113. currentLock = null;
  13114. }
  13115. currentLock = fileChannel.lock();
  13116. if (currentLock != null) {
  13117. return RubyFixnum.zero(context.getRuntime());
  13118. }
  13119. break;
  13120. case LOCK_EX | LOCK_NB:
  13121. if (currentLock != null) {
  13122. currentLock.release();
  13123. currentLock = null;
  13124. }
  13125. currentLock = fileChannel.tryLock();
  13126. if (currentLock != null) {
  13127. return RubyFixnum.zero(context.getRuntime());
  13128. }
  13129. break;
  13130. case LOCK_SH:
  13131. if (currentLock != null) {
  13132. currentLock.release();
  13133. currentLock = null;
  13134. }
  13135. currentLock = fileChannel.lock(0L, Long.MAX_VALUE, true);
  13136. if (currentLock != null) {
  13137. return RubyFixnum.zero(context.getRuntime());
  13138. }
  13139. break;
  13140. case LOCK_SH | LOCK_NB:
  13141. if (currentLock != null) {
  13142. currentLock.release();
  13143. currentLock = null;
  13144. }
  13145. currentLock = fileChannel.tryLock(0L, Long.MAX_VALUE, true);
  13146. if (currentLock != null) {
  13147. return RubyFixnum.zero(context.getRuntime());
  13148. }
  13149. break;
  13150. default:
  13151. }
  13152. } catch (IOException ioe) {
  13153. if (context.getRuntime().getDebug().isTrue()) {
  13154. ioe.printStackTrace(System.err);
  13155. }
  13156. // Return false here
  13157. } catch (java.nio.channels.OverlappingFileLockException ioe) {
  13158. if (context.getRuntime().getDebug().isTrue()) {
  13159. ioe.printStackTrace(System.err);
  13160. }
  13161. // Return false here
  13162. }
  13163. return context.getRuntime().getFalse();
  13164. }
  13165. @JRubyMethod(required = 1, optional = 2, frame = true, visibility = Visibility.PRIVATE)
  13166. @Override
  13167. public IRubyObject initialize(IRubyObject[] args, Block block) {
  13168. if (openFile == null) {
  13169. throw getRuntime().newRuntimeError("reinitializing File");
  13170. }
  13171. if (args.length > 0 && args.length < 3) {
  13172. IRubyObject fd = TypeConverter.convertToTypeWithCheck(args[0], getRuntime().getFixnum(), MethodIndex.TO_INT, "to_int");
  13173. if (!fd.isNil()) {
  13174. args[0] = fd;
  13175. return super.initialize(args, block);
  13176. }
  13177. }
  13178. return openFile(args);
  13179. }
  13180. private IRubyObject openFile(IRubyObject args[]) {
  13181. IRubyObject filename = args[0].convertToString();
  13182. getRuntime().checkSafeString(filename);
  13183. path = filename.convertToString().getUnicodeValue();
  13184. String modeString;
  13185. ModeFlags modes;
  13186. int perm;
  13187. try {
  13188. if ((args.length > 1 && args[1] instanceof RubyFixnum) || (args.length > 2 && !args[2].isNil())) {
  13189. if (args[1] instanceof RubyFixnum) {
  13190. modes = new ModeFlags(RubyNumeric.num2int(args[1]));
  13191. } else {
  13192. modeString = args[1].convertToString().toString();
  13193. modes = getIOModes(getRuntime(), modeString);
  13194. }
  13195. if (args.length > 2 && !args[2].isNil()) {
  13196. perm = RubyNumeric.num2int(args[2]);
  13197. } else {
  13198. perm = 438; // 0666
  13199. }
  13200. sysopenInternal(path, modes, perm);
  13201. } else {
  13202. modeString = "r";
  13203. if (args.length > 1) {
  13204. if (!args[1].isNil()) {
  13205. modeString = args[1].convertToString().toString();
  13206. }
  13207. }
  13208. openInternal(path, modeString);
  13209. }
  13210. } catch (InvalidValueException ex) {
  13211. throw getRuntime().newErrnoEINVALError();
  13212. } finally {}
  13213. return this;
  13214. }
  13215. private void sysopenInternal(String path, ModeFlags modes, int perm) throws InvalidValueException {
  13216. openFile = new OpenFile();
  13217. openFile.setPath(path);
  13218. openFile.setMode(modes.getOpenFileFlags());
  13219. ChannelDescriptor descriptor = sysopen(path, modes, perm);
  13220. openFile.setMainStream(fdopen(descriptor, modes));
  13221. registerDescriptor(descriptor);
  13222. }
  13223. private void openInternal(String path, String modeString) throws InvalidValueException {
  13224. openFile = new OpenFile();
  13225. openFile.setMode(getIOModes(getRuntime(), modeString).getOpenFileFlags());
  13226. openFile.setPath(path);
  13227. openFile.setMainStream(fopen(path, modeString));
  13228. registerDescriptor(openFile.getMainStream().getDescriptor());
  13229. }
  13230. private ChannelDescriptor sysopen(String path, ModeFlags modes, int perm) throws InvalidValueException {
  13231. try {
  13232. ChannelDescriptor descriptor = ChannelDescriptor.open(
  13233. getRuntime().getCurrentDirectory(),
  13234. path,
  13235. modes,
  13236. perm,
  13237. getRuntime().getPosix());
  13238. // TODO: check if too many open files, GC and try again
  13239. return descriptor;
  13240. } catch (FileNotFoundException fnfe) {
  13241. throw getRuntime().newErrnoENOENTError();
  13242. } catch (DirectoryAsFileException dafe) {
  13243. throw getRuntime().newErrnoEISDirError();
  13244. } catch (FileExistsException fee) {
  13245. throw getRuntime().newErrnoEEXISTError("file exists: " + path);
  13246. } catch (IOException ioe) {
  13247. throw getRuntime().newIOErrorFromException(ioe);
  13248. }
  13249. }
  13250. private Stream fopen(String path, String modeString) {
  13251. try {
  13252. Stream stream = ChannelStream.fopen(
  13253. getRuntime(),
  13254. path,
  13255. getIOModes(getRuntime(), modeString));
  13256. if (stream == null) {
  13257. // TODO
  13258. // if (errno == EMFILE || errno == ENFILE) {
  13259. // rb_gc();
  13260. // file = fopen(fname, mode);
  13261. // }
  13262. // if (!file) {
  13263. // rb_sys_fail(fname);
  13264. // }
  13265. }
  13266. // Do we need to be in SETVBUF mode for buffering to make sense? This comes up elsewhere.
  13267. // #ifdef USE_SETVBUF
  13268. // if (setvbuf(file, NULL, _IOFBF, 0) != 0)
  13269. // rb_warn("setvbuf() can't be honoured for %s", fname);
  13270. // #endif
  13271. // #ifdef __human68k__
  13272. // fmode(file, _IOTEXT);
  13273. // #endif
  13274. return stream;
  13275. } catch (BadDescriptorException e) {
  13276. throw getRuntime().newErrnoEBADFError();
  13277. } catch (FileNotFoundException ex) {
  13278. // FNFException can be thrown in both cases, when the file
  13279. // is not found, or when permission is denied.
  13280. if (Ruby.isSecurityRestricted() || new File(path).exists()) {
  13281. throw getRuntime().newErrnoEACCESError(
  13282. "Permission denied - " + path);
  13283. }
  13284. throw getRuntime().newErrnoENOENTError(
  13285. "File not found - " + path);
  13286. } catch (DirectoryAsFileException ex) {
  13287. throw getRuntime().newErrnoEISDirError();
  13288. } catch (FileExistsException ex) {
  13289. throw getRuntime().newErrnoEEXISTError(path);
  13290. } catch (IOException ex) {
  13291. throw getRuntime().newIOErrorFromException(ex);
  13292. } catch (InvalidValueException ex) {
  13293. throw getRuntime().newErrnoEINVALError();
  13294. } catch (PipeException ex) {
  13295. throw getRuntime().newErrnoEPIPEError();
  13296. }
  13297. }
  13298. @JRubyMethod(required = 1)
  13299. public IRubyObject chmod(ThreadContext context, IRubyObject arg) {
  13300. int mode = (int) arg.convertToInteger().getLongValue();
  13301. if (!new File(path).exists()) {
  13302. throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
  13303. }
  13304. return context.getRuntime().newFixnum(context.getRuntime().getPosix().chmod(path, mode));
  13305. }
  13306. @JRubyMethod(required = 2)
  13307. public IRubyObject chown(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  13308. int owner = -1;
  13309. if (!arg1.isNil()) {
  13310. owner = RubyNumeric.num2int(arg1);
  13311. }
  13312. int group = -1;
  13313. if (!arg2.isNil()) {
  13314. group = RubyNumeric.num2int(arg2);
  13315. }
  13316. if (!new File(path).exists()) {
  13317. throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
  13318. }
  13319. return context.getRuntime().newFixnum(context.getRuntime().getPosix().chown(path, owner, group));
  13320. }
  13321. @JRubyMethod
  13322. public IRubyObject atime(ThreadContext context) {
  13323. return context.getRuntime().newFileStat(path, false).atime();
  13324. }
  13325. @JRubyMethod
  13326. public IRubyObject ctime(ThreadContext context) {
  13327. return context.getRuntime().newFileStat(path, false).ctime();
  13328. }
  13329. @JRubyMethod(required = 1)
  13330. public IRubyObject lchmod(ThreadContext context, IRubyObject arg) {
  13331. int mode = (int) arg.convertToInteger().getLongValue();
  13332. if (!new File(path).exists()) {
  13333. throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
  13334. }
  13335. return context.getRuntime().newFixnum(context.getRuntime().getPosix().lchmod(path, mode));
  13336. }
  13337. // TODO: this method is not present in MRI!
  13338. @JRubyMethod(required = 2)
  13339. public IRubyObject lchown(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  13340. int owner = -1;
  13341. if (!arg1.isNil()) {
  13342. owner = RubyNumeric.num2int(arg1);
  13343. }
  13344. int group = -1;
  13345. if (!arg2.isNil()) {
  13346. group = RubyNumeric.num2int(arg2);
  13347. }
  13348. if (!new File(path).exists()) {
  13349. throw context.getRuntime().newErrnoENOENTError("No such file or directory - " + path);
  13350. }
  13351. return context.getRuntime().newFixnum(context.getRuntime().getPosix().lchown(path, owner, group));
  13352. }
  13353. @JRubyMethod
  13354. public IRubyObject lstat(ThreadContext context) {
  13355. return context.getRuntime().newFileStat(path, true);
  13356. }
  13357. @JRubyMethod
  13358. public IRubyObject mtime(ThreadContext context) {
  13359. return getLastModified(context.getRuntime(), path);
  13360. }
  13361. @JRubyMethod
  13362. public RubyString path(ThreadContext context) {
  13363. return context.getRuntime().newString(path);
  13364. }
  13365. @JRubyMethod
  13366. @Override
  13367. public IRubyObject stat(ThreadContext context) {
  13368. openFile.checkClosed(context.getRuntime());
  13369. return context.getRuntime().newFileStat(path, false);
  13370. }
  13371. @JRubyMethod(required = 1)
  13372. public IRubyObject truncate(ThreadContext context, IRubyObject arg) {
  13373. RubyInteger newLength = arg.convertToInteger();
  13374. if (newLength.getLongValue() < 0) {
  13375. throw context.getRuntime().newErrnoEINVALError("invalid argument: " + path);
  13376. }
  13377. try {
  13378. openFile.checkWritable(context.getRuntime());
  13379. openFile.getMainStream().ftruncate(newLength.getLongValue());
  13380. } catch (BadDescriptorException e) {
  13381. throw context.getRuntime().newErrnoEBADFError();
  13382. } catch (PipeException e) {
  13383. throw context.getRuntime().newErrnoESPIPEError();
  13384. } catch (InvalidValueException ex) {
  13385. throw context.getRuntime().newErrnoEINVALError();
  13386. } catch (IOException e) {
  13387. // Should we do anything?
  13388. }
  13389. return RubyFixnum.zero(context.getRuntime());
  13390. }
  13391. @Override
  13392. public String toString() {
  13393. return "RubyFile(" + path + ", " + openFile.getMode() + ", " + openFile.getMainStream().getDescriptor().getFileno() + ")";
  13394. }
  13395. // TODO: This is also defined in the MetaClass too...Consolidate somewhere.
  13396. private static ModeFlags getModes(Ruby runtime, IRubyObject object) throws InvalidValueException {
  13397. if (object instanceof RubyString) {
  13398. return getIOModes(runtime, ((RubyString) object).toString());
  13399. } else if (object instanceof RubyFixnum) {
  13400. return new ModeFlags(((RubyFixnum) object).getLongValue());
  13401. }
  13402. throw runtime.newTypeError("Invalid type for modes");
  13403. }
  13404. @JRubyMethod
  13405. @Override
  13406. public IRubyObject inspect() {
  13407. StringBuilder val = new StringBuilder();
  13408. val.append("#<File:").append(path);
  13409. if(!openFile.isOpen()) {
  13410. val.append(" (closed)");
  13411. }
  13412. val.append(">");
  13413. return getRuntime().newString(val.toString());
  13414. }
  13415. /* File class methods */
  13416. @JRubyMethod(required = 1, optional = 1, meta = true)
  13417. public static IRubyObject basename(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13418. String name = RubyString.stringValue(args[0]).toString();
  13419. // MRI-compatible basename handling for windows drive letter paths
  13420. if (Platform.IS_WINDOWS) {
  13421. if (name.length() > 1 && name.charAt(1) == ':' && Character.isLetter(name.charAt(0))) {
  13422. switch (name.length()) {
  13423. case 2:
  13424. return RubyString.newEmptyString(context.getRuntime()).infectBy(args[0]);
  13425. case 3:
  13426. return context.getRuntime().newString(name.substring(2)).infectBy(args[0]);
  13427. default:
  13428. switch (name.charAt(2)) {
  13429. case '/':
  13430. case '\\':
  13431. break;
  13432. default:
  13433. // strip c: away from relative-pathed name
  13434. name = name.substring(2);
  13435. break;
  13436. }
  13437. break;
  13438. }
  13439. }
  13440. }
  13441. while (name.length() > 1 && name.charAt(name.length() - 1) == '/') {
  13442. name = name.substring(0, name.length() - 1);
  13443. }
  13444. // Paths which end in "/" or "\\" must be stripped off.
  13445. int slashCount = 0;
  13446. int length = name.length();
  13447. for (int i = length - 1; i >= 0; i--) {
  13448. char c = name.charAt(i);
  13449. if (c != '/' && c != '\\') {
  13450. break;
  13451. }
  13452. slashCount++;
  13453. }
  13454. if (slashCount > 0 && length > 1) {
  13455. name = name.substring(0, name.length() - slashCount);
  13456. }
  13457. int index = name.lastIndexOf('/');
  13458. if (index == -1) {
  13459. // XXX actually only on windows...
  13460. index = name.lastIndexOf('\\');
  13461. }
  13462. if (!name.equals("/") && index != -1) {
  13463. name = name.substring(index + 1);
  13464. }
  13465. if (args.length == 2) {
  13466. String ext = RubyString.stringValue(args[1]).toString();
  13467. if (".*".equals(ext)) {
  13468. index = name.lastIndexOf('.');
  13469. if (index > 0) { // -1 no match; 0 it is dot file not extension
  13470. name = name.substring(0, index);
  13471. }
  13472. } else if (name.endsWith(ext)) {
  13473. name = name.substring(0, name.length() - ext.length());
  13474. }
  13475. }
  13476. return context.getRuntime().newString(name).infectBy(args[0]);
  13477. }
  13478. @JRubyMethod(required = 2, rest = true, meta = true)
  13479. public static IRubyObject chmod(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13480. Ruby runtime = context.getRuntime();
  13481. int count = 0;
  13482. RubyInteger mode = args[0].convertToInteger();
  13483. for (int i = 1; i < args.length; i++) {
  13484. IRubyObject filename = args[i];
  13485. if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
  13486. throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
  13487. }
  13488. boolean result = 0 == runtime.getPosix().chmod(filename.toString(), (int)mode.getLongValue());
  13489. if (result) {
  13490. count++;
  13491. }
  13492. }
  13493. return runtime.newFixnum(count);
  13494. }
  13495. @JRubyMethod(required = 3, rest = true, meta = true)
  13496. public static IRubyObject chown(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13497. Ruby runtime = context.getRuntime();
  13498. int count = 0;
  13499. int owner = -1;
  13500. if (!args[0].isNil()) {
  13501. owner = RubyNumeric.num2int(args[0]);
  13502. }
  13503. int group = -1;
  13504. if (!args[1].isNil()) {
  13505. group = RubyNumeric.num2int(args[1]);
  13506. }
  13507. for (int i = 2; i < args.length; i++) {
  13508. IRubyObject filename = args[i];
  13509. if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
  13510. throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
  13511. }
  13512. boolean result = 0 == runtime.getPosix().chown(filename.toString(), owner, group);
  13513. if (result) {
  13514. count++;
  13515. }
  13516. }
  13517. return runtime.newFixnum(count);
  13518. }
  13519. @JRubyMethod(required = 1, meta = true)
  13520. public static IRubyObject dirname(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  13521. RubyString filename = RubyString.stringValue(arg);
  13522. String jfilename = filename.toString();
  13523. String name = jfilename.replace('\\', '/');
  13524. int minPathLength = 1;
  13525. boolean trimmedSlashes = false;
  13526. boolean startsWithDriveLetterOnWindows = startsWithDriveLetterOnWindows(name);
  13527. if (startsWithDriveLetterOnWindows) {
  13528. minPathLength = 3;
  13529. }
  13530. while (name.length() > minPathLength && name.charAt(name.length() - 1) == '/') {
  13531. trimmedSlashes = true;
  13532. name = name.substring(0, name.length() - 1);
  13533. }
  13534. String result;
  13535. if (startsWithDriveLetterOnWindows && name.length() == 2) {
  13536. if (trimmedSlashes) {
  13537. // C:\ is returned unchanged
  13538. result = jfilename.substring(0, 3);
  13539. } else {
  13540. result = jfilename.substring(0, 2) + '.';
  13541. }
  13542. } else {
  13543. //TODO deal with UNC names
  13544. int index = name.lastIndexOf('/');
  13545. if (index == -1) {
  13546. if (startsWithDriveLetterOnWindows) {
  13547. return context.getRuntime().newString(jfilename.substring(0, 2) + ".");
  13548. } else {
  13549. return context.getRuntime().newString(".");
  13550. }
  13551. }
  13552. if (index == 0) return context.getRuntime().newString("/");
  13553. if (startsWithDriveLetterOnWindows && index == 2) {
  13554. // Include additional path separator
  13555. // (so that dirname of "C:\file.txt" is "C:\", not "C:")
  13556. index++;
  13557. }
  13558. result = jfilename.substring(0, index);
  13559. }
  13560. char endChar;
  13561. // trim trailing slashes
  13562. while (result.length() > minPathLength) {
  13563. endChar = result.charAt(result.length() - 1);
  13564. if (endChar == '/' || endChar == '\\') {
  13565. result = result.substring(0, result.length() - 1);
  13566. } else {
  13567. break;
  13568. }
  13569. }
  13570. return context.getRuntime().newString(result).infectBy(filename);
  13571. }
  13572. private static boolean isWindowsDriveLetter(char c) {
  13573. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
  13574. }
  13575. /**
  13576. * Returns the extension name of the file. An empty string is returned if
  13577. * the filename (not the entire path) starts or ends with a dot.
  13578. * @param recv
  13579. * @param arg Path to get extension name of
  13580. * @return Extension, including the dot, or an empty string
  13581. */
  13582. @JRubyMethod(required = 1, meta = true)
  13583. public static IRubyObject extname(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  13584. IRubyObject baseFilename = basename(context, recv, new IRubyObject[]{arg});
  13585. String filename = RubyString.stringValue(baseFilename).toString();
  13586. String result = "";
  13587. int dotIndex = filename.lastIndexOf(".");
  13588. if (dotIndex > 0 && dotIndex != (filename.length() - 1)) {
  13589. // Dot is not at beginning and not at end of filename.
  13590. result = filename.substring(dotIndex);
  13591. }
  13592. return context.getRuntime().newString(result);
  13593. }
  13594. /**
  13595. * Converts a pathname to an absolute pathname. Relative paths are
  13596. * referenced from the current working directory of the process unless
  13597. * a second argument is given, in which case it will be used as the
  13598. * starting point. If the second argument is also relative, it will
  13599. * first be converted to an absolute pathname.
  13600. * @param recv
  13601. * @param args
  13602. * @return Resulting absolute path as a String
  13603. */
  13604. @JRubyMethod(required = 1, optional = 1, meta = true)
  13605. public static IRubyObject expand_path(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13606. Ruby runtime = context.getRuntime();
  13607. String relativePath = RubyString.stringValue(args[0]).toString();
  13608. boolean isAbsoluteWithFilePrefix = relativePath.startsWith("file:");
  13609. String cwd = null;
  13610. // Handle ~user paths
  13611. relativePath = expandUserPath(context, relativePath);
  13612. // If there's a second argument, it's the path to which the first
  13613. // argument is relative.
  13614. if (args.length == 2 && !args[1].isNil()) {
  13615. String cwdArg = RubyString.stringValue(args[1]).toString();
  13616. // Handle ~user paths.
  13617. cwd = expandUserPath(context, cwdArg);
  13618. cwd = adjustRootPathOnWindows(runtime, cwd, null);
  13619. boolean startsWithSlashNotOnWindows = (cwd != null)
  13620. && !Platform.IS_WINDOWS && cwd.length() > 0
  13621. && cwd.charAt(0) == '/';
  13622. // TODO: better detection when path is absolute or not.
  13623. // If the path isn't absolute, then prepend the current working
  13624. // directory to the path.
  13625. if (!startsWithSlashNotOnWindows && !startsWithDriveLetterOnWindows(cwd)) {
  13626. cwd = new File(runtime.getCurrentDirectory(), cwd).getAbsolutePath();
  13627. }
  13628. } else {
  13629. // If there's no second argument, simply use the working directory
  13630. // of the runtime.
  13631. cwd = runtime.getCurrentDirectory();
  13632. }
  13633. // Something wrong we don't know the cwd...
  13634. // TODO: Is this behavior really desirable? /mov
  13635. if (cwd == null) return runtime.getNil();
  13636. /* The counting of slashes that follows is simply a way to adhere to
  13637. * Ruby's UNC (or something) compatibility. When Ruby's expand_path is
  13638. * called with "//foo//bar" it will return "//foo/bar". JRuby uses
  13639. * java.io.File, and hence returns "/foo/bar". In order to retain
  13640. * java.io.File in the lower layers and provide full Ruby
  13641. * compatibility, the number of extra slashes must be counted and
  13642. * prepended to the result.
  13643. */
  13644. // TODO: special handling on windows for some corner cases
  13645. // if (IS_WINDOWS) {
  13646. // if (relativePath.startsWith("//")) {
  13647. // if (relativePath.length() > 2 && relativePath.charAt(2) != '/') {
  13648. // int nextSlash = relativePath.indexOf('/', 3);
  13649. // if (nextSlash != -1) {
  13650. // return runtime.newString(
  13651. // relativePath.substring(0, nextSlash)
  13652. // + canonicalize(relativePath.substring(nextSlash)));
  13653. // } else {
  13654. // return runtime.newString(relativePath);
  13655. // }
  13656. // }
  13657. // }
  13658. // }
  13659. // Find out which string to check.
  13660. String padSlashes = "";
  13661. if (!Platform.IS_WINDOWS) {
  13662. if (relativePath.length() > 0 && relativePath.charAt(0) == '/') {
  13663. padSlashes = countSlashes(relativePath);
  13664. } else if (cwd.length() > 0 && cwd.charAt(0) == '/') {
  13665. padSlashes = countSlashes(cwd);
  13666. }
  13667. }
  13668. JRubyFile path;
  13669. if (relativePath.length() == 0) {
  13670. path = JRubyFile.create(relativePath, cwd);
  13671. } else {
  13672. relativePath = adjustRootPathOnWindows(runtime, relativePath, cwd);
  13673. path = JRubyFile.create(cwd, relativePath);
  13674. }
  13675. String tempResult = padSlashes + canonicalize(path.getAbsolutePath());
  13676. if(isAbsoluteWithFilePrefix) {
  13677. tempResult = tempResult.substring(tempResult.indexOf("file:"));
  13678. }
  13679. return runtime.newString(tempResult);
  13680. }
  13681. /**
  13682. * This method checks a path, and if it starts with ~, then it expands
  13683. * the path to the absolute path of the user's home directory. If the
  13684. * string does not begin with ~, then the string is simply returned.
  13685. * unaltered.
  13686. * @param recv
  13687. * @param path Path to check
  13688. * @return Expanded path
  13689. */
  13690. public static String expandUserPath(ThreadContext context, String path) {
  13691. int pathLength = path.length();
  13692. if (pathLength >= 1 && path.charAt(0) == '~') {
  13693. // Enebo : Should ~frogger\\foo work (it doesnt in linux ruby)?
  13694. int userEnd = path.indexOf('/');
  13695. if (userEnd == -1) {
  13696. if (pathLength == 1) {
  13697. // Single '~' as whole path to expand
  13698. path = RubyDir.getHomeDirectoryPath(context).toString();
  13699. } else {
  13700. // No directory delimeter. Rest of string is username
  13701. userEnd = pathLength;
  13702. }
  13703. }
  13704. if (userEnd == 1) {
  13705. // '~/...' as path to expand
  13706. path = RubyDir.getHomeDirectoryPath(context).toString() +
  13707. path.substring(1);
  13708. } else if (userEnd > 1){
  13709. // '~user/...' as path to expand
  13710. String user = path.substring(1, userEnd);
  13711. IRubyObject dir = RubyDir.getHomeDirectoryPath(context, user);
  13712. if (dir.isNil()) {
  13713. throw context.getRuntime().newArgumentError("user " + user + " does not exist");
  13714. }
  13715. path = "" + dir + (pathLength == userEnd ? "" : path.substring(userEnd));
  13716. }
  13717. }
  13718. return path;
  13719. }
  13720. /**
  13721. * Returns a string consisting of <code>n-1</code> slashes, where
  13722. * <code>n</code> is the number of slashes at the beginning of the input
  13723. * string.
  13724. * @param stringToCheck
  13725. * @return
  13726. */
  13727. private static String countSlashes( String stringToCheck ) {
  13728. // Count number of extra slashes in the beginning of the string.
  13729. int slashCount = 0;
  13730. for (int i = 0; i < stringToCheck.length(); i++) {
  13731. if (stringToCheck.charAt(i) == '/') {
  13732. slashCount++;
  13733. } else {
  13734. break;
  13735. }
  13736. }
  13737. // If there are N slashes, then we want N-1.
  13738. if (slashCount > 0) {
  13739. slashCount--;
  13740. }
  13741. // Prepare a string with the same number of redundant slashes so that
  13742. // we easily can prepend it to the result.
  13743. byte[] slashes = new byte[slashCount];
  13744. for (int i = 0; i < slashCount; i++) {
  13745. slashes[i] = '/';
  13746. }
  13747. return new String(slashes);
  13748. }
  13749. private static String canonicalize(String path) {
  13750. return canonicalize(null, path);
  13751. }
  13752. private static String canonicalize(String canonicalPath, String remaining) {
  13753. if (remaining == null) {
  13754. if ("".equals(canonicalPath)) {
  13755. return "/";
  13756. } else {
  13757. // compensate for missing slash after drive letter on windows
  13758. if (startsWithDriveLetterOnWindows(canonicalPath)
  13759. && canonicalPath.length() == 2) {
  13760. canonicalPath += "/";
  13761. }
  13762. }
  13763. return canonicalPath;
  13764. }
  13765. String child;
  13766. int slash = remaining.indexOf('/');
  13767. if (slash == -1) {
  13768. child = remaining;
  13769. remaining = null;
  13770. } else {
  13771. child = remaining.substring(0, slash);
  13772. remaining = remaining.substring(slash + 1);
  13773. }
  13774. if (child.equals(".")) {
  13775. // skip it
  13776. if (canonicalPath != null && canonicalPath.length() == 0 ) canonicalPath += "/";
  13777. } else if (child.equals("..")) {
  13778. if (canonicalPath == null) throw new IllegalArgumentException("Cannot have .. at the start of an absolute path");
  13779. int lastDir = canonicalPath.lastIndexOf('/');
  13780. if (lastDir == -1) {
  13781. if (startsWithDriveLetterOnWindows(canonicalPath)) {
  13782. // do nothing, we should not delete the drive letter
  13783. } else {
  13784. canonicalPath = "";
  13785. }
  13786. } else {
  13787. canonicalPath = canonicalPath.substring(0, lastDir);
  13788. }
  13789. } else if (canonicalPath == null) {
  13790. canonicalPath = child;
  13791. } else {
  13792. canonicalPath += "/" + child;
  13793. }
  13794. return canonicalize(canonicalPath, remaining);
  13795. }
  13796. /**
  13797. * Returns true if path matches against pattern The pattern is not a regular expression;
  13798. * instead it follows rules similar to shell filename globbing. It may contain the following
  13799. * metacharacters:
  13800. * *: Glob - match any sequence chars (re: .*). If like begins with '.' then it doesn't.
  13801. * ?: Matches a single char (re: .).
  13802. * [set]: Matches a single char in a set (re: [...]).
  13803. *
  13804. */
  13805. @JRubyMethod(name = {"fnmatch", "fnmatch?"}, required = 2, optional = 1, meta = true)
  13806. public static IRubyObject fnmatch(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13807. int flags = args.length == 3 ? RubyNumeric.num2int(args[2]) : 0;
  13808. ByteList pattern = args[0].convertToString().getByteList();
  13809. ByteList path = args[1].convertToString().getByteList();
  13810. if (org.jruby.util.Dir.fnmatch(pattern.bytes, pattern.begin, pattern.begin+pattern.realSize,
  13811. path.bytes, path.begin, path.begin+path.realSize, flags) == 0) {
  13812. return context.getRuntime().getTrue();
  13813. }
  13814. return context.getRuntime().getFalse();
  13815. }
  13816. @JRubyMethod(name = "ftype", required = 1, meta = true)
  13817. public static IRubyObject ftype(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13818. return context.getRuntime().newFileStat(filename.convertToString().toString(), true).ftype();
  13819. }
  13820. private static String inspectJoin(ThreadContext context, IRubyObject recv, RubyArray parent, RubyArray array) {
  13821. Ruby runtime = context.getRuntime();
  13822. // If already inspecting, there is no need to register/unregister again.
  13823. if (runtime.isInspecting(parent)) return join(context, recv, array).toString();
  13824. try {
  13825. runtime.registerInspecting(parent);
  13826. return join(context, recv, array).toString();
  13827. } finally {
  13828. runtime.unregisterInspecting(parent);
  13829. }
  13830. }
  13831. private static RubyString join(ThreadContext context, IRubyObject recv, RubyArray ary) {
  13832. IRubyObject[] args = ary.toJavaArray();
  13833. boolean isTainted = false;
  13834. StringBuilder buffer = new StringBuilder();
  13835. Ruby runtime = context.getRuntime();
  13836. for (int i = 0; i < args.length; i++) {
  13837. if (args[i].isTaint()) {
  13838. isTainted = true;
  13839. }
  13840. String element;
  13841. if (args[i] instanceof RubyString) {
  13842. element = args[i].toString();
  13843. } else if (args[i] instanceof RubyArray) {
  13844. if (runtime.isInspecting(args[i])) {
  13845. element = "[...]";
  13846. } else {
  13847. element = inspectJoin(context, recv, ary, ((RubyArray)args[i]));
  13848. }
  13849. } else {
  13850. element = args[i].convertToString().toString();
  13851. }
  13852. chomp(buffer);
  13853. if (i > 0 && !element.startsWith("/") && !element.startsWith("\\")) {
  13854. buffer.append("/");
  13855. }
  13856. buffer.append(element);
  13857. }
  13858. RubyString fixedStr = RubyString.newString(runtime, buffer.toString());
  13859. fixedStr.setTaint(isTainted);
  13860. return fixedStr;
  13861. }
  13862. /*
  13863. * Fixme: This does not have exact same semantics as RubyArray.join, but they
  13864. * probably could be consolidated (perhaps as join(args[], sep, doChomp)).
  13865. */
  13866. @JRubyMethod(rest = true, meta = true)
  13867. public static RubyString join(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13868. return join(context, recv, RubyArray.newArrayNoCopyLight(context.getRuntime(), args));
  13869. }
  13870. private static void chomp(StringBuilder buffer) {
  13871. int lastIndex = buffer.length() - 1;
  13872. while (lastIndex >= 0 && (buffer.lastIndexOf("/") == lastIndex || buffer.lastIndexOf("\\") == lastIndex)) {
  13873. buffer.setLength(lastIndex);
  13874. lastIndex--;
  13875. }
  13876. }
  13877. @JRubyMethod(name = "lstat", required = 1, meta = true)
  13878. public static IRubyObject lstat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13879. String f = filename.convertToString().toString();
  13880. if(f.startsWith("file:") && f.indexOf('!') != -1) {
  13881. f = f.substring(5, f.indexOf("!"));
  13882. }
  13883. return context.getRuntime().newFileStat(f, true);
  13884. }
  13885. @JRubyMethod(name = "stat", required = 1, meta = true)
  13886. public static IRubyObject stat(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13887. String f = filename.convertToString().toString();
  13888. if(f.startsWith("file:") && f.indexOf('!') != -1) {
  13889. f = f.substring(5, f.indexOf("!"));
  13890. }
  13891. return context.getRuntime().newFileStat(f, false);
  13892. }
  13893. @JRubyMethod(name = "atime", required = 1, meta = true)
  13894. public static IRubyObject atime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13895. String f = filename.convertToString().toString();
  13896. if(f.startsWith("file:") && f.indexOf('!') != -1) {
  13897. f = f.substring(5, f.indexOf("!"));
  13898. }
  13899. return context.getRuntime().newFileStat(f, false).atime();
  13900. }
  13901. @JRubyMethod(name = "ctime", required = 1, meta = true)
  13902. public static IRubyObject ctime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13903. String f = filename.convertToString().toString();
  13904. if(f.startsWith("file:") && f.indexOf('!') != -1) {
  13905. f = f.substring(5, f.indexOf("!"));
  13906. }
  13907. return context.getRuntime().newFileStat(f, false).ctime();
  13908. }
  13909. @JRubyMethod(required = 2, rest = true, meta = true)
  13910. public static IRubyObject lchmod(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13911. Ruby runtime = context.getRuntime();
  13912. int count = 0;
  13913. RubyInteger mode = args[0].convertToInteger();
  13914. for (int i = 1; i < args.length; i++) {
  13915. IRubyObject filename = args[i];
  13916. if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
  13917. throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
  13918. }
  13919. boolean result = 0 == runtime.getPosix().lchmod(filename.toString(), (int)mode.getLongValue());
  13920. if (result) {
  13921. count++;
  13922. }
  13923. }
  13924. return runtime.newFixnum(count);
  13925. }
  13926. @JRubyMethod(required = 3, rest = true, meta = true)
  13927. public static IRubyObject lchown(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  13928. Ruby runtime = context.getRuntime();
  13929. int owner = !args[0].isNil() ? RubyNumeric.num2int(args[0]) : -1;
  13930. int group = !args[1].isNil() ? RubyNumeric.num2int(args[1]) : -1;
  13931. int count = 0;
  13932. for (int i = 2; i < args.length; i++) {
  13933. IRubyObject filename = args[i];
  13934. if (!RubyFileTest.exist_p(filename, filename.convertToString()).isTrue()) {
  13935. throw runtime.newErrnoENOENTError("No such file or directory - " + filename);
  13936. }
  13937. boolean result = 0 == runtime.getPosix().lchown(filename.toString(), owner, group);
  13938. if (result) {
  13939. count++;
  13940. }
  13941. }
  13942. return runtime.newFixnum(count);
  13943. }
  13944. @JRubyMethod(required = 2, meta = true)
  13945. public static IRubyObject link(ThreadContext context, IRubyObject recv, IRubyObject from, IRubyObject to) {
  13946. Ruby runtime = context.getRuntime();
  13947. RubyString fromStr = RubyString.stringValue(from);
  13948. RubyString toStr = RubyString.stringValue(to);
  13949. try {
  13950. if (runtime.getPosix().link(
  13951. fromStr.toString(),toStr.toString()) == -1) {
  13952. // FIXME: When we get JNA3 we need to properly write this to errno.
  13953. throw runtime.newErrnoEEXISTError("File exists - "
  13954. + fromStr + " or " + toStr);
  13955. }
  13956. } catch (java.lang.UnsatisfiedLinkError ule) {
  13957. throw runtime.newNotImplementedError("link() function is unimplemented on this machine");
  13958. }
  13959. return runtime.newFixnum(0);
  13960. }
  13961. @JRubyMethod(name = "mtime", required = 1, meta = true)
  13962. public static IRubyObject mtime(ThreadContext context, IRubyObject recv, IRubyObject filename) {
  13963. return getLastModified(context.getRuntime(), filename.convertToString().toString());
  13964. }
  13965. @JRubyMethod(required = 2, meta = true)
  13966. public static IRubyObject rename(ThreadContext context, IRubyObject recv, IRubyObject oldName, IRubyObject newName) {
  13967. Ruby runtime = context.getRuntime();
  13968. RubyString oldNameString = RubyString.stringValue(oldName);
  13969. RubyString newNameString = RubyString.stringValue(newName);
  13970. runtime.checkSafeString(oldNameString);
  13971. runtime.checkSafeString(newNameString);
  13972. JRubyFile oldFile = JRubyFile.create(runtime.getCurrentDirectory(), oldNameString.toString());
  13973. JRubyFile newFile = JRubyFile.create(runtime.getCurrentDirectory(), newNameString.toString());
  13974. if (!oldFile.exists() || !newFile.getParentFile().exists()) {
  13975. throw runtime.newErrnoENOENTError("No such file or directory - " + oldNameString +
  13976. " or " + newNameString);
  13977. }
  13978. JRubyFile dest = JRubyFile.create(runtime.getCurrentDirectory(), newNameString.toString());
  13979. if (oldFile.renameTo(dest)) { // rename is successful
  13980. return RubyFixnum.zero(runtime);
  13981. }
  13982. // rename via Java API call wasn't successful, let's try some tricks, similar to MRI
  13983. if (newFile.exists()) {
  13984. runtime.getPosix().chmod(newNameString.toString(), 0666);
  13985. newFile.delete();
  13986. }
  13987. if (oldFile.renameTo(dest)) { // try to rename one more time
  13988. return RubyFixnum.zero(runtime);
  13989. }
  13990. throw runtime.newErrnoEACCESError("Permission denied - " + oldNameString + " or " +
  13991. newNameString);
  13992. }
  13993. @JRubyMethod(required = 1, meta = true)
  13994. public static RubyArray split(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  13995. RubyString filename = RubyString.stringValue(arg);
  13996. return context.getRuntime().newArray(dirname(context, recv, filename),
  13997. basename(context, recv, new IRubyObject[] { filename }));
  13998. }
  13999. @JRubyMethod(required = 2, meta = true)
  14000. public static IRubyObject symlink(ThreadContext context, IRubyObject recv, IRubyObject from, IRubyObject to) {
  14001. Ruby runtime = context.getRuntime();
  14002. RubyString fromStr = RubyString.stringValue(from);
  14003. RubyString toStr = RubyString.stringValue(to);
  14004. try {
  14005. if (runtime.getPosix().symlink(
  14006. fromStr.toString(), toStr.toString()) == -1) {
  14007. // FIXME: When we get JNA3 we need to properly write this to errno.
  14008. throw runtime.newErrnoEEXISTError("File exists - "
  14009. + fromStr + " or " + toStr);
  14010. }
  14011. } catch (java.lang.UnsatisfiedLinkError ule) {
  14012. throw runtime.newNotImplementedError("symlink() function is unimplemented on this machine");
  14013. }
  14014. return runtime.newFixnum(0);
  14015. }
  14016. @JRubyMethod(required = 1, meta = true)
  14017. public static IRubyObject readlink(ThreadContext context, IRubyObject recv, IRubyObject path) {
  14018. Ruby runtime = context.getRuntime();
  14019. try {
  14020. String realPath = runtime.getPosix().readlink(path.toString());
  14021. if (!RubyFileTest.exist_p(recv, path).isTrue()) {
  14022. throw runtime.newErrnoENOENTError("No such file or directory - " + path);
  14023. }
  14024. if (!RubyFileTest.symlink_p(recv, path).isTrue()) {
  14025. throw runtime.newErrnoEINVALError("invalid argument - " + path);
  14026. }
  14027. if (realPath == null) {
  14028. //FIXME: When we get JNA3 we need to properly write this to errno.
  14029. }
  14030. return runtime.newString(realPath);
  14031. } catch (IOException e) {
  14032. throw runtime.newIOError(e.getMessage());
  14033. }
  14034. }
  14035. // Can we produce IOError which bypasses a close?
  14036. @JRubyMethod(required = 2, meta = true)
  14037. public static IRubyObject truncate(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
  14038. Ruby runtime = context.getRuntime();
  14039. RubyString filename = arg1.convertToString(); // TODO: SafeStringValue here
  14040. RubyInteger newLength = arg2.convertToInteger();
  14041. if (!new File(runtime.getCurrentDirectory(), filename.getByteList().toString()).exists()) {
  14042. throw runtime.newErrnoENOENTError(
  14043. "No such file or directory - " + filename.getByteList().toString());
  14044. }
  14045. if (newLength.getLongValue() < 0) {
  14046. throw runtime.newErrnoEINVALError("invalid argument: " + filename);
  14047. }
  14048. IRubyObject[] args = new IRubyObject[] { filename, runtime.newString("r+") };
  14049. RubyFile file = (RubyFile) open(context, recv, args, Block.NULL_BLOCK);
  14050. file.truncate(context, newLength);
  14051. file.close();
  14052. return RubyFixnum.zero(runtime);
  14053. }
  14054. @JRubyMethod(meta = true, optional = 1)
  14055. public static IRubyObject umask(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  14056. Ruby runtime = context.getRuntime();
  14057. int oldMask = 0;
  14058. if (args.length == 0) {
  14059. oldMask = runtime.getPosix().umask(0);
  14060. runtime.getPosix().umask(oldMask);
  14061. } else if (args.length == 1) {
  14062. oldMask = runtime.getPosix().umask((int) args[0].convertToInteger().getLongValue());
  14063. } else {
  14064. runtime.newArgumentError("wrong number of arguments");
  14065. }
  14066. return runtime.newFixnum(oldMask);
  14067. }
  14068. /**
  14069. * This method does NOT set atime, only mtime, since Java doesn't support anything else.
  14070. */
  14071. @JRubyMethod(required = 2, rest = true, meta = true)
  14072. public static IRubyObject utime(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  14073. Ruby runtime = context.getRuntime();
  14074. // Ignore access_time argument since Java does not support it.
  14075. long mtime;
  14076. if (args[1] instanceof RubyTime) {
  14077. mtime = ((RubyTime) args[1]).getJavaDate().getTime();
  14078. } else if (args[1] instanceof RubyNumeric) {
  14079. mtime = RubyNumeric.num2long(args[1]);
  14080. } else if (args[1] == runtime.getNil()) {
  14081. mtime = System.currentTimeMillis();
  14082. } else {
  14083. RubyTime time = (RubyTime) TypeConverter.convertToType(args[1], runtime.getTime(), MethodIndex.NO_INDEX,"to_time", true);
  14084. mtime = time.getJavaDate().getTime();
  14085. }
  14086. for (int i = 2, j = args.length; i < j; i++) {
  14087. RubyString filename = RubyString.stringValue(args[i]);
  14088. runtime.checkSafeString(filename);
  14089. JRubyFile fileToTouch = JRubyFile.create(runtime.getCurrentDirectory(),filename.toString());
  14090. if (!fileToTouch.exists()) {
  14091. throw runtime.newErrnoENOENTError(" No such file or directory - \"" + filename + "\"");
  14092. }
  14093. fileToTouch.setLastModified(mtime);
  14094. }
  14095. return runtime.newFixnum(args.length - 2);
  14096. }
  14097. @JRubyMethod(name = {"unlink", "delete"}, rest = true, meta = true)
  14098. public static IRubyObject unlink(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  14099. Ruby runtime = context.getRuntime();
  14100. for (int i = 0; i < args.length; i++) {
  14101. RubyString filename = RubyString.stringValue(args[i]);
  14102. runtime.checkSafeString(filename);
  14103. JRubyFile lToDelete = JRubyFile.create(runtime.getCurrentDirectory(),filename.toString());
  14104. boolean isSymlink = RubyFileTest.symlink_p(recv, filename).isTrue();
  14105. // Broken symlinks considered by exists() as non-existing,
  14106. // so we need to check for symlinks explicitly.
  14107. if (!lToDelete.exists() && !isSymlink) {
  14108. throw runtime.newErrnoENOENTError(" No such file or directory - \"" + filename + "\"");
  14109. }
  14110. if (!lToDelete.delete()) {
  14111. throw runtime.newErrnoEACCESError("Permission denied - \"" + filename + "\"");
  14112. }
  14113. }
  14114. return runtime.newFixnum(args.length);
  14115. }
  14116. // Fast path since JNA stat is about 10x slower than this
  14117. private static IRubyObject getLastModified(Ruby runtime, String path) {
  14118. JRubyFile file = JRubyFile.create(runtime.getCurrentDirectory(), path);
  14119. if (!file.exists()) {
  14120. throw runtime.newErrnoENOENTError("No such file or directory - " + path);
  14121. }
  14122. return runtime.newTime(file.lastModified());
  14123. }
  14124. }
  14125. /*
  14126. ***** BEGIN LICENSE BLOCK *****
  14127. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  14128. *
  14129. * The contents of this file are subject to the Common Public
  14130. * License Version 1.0 (the "License"); you may not use this file
  14131. * except in compliance with the License. You may obtain a copy of
  14132. * the License at http://www.eclipse.org/legal/cpl-v10.html
  14133. *
  14134. * Software distributed under the License is distributed on an "AS
  14135. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  14136. * implied. See the License for the specific language governing
  14137. * rights and limitations under the License.
  14138. *
  14139. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  14140. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  14141. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  14142. * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
  14143. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  14144. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  14145. *
  14146. * Alternatively, the contents of this file may be used under the terms of
  14147. * either of the GNU General Public License Version 2 or later (the "GPL"),
  14148. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  14149. * in which case the provisions of the GPL or the LGPL are applicable instead
  14150. * of those above. If you wish to allow use of your version of this file only
  14151. * under the terms of either the GPL or the LGPL, and not to allow others to
  14152. * use your version of this file under the terms of the CPL, indicate your
  14153. * decision by deleting the provisions above and replace them with the notice
  14154. * and other provisions required by the GPL or the LGPL. If you do not delete
  14155. * the provisions above, a recipient may use your version of this file under
  14156. * the terms of any one of the CPL, the GPL or the LGPL.
  14157. ***** END LICENSE BLOCK *****/
  14158. package org.jruby;
  14159. import java.io.FileDescriptor;
  14160. import org.jruby.anno.JRubyMethod;
  14161. import org.jruby.anno.JRubyClass;
  14162. import org.jruby.ext.posix.FileStat;
  14163. import org.jruby.ext.posix.util.Platform;
  14164. import org.jruby.runtime.Block;
  14165. import org.jruby.runtime.ObjectAllocator;
  14166. import org.jruby.runtime.Visibility;
  14167. import org.jruby.runtime.builtin.IRubyObject;
  14168. import org.jruby.util.JRubyFile;
  14169. /**
  14170. * Implements File::Stat
  14171. */
  14172. @JRubyClass(name="File::Stat", include="Comparable")
  14173. public class RubyFileStat extends RubyObject {
  14174. private static final long serialVersionUID = 1L;
  14175. private JRubyFile file;
  14176. private FileStat stat;
  14177. private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
  14178. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  14179. return new RubyFileStat(runtime, klass);
  14180. }
  14181. };
  14182. public static RubyClass createFileStatClass(Ruby runtime) {
  14183. // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
  14184. final RubyClass fileStatClass = runtime.getFile().defineClassUnder("Stat",runtime.getObject(), ALLOCATOR);
  14185. runtime.setFileStat(fileStatClass);
  14186. fileStatClass.includeModule(runtime.fastGetModule("Comparable"));
  14187. fileStatClass.defineAnnotatedMethods(RubyFileStat.class);
  14188. return fileStatClass;
  14189. }
  14190. protected RubyFileStat(Ruby runtime, RubyClass clazz) {
  14191. super(runtime, clazz);
  14192. }
  14193. public static RubyFileStat newFileStat(Ruby runtime, String filename, boolean lstat) {
  14194. RubyFileStat stat = new RubyFileStat(runtime, runtime.getFileStat());
  14195. stat.setup(filename, lstat);
  14196. return stat;
  14197. }
  14198. public static RubyFileStat newFileStat(Ruby runtime, FileDescriptor descriptor) {
  14199. RubyFileStat stat = new RubyFileStat(runtime, runtime.getFileStat());
  14200. stat.setup(descriptor);
  14201. return stat;
  14202. }
  14203. private void setup(FileDescriptor descriptor) {
  14204. stat = getRuntime().getPosix().fstat(descriptor);
  14205. }
  14206. private void setup(String filename, boolean lstat) {
  14207. if (Platform.IS_WINDOWS && filename.length() == 2
  14208. && filename.charAt(1) == ':' && Character.isLetter(filename.charAt(0))) {
  14209. filename += "/";
  14210. }
  14211. file = JRubyFile.create(getRuntime().getCurrentDirectory(), filename);
  14212. if (lstat) {
  14213. stat = getRuntime().getPosix().lstat(file.getAbsolutePath());
  14214. } else {
  14215. stat = getRuntime().getPosix().stat(file.getAbsolutePath());
  14216. }
  14217. }
  14218. @JRubyMethod(name = "initialize", required = 1, visibility = Visibility.PRIVATE)
  14219. public IRubyObject initialize(IRubyObject fname, Block unusedBlock) {
  14220. setup(fname.convertToString().toString(), false);
  14221. return this;
  14222. }
  14223. @JRubyMethod(name = "atime")
  14224. public IRubyObject atime() {
  14225. return getRuntime().newTime(stat.atime() * 1000);
  14226. }
  14227. @JRubyMethod(name = "blksize")
  14228. public RubyFixnum blksize() {
  14229. return getRuntime().newFixnum(stat.blockSize());
  14230. }
  14231. @JRubyMethod(name = "blockdev?")
  14232. public IRubyObject blockdev_p() {
  14233. return getRuntime().newBoolean(stat.isBlockDev());
  14234. }
  14235. @JRubyMethod(name = "blocks")
  14236. public IRubyObject blocks() {
  14237. return getRuntime().newFixnum(stat.blocks());
  14238. }
  14239. @JRubyMethod(name = "chardev?")
  14240. public IRubyObject chardev_p() {
  14241. return getRuntime().newBoolean(stat.isCharDev());
  14242. }
  14243. @JRubyMethod(name = "<=>", required = 1)
  14244. public IRubyObject cmp(IRubyObject other) {
  14245. if (!(other instanceof RubyFileStat)) getRuntime().getNil();
  14246. long time1 = stat.mtime();
  14247. long time2 = ((RubyFileStat) other).stat.mtime();
  14248. if (time1 == time2) {
  14249. return getRuntime().newFixnum(0);
  14250. } else if (time1 < time2) {
  14251. return getRuntime().newFixnum(-1);
  14252. }
  14253. return getRuntime().newFixnum(1);
  14254. }
  14255. @JRubyMethod(name = "ctime")
  14256. public IRubyObject ctime() {
  14257. return getRuntime().newTime(stat.ctime() * 1000);
  14258. }
  14259. @JRubyMethod(name = "dev")
  14260. public IRubyObject dev() {
  14261. return getRuntime().newFixnum(stat.dev());
  14262. }
  14263. @JRubyMethod(name = "dev_major")
  14264. public IRubyObject devMajor() {
  14265. return getRuntime().newFixnum(stat.major(stat.dev()));
  14266. }
  14267. @JRubyMethod(name = "dev_minor")
  14268. public IRubyObject devMinor() {
  14269. return getRuntime().newFixnum(stat.minor(stat.dev()));
  14270. }
  14271. @JRubyMethod(name = "directory?")
  14272. public RubyBoolean directory_p() {
  14273. return getRuntime().newBoolean(stat.isDirectory());
  14274. }
  14275. @JRubyMethod(name = "executable?")
  14276. public IRubyObject executable_p() {
  14277. return getRuntime().newBoolean(stat.isExecutable());
  14278. }
  14279. @JRubyMethod(name = "executable_real?")
  14280. public IRubyObject executableReal_p() {
  14281. return getRuntime().newBoolean(stat.isExecutableReal());
  14282. }
  14283. @JRubyMethod(name = "file?")
  14284. public RubyBoolean file_p() {
  14285. return getRuntime().newBoolean(stat.isFile());
  14286. }
  14287. @JRubyMethod(name = "ftype")
  14288. public RubyString ftype() {
  14289. return getRuntime().newString(stat.ftype());
  14290. }
  14291. @JRubyMethod(name = "gid")
  14292. public IRubyObject gid() {
  14293. return getRuntime().newFixnum(stat.gid());
  14294. }
  14295. @JRubyMethod(name = "grpowned?")
  14296. public IRubyObject group_owned_p() {
  14297. return getRuntime().newBoolean(stat.isGroupOwned());
  14298. }
  14299. @JRubyMethod(name = "initialize_copy", required = 1)
  14300. public IRubyObject initialize_copy(IRubyObject original) {
  14301. if (!(original instanceof RubyFileStat)) {
  14302. throw getRuntime().newTypeError("wrong argument class");
  14303. }
  14304. RubyFileStat originalFileStat = (RubyFileStat) original;
  14305. file = originalFileStat.file;
  14306. stat = originalFileStat.stat;
  14307. return this;
  14308. }
  14309. @JRubyMethod(name = "ino")
  14310. public IRubyObject ino() {
  14311. return getRuntime().newFixnum(stat.ino());
  14312. }
  14313. @JRubyMethod(name = "inspect")
  14314. public IRubyObject inspect() {
  14315. StringBuilder buf = new StringBuilder("#<");
  14316. buf.append(getMetaClass().getRealClass().getName());
  14317. buf.append(" ");
  14318. // FIXME: Obvious issue that not all platforms can display all attributes. Ugly hacks.
  14319. // Using generic posix library makes pushing inspect behavior into specific system impls
  14320. // rather painful.
  14321. try { buf.append("dev=0x").append(Long.toHexString(stat.dev())).append(", "); } catch (Exception e) {}
  14322. try { buf.append("ino=").append(stat.ino()).append(", "); } catch (Exception e) {}
  14323. buf.append("mode=0").append(Integer.toOctalString(stat.mode())).append(", ");
  14324. try { buf.append("nlink=").append(stat.nlink()).append(", "); } catch (Exception e) {}
  14325. try { buf.append("uid=").append(stat.uid()).append(", "); } catch (Exception e) {}
  14326. try { buf.append("gid=").append(stat.gid()).append(", "); } catch (Exception e) {}
  14327. try { buf.append("rdev=0x").append(Long.toHexString(stat.rdev())).append(", "); } catch (Exception e) {}
  14328. buf.append("size=").append(stat.st_size()).append(", ");
  14329. try { buf.append("blksize=").append(stat.blockSize()).append(", "); } catch (Exception e) {}
  14330. try { buf.append("blocks=").append(stat.blocks()).append(", "); } catch (Exception e) {}
  14331. buf.append("atime=").append(atime()).append(", ");
  14332. buf.append("mtime=").append(mtime()).append(", ");
  14333. buf.append("ctime=").append(ctime());
  14334. buf.append(">");
  14335. return getRuntime().newString(buf.toString());
  14336. }
  14337. @JRubyMethod(name = "uid")
  14338. public IRubyObject uid() {
  14339. return getRuntime().newFixnum(stat.uid());
  14340. }
  14341. @JRubyMethod(name = "mode")
  14342. public IRubyObject mode() {
  14343. return getRuntime().newFixnum(stat.mode());
  14344. }
  14345. @JRubyMethod(name = "mtime")
  14346. public IRubyObject mtime() {
  14347. return getRuntime().newTime(stat.mtime() * 1000);
  14348. }
  14349. public IRubyObject mtimeEquals(IRubyObject other) {
  14350. return getRuntime().newBoolean(stat.mtime() == newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
  14351. }
  14352. public IRubyObject mtimeGreaterThan(IRubyObject other) {
  14353. return getRuntime().newBoolean(stat.mtime() > newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
  14354. }
  14355. public IRubyObject mtimeLessThan(IRubyObject other) {
  14356. return getRuntime().newBoolean(stat.mtime() < newFileStat(getRuntime(), other.convertToString().toString(), false).stat.mtime());
  14357. }
  14358. @JRubyMethod(name = "nlink")
  14359. public IRubyObject nlink() {
  14360. return getRuntime().newFixnum(stat.nlink());
  14361. }
  14362. @JRubyMethod(name = "owned?")
  14363. public IRubyObject owned_p() {
  14364. return getRuntime().newBoolean(stat.isOwned());
  14365. }
  14366. @JRubyMethod(name = "pipe?")
  14367. public IRubyObject pipe_p() {
  14368. return getRuntime().newBoolean(stat.isNamedPipe());
  14369. }
  14370. @JRubyMethod(name = "rdev")
  14371. public IRubyObject rdev() {
  14372. return getRuntime().newFixnum(stat.rdev());
  14373. }
  14374. @JRubyMethod(name = "rdev_major")
  14375. public IRubyObject rdevMajor() {
  14376. return getRuntime().newFixnum(stat.major(stat.rdev()));
  14377. }
  14378. @JRubyMethod(name = "rdev_minor")
  14379. public IRubyObject rdevMinor() {
  14380. return getRuntime().newFixnum(stat.minor(stat.rdev()));
  14381. }
  14382. @JRubyMethod(name = "readable?")
  14383. public IRubyObject readable_p() {
  14384. return getRuntime().newBoolean(stat.isReadable());
  14385. }
  14386. @JRubyMethod(name = "readable_real?")
  14387. public IRubyObject readableReal_p() {
  14388. return getRuntime().newBoolean(stat.isReadableReal());
  14389. }
  14390. @JRubyMethod(name = "setgid?")
  14391. public IRubyObject setgid_p() {
  14392. return getRuntime().newBoolean(stat.isSetgid());
  14393. }
  14394. @JRubyMethod(name = "setuid?")
  14395. public IRubyObject setuid_p() {
  14396. return getRuntime().newBoolean(stat.isSetuid());
  14397. }
  14398. @JRubyMethod(name = "size")
  14399. public IRubyObject size() {
  14400. return getRuntime().newFixnum(stat.st_size());
  14401. }
  14402. @JRubyMethod(name = "size?")
  14403. public IRubyObject size_p() {
  14404. long size = stat.st_size();
  14405. if (size == 0) return getRuntime().getNil();
  14406. return getRuntime().newFixnum(size);
  14407. }
  14408. @JRubyMethod(name = "socket?")
  14409. public IRubyObject socket_p() {
  14410. return getRuntime().newBoolean(stat.isSocket());
  14411. }
  14412. @JRubyMethod(name = "sticky?")
  14413. public IRubyObject sticky_p() {
  14414. return getRuntime().newBoolean(stat.isSticky());
  14415. }
  14416. @JRubyMethod(name = "symlink?")
  14417. public IRubyObject symlink_p() {
  14418. return getRuntime().newBoolean(stat.isSymlink());
  14419. }
  14420. @JRubyMethod(name = "writable?")
  14421. public IRubyObject writable_p() {
  14422. return getRuntime().newBoolean(stat.isWritable());
  14423. }
  14424. @JRubyMethod(name = "writable_real?")
  14425. public IRubyObject writableReal_p() {
  14426. return getRuntime().newBoolean(stat.isWritableReal());
  14427. }
  14428. @JRubyMethod(name = "zero?")
  14429. public IRubyObject zero_p() {
  14430. return getRuntime().newBoolean(stat.isEmpty());
  14431. }
  14432. }
  14433. /***** BEGIN LICENSE BLOCK *****
  14434. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  14435. *
  14436. * The contents of this file are subject to the Common Public
  14437. * License Version 1.0 (the "License"); you may not use this file
  14438. * except in compliance with the License. You may obtain a copy of
  14439. * the License at http://www.eclipse.org/legal/cpl-v10.html
  14440. *
  14441. * Software distributed under the License is distributed on an "AS
  14442. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  14443. * implied. See the License for the specific language governing
  14444. * rights and limitations under the License.
  14445. *
  14446. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  14447. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  14448. * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  14449. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  14450. *
  14451. * Alternatively, the contents of this file may be used under the terms of
  14452. * either of the GNU General Public License Version 2 or later (the "GPL"),
  14453. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  14454. * in which case the provisions of the GPL or the LGPL are applicable instead
  14455. * of those above. If you wish to allow use of your version of this file only
  14456. * under the terms of either the GPL or the LGPL, and not to allow others to
  14457. * use your version of this file under the terms of the CPL, indicate your
  14458. * decision by deleting the provisions above and replace them with the notice
  14459. * and other provisions required by the GPL or the LGPL. If you do not delete
  14460. * the provisions above, a recipient may use your version of this file under
  14461. * the terms of any one of the CPL, the GPL or the LGPL.
  14462. ***** END LICENSE BLOCK *****/
  14463. package org.jruby;
  14464. import org.jruby.anno.JRubyMethod;
  14465. import org.jruby.anno.JRubyModule;
  14466. import org.jruby.exceptions.RaiseException;
  14467. import org.jruby.runtime.builtin.IRubyObject;
  14468. import org.jruby.util.JRubyFile;
  14469. @JRubyModule(name="FileTest")
  14470. public class RubyFileTest {
  14471. public static RubyModule createFileTestModule(Ruby runtime) {
  14472. RubyModule fileTestModule = runtime.defineModule("FileTest");
  14473. runtime.setFileTest(fileTestModule);
  14474. fileTestModule.defineAnnotatedMethods(RubyFileTest.class);
  14475. return fileTestModule;
  14476. }
  14477. @JRubyMethod(name = "blockdev?", required = 1, module = true)
  14478. public static IRubyObject blockdev_p(IRubyObject recv, IRubyObject filename) {
  14479. Ruby runtime = recv.getRuntime();
  14480. JRubyFile file = file(filename);
  14481. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isBlockDev());
  14482. }
  14483. @JRubyMethod(name = "chardev?", required = 1, module = true)
  14484. public static IRubyObject chardev_p(IRubyObject recv, IRubyObject filename) {
  14485. Ruby runtime = recv.getRuntime();
  14486. JRubyFile file = file(filename);
  14487. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isCharDev());
  14488. }
  14489. @JRubyMethod(name = "directory?", required = 1, module = true)
  14490. public static IRubyObject directory_p(IRubyObject recv, IRubyObject filename) {
  14491. Ruby runtime = recv.getRuntime();
  14492. JRubyFile file = file(filename);
  14493. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isDirectory());
  14494. }
  14495. @JRubyMethod(name = "executable?", required = 1, module = true)
  14496. public static IRubyObject executable_p(IRubyObject recv, IRubyObject filename) {
  14497. Ruby runtime = recv.getRuntime();
  14498. JRubyFile file = file(filename);
  14499. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isExecutable());
  14500. }
  14501. @JRubyMethod(name = "executable_real?", required = 1, module = true)
  14502. public static IRubyObject executable_real_p(IRubyObject recv, IRubyObject filename) {
  14503. Ruby runtime = recv.getRuntime();
  14504. JRubyFile file = file(filename);
  14505. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isExecutableReal());
  14506. }
  14507. @JRubyMethod(name = {"exist?", "exists?"}, required = 1, module = true)
  14508. public static IRubyObject exist_p(IRubyObject recv, IRubyObject filename) {
  14509. if (Ruby.isSecurityRestricted()) {
  14510. return recv.getRuntime().getFalse();
  14511. }
  14512. if(filename.convertToString().toString().startsWith("file:")) {
  14513. String file = filename.convertToString().toString().substring(5);
  14514. int bang = file.indexOf('!');
  14515. if (bang == -1 || bang == file.length() - 1) {
  14516. return recv.getRuntime().getFalse();
  14517. }
  14518. String jar = file.substring(0, bang);
  14519. String after = file.substring(bang + 2);
  14520. try {
  14521. java.util.jar.JarFile jf = new java.util.jar.JarFile(jar);
  14522. if(jf.getJarEntry(after) != null) {
  14523. return recv.getRuntime().getTrue();
  14524. } else {
  14525. return recv.getRuntime().getFalse();
  14526. }
  14527. } catch(Exception e) {
  14528. return recv.getRuntime().getFalse();
  14529. }
  14530. }
  14531. return recv.getRuntime().newBoolean(file(filename).exists());
  14532. }
  14533. @JRubyMethod(name = "file?", required = 1, module = true)
  14534. public static RubyBoolean file_p(IRubyObject recv, IRubyObject filename) {
  14535. JRubyFile file = file(filename);
  14536. return filename.getRuntime().newBoolean(file.exists() && file.isFile());
  14537. }
  14538. @JRubyMethod(name = "grpowned?", required = 1, module = true)
  14539. public static IRubyObject grpowned_p(IRubyObject recv, IRubyObject filename) {
  14540. Ruby runtime = recv.getRuntime();
  14541. JRubyFile file = file(filename);
  14542. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isGroupOwned());
  14543. }
  14544. @JRubyMethod(name = "identical?", required = 2, module = true)
  14545. public static IRubyObject identical_p(IRubyObject recv, IRubyObject filename1, IRubyObject filename2) {
  14546. Ruby runtime = recv.getRuntime();
  14547. JRubyFile file1 = file(filename1);
  14548. JRubyFile file2 = file(filename2);
  14549. return runtime.newBoolean(file1.exists() && file2.exists() &&
  14550. runtime.getPosix().stat(file1.getAbsolutePath()).isIdentical(runtime.getPosix().stat(file2.getAbsolutePath())));
  14551. }
  14552. @JRubyMethod(name = "owned?", required = 1, module = true)
  14553. public static IRubyObject owned_p(IRubyObject recv, IRubyObject filename) {
  14554. Ruby runtime = recv.getRuntime();
  14555. JRubyFile file = file(filename);
  14556. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isOwned());
  14557. }
  14558. @JRubyMethod(name = "pipe?", required = 1, module = true)
  14559. public static IRubyObject pipe_p(IRubyObject recv, IRubyObject filename) {
  14560. Ruby runtime = recv.getRuntime();
  14561. JRubyFile file = file(filename);
  14562. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isNamedPipe());
  14563. }
  14564. // We use file test since it is faster than a stat; also euid == uid in Java always
  14565. @JRubyMethod(name = {"readable?", "readable_real?"}, required = 1, module = true)
  14566. public static IRubyObject readable_p(IRubyObject recv, IRubyObject filename) {
  14567. JRubyFile file = file(filename);
  14568. return recv.getRuntime().newBoolean(file.exists() && file.canRead());
  14569. }
  14570. // Not exposed by filetest, but so similiar in nature that it is stored here
  14571. public static IRubyObject rowned_p(IRubyObject recv, IRubyObject filename) {
  14572. Ruby runtime = recv.getRuntime();
  14573. JRubyFile file = file(filename);
  14574. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isROwned());
  14575. }
  14576. @JRubyMethod(name = "setgid?", required = 1, module = true)
  14577. public static IRubyObject setgid_p(IRubyObject recv, IRubyObject filename) {
  14578. Ruby runtime = recv.getRuntime();
  14579. JRubyFile file = file(filename);
  14580. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSetgid());
  14581. }
  14582. @JRubyMethod(name = "setuid?", required = 1, module = true)
  14583. public static IRubyObject setuid_p(IRubyObject recv, IRubyObject filename) {
  14584. Ruby runtime = recv.getRuntime();
  14585. JRubyFile file = file(filename);
  14586. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSetuid());
  14587. }
  14588. @JRubyMethod(name = "size", required = 1, module = true)
  14589. public static IRubyObject size(IRubyObject recv, IRubyObject filename) {
  14590. JRubyFile file = file(filename);
  14591. if (!file.exists()) noFileError(filename);
  14592. return recv.getRuntime().newFixnum(file.length());
  14593. }
  14594. @JRubyMethod(name = "size?", required = 1, module = true)
  14595. public static IRubyObject size_p(IRubyObject recv, IRubyObject filename) {
  14596. JRubyFile file = file(filename);
  14597. if (!file.exists()) {
  14598. return recv.getRuntime().getNil();
  14599. }
  14600. long length = file.length();
  14601. if (length > 0) {
  14602. return recv.getRuntime().newFixnum(length);
  14603. } else {
  14604. return recv.getRuntime().getNil();
  14605. }
  14606. }
  14607. @JRubyMethod(name = "socket?", required = 1, module = true)
  14608. public static IRubyObject socket_p(IRubyObject recv, IRubyObject filename) {
  14609. Ruby runtime = recv.getRuntime();
  14610. JRubyFile file = file(filename);
  14611. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSocket());
  14612. }
  14613. @JRubyMethod(name = "sticky?", required = 1, module = true)
  14614. public static IRubyObject sticky_p(IRubyObject recv, IRubyObject filename) {
  14615. Ruby runtime = recv.getRuntime();
  14616. JRubyFile file = file(filename);
  14617. return runtime.newBoolean(file.exists() && runtime.getPosix().stat(file.getAbsolutePath()).isSticky());
  14618. }
  14619. @JRubyMethod(name = "symlink?", required = 1, module = true)
  14620. public static RubyBoolean symlink_p(IRubyObject recv, IRubyObject filename) {
  14621. Ruby runtime = recv.getRuntime();
  14622. JRubyFile file = file(filename);
  14623. try {
  14624. // Note: We can't use file.exists() to check whether the symlink
  14625. // exists or not, because that method returns false for existing
  14626. // but broken symlink. So, we try without the existence check,
  14627. // but in the try-catch block.
  14628. // MRI behavior: symlink? on broken symlink should return true.
  14629. return runtime.newBoolean(runtime.getPosix().lstat(file.getAbsolutePath()).isSymlink());
  14630. } catch (RaiseException re) {
  14631. return runtime.getFalse();
  14632. }
  14633. }
  14634. // We do both writable and writable_real through the same method because
  14635. // in our java process effective and real userid will always be the same.
  14636. @JRubyMethod(name = {"writable?", "writable_real?"}, required = 1, module = true)
  14637. public static RubyBoolean writable_p(IRubyObject recv, IRubyObject filename) {
  14638. return filename.getRuntime().newBoolean(file(filename).canWrite());
  14639. }
  14640. @JRubyMethod(name = "zero?", required = 1, module = true)
  14641. public static RubyBoolean zero_p(IRubyObject recv, IRubyObject filename) {
  14642. JRubyFile file = file(filename);
  14643. return filename.getRuntime().newBoolean(file.exists() && file.length() == 0L);
  14644. }
  14645. private static JRubyFile file(IRubyObject path) {
  14646. String filename = path.convertToString().toString();
  14647. return JRubyFile.create(path.getRuntime().getCurrentDirectory(), filename);
  14648. }
  14649. private static void noFileError(IRubyObject filename) {
  14650. throw filename.getRuntime().newErrnoENOENTError("No such file or directory - " +
  14651. filename.convertToString());
  14652. }
  14653. }
  14654. /*
  14655. ***** BEGIN LICENSE BLOCK *****
  14656. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  14657. *
  14658. * The contents of this file are subject to the Common Public
  14659. * License Version 1.0 (the "License"); you may not use this file
  14660. * except in compliance with the License. You may obtain a copy of
  14661. * the License at http://www.eclipse.org/legal/cpl-v10.html
  14662. *
  14663. * Software distributed under the License is distributed on an "AS
  14664. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  14665. * implied. See the License for the specific language governing
  14666. * rights and limitations under the License.
  14667. *
  14668. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  14669. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  14670. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  14671. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  14672. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  14673. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  14674. * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
  14675. * Copyright (C) 2006 Antti Karanta <antti.karanta@napa.fi>
  14676. * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
  14677. *
  14678. * Alternatively, the contents of this file may be used under the terms of
  14679. * either of the GNU General Public License Version 2 or later (the "GPL"),
  14680. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  14681. * in which case the provisions of the GPL or the LGPL are applicable instead
  14682. * of those above. If you wish to allow use of your version of this file only
  14683. * under the terms of either the GPL or the LGPL, and not to allow others to
  14684. * use your version of this file under the terms of the CPL, indicate your
  14685. * decision by deleting the provisions above and replace them with the notice
  14686. * and other provisions required by the GPL or the LGPL. If you do not delete
  14687. * the provisions above, a recipient may use your version of this file under
  14688. * the terms of any one of the CPL, the GPL or the LGPL.
  14689. ***** END LICENSE BLOCK *****/
  14690. package org.jruby;
  14691. import java.math.BigInteger;
  14692. import java.util.HashMap;
  14693. import java.util.Map;
  14694. import org.jruby.anno.JRubyClass;
  14695. import org.jruby.anno.JRubyMethod;
  14696. import org.jruby.common.IRubyWarnings.ID;
  14697. import org.jruby.java.MiniJava;
  14698. import org.jruby.runtime.ClassIndex;
  14699. import org.jruby.runtime.ObjectAllocator;
  14700. import org.jruby.runtime.ThreadContext;
  14701. import org.jruby.runtime.builtin.IRubyObject;
  14702. import org.jruby.runtime.marshal.UnmarshalStream;
  14703. import org.jruby.util.Convert;
  14704. import org.jruby.util.Numeric;
  14705. import org.jruby.util.TypeCoercer;
  14706. /**
  14707. * Implementation of the Fixnum class.
  14708. */
  14709. @JRubyClass(name="Fixnum", parent="Integer", include="Precision")
  14710. public class RubyFixnum extends RubyInteger {
  14711. public static RubyClass createFixnumClass(Ruby runtime) {
  14712. RubyClass fixnum = runtime.defineClass("Fixnum", runtime.getInteger(),
  14713. ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  14714. runtime.setFixnum(fixnum);
  14715. fixnum.index = ClassIndex.FIXNUM;
  14716. fixnum.kindOf = new RubyModule.KindOf() {
  14717. @Override
  14718. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  14719. return obj instanceof RubyFixnum;
  14720. }
  14721. };
  14722. fixnum.includeModule(runtime.getPrecision());
  14723. fixnum.defineAnnotatedMethods(RubyFixnum.class);
  14724. for (int i = 0; i < runtime.fixnumCache.length; i++) {
  14725. runtime.fixnumCache[i] = new RubyFixnum(runtime, fixnum, i - 128);
  14726. }
  14727. return fixnum;
  14728. }
  14729. private final long value;
  14730. private static final int BIT_SIZE = 64;
  14731. public static final long SIGN_BIT = (1L << (BIT_SIZE - 1));
  14732. public static final long MAX = (1L<<(BIT_SIZE - 1)) - 1;
  14733. public static final long MIN = -1 * MAX - 1;
  14734. public static final long MAX_MARSHAL_FIXNUM = (1L << 30) - 1; // 0x3fff_ffff
  14735. public static final long MIN_MARSHAL_FIXNUM = - (1L << 30); // -0x4000_0000
  14736. private static IRubyObject fixCoerce(IRubyObject x) {
  14737. do {
  14738. x = x.convertToInteger();
  14739. } while (!(x instanceof RubyFixnum) && !(x instanceof RubyBignum));
  14740. return x;
  14741. }
  14742. public RubyFixnum(Ruby runtime) {
  14743. this(runtime, 0);
  14744. }
  14745. public RubyFixnum(Ruby runtime, long value) {
  14746. super(runtime, runtime.getFixnum(), false);
  14747. this.value = value;
  14748. }
  14749. private RubyFixnum(Ruby runtime, RubyClass klazz, long value) {
  14750. super(runtime, klazz, false);
  14751. this.value = value;
  14752. }
  14753. @Override
  14754. public int getNativeTypeIndex() {
  14755. return ClassIndex.FIXNUM;
  14756. }
  14757. /**
  14758. * short circuit for Fixnum key comparison
  14759. */
  14760. @Override
  14761. public final boolean eql(IRubyObject other) {
  14762. return other instanceof RubyFixnum && value == ((RubyFixnum)other).value;
  14763. }
  14764. @Override
  14765. public boolean isImmediate() {
  14766. return true;
  14767. }
  14768. @Override
  14769. public RubyClass getSingletonClass() {
  14770. throw getRuntime().newTypeError("can't define singleton");
  14771. }
  14772. @Override
  14773. public Class<?> getJavaClass() {
  14774. // this precision-guessing needs to be thought out more, since in the
  14775. // case of coercing to Object we generally want to get the same type
  14776. // always
  14777. // if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
  14778. // return byte.class;
  14779. // } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
  14780. // return short.class;
  14781. // } else if (value >= Character.MIN_VALUE && value <= Character.MAX_VALUE) {
  14782. // return char.class;
  14783. // } else if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
  14784. // return int.class;
  14785. // }
  14786. return long.class;
  14787. }
  14788. @Override
  14789. public double getDoubleValue() {
  14790. return value;
  14791. }
  14792. @Override
  14793. public long getLongValue() {
  14794. return value;
  14795. }
  14796. private static final int CACHE_OFFSET = 128;
  14797. public static RubyFixnum newFixnum(Ruby runtime, long value) {
  14798. if (isInCacheRange(value)) {
  14799. return runtime.fixnumCache[(int) value + CACHE_OFFSET];
  14800. }
  14801. return new RubyFixnum(runtime, value);
  14802. }
  14803. private static boolean isInCacheRange(long value) {
  14804. return value <= 127 && value >= -128;
  14805. }
  14806. public RubyFixnum newFixnum(long newValue) {
  14807. return newFixnum(getRuntime(), newValue);
  14808. }
  14809. public static RubyFixnum zero(Ruby runtime) {
  14810. return runtime.fixnumCache[CACHE_OFFSET];
  14811. }
  14812. public static RubyFixnum one(Ruby runtime) {
  14813. return runtime.fixnumCache[CACHE_OFFSET + 1];
  14814. }
  14815. public static RubyFixnum two(Ruby runtime) {
  14816. return runtime.fixnumCache[CACHE_OFFSET + 2];
  14817. }
  14818. public static RubyFixnum three(Ruby runtime) {
  14819. return runtime.fixnumCache[CACHE_OFFSET + 3];
  14820. }
  14821. public static RubyFixnum four(Ruby runtime) {
  14822. return runtime.fixnumCache[CACHE_OFFSET + 4];
  14823. }
  14824. public static RubyFixnum five(Ruby runtime) {
  14825. return runtime.fixnumCache[CACHE_OFFSET + 5];
  14826. }
  14827. public static RubyFixnum minus_one(Ruby runtime) {
  14828. return runtime.fixnumCache[CACHE_OFFSET - 1];
  14829. }
  14830. @Override
  14831. public RubyFixnum hash() {
  14832. return newFixnum(hashCode());
  14833. }
  14834. @Override
  14835. public final int hashCode() {
  14836. return (int)(value ^ value >>> 32);
  14837. }
  14838. @Override
  14839. public boolean equals(Object other) {
  14840. if (other == this) {
  14841. return true;
  14842. }
  14843. if (other instanceof RubyFixnum) {
  14844. RubyFixnum num = (RubyFixnum)other;
  14845. if (num.value == value) {
  14846. return true;
  14847. }
  14848. }
  14849. return false;
  14850. }
  14851. /* ================
  14852. * Instance Methods
  14853. * ================
  14854. */
  14855. /** fix_to_s
  14856. *
  14857. */
  14858. public RubyString to_s(IRubyObject[] args) {
  14859. switch (args.length) {
  14860. case 0: return to_s();
  14861. case 1: return to_s(args[0]);
  14862. default: throw getRuntime().newArgumentError(args.length, 1);
  14863. }
  14864. }
  14865. @JRubyMethod
  14866. @Override
  14867. public RubyString to_s() {
  14868. int base = 10;
  14869. return getRuntime().newString(Convert.longToByteList(value, base));
  14870. }
  14871. @JRubyMethod
  14872. public RubyString to_s(IRubyObject arg0) {
  14873. int base = num2int(arg0);
  14874. if (base < 2 || base > 36) {
  14875. throw getRuntime().newArgumentError("illegal radix " + base);
  14876. }
  14877. return getRuntime().newString(Convert.longToByteList(value, base));
  14878. }
  14879. /** fix_id2name
  14880. *
  14881. */
  14882. @JRubyMethod
  14883. public IRubyObject id2name() {
  14884. RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
  14885. if (symbol != null) return getRuntime().newString(symbol.asJavaString());
  14886. return getRuntime().getNil();
  14887. }
  14888. /** fix_to_sym
  14889. *
  14890. */
  14891. @JRubyMethod
  14892. public IRubyObject to_sym() {
  14893. RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
  14894. return symbol != null ? symbol : getRuntime().getNil();
  14895. }
  14896. /** fix_uminus
  14897. *
  14898. */
  14899. @JRubyMethod(name = "-@")
  14900. public IRubyObject op_uminus() {
  14901. if (value == MIN) { // a gotcha
  14902. return RubyBignum.newBignum(getRuntime(), BigInteger.valueOf(value).negate());
  14903. }
  14904. return RubyFixnum.newFixnum(getRuntime(), -value);
  14905. }
  14906. /** fix_plus
  14907. *
  14908. */
  14909. @JRubyMethod(name = "+")
  14910. public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
  14911. if (other instanceof RubyFixnum) {
  14912. return addFixnum(context, (RubyFixnum)other);
  14913. }
  14914. return addOther(context, other);
  14915. }
  14916. private IRubyObject addFixnum(ThreadContext context, RubyFixnum other) {
  14917. long otherValue = other.value;
  14918. long result = value + otherValue;
  14919. if (additionOverflowed(value, otherValue, result)) {
  14920. return addAsBignum(context, other);
  14921. }
  14922. return newFixnum(context.getRuntime(), result);
  14923. }
  14924. private static boolean additionOverflowed(long original, long other, long result) {
  14925. return (~(original ^ other) & (original ^ result) & SIGN_BIT) != 0;
  14926. }
  14927. private static boolean subtractionOverflowed(long original, long other, long result) {
  14928. return (~(original ^ ~other) & (original ^ result) & SIGN_BIT) != 0;
  14929. }
  14930. private IRubyObject addAsBignum(ThreadContext context, RubyFixnum other) {
  14931. return RubyBignum.newBignum(context.getRuntime(), value).op_plus(context, other);
  14932. }
  14933. private IRubyObject addOther(ThreadContext context, IRubyObject other) {
  14934. if (other instanceof RubyBignum) {
  14935. return ((RubyBignum) other).op_plus(context, this);
  14936. }
  14937. if (other instanceof RubyFloat) {
  14938. return context.getRuntime().newFloat((double) value + ((RubyFloat) other).getDoubleValue());
  14939. }
  14940. return coerceBin(context, "+", other);
  14941. }
  14942. /** fix_minus
  14943. *
  14944. */
  14945. @JRubyMethod(name = "-")
  14946. public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
  14947. if (other instanceof RubyFixnum) {
  14948. return subtractFixnum(context, (RubyFixnum)other);
  14949. }
  14950. return subtractOther(context, other);
  14951. }
  14952. private IRubyObject subtractFixnum(ThreadContext context, RubyFixnum other) {
  14953. long otherValue = other.value;
  14954. long result = value - otherValue;
  14955. if (subtractionOverflowed(value, otherValue, result)) {
  14956. return subtractAsBignum(context, other);
  14957. }
  14958. return newFixnum(context.getRuntime(), result);
  14959. }
  14960. private IRubyObject subtractAsBignum(ThreadContext context, RubyFixnum other) {
  14961. return RubyBignum.newBignum(context.getRuntime(), value).op_minus(context, other);
  14962. }
  14963. private IRubyObject subtractOther(ThreadContext context, IRubyObject other) {
  14964. if (other instanceof RubyBignum) {
  14965. return RubyBignum.newBignum(context.getRuntime(), value).op_minus(context, other);
  14966. } else if (other instanceof RubyFloat) {
  14967. return context.getRuntime().newFloat((double) value - ((RubyFloat) other).getDoubleValue());
  14968. }
  14969. return coerceBin(context, "-", other);
  14970. }
  14971. /** fix_mul
  14972. *
  14973. */
  14974. @JRubyMethod(name = "*")
  14975. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  14976. Ruby runtime = context.getRuntime();
  14977. if (other instanceof RubyFixnum) {
  14978. long otherValue = ((RubyFixnum) other).value;
  14979. if (value == 0) {
  14980. return RubyFixnum.zero(runtime);
  14981. }
  14982. long result = value * otherValue;
  14983. if (result / value != otherValue) {
  14984. return RubyBignum.newBignum(runtime, value).op_mul(context, other);
  14985. }
  14986. return newFixnum(runtime, result);
  14987. } else if (other instanceof RubyBignum) {
  14988. return ((RubyBignum) other).op_mul(context, this);
  14989. } else if (other instanceof RubyFloat) {
  14990. return runtime.newFloat((double) value * ((RubyFloat) other).getDoubleValue());
  14991. }
  14992. return coerceBin(context, "*", other);
  14993. }
  14994. /** fix_div
  14995. * here is terrible MRI gotcha:
  14996. * 1.div 3.0 -> 0
  14997. * 1 / 3.0 -> 0.3333333333333333
  14998. *
  14999. * MRI is also able to do it in one place by looking at current frame in rb_num_coerce_bin:
  15000. * rb_funcall(x, ruby_frame->orig_func, 1, y);
  15001. *
  15002. * also note that RubyFloat doesn't override Numeric.div
  15003. */
  15004. @JRubyMethod(name = "div")
  15005. public IRubyObject div_div(ThreadContext context, IRubyObject other) {
  15006. return idiv(context, other, "div");
  15007. }
  15008. @JRubyMethod(name = "/")
  15009. public IRubyObject op_div(ThreadContext context, IRubyObject other) {
  15010. return idiv(context, other, "/");
  15011. }
  15012. @JRubyMethod(name = {"odd?"})
  15013. public RubyBoolean odd_p() {
  15014. if(value%2 != 0) {
  15015. return getRuntime().getTrue();
  15016. }
  15017. return getRuntime().getFalse();
  15018. }
  15019. @JRubyMethod(name = {"even?"})
  15020. public RubyBoolean even_p() {
  15021. if(value%2 == 0) {
  15022. return getRuntime().getTrue();
  15023. }
  15024. return getRuntime().getFalse();
  15025. }
  15026. @JRubyMethod
  15027. public IRubyObject pred() {
  15028. return getRuntime().newFixnum(value-1);
  15029. }
  15030. public IRubyObject idiv(ThreadContext context, IRubyObject other, String method) {
  15031. if (other instanceof RubyFixnum) {
  15032. long x = value;
  15033. long y = ((RubyFixnum) other).value;
  15034. if (y == 0) {
  15035. throw context.getRuntime().newZeroDivisionError();
  15036. }
  15037. long div = x / y;
  15038. long mod = x % y;
  15039. if (mod < 0 && y > 0 || mod > 0 && y < 0) {
  15040. div -= 1;
  15041. }
  15042. return context.getRuntime().newFixnum(div);
  15043. }
  15044. return coerceBin(context, method, other);
  15045. }
  15046. /** fix_mod
  15047. *
  15048. */
  15049. @JRubyMethod(name = {"%", "modulo"})
  15050. public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
  15051. if (other instanceof RubyFixnum) {
  15052. // Java / and % are not the same as ruby
  15053. long x = value;
  15054. long y = ((RubyFixnum) other).value;
  15055. if (y == 0) {
  15056. throw context.getRuntime().newZeroDivisionError();
  15057. }
  15058. long mod = x % y;
  15059. if (mod < 0 && y > 0 || mod > 0 && y < 0) {
  15060. mod += y;
  15061. }
  15062. return context.getRuntime().newFixnum(mod);
  15063. }
  15064. return coerceBin(context, "%", other);
  15065. }
  15066. /** fix_divmod
  15067. *
  15068. */
  15069. @JRubyMethod
  15070. @Override
  15071. public IRubyObject divmod(ThreadContext context, IRubyObject other) {
  15072. if (other instanceof RubyFixnum) {
  15073. long x = value;
  15074. long y = ((RubyFixnum) other).value;
  15075. final Ruby runtime = context.getRuntime();
  15076. if (y == 0) {
  15077. throw runtime.newZeroDivisionError();
  15078. }
  15079. long div = x / y;
  15080. long mod = x % y;
  15081. if (mod < 0 && y > 0 || mod > 0 && y < 0) {
  15082. div -= 1;
  15083. mod += y;
  15084. }
  15085. IRubyObject fixDiv = RubyFixnum.newFixnum(runtime, div);
  15086. IRubyObject fixMod = RubyFixnum.newFixnum(runtime, mod);
  15087. return RubyArray.newArray(runtime, fixDiv, fixMod);
  15088. }
  15089. return coerceBin(context, "divmod", other);
  15090. }
  15091. /** fix_quo
  15092. *
  15093. */
  15094. @JRubyMethod(name = "quo", compat = CompatVersion.RUBY1_8)
  15095. public IRubyObject quo(ThreadContext context, IRubyObject other) {
  15096. if (other instanceof RubyFixnum) {
  15097. return RubyFloat.newFloat(context.getRuntime(), (double) value / (double) ((RubyFixnum) other).value);
  15098. }
  15099. return coerceBin(context, "quo", other);
  15100. }
  15101. /** fix_pow
  15102. *
  15103. */
  15104. @JRubyMethod(name = "**")
  15105. public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
  15106. if(other instanceof RubyFixnum) {
  15107. long b = ((RubyFixnum) other).value;
  15108. if (b == 0) {
  15109. return RubyFixnum.one(context.getRuntime());
  15110. }
  15111. if (b == 1) {
  15112. return this;
  15113. }
  15114. if (b > 0) {
  15115. return RubyBignum.newBignum(context.getRuntime(), value).op_pow(context, other);
  15116. }
  15117. return RubyFloat.newFloat(context.getRuntime(), Math.pow(value, b));
  15118. } else if (other instanceof RubyFloat) {
  15119. return RubyFloat.newFloat(context.getRuntime(), Math.pow(value, ((RubyFloat) other)
  15120. .getDoubleValue()));
  15121. }
  15122. return coerceBin(context, "**", other);
  15123. }
  15124. /** fix_pow
  15125. *
  15126. */
  15127. @JRubyMethod(name = "**", compat = CompatVersion.RUBY1_9)
  15128. public IRubyObject op_pow_19(ThreadContext context, IRubyObject other) {
  15129. Ruby runtime = context.getRuntime();
  15130. long a = value;
  15131. if (other instanceof RubyFixnum) {
  15132. long b = ((RubyFixnum) other).value;
  15133. if (b < 0) {
  15134. return RubyRational.newRationalRaw(context.getRuntime(), this).callMethod(context, "**", other);
  15135. }
  15136. if (b == 0) return RubyFixnum.one(runtime);
  15137. if (b == 1) return this;
  15138. if (a == 0) {
  15139. return b > 0 ? RubyFixnum.zero(runtime) : RubyNumeric.dbl2num(runtime, 1.0 / 0.0);
  15140. }
  15141. if (a == 1) return RubyFixnum.one(runtime);
  15142. if (a == -1) {
  15143. return b % 2 == 0 ? RubyFixnum.one(runtime) : RubyFixnum.minus_one(runtime);
  15144. }
  15145. return Numeric.int_pow(context, a, b);
  15146. } else if (other instanceof RubyBignum) {
  15147. if (other.callMethod(context, "<", RubyFixnum.zero(runtime)).isTrue()) {
  15148. return RubyRational.newRationalRaw(runtime, this).callMethod(context, "**", other);
  15149. }
  15150. if (a == 0) return RubyFixnum.zero(runtime);
  15151. if (a == 1) return RubyFixnum.one(runtime);
  15152. if (a == -1) {
  15153. return RubyInteger.even_p(context, other).isTrue() ? RubyFixnum.one(runtime) : RubyFixnum.minus_one(runtime);
  15154. }
  15155. RubyBignum.newBignum(runtime, RubyBignum.fix2big(this)).op_pow(context, other);
  15156. } else if (other instanceof RubyFloat) {
  15157. return RubyFloat.newFloat(context.getRuntime(), Math.pow(a, ((RubyFloat) other).getDoubleValue()));
  15158. }
  15159. return coerceBin(context, "**", other);
  15160. }
  15161. /** fix_abs
  15162. *
  15163. */
  15164. @JRubyMethod
  15165. public IRubyObject abs() {
  15166. if (value < 0) {
  15167. // A gotcha for Long.MIN_VALUE: value = -value
  15168. if (value == Long.MIN_VALUE) {
  15169. return RubyBignum.newBignum(
  15170. getRuntime(), BigInteger.valueOf(value).negate());
  15171. }
  15172. return RubyFixnum.newFixnum(getRuntime(), -value);
  15173. }
  15174. return this;
  15175. }
  15176. /** fix_equal
  15177. *
  15178. */
  15179. @JRubyMethod(name = "==")
  15180. @Override
  15181. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  15182. if (other instanceof RubyFixnum) {
  15183. return RubyBoolean.newBoolean(context.getRuntime(), value == ((RubyFixnum) other).value);
  15184. }
  15185. return super.op_num_equal(context, other);
  15186. }
  15187. /** fix_cmp
  15188. *
  15189. */
  15190. @JRubyMethod(name = "<=>")
  15191. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  15192. if (other instanceof RubyFixnum) {
  15193. return compareFixnum(context, (RubyFixnum)other);
  15194. }
  15195. return coerceCmp(context, "<=>", other);
  15196. }
  15197. private IRubyObject compareFixnum(ThreadContext context, RubyFixnum other) {
  15198. long otherValue = ((RubyFixnum) other).value;
  15199. if (value == otherValue) {
  15200. return RubyFixnum.zero(context.getRuntime());
  15201. }
  15202. if (value > otherValue) {
  15203. return RubyFixnum.one(context.getRuntime());
  15204. }
  15205. return RubyFixnum.minus_one(context.getRuntime());
  15206. }
  15207. /** fix_gt
  15208. *
  15209. */
  15210. @JRubyMethod(name = ">")
  15211. public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
  15212. if (other instanceof RubyFixnum) {
  15213. return RubyBoolean.newBoolean(context.getRuntime(), value > ((RubyFixnum) other).value);
  15214. }
  15215. return coerceRelOp(context, ">", other);
  15216. }
  15217. /** fix_ge
  15218. *
  15219. */
  15220. @JRubyMethod(name = ">=")
  15221. public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
  15222. if (other instanceof RubyFixnum) {
  15223. return RubyBoolean.newBoolean(context.getRuntime(), value >= ((RubyFixnum) other).value);
  15224. }
  15225. return coerceRelOp(context, ">=", other);
  15226. }
  15227. /** fix_lt
  15228. *
  15229. */
  15230. @JRubyMethod(name = "<")
  15231. public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
  15232. if (other instanceof RubyFixnum) {
  15233. return RubyBoolean.newBoolean(context.getRuntime(), value < ((RubyFixnum) other).value);
  15234. }
  15235. return coerceRelOp(context, "<", other);
  15236. }
  15237. /** fix_le
  15238. *
  15239. */
  15240. @JRubyMethod(name = "<=")
  15241. public IRubyObject op_le(ThreadContext context, IRubyObject other) {
  15242. if (other instanceof RubyFixnum) {
  15243. return RubyBoolean.newBoolean(context.getRuntime(), value <= ((RubyFixnum) other).value);
  15244. }
  15245. return coerceRelOp(context, "<=", other);
  15246. }
  15247. /** fix_rev
  15248. *
  15249. */
  15250. @JRubyMethod(name = "~")
  15251. public IRubyObject op_neg() {
  15252. return newFixnum(~value);
  15253. }
  15254. /** fix_and
  15255. *
  15256. */
  15257. @JRubyMethod(name = "&")
  15258. public IRubyObject op_and(ThreadContext context, IRubyObject other) {
  15259. if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
  15260. return newFixnum(context.getRuntime(), value & ((RubyFixnum) other).value);
  15261. }
  15262. return ((RubyBignum) other).op_and(context, this);
  15263. }
  15264. /** fix_or
  15265. *
  15266. */
  15267. @JRubyMethod(name = "|")
  15268. public IRubyObject op_or(ThreadContext context, IRubyObject other) {
  15269. if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
  15270. return newFixnum(context.getRuntime(), value | ((RubyFixnum) other).value);
  15271. }
  15272. return ((RubyBignum) other).op_or(context, this);
  15273. }
  15274. /** fix_xor
  15275. *
  15276. */
  15277. @JRubyMethod(name = "^")
  15278. public IRubyObject op_xor(ThreadContext context, IRubyObject other) {
  15279. if (other instanceof RubyFixnum || (other = fixCoerce(other)) instanceof RubyFixnum) {
  15280. return newFixnum(context.getRuntime(), value ^ ((RubyFixnum) other).value);
  15281. }
  15282. return ((RubyBignum) other).op_xor(context, this);
  15283. }
  15284. /** fix_aref
  15285. *
  15286. */
  15287. @JRubyMethod(name = "[]")
  15288. public IRubyObject op_aref(IRubyObject other) {
  15289. if(!(other instanceof RubyFixnum) && !((other = fixCoerce(other)) instanceof RubyFixnum)) {
  15290. RubyBignum big = (RubyBignum) other;
  15291. RubyObject tryFix = RubyBignum.bignorm(getRuntime(), big.getValue());
  15292. if (!(tryFix instanceof RubyFixnum)) {
  15293. return big.getValue().signum() == 0 || value >= 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.one(getRuntime());
  15294. }
  15295. }
  15296. long otherValue = fix2long(other);
  15297. if (otherValue < 0) return RubyFixnum.zero(getRuntime());
  15298. if (BIT_SIZE - 1 < otherValue) {
  15299. return value < 0 ? RubyFixnum.one(getRuntime()) : RubyFixnum.zero(getRuntime());
  15300. }
  15301. return (value & (1L << otherValue)) == 0 ? RubyFixnum.zero(getRuntime()) : RubyFixnum.one(getRuntime());
  15302. }
  15303. /** fix_lshift
  15304. *
  15305. */
  15306. @JRubyMethod(name = "<<")
  15307. public IRubyObject op_lshift(IRubyObject other) {
  15308. if (!(other instanceof RubyFixnum)) return RubyBignum.newBignum(getRuntime(), value).op_lshift(other);
  15309. long width = ((RubyFixnum)other).getLongValue();
  15310. return width < 0 ? rshift(-width) : lshift(width);
  15311. }
  15312. private IRubyObject lshift(long width) {
  15313. if (width > BIT_SIZE - 1 || ((~0L << BIT_SIZE - width - 1) & value) != 0) {
  15314. return RubyBignum.newBignum(getRuntime(), value).op_lshift(RubyFixnum.newFixnum(getRuntime(), width));
  15315. }
  15316. return RubyFixnum.newFixnum(getRuntime(), value << width);
  15317. }
  15318. /** fix_rshift
  15319. *
  15320. */
  15321. @JRubyMethod(name = ">>")
  15322. public IRubyObject op_rshift(IRubyObject other) {
  15323. if (!(other instanceof RubyFixnum)) return RubyBignum.newBignum(getRuntime(), value).op_rshift(other);
  15324. long width = ((RubyFixnum)other).getLongValue();
  15325. if (width == 0) return this;
  15326. return width < 0 ? lshift(-width) : rshift(width);
  15327. }
  15328. private IRubyObject rshift(long width) {
  15329. if (width >= BIT_SIZE - 1) {
  15330. return value < 0 ? RubyFixnum.minus_one(getRuntime()) : RubyFixnum.zero(getRuntime());
  15331. }
  15332. return RubyFixnum.newFixnum(getRuntime(), value >> width);
  15333. }
  15334. /** fix_to_f
  15335. *
  15336. */
  15337. @JRubyMethod
  15338. public IRubyObject to_f() {
  15339. return RubyFloat.newFloat(getRuntime(), (double) value);
  15340. }
  15341. /** fix_size
  15342. *
  15343. */
  15344. @JRubyMethod
  15345. public IRubyObject size() {
  15346. return newFixnum((long) ((BIT_SIZE + 7) / 8));
  15347. }
  15348. /** fix_zero_p
  15349. *
  15350. */
  15351. @JRubyMethod(name = "zero?")
  15352. public IRubyObject zero_p() {
  15353. return RubyBoolean.newBoolean(getRuntime(), value == 0);
  15354. }
  15355. @JRubyMethod
  15356. @Override
  15357. public IRubyObject id() {
  15358. if (value <= Long.MAX_VALUE / 2 && value >= Long.MIN_VALUE / 2) {
  15359. return newFixnum(2 * value + 1);
  15360. }
  15361. return super.id();
  15362. }
  15363. @Override
  15364. public IRubyObject taint(ThreadContext context) {
  15365. return this;
  15366. }
  15367. @Override
  15368. public IRubyObject freeze(ThreadContext context) {
  15369. return this;
  15370. }
  15371. // Piece of mri rb_to_id
  15372. @Override
  15373. public String asJavaString() {
  15374. getRuntime().getWarnings().warn(ID.FIXNUMS_NOT_SYMBOLS, "do not use Fixnums as Symbols");
  15375. // FIXME: I think this chunk is equivalent to MRI id2name (and not our public method
  15376. // id2name). Make into method if used more than once.
  15377. RubySymbol symbol = RubySymbol.getSymbolLong(getRuntime(), value);
  15378. if (symbol == null) {
  15379. throw getRuntime().newArgumentError("" + value + " is not a symbol");
  15380. }
  15381. return symbol.asJavaString();
  15382. }
  15383. public static RubyFixnum unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  15384. return input.getRuntime().newFixnum(input.unmarshalInt());
  15385. }
  15386. /* ================
  15387. * Singleton Methods
  15388. * ================
  15389. */
  15390. /** rb_fix_induced_from
  15391. *
  15392. */
  15393. @JRubyMethod(meta = true)
  15394. public static IRubyObject induced_from(IRubyObject recv, IRubyObject other) {
  15395. return RubyNumeric.num2fix(other);
  15396. }
  15397. @Override
  15398. public IRubyObject to_java() {
  15399. return MiniJava.javaToRuby(getRuntime(), Long.valueOf(value));
  15400. }
  15401. @Override
  15402. public IRubyObject as(Class javaClass) {
  15403. return MiniJava.javaToRuby(getRuntime(), coerceToJavaType(getRuntime(), this, javaClass));
  15404. }
  15405. private static Object coerceToJavaType(Ruby ruby, RubyFixnum self, Class javaClass) {
  15406. if (!Number.class.isAssignableFrom(javaClass)) {
  15407. throw ruby.newTypeError(javaClass.getCanonicalName() + " is not a numeric type");
  15408. }
  15409. TypeCoercer coercer = JAVA_COERCERS.get(javaClass);
  15410. if (coercer == null) {
  15411. throw ruby.newTypeError("Cannot coerce Fixnum to " + javaClass.getCanonicalName());
  15412. }
  15413. return coercer.coerce(self);
  15414. }
  15415. private static final Map<Class, TypeCoercer> JAVA_COERCERS = new HashMap<Class, TypeCoercer>();
  15416. static {
  15417. TypeCoercer intCoercer = new TypeCoercer() {
  15418. public Object coerce(IRubyObject self) {
  15419. RubyFixnum fixnum = (RubyFixnum)self;
  15420. if (fixnum.value > Integer.MAX_VALUE) {
  15421. throw self.getRuntime().newRangeError("Fixnum " + fixnum.value + " is too large for Java int");
  15422. }
  15423. return Integer.valueOf((int)fixnum.value);
  15424. }
  15425. };
  15426. JAVA_COERCERS.put(int.class, intCoercer);
  15427. JAVA_COERCERS.put(Integer.class, intCoercer);
  15428. }
  15429. }
  15430. /*
  15431. ***** BEGIN LICENSE BLOCK *****
  15432. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  15433. *
  15434. * The contents of this file are subject to the Common Public
  15435. * License Version 1.0 (the "License"); you may not use this file
  15436. * except in compliance with the License. You may obtain a copy of
  15437. * the License at http://www.eclipse.org/legal/cpl-v10.html
  15438. *
  15439. * Software distributed under the License is distributed on an "AS
  15440. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  15441. * implied. See the License for the specific language governing
  15442. * rights and limitations under the License.
  15443. *
  15444. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  15445. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  15446. * Copyright (C) 2002 Don Schwartz <schwardo@users.sourceforge.net>
  15447. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  15448. * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  15449. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  15450. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  15451. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  15452. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  15453. *
  15454. * Alternatively, the contents of this file may be used under the terms of
  15455. * either of the GNU General Public License Version 2 or later (the "GPL"),
  15456. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  15457. * in which case the provisions of the GPL or the LGPL are applicable instead
  15458. * of those above. If you wish to allow use of your version of this file only
  15459. * under the terms of either the GPL or the LGPL, and not to allow others to
  15460. * use your version of this file under the terms of the CPL, indicate your
  15461. * decision by deleting the provisions above and replace them with the notice
  15462. * and other provisions required by the GPL or the LGPL. If you do not delete
  15463. * the provisions above, a recipient may use your version of this file under
  15464. * the terms of any one of the CPL, the GPL or the LGPL.
  15465. ***** END LICENSE BLOCK *****/
  15466. package org.jruby;
  15467. import static org.jruby.util.Numeric.f_expt;
  15468. import static org.jruby.util.Numeric.f_mul;
  15469. import static org.jruby.util.Numeric.f_to_i;
  15470. import static org.jruby.util.Numeric.frexp;
  15471. import static org.jruby.util.Numeric.ldexp;
  15472. import java.text.DecimalFormat;
  15473. import java.text.DecimalFormatSymbols;
  15474. import java.util.Locale;
  15475. import org.jruby.anno.JRubyClass;
  15476. import org.jruby.anno.JRubyMethod;
  15477. import org.jruby.runtime.ClassIndex;
  15478. import org.jruby.runtime.MethodIndex;
  15479. import org.jruby.runtime.ObjectAllocator;
  15480. import org.jruby.runtime.ThreadContext;
  15481. import org.jruby.runtime.builtin.IRubyObject;
  15482. import org.jruby.runtime.marshal.MarshalStream;
  15483. import org.jruby.runtime.marshal.UnmarshalStream;
  15484. /**
  15485. * A representation of a float object
  15486. */
  15487. @JRubyClass(name="Float", parent="Numeric", include="Precision")
  15488. public class RubyFloat extends RubyNumeric {
  15489. public static RubyClass createFloatClass(Ruby runtime) {
  15490. RubyClass floatc = runtime.defineClass("Float", runtime.getNumeric(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  15491. runtime.setFloat(floatc);
  15492. floatc.index = ClassIndex.FLOAT;
  15493. floatc.kindOf = new RubyModule.KindOf() {
  15494. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  15495. return obj instanceof RubyFloat;
  15496. }
  15497. };
  15498. floatc.getSingletonClass().undefineMethod("new");
  15499. floatc.includeModule(runtime.getPrecision());
  15500. // Java Doubles are 64 bit long:
  15501. floatc.defineConstant("ROUNDS", RubyFixnum.newFixnum(runtime, 1));
  15502. floatc.defineConstant("RADIX", RubyFixnum.newFixnum(runtime, 2));
  15503. floatc.defineConstant("MANT_DIG", RubyFixnum.newFixnum(runtime, 53));
  15504. floatc.defineConstant("DIG", RubyFixnum.newFixnum(runtime, 15));
  15505. // Double.MAX_EXPONENT since Java 1.6
  15506. floatc.defineConstant("MIN_EXP", RubyFixnum.newFixnum(runtime, -1021));
  15507. // Double.MAX_EXPONENT since Java 1.6
  15508. floatc.defineConstant("MAX_EXP", RubyFixnum.newFixnum(runtime, 1024));
  15509. floatc.defineConstant("MIN_10_EXP", RubyFixnum.newFixnum(runtime, -307));
  15510. floatc.defineConstant("MAX_10_EXP", RubyFixnum.newFixnum(runtime, 308));
  15511. floatc.defineConstant("MIN", RubyFloat.newFloat(runtime, Double.MIN_VALUE));
  15512. floatc.defineConstant("MAX", RubyFloat.newFloat(runtime, Double.MAX_VALUE));
  15513. floatc.defineConstant("EPSILON", RubyFloat.newFloat(runtime, 2.2204460492503131e-16));
  15514. floatc.defineAnnotatedMethods(RubyFloat.class);
  15515. return floatc;
  15516. }
  15517. private final double value;
  15518. public int getNativeTypeIndex() {
  15519. return ClassIndex.FLOAT;
  15520. }
  15521. public RubyFloat(Ruby runtime) {
  15522. this(runtime, 0.0);
  15523. }
  15524. public RubyFloat(Ruby runtime, double value) {
  15525. super(runtime, runtime.getFloat());
  15526. this.value = value;
  15527. }
  15528. public Class<?> getJavaClass() {
  15529. // this needs to be thought out more along with the changes in RubyFixnum
  15530. // since "to Object" coercion will generally want to produce the same
  15531. // type every time
  15532. // if (value >= Float.MIN_VALUE && value <= Float.MAX_VALUE) {
  15533. // return float.class;
  15534. // }
  15535. return double.class;
  15536. }
  15537. /** Getter for property value.
  15538. * @return Value of property value.
  15539. */
  15540. public double getValue() {
  15541. return this.value;
  15542. }
  15543. public double getDoubleValue() {
  15544. return value;
  15545. }
  15546. public long getLongValue() {
  15547. return (long) value;
  15548. }
  15549. public RubyFloat convertToFloat() {
  15550. return this;
  15551. }
  15552. protected int compareValue(RubyNumeric other) {
  15553. double otherVal = other.getDoubleValue();
  15554. return getValue() > otherVal ? 1 : getValue() < otherVal ? -1 : 0;
  15555. }
  15556. public static RubyFloat newFloat(Ruby runtime, double value) {
  15557. return new RubyFloat(runtime, value);
  15558. }
  15559. /* ================
  15560. * Instance Methods
  15561. * ================
  15562. */
  15563. /** rb_flo_induced_from
  15564. *
  15565. */
  15566. @JRubyMethod(required = 1, meta = true)
  15567. public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject number) {
  15568. if (number instanceof RubyFixnum || number instanceof RubyBignum || number instanceof RubyRational) {
  15569. return number.callMethod(context, MethodIndex.TO_F, "to_f");
  15570. } else if (number instanceof RubyFloat) {
  15571. return number;
  15572. }
  15573. throw recv.getRuntime().newTypeError(
  15574. "failed to convert " + number.getMetaClass() + " into Float");
  15575. }
  15576. private final static DecimalFormat FORMAT = new DecimalFormat("##############0.0##############",
  15577. new DecimalFormatSymbols(Locale.ENGLISH));
  15578. /** flo_to_s
  15579. *
  15580. */
  15581. @JRubyMethod(name = "to_s")
  15582. public IRubyObject to_s() {
  15583. if (Double.isInfinite(value)) {
  15584. return RubyString.newString(getRuntime(), value < 0 ? "-Infinity" : "Infinity");
  15585. }
  15586. if (Double.isNaN(value)) {
  15587. return RubyString.newString(getRuntime(), "NaN");
  15588. }
  15589. String val = ""+value;
  15590. if(val.indexOf('E') != -1) {
  15591. String v2 = FORMAT.format(value);
  15592. int ix = v2.length()-1;
  15593. while(v2.charAt(ix) == '0' && v2.charAt(ix-1) != '.') {
  15594. ix--;
  15595. }
  15596. if(ix > 15 || "0.0".equals(v2.substring(0,ix+1))) {
  15597. val = val.replaceFirst("E(\\d)","e+$1").replaceFirst("E-","e-");
  15598. } else {
  15599. val = v2.substring(0,ix+1);
  15600. }
  15601. }
  15602. return RubyString.newString(getRuntime(), val);
  15603. }
  15604. /** flo_coerce
  15605. *
  15606. */
  15607. @JRubyMethod(name = "coerce", required = 1)
  15608. public IRubyObject coerce(IRubyObject other) {
  15609. return getRuntime().newArray(RubyKernel.new_float(this, other), this);
  15610. }
  15611. /** flo_uminus
  15612. *
  15613. */
  15614. @JRubyMethod(name = "-@")
  15615. public IRubyObject op_uminus() {
  15616. return RubyFloat.newFloat(getRuntime(), -value);
  15617. }
  15618. /** flo_plus
  15619. *
  15620. */
  15621. @JRubyMethod(name = "+", required = 1)
  15622. public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
  15623. switch (other.getMetaClass().index) {
  15624. case ClassIndex.FIXNUM:
  15625. case ClassIndex.BIGNUM:
  15626. case ClassIndex.FLOAT:
  15627. return RubyFloat.newFloat(getRuntime(), value + ((RubyNumeric) other).getDoubleValue());
  15628. default:
  15629. return coerceBin(context, "+", other);
  15630. }
  15631. }
  15632. /** flo_minus
  15633. *
  15634. */
  15635. @JRubyMethod(name = "-", required = 1)
  15636. public IRubyObject op_minus(ThreadContext context, IRubyObject other) {
  15637. switch (other.getMetaClass().index) {
  15638. case ClassIndex.FIXNUM:
  15639. case ClassIndex.BIGNUM:
  15640. case ClassIndex.FLOAT:
  15641. return RubyFloat.newFloat(getRuntime(), value - ((RubyNumeric) other).getDoubleValue());
  15642. default:
  15643. return coerceBin(context, "-", other);
  15644. }
  15645. }
  15646. /** flo_mul
  15647. *
  15648. */
  15649. @JRubyMethod(name = "*", required = 1)
  15650. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  15651. switch (other.getMetaClass().index) {
  15652. case ClassIndex.FIXNUM:
  15653. case ClassIndex.BIGNUM:
  15654. case ClassIndex.FLOAT:
  15655. return RubyFloat.newFloat(
  15656. getRuntime(), value * ((RubyNumeric) other).getDoubleValue());
  15657. default:
  15658. return coerceBin(context, "*", other);
  15659. }
  15660. }
  15661. /** flo_div
  15662. *
  15663. */
  15664. @JRubyMethod(name = "/", required = 1)
  15665. public IRubyObject op_fdiv(ThreadContext context, IRubyObject other) { // don't override Numeric#div !
  15666. switch (other.getMetaClass().index) {
  15667. case ClassIndex.FIXNUM:
  15668. case ClassIndex.BIGNUM:
  15669. case ClassIndex.FLOAT:
  15670. return RubyFloat.newFloat(getRuntime(), value / ((RubyNumeric) other).getDoubleValue());
  15671. default:
  15672. return coerceBin(context, "/", other);
  15673. }
  15674. }
  15675. /** flo_mod
  15676. *
  15677. */
  15678. @JRubyMethod(name = {"%", "modulo"}, required = 1)
  15679. public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
  15680. switch (other.getMetaClass().index) {
  15681. case ClassIndex.FIXNUM:
  15682. case ClassIndex.BIGNUM:
  15683. case ClassIndex.FLOAT:
  15684. double y = ((RubyNumeric) other).getDoubleValue();
  15685. // Modelled after c ruby implementation (java /,% not same as ruby)
  15686. double x = value;
  15687. double mod = Math.IEEEremainder(x, y);
  15688. if (y * mod < 0) {
  15689. mod += y;
  15690. }
  15691. return RubyFloat.newFloat(getRuntime(), mod);
  15692. default:
  15693. return coerceBin(context, "%", other);
  15694. }
  15695. }
  15696. /** flo_divmod
  15697. *
  15698. */
  15699. @JRubyMethod(name = "divmod", required = 1)
  15700. public IRubyObject divmod(ThreadContext context, IRubyObject other) {
  15701. switch (other.getMetaClass().index) {
  15702. case ClassIndex.FIXNUM:
  15703. case ClassIndex.BIGNUM:
  15704. case ClassIndex.FLOAT:
  15705. double y = ((RubyNumeric) other).getDoubleValue();
  15706. double x = value;
  15707. double mod = Math.IEEEremainder(x, y);
  15708. // MRI behavior:
  15709. if (Double.isNaN(mod)) {
  15710. throw getRuntime().newFloatDomainError("NaN");
  15711. }
  15712. double div = Math.floor(x / y);
  15713. if (y * mod < 0) {
  15714. mod += y;
  15715. }
  15716. final Ruby runtime = getRuntime();
  15717. IRubyObject car = dbl2num(runtime, div);
  15718. RubyFloat cdr = RubyFloat.newFloat(runtime, mod);
  15719. return RubyArray.newArray(runtime, car, cdr);
  15720. default:
  15721. return coerceBin(context, "divmod", other);
  15722. }
  15723. }
  15724. /** flo_pow
  15725. *
  15726. */
  15727. @JRubyMethod(name = "**", required = 1)
  15728. public IRubyObject op_pow(ThreadContext context, IRubyObject other) {
  15729. switch (other.getMetaClass().index) {
  15730. case ClassIndex.FIXNUM:
  15731. case ClassIndex.BIGNUM:
  15732. case ClassIndex.FLOAT:
  15733. return RubyFloat.newFloat(getRuntime(), Math.pow(value, ((RubyNumeric) other)
  15734. .getDoubleValue()));
  15735. default:
  15736. return coerceBin(context, "**", other);
  15737. }
  15738. }
  15739. /** flo_eq
  15740. *
  15741. */
  15742. @JRubyMethod(name = "==", required = 1)
  15743. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  15744. if (Double.isNaN(value)) {
  15745. return getRuntime().getFalse();
  15746. }
  15747. switch (other.getMetaClass().index) {
  15748. case ClassIndex.FIXNUM:
  15749. case ClassIndex.BIGNUM:
  15750. case ClassIndex.FLOAT:
  15751. return RubyBoolean.newBoolean(getRuntime(), value == ((RubyNumeric) other)
  15752. .getDoubleValue());
  15753. default:
  15754. // Numeric.equal
  15755. return super.op_num_equal(context, other);
  15756. }
  15757. }
  15758. /** flo_cmp
  15759. *
  15760. */
  15761. @JRubyMethod(name = "<=>", required = 1)
  15762. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  15763. switch (other.getMetaClass().index) {
  15764. case ClassIndex.FIXNUM:
  15765. case ClassIndex.BIGNUM:
  15766. case ClassIndex.FLOAT:
  15767. double b = ((RubyNumeric) other).getDoubleValue();
  15768. return dbl_cmp(getRuntime(), value, b);
  15769. default:
  15770. return coerceCmp(context, "<=>", other);
  15771. }
  15772. }
  15773. /** flo_gt
  15774. *
  15775. */
  15776. @JRubyMethod(name = ">", required = 1)
  15777. public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
  15778. switch (other.getMetaClass().index) {
  15779. case ClassIndex.FIXNUM:
  15780. case ClassIndex.BIGNUM:
  15781. case ClassIndex.FLOAT:
  15782. double b = ((RubyNumeric) other).getDoubleValue();
  15783. return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value > b);
  15784. default:
  15785. return coerceRelOp(context, ">", other);
  15786. }
  15787. }
  15788. /** flo_ge
  15789. *
  15790. */
  15791. @JRubyMethod(name = ">=", required = 1)
  15792. public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
  15793. switch (other.getMetaClass().index) {
  15794. case ClassIndex.FIXNUM:
  15795. case ClassIndex.BIGNUM:
  15796. case ClassIndex.FLOAT:
  15797. double b = ((RubyNumeric) other).getDoubleValue();
  15798. return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value >= b);
  15799. default:
  15800. return coerceRelOp(context, ">=", other);
  15801. }
  15802. }
  15803. /** flo_lt
  15804. *
  15805. */
  15806. @JRubyMethod(name = "<", required = 1)
  15807. public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
  15808. switch (other.getMetaClass().index) {
  15809. case ClassIndex.FIXNUM:
  15810. case ClassIndex.BIGNUM:
  15811. case ClassIndex.FLOAT:
  15812. double b = ((RubyNumeric) other).getDoubleValue();
  15813. return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value < b);
  15814. default:
  15815. return coerceRelOp(context, "<", other);
  15816. }
  15817. }
  15818. /** flo_le
  15819. *
  15820. */
  15821. @JRubyMethod(name = "<=", required = 1)
  15822. public IRubyObject op_le(ThreadContext context, IRubyObject other) {
  15823. switch (other.getMetaClass().index) {
  15824. case ClassIndex.FIXNUM:
  15825. case ClassIndex.BIGNUM:
  15826. case ClassIndex.FLOAT:
  15827. double b = ((RubyNumeric) other).getDoubleValue();
  15828. return RubyBoolean.newBoolean(getRuntime(), !Double.isNaN(b) && value <= b);
  15829. default:
  15830. return coerceRelOp(context, "<=", other);
  15831. }
  15832. }
  15833. /** flo_eql
  15834. *
  15835. */
  15836. @JRubyMethod(name = "eql?", required = 1)
  15837. public IRubyObject eql_p(IRubyObject other) {
  15838. if (other instanceof RubyFloat) {
  15839. double b = ((RubyFloat) other).value;
  15840. if (Double.isNaN(value) || Double.isNaN(b)) {
  15841. return getRuntime().getFalse();
  15842. }
  15843. if (value == b) {
  15844. return getRuntime().getTrue();
  15845. }
  15846. }
  15847. return getRuntime().getFalse();
  15848. }
  15849. /** flo_hash
  15850. *
  15851. */
  15852. @JRubyMethod(name = "hash")
  15853. public RubyFixnum hash() {
  15854. return getRuntime().newFixnum(hashCode());
  15855. }
  15856. public final int hashCode() {
  15857. long l = Double.doubleToLongBits(value);
  15858. return (int)(l ^ l >>> 32);
  15859. }
  15860. /** flo_fo
  15861. *
  15862. */
  15863. @JRubyMethod(name = "to_f")
  15864. public IRubyObject to_f() {
  15865. return this;
  15866. }
  15867. /** flo_abs
  15868. *
  15869. */
  15870. @JRubyMethod(name = "abs")
  15871. public IRubyObject abs() {
  15872. if (Double.doubleToLongBits(value) < 0) {
  15873. return RubyFloat.newFloat(getRuntime(), Math.abs(value));
  15874. }
  15875. return this;
  15876. }
  15877. /** flo_zero_p
  15878. *
  15879. */
  15880. @JRubyMethod(name = "zero?")
  15881. public IRubyObject zero_p() {
  15882. return RubyBoolean.newBoolean(getRuntime(), value == 0.0);
  15883. }
  15884. /** flo_truncate
  15885. *
  15886. */
  15887. @JRubyMethod(name = {"truncate", "to_i", "to_int"})
  15888. public IRubyObject truncate() {
  15889. double f = value;
  15890. if (f > 0.0) f = Math.floor(f);
  15891. if (f < 0.0) f = Math.ceil(f);
  15892. return dbl2num(getRuntime(), f);
  15893. }
  15894. /** float_to_r, float_decode
  15895. *
  15896. */
  15897. static final int DBL_MANT_DIG = 53;
  15898. static final int FLT_RADIX = 2;
  15899. @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
  15900. public IRubyObject to_r(ThreadContext context) {
  15901. long[]exp = new long[1];
  15902. double f = frexp(value, exp);
  15903. f = ldexp(f, DBL_MANT_DIG);
  15904. long n = exp[0] - DBL_MANT_DIG;
  15905. Ruby runtime = context.getRuntime();
  15906. IRubyObject x = f_mul(context, f_to_i(context, runtime.newFloat(f)),
  15907. f_expt(context,
  15908. RubyFixnum.newFixnum(context.getRuntime(), FLT_RADIX),
  15909. RubyFixnum.newFixnum(runtime, n)));
  15910. return x;
  15911. }
  15912. /** floor
  15913. *
  15914. */
  15915. @JRubyMethod(name = "floor")
  15916. public IRubyObject floor() {
  15917. return dbl2num(getRuntime(), Math.floor(value));
  15918. }
  15919. /** flo_ceil
  15920. *
  15921. */
  15922. @JRubyMethod(name = "ceil")
  15923. public IRubyObject ceil() {
  15924. return dbl2num(getRuntime(), Math.ceil(value));
  15925. }
  15926. /** flo_round
  15927. *
  15928. */
  15929. @JRubyMethod(name = "round")
  15930. public IRubyObject round() {
  15931. double f = value;
  15932. if (f > 0.0) {
  15933. f = Math.floor(f + 0.5);
  15934. }
  15935. if (f < 0.0) {
  15936. f = Math.ceil(f - 0.5);
  15937. }
  15938. return dbl2num(getRuntime(), f);
  15939. }
  15940. /** flo_is_nan_p
  15941. *
  15942. */
  15943. @JRubyMethod(name = "nan?")
  15944. public IRubyObject nan_p() {
  15945. return RubyBoolean.newBoolean(getRuntime(), Double.isNaN(value));
  15946. }
  15947. /** flo_is_infinite_p
  15948. *
  15949. */
  15950. @JRubyMethod(name = "infinite?")
  15951. public IRubyObject infinite_p() {
  15952. if (Double.isInfinite(value)) {
  15953. return RubyFixnum.newFixnum(getRuntime(), value < 0 ? -1 : 1);
  15954. }
  15955. return getRuntime().getNil();
  15956. }
  15957. /** flo_is_finite_p
  15958. *
  15959. */
  15960. @JRubyMethod(name = "finite?")
  15961. public IRubyObject finite_p() {
  15962. if (Double.isInfinite(value) || Double.isNaN(value)) {
  15963. return getRuntime().getFalse();
  15964. }
  15965. return getRuntime().getTrue();
  15966. }
  15967. public static void marshalTo(RubyFloat aFloat, MarshalStream output) throws java.io.IOException {
  15968. output.registerLinkTarget(aFloat);
  15969. String strValue = aFloat.toString();
  15970. if (Double.isInfinite(aFloat.value)) {
  15971. strValue = aFloat.value < 0 ? "-inf" : "inf";
  15972. } else if (Double.isNaN(aFloat.value)) {
  15973. strValue = "nan";
  15974. }
  15975. output.writeString(strValue);
  15976. }
  15977. public static RubyFloat unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  15978. RubyFloat result = RubyFloat.newFloat(input.getRuntime(), org.jruby.util.Convert.byteListToDouble(input.unmarshalString(),false));
  15979. input.registerLinkTarget(result);
  15980. return result;
  15981. }
  15982. }
  15983. /***** BEGIN LICENSE BLOCK *****
  15984. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  15985. *
  15986. * The contents of this file are subject to the Common Public
  15987. * License Version 1.0 (the "License"); you may not use this file
  15988. * except in compliance with the License. You may obtain a copy of
  15989. * the License at http://www.eclipse.org/legal/cpl-v10.html
  15990. *
  15991. * Software distributed under the License is distributed on an "AS
  15992. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  15993. * implied. See the License for the specific language governing
  15994. * rights and limitations under the License.
  15995. *
  15996. * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  15997. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  15998. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  15999. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  16000. *
  16001. * Alternatively, the contents of this file may be used under the terms of
  16002. * either of the GNU General Public License Version 2 or later (the "GPL"),
  16003. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  16004. * in which case the provisions of the GPL or the LGPL are applicable instead
  16005. * of those above. If you wish to allow use of your version of this file only
  16006. * under the terms of either the GPL or the LGPL, and not to allow others to
  16007. * use your version of this file under the terms of the CPL, indicate your
  16008. * decision by deleting the provisions above and replace them with the notice
  16009. * and other provisions required by the GPL or the LGPL. If you do not delete
  16010. * the provisions above, a recipient may use your version of this file under
  16011. * the terms of any one of the CPL, the GPL or the LGPL.
  16012. ***** END LICENSE BLOCK *****/
  16013. package org.jruby;
  16014. import org.jruby.anno.JRubyMethod;
  16015. import org.jruby.anno.JRubyModule;
  16016. import org.jruby.common.IRubyWarnings.ID;
  16017. import org.jruby.runtime.Visibility;
  16018. import org.jruby.runtime.builtin.IRubyObject;
  16019. /**
  16020. * GC (Garbage Collection) Module
  16021. *
  16022. * Note: Since we rely on Java's memory model we can't provide the
  16023. * kind of control over garbage collection that MRI provides.
  16024. *
  16025. * @author Anders
  16026. */
  16027. @JRubyModule(name="GC")
  16028. public class RubyGC {
  16029. public static RubyModule createGCModule(Ruby runtime) {
  16030. RubyModule result = runtime.defineModule("GC");
  16031. runtime.setGC(result);
  16032. result.defineAnnotatedMethods(RubyGC.class);
  16033. return result;
  16034. }
  16035. @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
  16036. public static IRubyObject start(IRubyObject recv) {
  16037. System.gc();
  16038. return recv.getRuntime().getNil();
  16039. }
  16040. @JRubyMethod
  16041. public static IRubyObject garbage_collect(IRubyObject recv) {
  16042. System.gc();
  16043. return recv.getRuntime().getNil();
  16044. }
  16045. @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
  16046. public static IRubyObject enable(IRubyObject recv) {
  16047. recv.getRuntime().getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "GC.enable will not work on JRuby", "GC.enable");
  16048. return recv.getRuntime().getNil();
  16049. }
  16050. @JRubyMethod(module = true, visibility = Visibility.PRIVATE)
  16051. public static IRubyObject disable(IRubyObject recv) {
  16052. recv.getRuntime().getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "GC.disable will not work on JRuby", "GC.disable");
  16053. return recv.getRuntime().getNil();
  16054. }
  16055. }
  16056. /***** BEGIN LICENSE BLOCK *****
  16057. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  16058. *
  16059. * The contents of this file are subject to the Common Public
  16060. * License Version 1.0 (the "License"); you may not use this file
  16061. * except in compliance with the License. You may obtain a copy of
  16062. * the License at http://www.eclipse.org/legal/cpl-v10.html
  16063. *
  16064. * Software distributed under the License is distributed on an "AS
  16065. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  16066. * implied. See the License for the specific language governing
  16067. * rights and limitations under the License.
  16068. *
  16069. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  16070. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  16071. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  16072. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  16073. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  16074. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  16075. * Copyright (C) 2006 Tim Azzopardi <tim@tigerfive.com>
  16076. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  16077. * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
  16078. *
  16079. * Alternatively, the contents of this file may be used under the terms of
  16080. * either of the GNU General Public License Version 2 or later (the "GPL"),
  16081. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  16082. * in which case the provisions of the GPL or the LGPL are applicable instead
  16083. * of those above. If you wish to allow use of your version of this file only
  16084. * under the terms of either the GPL or the LGPL, and not to allow others to
  16085. * use your version of this file under the terms of the CPL, indicate your
  16086. * decision by deleting the provisions above and replace them with the notice
  16087. * and other provisions required by the GPL or the LGPL. If you do not delete
  16088. * the provisions above, a recipient may use your version of this file under
  16089. * the terms of any one of the CPL, the GPL or the LGPL.
  16090. ***** END LICENSE BLOCK *****/
  16091. package org.jruby;
  16092. import org.jruby.util.io.STDIO;
  16093. import java.util.HashMap;
  16094. import java.util.Map;
  16095. import org.jruby.anno.JRubyMethod;
  16096. import org.jruby.common.IRubyWarnings.ID;
  16097. import org.jruby.environment.OSEnvironmentReaderExcepton;
  16098. import org.jruby.environment.OSEnvironment;
  16099. import org.jruby.internal.runtime.ValueAccessor;
  16100. import org.jruby.javasupport.JavaUtil;
  16101. import org.jruby.javasupport.util.RuntimeHelpers;
  16102. import org.jruby.runtime.Constants;
  16103. import org.jruby.runtime.GlobalVariable;
  16104. import org.jruby.runtime.IAccessor;
  16105. import org.jruby.runtime.ReadonlyGlobalVariable;
  16106. import org.jruby.runtime.ThreadContext;
  16107. import org.jruby.runtime.builtin.IRubyObject;
  16108. import org.jruby.util.KCode;
  16109. /** This class initializes global variables and constants.
  16110. *
  16111. * @author jpetersen
  16112. */
  16113. public class RubyGlobal {
  16114. /**
  16115. * Obligate string-keyed and string-valued hash, used for ENV and ENV_JAVA
  16116. *
  16117. */
  16118. public static class StringOnlyRubyHash extends RubyHash {
  16119. public StringOnlyRubyHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
  16120. super(runtime, valueMap, defaultValue);
  16121. }
  16122. @Override
  16123. public RubyHash to_hash() {
  16124. Ruby runtime = getRuntime();
  16125. RubyHash hash = RubyHash.newHash(runtime);
  16126. hash.replace(runtime.getCurrentContext(), this);
  16127. return hash;
  16128. }
  16129. @Override
  16130. public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
  16131. return super.op_aref(context, key.convertToString());
  16132. }
  16133. @Override
  16134. public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
  16135. if (!key.respondsTo("to_str")) {
  16136. throw getRuntime().newTypeError("can't convert " + key.getMetaClass() + " into String");
  16137. }
  16138. if (!value.respondsTo("to_str") && !value.isNil()) {
  16139. throw getRuntime().newTypeError("can't convert " + value.getMetaClass() + " into String");
  16140. }
  16141. if (value.isNil()) {
  16142. return super.delete(context, key, org.jruby.runtime.Block.NULL_BLOCK);
  16143. }
  16144. //return super.aset(getRuntime().newString("sadfasdF"), getRuntime().newString("sadfasdF"));
  16145. return super.op_aset(context, RuntimeHelpers.invoke(context, key, "to_str"),
  16146. value.isNil() ? getRuntime().getNil() : RuntimeHelpers.invoke(context, value, "to_str"));
  16147. }
  16148. @JRubyMethod
  16149. @Override
  16150. public IRubyObject to_s(){
  16151. return getRuntime().newString("ENV");
  16152. }
  16153. }
  16154. public static void createGlobals(ThreadContext context, Ruby runtime) {
  16155. runtime.defineGlobalConstant("TOPLEVEL_BINDING", runtime.newBinding());
  16156. runtime.defineGlobalConstant("TRUE", runtime.getTrue());
  16157. runtime.defineGlobalConstant("FALSE", runtime.getFalse());
  16158. runtime.defineGlobalConstant("NIL", runtime.getNil());
  16159. // define ARGV and $* for this runtime
  16160. RubyArray argvArray = runtime.newArray();
  16161. String[] argv = runtime.getInstanceConfig().getArgv();
  16162. for (int i = 0; i < argv.length; i++) {
  16163. argvArray.append(RubyString.newString(runtime, argv[i].getBytes()));
  16164. }
  16165. runtime.defineGlobalConstant("ARGV", argvArray);
  16166. runtime.getGlobalVariables().defineReadonly("$*", new ValueAccessor(argvArray));
  16167. IAccessor d = new ValueAccessor(runtime.newString(
  16168. runtime.getInstanceConfig().displayedFileName()));
  16169. runtime.getGlobalVariables().define("$PROGRAM_NAME", d);
  16170. runtime.getGlobalVariables().define("$0", d);
  16171. // Version information:
  16172. IRubyObject version = runtime.newString(Constants.RUBY_VERSION).freeze(context);
  16173. IRubyObject release = runtime.newString(Constants.COMPILE_DATE).freeze(context);
  16174. IRubyObject platform = runtime.newString(Constants.PLATFORM).freeze(context);
  16175. IRubyObject engine = runtime.newString(Constants.ENGINE).freeze(context);
  16176. runtime.defineGlobalConstant("RUBY_VERSION", version);
  16177. runtime.defineGlobalConstant("RUBY_PATCHLEVEL", runtime.newString(Constants.RUBY_PATCHLEVEL).freeze(context));
  16178. runtime.defineGlobalConstant("RUBY_RELEASE_DATE", release);
  16179. runtime.defineGlobalConstant("RUBY_PLATFORM", platform);
  16180. runtime.defineGlobalConstant("RUBY_ENGINE", engine);
  16181. runtime.defineGlobalConstant("VERSION", version);
  16182. runtime.defineGlobalConstant("RELEASE_DATE", release);
  16183. runtime.defineGlobalConstant("PLATFORM", platform);
  16184. IRubyObject jrubyVersion = runtime.newString(Constants.VERSION).freeze(context);
  16185. runtime.defineGlobalConstant("JRUBY_VERSION", jrubyVersion);
  16186. GlobalVariable kcodeGV = new KCodeGlobalVariable(runtime, "$KCODE", runtime.newString("NONE"));
  16187. runtime.defineVariable(kcodeGV);
  16188. runtime.defineVariable(new GlobalVariable.Copy(runtime, "$-K", kcodeGV));
  16189. IRubyObject defaultRS = runtime.newString(runtime.getInstanceConfig().getRecordSeparator()).freeze(context);
  16190. GlobalVariable rs = new StringGlobalVariable(runtime, "$/", defaultRS);
  16191. runtime.defineVariable(rs);
  16192. runtime.setRecordSeparatorVar(rs);
  16193. runtime.getGlobalVariables().setDefaultSeparator(defaultRS);
  16194. runtime.defineVariable(new StringGlobalVariable(runtime, "$\\", runtime.getNil()));
  16195. runtime.defineVariable(new StringGlobalVariable(runtime, "$,", runtime.getNil()));
  16196. runtime.defineVariable(new LineNumberGlobalVariable(runtime, "$.", RubyFixnum.one(runtime)));
  16197. runtime.defineVariable(new LastlineGlobalVariable(runtime, "$_"));
  16198. runtime.defineVariable(new LastExitStatusVariable(runtime, "$?"));
  16199. runtime.defineVariable(new ErrorInfoGlobalVariable(runtime, "$!", runtime.getNil()));
  16200. runtime.defineVariable(new NonEffectiveGlobalVariable(runtime, "$=", runtime.getFalse()));
  16201. if(runtime.getInstanceConfig().getInputFieldSeparator() == null) {
  16202. runtime.defineVariable(new GlobalVariable(runtime, "$;", runtime.getNil()));
  16203. } else {
  16204. runtime.defineVariable(new GlobalVariable(runtime, "$;", RubyRegexp.newRegexp(runtime, runtime.getInstanceConfig().getInputFieldSeparator(), 0)));
  16205. }
  16206. Boolean verbose = runtime.getInstanceConfig().getVerbose();
  16207. IRubyObject verboseValue = null;
  16208. if (verbose == null) {
  16209. verboseValue = runtime.getNil();
  16210. } else if(verbose == Boolean.TRUE) {
  16211. verboseValue = runtime.getTrue();
  16212. } else {
  16213. verboseValue = runtime.getFalse();
  16214. }
  16215. runtime.defineVariable(new VerboseGlobalVariable(runtime, "$VERBOSE", verboseValue));
  16216. IRubyObject debug = runtime.newBoolean(runtime.getInstanceConfig().isDebug());
  16217. runtime.defineVariable(new DebugGlobalVariable(runtime, "$DEBUG", debug));
  16218. runtime.defineVariable(new DebugGlobalVariable(runtime, "$-d", debug));
  16219. runtime.defineVariable(new SafeGlobalVariable(runtime, "$SAFE"));
  16220. runtime.defineVariable(new BacktraceGlobalVariable(runtime, "$@"));
  16221. IRubyObject stdin = new RubyIO(runtime, STDIO.IN);
  16222. IRubyObject stdout = new RubyIO(runtime, STDIO.OUT);
  16223. IRubyObject stderr = new RubyIO(runtime, STDIO.ERR);
  16224. runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", stdin));
  16225. runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", stdout));
  16226. runtime.getGlobalVariables().alias("$>", "$stdout");
  16227. runtime.getGlobalVariables().alias("$defout", "$stdout");
  16228. runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", stderr));
  16229. runtime.getGlobalVariables().alias("$deferr", "$stderr");
  16230. runtime.defineGlobalConstant("STDIN", stdin);
  16231. runtime.defineGlobalConstant("STDOUT", stdout);
  16232. runtime.defineGlobalConstant("STDERR", stderr);
  16233. runtime.defineVariable(new LoadedFeatures(runtime, "$\""));
  16234. runtime.defineVariable(new LoadedFeatures(runtime, "$LOADED_FEATURES"));
  16235. runtime.defineVariable(new LoadPath(runtime, "$:"));
  16236. runtime.defineVariable(new LoadPath(runtime, "$-I"));
  16237. runtime.defineVariable(new LoadPath(runtime, "$LOAD_PATH"));
  16238. runtime.defineVariable(new MatchMatchGlobalVariable(runtime, "$&"));
  16239. runtime.defineVariable(new PreMatchGlobalVariable(runtime, "$`"));
  16240. runtime.defineVariable(new PostMatchGlobalVariable(runtime, "$'"));
  16241. runtime.defineVariable(new LastMatchGlobalVariable(runtime, "$+"));
  16242. runtime.defineVariable(new BackRefGlobalVariable(runtime, "$~"));
  16243. // On platforms without a c-library accessable through JNA, getpid will return hashCode
  16244. // as $$ used to. Using $$ to kill processes could take down many runtimes, but by basing
  16245. // $$ on getpid() where available, we have the same semantics as MRI.
  16246. runtime.getGlobalVariables().defineReadonly("$$", new ValueAccessor(runtime.newFixnum(runtime.getPosix().getpid())));
  16247. // after defn of $stderr as the call may produce warnings
  16248. defineGlobalEnvConstants(runtime);
  16249. // Fixme: Do we need the check or does Main.java not call this...they should consolidate
  16250. if (runtime.getGlobalVariables().get("$*").isNil()) {
  16251. runtime.getGlobalVariables().defineReadonly("$*", new ValueAccessor(runtime.newArray()));
  16252. }
  16253. runtime.getGlobalVariables().defineReadonly("$-p",
  16254. new ValueAccessor(runtime.getInstanceConfig().isAssumePrinting() ? runtime.getTrue() : runtime.getNil()));
  16255. runtime.getGlobalVariables().defineReadonly("$-n",
  16256. new ValueAccessor(runtime.getInstanceConfig().isAssumeLoop() ? runtime.getTrue() : runtime.getNil()));
  16257. runtime.getGlobalVariables().defineReadonly("$-a",
  16258. new ValueAccessor(runtime.getInstanceConfig().isSplit() ? runtime.getTrue() : runtime.getNil()));
  16259. runtime.getGlobalVariables().defineReadonly("$-l",
  16260. new ValueAccessor(runtime.getInstanceConfig().isProcessLineEnds() ? runtime.getTrue() : runtime.getNil()));
  16261. // ARGF, $< object
  16262. RubyArgsFile.initArgsFile(runtime);
  16263. }
  16264. private static void defineGlobalEnvConstants(Ruby runtime) {
  16265. Map environmentVariableMap = null;
  16266. OSEnvironment environment = new OSEnvironment();
  16267. try {
  16268. environmentVariableMap = environment.getEnvironmentVariableMap(runtime);
  16269. } catch (OSEnvironmentReaderExcepton e) {
  16270. // If the environment variables are not accessible shouldn't terminate
  16271. runtime.getWarnings().warn(ID.MISCELLANEOUS, e.getMessage());
  16272. }
  16273. if (environmentVariableMap == null) {
  16274. // if the environment variables can't be obtained, define an empty ENV
  16275. environmentVariableMap = new HashMap();
  16276. }
  16277. StringOnlyRubyHash h1 = new StringOnlyRubyHash(runtime,
  16278. environmentVariableMap, runtime.getNil());
  16279. h1.getSingletonClass().defineAnnotatedMethods(StringOnlyRubyHash.class);
  16280. runtime.defineGlobalConstant("ENV", h1);
  16281. // Define System.getProperties() in ENV_JAVA
  16282. Map systemProps = environment.getSystemPropertiesMap(runtime);
  16283. runtime.defineGlobalConstant("ENV_JAVA", new StringOnlyRubyHash(
  16284. runtime, systemProps, runtime.getNil()));
  16285. }
  16286. private static class NonEffectiveGlobalVariable extends GlobalVariable {
  16287. public NonEffectiveGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16288. super(runtime, name, value);
  16289. }
  16290. @Override
  16291. public IRubyObject set(IRubyObject value) {
  16292. runtime.getWarnings().warn(ID.INEFFECTIVE_GLOBAL, "warning: variable " + name + " is no longer effective; ignored", name);
  16293. return value;
  16294. }
  16295. @Override
  16296. public IRubyObject get() {
  16297. runtime.getWarnings().warn(ID.INEFFECTIVE_GLOBAL, "warning: variable " + name + " is no longer effective", name);
  16298. return runtime.getFalse();
  16299. }
  16300. }
  16301. private static class LastExitStatusVariable extends GlobalVariable {
  16302. public LastExitStatusVariable(Ruby runtime, String name) {
  16303. super(runtime, name, runtime.getNil());
  16304. }
  16305. @Override
  16306. public IRubyObject get() {
  16307. IRubyObject lastExitStatus = runtime.getCurrentContext().getLastExitStatus();
  16308. return lastExitStatus == null ? runtime.getNil() : lastExitStatus;
  16309. }
  16310. @Override
  16311. public IRubyObject set(IRubyObject lastExitStatus) {
  16312. runtime.getCurrentContext().setLastExitStatus(lastExitStatus);
  16313. return lastExitStatus;
  16314. }
  16315. }
  16316. private static class MatchMatchGlobalVariable extends GlobalVariable {
  16317. public MatchMatchGlobalVariable(Ruby runtime, String name) {
  16318. super(runtime, name, runtime.getNil());
  16319. }
  16320. @Override
  16321. public IRubyObject get() {
  16322. return RubyRegexp.last_match(runtime.getCurrentContext().getCurrentFrame().getBackRef());
  16323. }
  16324. }
  16325. private static class PreMatchGlobalVariable extends GlobalVariable {
  16326. public PreMatchGlobalVariable(Ruby runtime, String name) {
  16327. super(runtime, name, runtime.getNil());
  16328. }
  16329. @Override
  16330. public IRubyObject get() {
  16331. return RubyRegexp.match_pre(runtime.getCurrentContext().getCurrentFrame().getBackRef());
  16332. }
  16333. }
  16334. private static class PostMatchGlobalVariable extends GlobalVariable {
  16335. public PostMatchGlobalVariable(Ruby runtime, String name) {
  16336. super(runtime, name, runtime.getNil());
  16337. }
  16338. @Override
  16339. public IRubyObject get() {
  16340. return RubyRegexp.match_post(runtime.getCurrentContext().getCurrentFrame().getBackRef());
  16341. }
  16342. }
  16343. private static class LastMatchGlobalVariable extends GlobalVariable {
  16344. public LastMatchGlobalVariable(Ruby runtime, String name) {
  16345. super(runtime, name, runtime.getNil());
  16346. }
  16347. @Override
  16348. public IRubyObject get() {
  16349. return RubyRegexp.match_last(runtime.getCurrentContext().getCurrentFrame().getBackRef());
  16350. }
  16351. }
  16352. private static class BackRefGlobalVariable extends GlobalVariable {
  16353. public BackRefGlobalVariable(Ruby runtime, String name) {
  16354. super(runtime, name, runtime.getNil());
  16355. }
  16356. @Override
  16357. public IRubyObject get() {
  16358. return RuntimeHelpers.getBackref(runtime, runtime.getCurrentContext());
  16359. }
  16360. @Override
  16361. public IRubyObject set(IRubyObject value) {
  16362. RuntimeHelpers.setBackref(runtime, runtime.getCurrentContext(), value);
  16363. return value;
  16364. }
  16365. }
  16366. // Accessor methods.
  16367. private static class LineNumberGlobalVariable extends GlobalVariable {
  16368. public LineNumberGlobalVariable(Ruby runtime, String name, RubyFixnum value) {
  16369. super(runtime, name, value);
  16370. }
  16371. @Override
  16372. public IRubyObject set(IRubyObject value) {
  16373. RubyArgsFile.setCurrentLineNumber(runtime.getGlobalVariables().get("$<"),RubyNumeric.fix2int(value));
  16374. return super.set(value);
  16375. }
  16376. }
  16377. private static class ErrorInfoGlobalVariable extends GlobalVariable {
  16378. public ErrorInfoGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16379. super(runtime, name, null);
  16380. set(value);
  16381. }
  16382. @Override
  16383. public IRubyObject set(IRubyObject value) {
  16384. if (!value.isNil() &&
  16385. !runtime.getException().isInstance(value) &&
  16386. !(JavaUtil.isJavaObject(value) && JavaUtil.unwrapJavaObject(value) instanceof Exception)) {
  16387. throw runtime.newTypeError("assigning non-exception to $!");
  16388. }
  16389. return runtime.getCurrentContext().setErrorInfo(value);
  16390. }
  16391. @Override
  16392. public IRubyObject get() {
  16393. return runtime.getCurrentContext().getErrorInfo();
  16394. }
  16395. }
  16396. // FIXME: move out of this class!
  16397. public static class StringGlobalVariable extends GlobalVariable {
  16398. public StringGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16399. super(runtime, name, value);
  16400. }
  16401. @Override
  16402. public IRubyObject set(IRubyObject value) {
  16403. if (!value.isNil() && ! (value instanceof RubyString)) {
  16404. throw runtime.newTypeError("value of " + name() + " must be a String");
  16405. }
  16406. return super.set(value);
  16407. }
  16408. }
  16409. public static class KCodeGlobalVariable extends GlobalVariable {
  16410. public KCodeGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16411. super(runtime, name, value);
  16412. }
  16413. @Override
  16414. public IRubyObject get() {
  16415. return runtime.getKCode().kcode(runtime);
  16416. }
  16417. @Override
  16418. public IRubyObject set(IRubyObject value) {
  16419. runtime.setKCode(KCode.create(runtime, value.convertToString().toString()));
  16420. return value;
  16421. }
  16422. }
  16423. private static class SafeGlobalVariable extends GlobalVariable {
  16424. public SafeGlobalVariable(Ruby runtime, String name) {
  16425. super(runtime, name, null);
  16426. }
  16427. @Override
  16428. public IRubyObject get() {
  16429. return runtime.newFixnum(runtime.getSafeLevel());
  16430. }
  16431. @Override
  16432. public IRubyObject set(IRubyObject value) {
  16433. // int level = RubyNumeric.fix2int(value);
  16434. // if (level < runtime.getSafeLevel()) {
  16435. // throw runtime.newSecurityError("tried to downgrade safe level from " +
  16436. // runtime.getSafeLevel() + " to " + level);
  16437. // }
  16438. // runtime.setSafeLevel(level);
  16439. // thread.setSafeLevel(level);
  16440. runtime.getWarnings().warn(ID.SAFE_NOT_SUPPORTED, "SAFE levels are not supported in JRuby");
  16441. return RubyFixnum.newFixnum(runtime, runtime.getSafeLevel());
  16442. }
  16443. }
  16444. private static class VerboseGlobalVariable extends GlobalVariable {
  16445. public VerboseGlobalVariable(Ruby runtime, String name, IRubyObject initialValue) {
  16446. super(runtime, name, initialValue);
  16447. set(initialValue);
  16448. }
  16449. @Override
  16450. public IRubyObject get() {
  16451. return runtime.getVerbose();
  16452. }
  16453. @Override
  16454. public IRubyObject set(IRubyObject newValue) {
  16455. if (newValue.isNil()) {
  16456. runtime.setVerbose(newValue);
  16457. } else {
  16458. runtime.setVerbose(runtime.newBoolean(newValue.isTrue()));
  16459. }
  16460. return newValue;
  16461. }
  16462. }
  16463. private static class DebugGlobalVariable extends GlobalVariable {
  16464. public DebugGlobalVariable(Ruby runtime, String name, IRubyObject initialValue) {
  16465. super(runtime, name, initialValue);
  16466. set(initialValue);
  16467. }
  16468. @Override
  16469. public IRubyObject get() {
  16470. return runtime.getDebug();
  16471. }
  16472. @Override
  16473. public IRubyObject set(IRubyObject newValue) {
  16474. if (newValue.isNil()) {
  16475. runtime.setDebug(newValue);
  16476. } else {
  16477. runtime.setDebug(runtime.newBoolean(newValue.isTrue()));
  16478. }
  16479. return newValue;
  16480. }
  16481. }
  16482. private static class BacktraceGlobalVariable extends GlobalVariable {
  16483. public BacktraceGlobalVariable(Ruby runtime, String name) {
  16484. super(runtime, name, null);
  16485. }
  16486. @Override
  16487. public IRubyObject get() {
  16488. IRubyObject errorInfo = runtime.getGlobalVariables().get("$!");
  16489. IRubyObject backtrace = errorInfo.isNil() ? runtime.getNil() : errorInfo.callMethod(errorInfo.getRuntime().getCurrentContext(), "backtrace");
  16490. //$@ returns nil if $!.backtrace is not an array
  16491. if (!(backtrace instanceof RubyArray)) {
  16492. backtrace = runtime.getNil();
  16493. }
  16494. return backtrace;
  16495. }
  16496. @Override
  16497. public IRubyObject set(IRubyObject value) {
  16498. if (runtime.getGlobalVariables().get("$!").isNil()) {
  16499. throw runtime.newArgumentError("$! not set.");
  16500. }
  16501. runtime.getGlobalVariables().get("$!").callMethod(value.getRuntime().getCurrentContext(), "set_backtrace", value);
  16502. return value;
  16503. }
  16504. }
  16505. private static class LastlineGlobalVariable extends GlobalVariable {
  16506. public LastlineGlobalVariable(Ruby runtime, String name) {
  16507. super(runtime, name, null);
  16508. }
  16509. @Override
  16510. public IRubyObject get() {
  16511. return RuntimeHelpers.getLastLine(runtime, runtime.getCurrentContext());
  16512. }
  16513. @Override
  16514. public IRubyObject set(IRubyObject value) {
  16515. RuntimeHelpers.setLastLine(runtime, runtime.getCurrentContext(), value);
  16516. return value;
  16517. }
  16518. }
  16519. private static class InputGlobalVariable extends GlobalVariable {
  16520. public InputGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16521. super(runtime, name, value);
  16522. }
  16523. @Override
  16524. public IRubyObject set(IRubyObject value) {
  16525. if (value == get()) {
  16526. return value;
  16527. }
  16528. return super.set(value);
  16529. }
  16530. }
  16531. private static class OutputGlobalVariable extends GlobalVariable {
  16532. public OutputGlobalVariable(Ruby runtime, String name, IRubyObject value) {
  16533. super(runtime, name, value);
  16534. }
  16535. @Override
  16536. public IRubyObject set(IRubyObject value) {
  16537. if (value == get()) {
  16538. return value;
  16539. }
  16540. if (value instanceof RubyIO) {
  16541. RubyIO io = (RubyIO)value;
  16542. // HACK: in order to have stdout/err act like ttys and flush always,
  16543. // we set anything assigned to stdout/stderr to sync
  16544. io.getHandler().setSync(true);
  16545. }
  16546. if (!value.respondsTo("write")) {
  16547. throw runtime.newTypeError(name() + " must have write method, " +
  16548. value.getType().getName() + " given");
  16549. }
  16550. return super.set(value);
  16551. }
  16552. }
  16553. private static class LoadPath extends ReadonlyGlobalVariable {
  16554. public LoadPath(Ruby runtime, String name) {
  16555. super(runtime, name, null);
  16556. }
  16557. /**
  16558. * @see org.jruby.runtime.GlobalVariable#get()
  16559. */
  16560. @Override
  16561. public IRubyObject get() {
  16562. return runtime.getLoadService().getLoadPath();
  16563. }
  16564. }
  16565. private static class LoadedFeatures extends ReadonlyGlobalVariable {
  16566. public LoadedFeatures(Ruby runtime, String name) {
  16567. super(runtime, name, null);
  16568. }
  16569. /**
  16570. * @see org.jruby.runtime.GlobalVariable#get()
  16571. */
  16572. @Override
  16573. public IRubyObject get() {
  16574. return runtime.getLoadService().getLoadedFeatures();
  16575. }
  16576. }
  16577. }
  16578. /***** BEGIN LICENSE BLOCK *****
  16579. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  16580. *
  16581. * The contents of this file are subject to the Common Public
  16582. * License Version 1.0 (the "License"); you may not use this file
  16583. * except in compliance with the License. You may obtain a copy of
  16584. * the License at http://www.eclipse.org/legal/cpl-v10.html
  16585. *
  16586. * Software distributed under the License is distributed on an "AS
  16587. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  16588. * implied. See the License for the specific language governing
  16589. * rights and limitations under the License.
  16590. *
  16591. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  16592. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  16593. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  16594. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  16595. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  16596. * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
  16597. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  16598. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  16599. * Copyright (C) 2006 Ola Bini <Ola.Bini@ki.se>
  16600. * Copyright (C) 2006 Tim Azzopardi <tim@tigerfive.com>
  16601. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  16602. * Copyright (C) 2007 MenTaLguY <mental@rydia.net>
  16603. *
  16604. * Alternatively, the contents of this file may be used under the terms of
  16605. * either of the GNU General Public License Version 2 or later (the "GPL"),
  16606. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  16607. * in which case the provisions of the GPL or the LGPL are applicable instead
  16608. * of those above. If you wish to allow use of your version of this file only
  16609. * under the terms of either the GPL or the LGPL, and not to allow others to
  16610. * use your version of this file under the terms of the CPL, indicate your
  16611. * decision by deleting the provisions above and replace them with the notice
  16612. * and other provisions required by the GPL or the LGPL. If you do not delete
  16613. * the provisions above, a recipient may use your version of this file under
  16614. * the terms of any one of the CPL, the GPL or the LGPL.
  16615. ***** END LICENSE BLOCK *****/
  16616. package org.jruby;
  16617. import java.io.IOException;
  16618. import java.util.AbstractCollection;
  16619. import java.util.AbstractSet;
  16620. import java.util.Collection;
  16621. import java.util.Iterator;
  16622. import java.util.Map;
  16623. import java.util.NoSuchElementException;
  16624. import java.util.Set;
  16625. import org.jruby.anno.JRubyClass;
  16626. import org.jruby.anno.JRubyMethod;
  16627. import org.jruby.common.IRubyWarnings.ID;
  16628. import org.jruby.javasupport.JavaUtil;
  16629. import org.jruby.javasupport.util.RuntimeHelpers;
  16630. import org.jruby.runtime.Arity;
  16631. import org.jruby.runtime.Block;
  16632. import org.jruby.runtime.ClassIndex;
  16633. import org.jruby.runtime.MethodIndex;
  16634. import org.jruby.runtime.ObjectAllocator;
  16635. import org.jruby.runtime.ThreadContext;
  16636. import org.jruby.runtime.Visibility;
  16637. import org.jruby.runtime.builtin.IRubyObject;
  16638. import org.jruby.runtime.marshal.MarshalStream;
  16639. import org.jruby.runtime.marshal.UnmarshalStream;
  16640. import org.jruby.util.ByteList;
  16641. import org.jruby.util.TypeConverter;
  16642. // Design overview:
  16643. //
  16644. // RubyHash is implemented as hash table with a singly-linked list of
  16645. // RubyHash.RubyHashEntry objects for each bucket. RubyHashEntry objects
  16646. // are also kept in a doubly-linked list which reflects their insertion
  16647. // order and is used for iteration. For simplicity, this latter list is
  16648. // circular; a dummy RubyHashEntry, RubyHash.head, is used to mark the
  16649. // ends of the list.
  16650. //
  16651. // When an entry is removed from the table, it is also removed from the
  16652. // doubly-linked list. However, while the reference to the previous
  16653. // RubyHashEntry is cleared (to mark the entry as dead), the reference
  16654. // to the next RubyHashEntry is preserved so that iterators are not
  16655. // invalidated: any iterator with a reference to a dead entry can climb
  16656. // back up into the list of live entries by chasing next references until
  16657. // it finds a live entry (or head).
  16658. //
  16659. // Ordinarily, this scheme would require O(N) time to clear a hash (since
  16660. // each RubyHashEntry would need to be visited and unlinked from the
  16661. // iteration list), but RubyHash also maintains a generation count. Every
  16662. // time the hash is cleared, the doubly-linked list is simply discarded and
  16663. // the generation count incremented. Iterators check to see whether the
  16664. // generation count has changed; if it has, they reset themselves back to
  16665. // the new start of the list.
  16666. //
  16667. // This design means that iterators are never invalidated by changes to the
  16668. // hashtable, and they do not need to modify the structure during their
  16669. // lifecycle.
  16670. //
  16671. /** Implementation of the Hash class.
  16672. *
  16673. * Concurrency: no synchronization is required among readers, but
  16674. * all users must synchronize externally with writers.
  16675. *
  16676. */
  16677. @JRubyClass(name = "Hash", include="Enumerable")
  16678. public class RubyHash extends RubyObject implements Map {
  16679. public static RubyClass createHashClass(Ruby runtime) {
  16680. RubyClass hashc = runtime.defineClass("Hash", runtime.getObject(), HASH_ALLOCATOR);
  16681. runtime.setHash(hashc);
  16682. hashc.index = ClassIndex.HASH;
  16683. hashc.kindOf = new RubyModule.KindOf() {
  16684. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  16685. return obj instanceof RubyHash;
  16686. }
  16687. };
  16688. hashc.includeModule(runtime.getEnumerable());
  16689. hashc.defineAnnotatedMethods(RubyHash.class);
  16690. return hashc;
  16691. }
  16692. private final static ObjectAllocator HASH_ALLOCATOR = new ObjectAllocator() {
  16693. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  16694. return new RubyHash(runtime, klass);
  16695. }
  16696. };
  16697. public int getNativeTypeIndex() {
  16698. return ClassIndex.HASH;
  16699. }
  16700. /** rb_hash_s_create
  16701. *
  16702. */
  16703. @JRubyMethod(name = "[]", rest = true, frame = true, meta = true)
  16704. public static IRubyObject create(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  16705. RubyClass klass = (RubyClass) recv;
  16706. Ruby runtime = context.getRuntime();
  16707. RubyHash hash;
  16708. if (args.length == 1) {
  16709. IRubyObject tmp = TypeConverter.convertToTypeWithCheck(
  16710. args[0], runtime.getHash(), MethodIndex.TO_HASH, "to_hash");
  16711. if (!tmp.isNil()) {
  16712. RubyHash otherHash = (RubyHash) tmp;
  16713. return new RubyHash(runtime, klass, otherHash);
  16714. }
  16715. }
  16716. if ((args.length & 1) != 0) {
  16717. throw runtime.newArgumentError("odd number of args for Hash");
  16718. }
  16719. hash = (RubyHash)klass.allocate();
  16720. for (int i=0; i < args.length; i+=2) hash.op_aset(context, args[i], args[i+1]);
  16721. return hash;
  16722. }
  16723. /** rb_hash_new
  16724. *
  16725. */
  16726. public static final RubyHash newHash(Ruby runtime) {
  16727. return new RubyHash(runtime);
  16728. }
  16729. /** rb_hash_new
  16730. *
  16731. */
  16732. public static final RubyHash newHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
  16733. assert defaultValue != null;
  16734. return new RubyHash(runtime, valueMap, defaultValue);
  16735. }
  16736. private RubyHashEntry[] table;
  16737. private int size = 0;
  16738. private int threshold;
  16739. private static final int PROCDEFAULT_HASH_F = 1 << 10;
  16740. private IRubyObject ifNone;
  16741. private RubyHash(Ruby runtime, RubyClass klass, RubyHash other) {
  16742. super(runtime, klass);
  16743. this.ifNone = runtime.getNil();
  16744. threshold = INITIAL_THRESHOLD;
  16745. table = other.internalCopyTable(head);
  16746. size = other.size;
  16747. }
  16748. public RubyHash(Ruby runtime, RubyClass klass) {
  16749. super(runtime, klass);
  16750. this.ifNone = runtime.getNil();
  16751. alloc();
  16752. }
  16753. public RubyHash(Ruby runtime) {
  16754. this(runtime, runtime.getNil());
  16755. }
  16756. public RubyHash(Ruby runtime, IRubyObject defaultValue) {
  16757. super(runtime, runtime.getHash());
  16758. this.ifNone = defaultValue;
  16759. alloc();
  16760. }
  16761. /*
  16762. * Constructor for internal usage (mainly for Array#|, Array#&, Array#- and Array#uniq)
  16763. * it doesn't initialize ifNone field
  16764. */
  16765. RubyHash(Ruby runtime, boolean objectSpace) {
  16766. super(runtime, runtime.getHash(), objectSpace);
  16767. alloc();
  16768. }
  16769. // TODO should this be deprecated ? (to be efficient, internals should deal with RubyHash directly)
  16770. public RubyHash(Ruby runtime, Map valueMap, IRubyObject defaultValue) {
  16771. super(runtime, runtime.getHash());
  16772. this.ifNone = defaultValue;
  16773. alloc();
  16774. for (Iterator iter = valueMap.entrySet().iterator();iter.hasNext();) {
  16775. Map.Entry e = (Map.Entry)iter.next();
  16776. internalPut((IRubyObject)e.getKey(), (IRubyObject)e.getValue());
  16777. }
  16778. }
  16779. private final void alloc() {
  16780. threshold = INITIAL_THRESHOLD;
  16781. generation++;
  16782. head.nextAdded = head.prevAdded = head;
  16783. table = new RubyHashEntry[MRI_HASH_RESIZE ? MRI_INITIAL_CAPACITY : JAVASOFT_INITIAL_CAPACITY];
  16784. }
  16785. /* ============================
  16786. * Here are hash internals
  16787. * (This could be extracted to a separate class but it's not too large though)
  16788. * ============================
  16789. */
  16790. private static final int MRI_PRIMES[] = {
  16791. 8 + 3, 16 + 3, 32 + 5, 64 + 3, 128 + 3, 256 + 27, 512 + 9, 1024 + 9, 2048 + 5, 4096 + 3,
  16792. 8192 + 27, 16384 + 43, 32768 + 3, 65536 + 45, 131072 + 29, 262144 + 3, 524288 + 21, 1048576 + 7,
  16793. 2097152 + 17, 4194304 + 15, 8388608 + 9, 16777216 + 43, 33554432 + 35, 67108864 + 15,
  16794. 134217728 + 29, 268435456 + 3, 536870912 + 11, 1073741824 + 85, 0
  16795. };
  16796. private static final int JAVASOFT_INITIAL_CAPACITY = 8; // 16 ?
  16797. private static final int MRI_INITIAL_CAPACITY = MRI_PRIMES[0];
  16798. private static final int INITIAL_THRESHOLD = JAVASOFT_INITIAL_CAPACITY - (JAVASOFT_INITIAL_CAPACITY >> 2);
  16799. private static final int MAXIMUM_CAPACITY = 1 << 30;
  16800. private static final RubyHashEntry NO_ENTRY = new RubyHashEntry();
  16801. private int generation = 0; // generation count for O(1) clears
  16802. private final RubyHashEntry head = new RubyHashEntry();
  16803. { head.prevAdded = head.nextAdded = head; }
  16804. static final class RubyHashEntry implements Map.Entry {
  16805. private IRubyObject key;
  16806. private IRubyObject value;
  16807. private RubyHashEntry next;
  16808. private RubyHashEntry prevAdded;
  16809. private RubyHashEntry nextAdded;
  16810. private int hash;
  16811. RubyHashEntry() {
  16812. key = NEVER;
  16813. }
  16814. RubyHashEntry(int h, IRubyObject k, IRubyObject v, RubyHashEntry e, RubyHashEntry head) {
  16815. key = k; value = v; next = e; hash = h;
  16816. prevAdded = head.prevAdded;
  16817. nextAdded = head;
  16818. nextAdded.prevAdded = this;
  16819. prevAdded.nextAdded = this;
  16820. }
  16821. public void detach() {
  16822. if (prevAdded != null) {
  16823. prevAdded.nextAdded = nextAdded;
  16824. nextAdded.prevAdded = prevAdded;
  16825. prevAdded = null;
  16826. }
  16827. }
  16828. public boolean isLive() {
  16829. return prevAdded != null;
  16830. }
  16831. public Object getKey() {
  16832. return key;
  16833. }
  16834. public Object getJavaifiedKey(){
  16835. return JavaUtil.convertRubyToJava(key);
  16836. }
  16837. public Object getValue() {
  16838. return value;
  16839. }
  16840. public Object getJavaifiedValue() {
  16841. return JavaUtil.convertRubyToJava(value);
  16842. }
  16843. public Object setValue(Object value) {
  16844. IRubyObject oldValue = this.value;
  16845. if (value instanceof IRubyObject) {
  16846. this.value = (IRubyObject)value;
  16847. } else {
  16848. throw new UnsupportedOperationException("directEntrySet() doesn't support setValue for non IRubyObject instance entries, convert them manually or use entrySet() instead");
  16849. }
  16850. return oldValue;
  16851. }
  16852. public boolean equals(Object other){
  16853. if(!(other instanceof RubyHashEntry)) return false;
  16854. RubyHashEntry otherEntry = (RubyHashEntry)other;
  16855. if(key == otherEntry.key || key.eql(otherEntry.key)){
  16856. if(value == otherEntry.value || value.equals(otherEntry.value)) return true;
  16857. }
  16858. return false;
  16859. }
  16860. public int hashCode(){
  16861. return key.hashCode() ^ value.hashCode();
  16862. }
  16863. }
  16864. private static int JavaSoftHashValue(int h) {
  16865. h ^= (h >>> 20) ^ (h >>> 12);
  16866. return h ^ (h >>> 7) ^ (h >>> 4);
  16867. }
  16868. private static int JavaSoftBucketIndex(final int h, final int length) {
  16869. return h & (length - 1);
  16870. }
  16871. private static int MRIHashValue(int h) {
  16872. return h & HASH_SIGN_BIT_MASK;
  16873. }
  16874. private static final int HASH_SIGN_BIT_MASK = ~(1 << 31);
  16875. private static int MRIBucketIndex(final int h, final int length) {
  16876. return (h % length);
  16877. }
  16878. private final void resize(int newCapacity) {
  16879. final RubyHashEntry[] oldTable = table;
  16880. final RubyHashEntry[] newTable = new RubyHashEntry[newCapacity];
  16881. for (int j = 0; j < oldTable.length; j++) {
  16882. RubyHashEntry entry = oldTable[j];
  16883. oldTable[j] = null;
  16884. while (entry != null) {
  16885. RubyHashEntry next = entry.next;
  16886. int i = bucketIndex(entry.hash, newCapacity);
  16887. entry.next = newTable[i];
  16888. newTable[i] = entry;
  16889. entry = next;
  16890. }
  16891. }
  16892. table = newTable;
  16893. }
  16894. private final void JavaSoftCheckResize() {
  16895. if (size > threshold) {
  16896. int oldCapacity = table.length;
  16897. if (oldCapacity == MAXIMUM_CAPACITY) {
  16898. threshold = Integer.MAX_VALUE;
  16899. return;
  16900. }
  16901. int newCapacity = table.length << 1;
  16902. resize(newCapacity);
  16903. threshold = newCapacity - (newCapacity >> 2);
  16904. }
  16905. }
  16906. private static final int MIN_CAPA = 8;
  16907. private static final int ST_DEFAULT_MAX_DENSITY = 5;
  16908. private final void MRICheckResize() {
  16909. if (size / table.length > ST_DEFAULT_MAX_DENSITY) {
  16910. int forSize = table.length + 1; // size + 1;
  16911. for (int i=0, newCapacity = MIN_CAPA; i < MRI_PRIMES.length; i++, newCapacity <<= 1) {
  16912. if (newCapacity > forSize) {
  16913. resize(MRI_PRIMES[i]);
  16914. return;
  16915. }
  16916. }
  16917. return; // suboptimal for large hashes (> 1073741824 + 85 entries) not very likely to happen
  16918. }
  16919. }
  16920. // ------------------------------
  16921. private static boolean MRI_HASH = true;
  16922. private static boolean MRI_HASH_RESIZE = true;
  16923. private static int hashValue(final int h) {
  16924. return MRI_HASH ? MRIHashValue(h) : JavaSoftHashValue(h);
  16925. }
  16926. private static int bucketIndex(final int h, final int length) {
  16927. return MRI_HASH ? MRIBucketIndex(h, length) : JavaSoftBucketIndex(h, length);
  16928. }
  16929. private void checkResize() {
  16930. if (MRI_HASH_RESIZE) MRICheckResize(); else JavaSoftCheckResize();
  16931. }
  16932. // ------------------------------
  16933. public static long collisions = 0;
  16934. // put implementation
  16935. private final void internalPut(final IRubyObject key, final IRubyObject value) {
  16936. internalPut(key, value, true);
  16937. }
  16938. private final void internalPut(final IRubyObject key, final IRubyObject value, final boolean checkForExisting) {
  16939. checkResize();
  16940. final int hash = hashValue(key.hashCode());
  16941. final int i = bucketIndex(hash, table.length);
  16942. // if (table[i] != null) collisions++;
  16943. if (checkForExisting) {
  16944. for (RubyHashEntry entry = table[i]; entry != null; entry = entry.next) {
  16945. IRubyObject k;
  16946. if (entry.hash == hash && ((k = entry.key) == key || key.eql(k))) {
  16947. entry.value = value;
  16948. return;
  16949. }
  16950. }
  16951. }
  16952. table[i] = new RubyHashEntry(hash, key, value, table[i], head);
  16953. size++;
  16954. }
  16955. // get implementation
  16956. private final IRubyObject internalGet(IRubyObject key) { // specialized for value
  16957. return internalGetEntry(key).value;
  16958. }
  16959. private final RubyHashEntry internalGetEntry(IRubyObject key) {
  16960. final int hash = hashValue(key.hashCode());
  16961. for (RubyHashEntry entry = table[bucketIndex(hash, table.length)]; entry != null; entry = entry.next) {
  16962. IRubyObject k;
  16963. if (entry.hash == hash && ((k = entry.key) == key || key.eql(k))) return entry;
  16964. }
  16965. return NO_ENTRY;
  16966. }
  16967. // delete implementation
  16968. private final RubyHashEntry internalDelete(final IRubyObject key) {
  16969. return internalDelete(hashValue(key.hashCode()), MATCH_KEY, key);
  16970. }
  16971. private final RubyHashEntry internalDeleteEntry(final RubyHashEntry entry) {
  16972. // n.b. we need to recompute the hash in case the key object was modified
  16973. return internalDelete(hashValue(entry.key.hashCode()), MATCH_ENTRY, entry);
  16974. }
  16975. private final RubyHashEntry internalDelete(final int hash, final EntryMatchType matchType, final Object obj) {
  16976. final int i = bucketIndex(hash, table.length);
  16977. RubyHashEntry entry = table[i];
  16978. if (entry != null) {
  16979. RubyHashEntry prior = null;
  16980. for (; entry != null; prior = entry, entry = entry.next) {
  16981. if (entry.hash == hash && matchType.matches(entry, obj)) {
  16982. if (prior != null) {
  16983. prior.next = entry.next;
  16984. } else {
  16985. table[i] = entry.next;
  16986. }
  16987. entry.detach();
  16988. size--;
  16989. return entry;
  16990. }
  16991. }
  16992. }
  16993. return NO_ENTRY;
  16994. }
  16995. private static abstract class EntryMatchType {
  16996. public abstract boolean matches(final RubyHashEntry entry, final Object obj);
  16997. }
  16998. private static final EntryMatchType MATCH_KEY = new EntryMatchType() {
  16999. public boolean matches(final RubyHashEntry entry, final Object obj) {
  17000. final IRubyObject key = entry.key;
  17001. return obj == key || (((IRubyObject)obj).eql(key));
  17002. }
  17003. };
  17004. private static final EntryMatchType MATCH_ENTRY = new EntryMatchType() {
  17005. public boolean matches(final RubyHashEntry entry, final Object obj) {
  17006. return entry.equals(obj);
  17007. }
  17008. };
  17009. private final RubyHashEntry[] internalCopyTable(RubyHashEntry destHead) {
  17010. RubyHashEntry[]newTable = new RubyHashEntry[table.length];
  17011. for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
  17012. int i = bucketIndex(entry.hash, table.length);
  17013. newTable[i] = new RubyHashEntry(entry.hash, entry.key, entry.value, newTable[i], destHead);
  17014. }
  17015. return newTable;
  17016. }
  17017. public static abstract class Visitor {
  17018. public abstract void visit(IRubyObject key, IRubyObject value);
  17019. }
  17020. public void visitAll(Visitor visitor) {
  17021. int startGeneration = generation;
  17022. for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
  17023. if (startGeneration != generation) {
  17024. startGeneration = generation;
  17025. entry = head.nextAdded;
  17026. if (entry == head) break;
  17027. }
  17028. if (entry.isLive()) visitor.visit(entry.key, entry.value);
  17029. }
  17030. }
  17031. /* ============================
  17032. * End of hash internals
  17033. * ============================
  17034. */
  17035. /* ================
  17036. * Instance Methods
  17037. * ================
  17038. */
  17039. /** rb_hash_initialize
  17040. *
  17041. */
  17042. @JRubyMethod(name = "initialize", optional = 1, frame = true, visibility = Visibility.PRIVATE)
  17043. public IRubyObject initialize(IRubyObject[] args, final Block block) {
  17044. modify();
  17045. if (block.isGiven()) {
  17046. if (args.length > 0) throw getRuntime().newArgumentError("wrong number of arguments");
  17047. ifNone = getRuntime().newProc(Block.Type.PROC, block);
  17048. flags |= PROCDEFAULT_HASH_F;
  17049. } else {
  17050. Arity.checkArgumentCount(getRuntime(), args, 0, 1);
  17051. if (args.length == 1) ifNone = args[0];
  17052. }
  17053. return this;
  17054. }
  17055. /** rb_hash_default
  17056. *
  17057. */
  17058. @Deprecated
  17059. public IRubyObject default_value_get(ThreadContext context, IRubyObject[] args) {
  17060. switch (args.length) {
  17061. case 0: return default_value_get(context);
  17062. case 1: return default_value_get(context, args[0]);
  17063. default: throw context.getRuntime().newArgumentError(args.length, 1);
  17064. }
  17065. }
  17066. @JRubyMethod(name = "default", frame = true)
  17067. public IRubyObject default_value_get(ThreadContext context) {
  17068. if ((flags & PROCDEFAULT_HASH_F) != 0) {
  17069. return getRuntime().getNil();
  17070. }
  17071. return ifNone;
  17072. }
  17073. @JRubyMethod(name = "default", frame = true)
  17074. public IRubyObject default_value_get(ThreadContext context, IRubyObject arg) {
  17075. if ((flags & PROCDEFAULT_HASH_F) != 0) {
  17076. return RuntimeHelpers.invoke(context, ifNone, "call", this, arg);
  17077. }
  17078. return ifNone;
  17079. }
  17080. /** rb_hash_set_default
  17081. *
  17082. */
  17083. @JRubyMethod(name = "default=", required = 1)
  17084. public IRubyObject default_value_set(final IRubyObject defaultValue) {
  17085. modify();
  17086. ifNone = defaultValue;
  17087. flags &= ~PROCDEFAULT_HASH_F;
  17088. return ifNone;
  17089. }
  17090. /** rb_hash_default_proc
  17091. *
  17092. */
  17093. @JRubyMethod(name = "default_proc", frame = true)
  17094. public IRubyObject default_proc() {
  17095. return (flags & PROCDEFAULT_HASH_F) != 0 ? ifNone : getRuntime().getNil();
  17096. }
  17097. /** rb_hash_modify
  17098. *
  17099. */
  17100. public void modify() {
  17101. testFrozen("hash");
  17102. if (isTaint() && getRuntime().getSafeLevel() >= 4) {
  17103. throw getRuntime().newSecurityError("Insecure: can't modify hash");
  17104. }
  17105. }
  17106. /** inspect_hash
  17107. *
  17108. */
  17109. private IRubyObject inspectHash(final ThreadContext context) {
  17110. final ByteList buffer = new ByteList();
  17111. buffer.append('{');
  17112. final boolean[] firstEntry = new boolean[1];
  17113. firstEntry[0] = true;
  17114. visitAll(new Visitor() {
  17115. public void visit(IRubyObject key, IRubyObject value) {
  17116. if (!firstEntry[0]) buffer.append(',').append(' ');
  17117. buffer.append(inspect(context, key).getByteList());
  17118. buffer.append('=').append('>');
  17119. buffer.append(inspect(context, value).getByteList());
  17120. firstEntry[0] = false;
  17121. }
  17122. });
  17123. buffer.append('}');
  17124. return getRuntime().newString(buffer);
  17125. }
  17126. /** rb_hash_inspect
  17127. *
  17128. */
  17129. @JRubyMethod(name = "inspect")
  17130. public IRubyObject inspect(ThreadContext context) {
  17131. if (size == 0) return getRuntime().newString("{}");
  17132. if (getRuntime().isInspecting(this)) return getRuntime().newString("{...}");
  17133. try {
  17134. getRuntime().registerInspecting(this);
  17135. return inspectHash(context);
  17136. } finally {
  17137. getRuntime().unregisterInspecting(this);
  17138. }
  17139. }
  17140. /** rb_hash_size
  17141. *
  17142. */
  17143. @JRubyMethod(name = {"size", "length"})
  17144. public RubyFixnum rb_size() {
  17145. return getRuntime().newFixnum(size);
  17146. }
  17147. /** rb_hash_empty_p
  17148. *
  17149. */
  17150. @JRubyMethod(name = "empty?")
  17151. public RubyBoolean empty_p() {
  17152. return size == 0 ? getRuntime().getTrue() : getRuntime().getFalse();
  17153. }
  17154. /** rb_hash_to_a
  17155. *
  17156. */
  17157. @JRubyMethod(name = "to_a")
  17158. public RubyArray to_a() {
  17159. final Ruby runtime = getRuntime();
  17160. final RubyArray result = RubyArray.newArray(runtime, size);
  17161. visitAll(new Visitor() {
  17162. public void visit(IRubyObject key, IRubyObject value) {
  17163. result.append(RubyArray.newArray(runtime, key, value));
  17164. }
  17165. });
  17166. result.setTaint(isTaint());
  17167. return result;
  17168. }
  17169. /** rb_hash_to_s & to_s_hash
  17170. *
  17171. */
  17172. @JRubyMethod(name = "to_s")
  17173. public IRubyObject to_s() {
  17174. if (getRuntime().isInspecting(this)) return getRuntime().newString("{...}");
  17175. try {
  17176. getRuntime().registerInspecting(this);
  17177. return to_a().to_s();
  17178. } finally {
  17179. getRuntime().unregisterInspecting(this);
  17180. }
  17181. }
  17182. /** rb_hash_rehash
  17183. *
  17184. */
  17185. @JRubyMethod(name = "rehash")
  17186. public RubyHash rehash() {
  17187. modify();
  17188. final RubyHashEntry[] oldTable = table;
  17189. final RubyHashEntry[] newTable = new RubyHashEntry[oldTable.length];
  17190. for (int j = 0; j < oldTable.length; j++) {
  17191. RubyHashEntry entry = oldTable[j];
  17192. oldTable[j] = null;
  17193. while (entry != null) {
  17194. RubyHashEntry next = entry.next;
  17195. entry.hash = entry.key.hashCode(); // update the hash value
  17196. int i = bucketIndex(entry.hash, newTable.length);
  17197. entry.next = newTable[i];
  17198. newTable[i] = entry;
  17199. entry = next;
  17200. }
  17201. }
  17202. table = newTable;
  17203. return this;
  17204. }
  17205. /** rb_hash_to_hash
  17206. *
  17207. */
  17208. @JRubyMethod(name = "to_hash")
  17209. public RubyHash to_hash() {
  17210. return this;
  17211. }
  17212. public RubyHash convertToHash() {
  17213. return this;
  17214. }
  17215. public final void fastASet(IRubyObject key, IRubyObject value) {
  17216. internalPut(key, value);
  17217. }
  17218. @Deprecated
  17219. public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
  17220. return op_aset(getRuntime().getCurrentContext(), key, value);
  17221. }
  17222. /** rb_hash_aset
  17223. *
  17224. */
  17225. @JRubyMethod(name = {"[]=", "store"}, required = 2)
  17226. public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject value) {
  17227. modify();
  17228. if (!(key instanceof RubyString)) {
  17229. internalPut(key, value);
  17230. } else {
  17231. final RubyHashEntry entry = internalGetEntry(key);
  17232. if (entry != NO_ENTRY) {
  17233. entry.value = value;
  17234. } else {
  17235. RubyString realKey = (RubyString)key;
  17236. if (!realKey.isFrozen()) {
  17237. realKey = realKey.strDup(context.getRuntime(), realKey.getMetaClass().getRealClass());;
  17238. realKey.setFrozen(true);
  17239. }
  17240. internalPut(realKey, value, false);
  17241. }
  17242. }
  17243. return value;
  17244. }
  17245. /**
  17246. * Note: this is included as a compatibility measure for AR-JDBC
  17247. * @deprecated use RubyHash.op_aset instead
  17248. */
  17249. public IRubyObject aset(IRubyObject key, IRubyObject value) {
  17250. return op_aset(getRuntime().getCurrentContext(), key, value);
  17251. }
  17252. /**
  17253. * Note: this is included as a compatibility measure for Mongrel+JRuby
  17254. * @deprecated use RubyHash.op_aref instead
  17255. */
  17256. public IRubyObject aref(IRubyObject key) {
  17257. return op_aref(getRuntime().getCurrentContext(), key);
  17258. }
  17259. public final IRubyObject fastARef(IRubyObject key) { // retuns null when not found to avoid unnecessary getRuntime().getNil() call
  17260. return internalGet(key);
  17261. }
  17262. /** rb_hash_aref
  17263. *
  17264. */
  17265. @JRubyMethod(name = "[]", required = 1)
  17266. public IRubyObject op_aref(ThreadContext context, IRubyObject key) {
  17267. IRubyObject value;
  17268. return ((value = internalGet(key)) == null) ? callMethod(context, MethodIndex.DEFAULT, "default", key) : value;
  17269. }
  17270. /** rb_hash_fetch
  17271. *
  17272. */
  17273. @JRubyMethod(name = "fetch", required = 1, optional = 1, frame = true)
  17274. public IRubyObject fetch(ThreadContext context, IRubyObject[] args, Block block) {
  17275. if (args.length == 2 && block.isGiven()) {
  17276. getRuntime().getWarnings().warn(ID.BLOCK_BEATS_DEFAULT_VALUE, "block supersedes default value argument");
  17277. }
  17278. IRubyObject value;
  17279. if ((value = internalGet(args[0])) == null) {
  17280. if (block.isGiven()) return block.yield(context, args[0]);
  17281. if (args.length == 1) throw getRuntime().newIndexError("key not found");
  17282. return args[1];
  17283. }
  17284. return value;
  17285. }
  17286. /** rb_hash_has_key
  17287. *
  17288. */
  17289. @JRubyMethod(name = {"has_key?", "key?", "include?", "member?"}, required = 1)
  17290. public RubyBoolean has_key_p(IRubyObject key) {
  17291. return internalGetEntry(key) == NO_ENTRY ? getRuntime().getFalse() : getRuntime().getTrue();
  17292. }
  17293. private class Found extends RuntimeException {}
  17294. private boolean hasValue(final ThreadContext context, final IRubyObject expected) {
  17295. try {
  17296. visitAll(new Visitor() {
  17297. public void visit(IRubyObject key, IRubyObject value) {
  17298. if (equalInternal(context, value, expected)) {
  17299. throw new Found();
  17300. }
  17301. }
  17302. });
  17303. return false;
  17304. } catch (Found found) {
  17305. return true;
  17306. }
  17307. }
  17308. /** rb_hash_has_value
  17309. *
  17310. */
  17311. @JRubyMethod(name = {"has_value?", "value?"}, required = 1)
  17312. public RubyBoolean has_value_p(ThreadContext context, IRubyObject expected) {
  17313. return getRuntime().newBoolean(hasValue(context, expected));
  17314. }
  17315. /** rb_hash_each
  17316. *
  17317. */
  17318. @JRubyMethod(name = "each", frame = true)
  17319. public RubyHash each(final ThreadContext context, final Block block) {
  17320. final Ruby runtime = getRuntime();
  17321. visitAll(new Visitor() {
  17322. public void visit(IRubyObject key, IRubyObject value) {
  17323. // rb_assoc_new equivalent
  17324. block.yield(context, RubyArray.newArray(runtime, key, value), null, null, false);
  17325. }
  17326. });
  17327. return this;
  17328. }
  17329. /** rb_hash_each_pair
  17330. *
  17331. */
  17332. @JRubyMethod(name = "each_pair", frame = true)
  17333. public RubyHash each_pair(final ThreadContext context, final Block block) {
  17334. final Ruby runtime = getRuntime();
  17335. visitAll(new Visitor() {
  17336. public void visit(IRubyObject key, IRubyObject value) {
  17337. // rb_yield_values(2,...) equivalent
  17338. block.yield(context, RubyArray.newArray(runtime, key, value), null, null, true);
  17339. }
  17340. });
  17341. return this;
  17342. }
  17343. /** rb_hash_each_value
  17344. *
  17345. */
  17346. @JRubyMethod(name = "each_value", frame = true)
  17347. public RubyHash each_value(final ThreadContext context, final Block block) {
  17348. visitAll(new Visitor() {
  17349. public void visit(IRubyObject key, IRubyObject value) {
  17350. block.yield(context, value);
  17351. }
  17352. });
  17353. return this;
  17354. }
  17355. /** rb_hash_each_key
  17356. *
  17357. */
  17358. @JRubyMethod(name = "each_key", frame = true)
  17359. public RubyHash each_key(final ThreadContext context, final Block block) {
  17360. visitAll(new Visitor() {
  17361. public void visit(IRubyObject key, IRubyObject value) {
  17362. block.yield(context, key);
  17363. }
  17364. });
  17365. return this;
  17366. }
  17367. /** rb_hash_sort
  17368. *
  17369. */
  17370. @JRubyMethod(name = "sort", frame = true)
  17371. public RubyArray sort(Block block) {
  17372. return to_a().sort_bang(block);
  17373. }
  17374. private static class FoundKey extends RuntimeException {
  17375. public IRubyObject key;
  17376. FoundKey(IRubyObject key) {
  17377. super();
  17378. this.key = key;
  17379. }
  17380. }
  17381. /** rb_hash_index
  17382. *
  17383. */
  17384. @JRubyMethod(name = "index", required = 1)
  17385. public IRubyObject index(ThreadContext context, IRubyObject expected) {
  17386. IRubyObject key = internalIndex(context, expected);
  17387. if (key != null) {
  17388. return key;
  17389. } else {
  17390. return getRuntime().getNil();
  17391. }
  17392. }
  17393. private IRubyObject internalIndex(final ThreadContext context, final IRubyObject expected) {
  17394. try {
  17395. visitAll(new Visitor() {
  17396. public void visit(IRubyObject key, IRubyObject value) {
  17397. if (equalInternal(context, value, expected)) {
  17398. throw new FoundKey(key);
  17399. }
  17400. }
  17401. });
  17402. return null;
  17403. } catch (FoundKey found) {
  17404. return found.key;
  17405. }
  17406. }
  17407. /** rb_hash_indexes
  17408. *
  17409. */
  17410. @JRubyMethod(name = {"indexes", "indices"}, rest = true)
  17411. public RubyArray indices(ThreadContext context, IRubyObject[] indices) {
  17412. return values_at(context, indices);
  17413. }
  17414. /** rb_hash_keys
  17415. *
  17416. */
  17417. @JRubyMethod(name = "keys")
  17418. public RubyArray keys() {
  17419. final Ruby runtime = getRuntime();
  17420. final RubyArray keys = RubyArray.newArray(runtime, size);
  17421. visitAll(new Visitor() {
  17422. public void visit(IRubyObject key, IRubyObject value) {
  17423. keys.append(key);
  17424. }
  17425. });
  17426. return keys;
  17427. }
  17428. /** rb_hash_values
  17429. *
  17430. */
  17431. @JRubyMethod(name = "values")
  17432. public RubyArray rb_values() {
  17433. final RubyArray values = RubyArray.newArray(getRuntime(), size);
  17434. visitAll(new Visitor() {
  17435. public void visit(IRubyObject key, IRubyObject value) {
  17436. values.append(value);
  17437. }
  17438. });
  17439. return values;
  17440. }
  17441. /** rb_hash_equal
  17442. *
  17443. */
  17444. private static final boolean EQUAL_CHECK_DEFAULT_VALUE = false;
  17445. private static class Mismatch extends RuntimeException {}
  17446. @Override
  17447. @JRubyMethod(name = "==", required = 1)
  17448. public IRubyObject op_equal(final ThreadContext context, final IRubyObject other) {
  17449. if (this == other) return getRuntime().getTrue();
  17450. if (!(other instanceof RubyHash)) {
  17451. if (other.respondsTo("to_hash") && equalInternal(context, other, this)) return getRuntime().getTrue();
  17452. return getRuntime().getFalse();
  17453. }
  17454. final RubyHash otherHash = (RubyHash)other;
  17455. if (size != otherHash.size) return getRuntime().getFalse();
  17456. final Ruby runtime = getRuntime();
  17457. if (EQUAL_CHECK_DEFAULT_VALUE) {
  17458. if (!equalInternal(context, ifNone, otherHash.ifNone) &&
  17459. (flags & PROCDEFAULT_HASH_F) != (otherHash.flags & PROCDEFAULT_HASH_F)) return runtime.getFalse();
  17460. }
  17461. try {
  17462. visitAll(new Visitor() {
  17463. public void visit(IRubyObject key, IRubyObject value) {
  17464. IRubyObject otherValue = otherHash.internalGet(key);
  17465. if (otherValue == null || !equalInternal(context, value, otherValue)) throw new Mismatch();
  17466. }
  17467. });
  17468. return runtime.getTrue();
  17469. } catch (Mismatch e) {
  17470. return runtime.getFalse();
  17471. }
  17472. }
  17473. /** rb_hash_shift
  17474. *
  17475. */
  17476. @JRubyMethod(name = "shift")
  17477. public IRubyObject shift(ThreadContext context) {
  17478. modify();
  17479. RubyHashEntry entry = head.nextAdded;
  17480. if (entry != head) {
  17481. RubyArray result = RubyArray.newArray(getRuntime(), entry.key, entry.value);
  17482. internalDeleteEntry(entry);
  17483. return result;
  17484. }
  17485. if ((flags & PROCDEFAULT_HASH_F) != 0) {
  17486. return RuntimeHelpers.invoke(context, ifNone, "call", this, getRuntime().getNil());
  17487. } else {
  17488. return ifNone;
  17489. }
  17490. }
  17491. public final boolean fastDelete(IRubyObject key) {
  17492. return internalDelete(key) != NO_ENTRY;
  17493. }
  17494. /** rb_hash_delete
  17495. *
  17496. */
  17497. @JRubyMethod(name = "delete", required = 1, frame = true)
  17498. public IRubyObject delete(ThreadContext context, IRubyObject key, Block block) {
  17499. modify();
  17500. final RubyHashEntry entry = internalDelete(key);
  17501. if (entry != NO_ENTRY) return entry.value;
  17502. if (block.isGiven()) return block.yield(context, key);
  17503. return getRuntime().getNil();
  17504. }
  17505. /** rb_hash_select
  17506. *
  17507. */
  17508. @JRubyMethod(name = "select", frame = true)
  17509. public IRubyObject select(final ThreadContext context, final Block block) {
  17510. final RubyArray result = getRuntime().newArray();
  17511. final Ruby runtime = getRuntime();
  17512. visitAll(new Visitor() {
  17513. public void visit(IRubyObject key, IRubyObject value) {
  17514. if (block.yield(context, runtime.newArray(key, value), null, null, true).isTrue()) {
  17515. result.append(runtime.newArray(key, value));
  17516. }
  17517. }
  17518. });
  17519. return result;
  17520. }
  17521. /** rb_hash_delete_if
  17522. *
  17523. */
  17524. @JRubyMethod(name = "delete_if", frame = true)
  17525. public RubyHash delete_if(final ThreadContext context, final Block block) {
  17526. modify();
  17527. final Ruby runtime = getRuntime();
  17528. final RubyHash self = this;
  17529. visitAll(new Visitor() {
  17530. public void visit(IRubyObject key, IRubyObject value) {
  17531. if (block.yield(context, RubyArray.newArray(runtime, key, value), null, null, true).isTrue()) {
  17532. self.delete(context, key, block);
  17533. }
  17534. }
  17535. });
  17536. return this;
  17537. }
  17538. /** rb_hash_reject
  17539. *
  17540. */
  17541. @JRubyMethod(name = "reject", frame = true)
  17542. public RubyHash reject(ThreadContext context, Block block) {
  17543. return ((RubyHash)dup()).delete_if(context, block);
  17544. }
  17545. /** rb_hash_reject_bang
  17546. *
  17547. */
  17548. @JRubyMethod(name = "reject!", frame = true)
  17549. public IRubyObject reject_bang(ThreadContext context, Block block) {
  17550. int n = size;
  17551. delete_if(context, block);
  17552. if (n == size) return getRuntime().getNil();
  17553. return this;
  17554. }
  17555. /** rb_hash_clear
  17556. *
  17557. */
  17558. @JRubyMethod(name = "clear")
  17559. public RubyHash rb_clear() {
  17560. modify();
  17561. if (size > 0) {
  17562. alloc();
  17563. size = 0;
  17564. }
  17565. return this;
  17566. }
  17567. /** rb_hash_invert
  17568. *
  17569. */
  17570. @JRubyMethod(name = "invert")
  17571. public RubyHash invert(final ThreadContext context) {
  17572. final RubyHash result = newHash(getRuntime());
  17573. visitAll(new Visitor() {
  17574. public void visit(IRubyObject key, IRubyObject value) {
  17575. result.op_aset(context, value, key);
  17576. }
  17577. });
  17578. return result;
  17579. }
  17580. /** rb_hash_update
  17581. *
  17582. */
  17583. @JRubyMethod(name = {"merge!", "update"}, required = 1, frame = true)
  17584. public RubyHash merge_bang(final ThreadContext context, final IRubyObject other, final Block block) {
  17585. modify();
  17586. final Ruby runtime = getRuntime();
  17587. final RubyHash otherHash = other.convertToHash();
  17588. final RubyHash self = this;
  17589. otherHash.visitAll(new Visitor() {
  17590. public void visit(IRubyObject key, IRubyObject value) {
  17591. if (block.isGiven()) {
  17592. IRubyObject existing = self.internalGet(key);
  17593. if (existing != null)
  17594. value = block.yield(context, RubyArray.newArrayNoCopy(runtime, new IRubyObject[]{key, existing, value}));
  17595. }
  17596. self.op_aset(context, key, value);
  17597. }
  17598. });
  17599. return this;
  17600. }
  17601. /** rb_hash_merge
  17602. *
  17603. */
  17604. @JRubyMethod(name = "merge", required = 1, frame = true)
  17605. public RubyHash merge(ThreadContext context, IRubyObject other, Block block) {
  17606. return ((RubyHash)dup()).merge_bang(context, other, block);
  17607. }
  17608. /** rb_hash_replace
  17609. *
  17610. */
  17611. @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
  17612. public RubyHash initialize_copy(ThreadContext context, IRubyObject other) {
  17613. return replace(context, other);
  17614. }
  17615. /** rb_hash_replace
  17616. *
  17617. */
  17618. @JRubyMethod(name = "replace", required = 1)
  17619. public RubyHash replace(final ThreadContext context, IRubyObject other) {
  17620. final RubyHash otherHash = other.convertToHash();
  17621. if (this == otherHash) return this;
  17622. rb_clear();
  17623. final RubyHash self = this;
  17624. otherHash.visitAll(new Visitor() {
  17625. public void visit(IRubyObject key, IRubyObject value) {
  17626. self.op_aset(context, key, value);
  17627. }
  17628. });
  17629. ifNone = otherHash.ifNone;
  17630. if ((otherHash.flags & PROCDEFAULT_HASH_F) != 0) {
  17631. flags |= PROCDEFAULT_HASH_F;
  17632. } else {
  17633. flags &= ~PROCDEFAULT_HASH_F;
  17634. }
  17635. return this;
  17636. }
  17637. /** rb_hash_values_at
  17638. *
  17639. */
  17640. @JRubyMethod(name = "values_at", rest = true)
  17641. public RubyArray values_at(ThreadContext context, IRubyObject[] args) {
  17642. RubyArray result = RubyArray.newArray(getRuntime(), args.length);
  17643. for (int i = 0; i < args.length; i++) {
  17644. result.append(op_aref(context, args[i]));
  17645. }
  17646. return result;
  17647. }
  17648. public boolean hasDefaultProc() {
  17649. return (flags & PROCDEFAULT_HASH_F) != 0;
  17650. }
  17651. public IRubyObject getIfNone(){
  17652. return ifNone;
  17653. }
  17654. private static class VisitorIOException extends RuntimeException {
  17655. VisitorIOException(Throwable cause) {
  17656. super(cause);
  17657. }
  17658. }
  17659. // FIXME: Total hack to get flash in Rails marshalling/unmarshalling in session ok...We need
  17660. // to totally change marshalling to work with overridden core classes.
  17661. public static void marshalTo(final RubyHash hash, final MarshalStream output) throws IOException {
  17662. output.registerLinkTarget(hash);
  17663. output.writeInt(hash.size);
  17664. try {
  17665. hash.visitAll(new Visitor() {
  17666. public void visit(IRubyObject key, IRubyObject value) {
  17667. try {
  17668. output.dumpObject(key);
  17669. output.dumpObject(value);
  17670. } catch (IOException e) {
  17671. throw new VisitorIOException(e);
  17672. }
  17673. }
  17674. });
  17675. } catch (VisitorIOException e) {
  17676. throw (IOException)e.getCause();
  17677. }
  17678. if (!hash.ifNone.isNil()) output.dumpObject(hash.ifNone);
  17679. }
  17680. public static RubyHash unmarshalFrom(UnmarshalStream input, boolean defaultValue) throws IOException {
  17681. RubyHash result = newHash(input.getRuntime());
  17682. input.registerLinkTarget(result);
  17683. int size = input.unmarshalInt();
  17684. ThreadContext context = input.getRuntime().getCurrentContext();
  17685. for (int i = 0; i < size; i++) {
  17686. result.op_aset(context, input.unmarshalObject(), input.unmarshalObject());
  17687. }
  17688. if (defaultValue) result.default_value_set(input.unmarshalObject());
  17689. return result;
  17690. }
  17691. public Class getJavaClass() {
  17692. return Map.class;
  17693. }
  17694. // Satisfy java.util.Set interface (for Java integration)
  17695. public int size() {
  17696. return size;
  17697. }
  17698. public boolean isEmpty() {
  17699. return size == 0;
  17700. }
  17701. public boolean containsKey(Object key) {
  17702. return internalGet(JavaUtil.convertJavaToRuby(getRuntime(), key)) != null;
  17703. }
  17704. public boolean containsValue(Object value) {
  17705. return hasValue(getRuntime().getCurrentContext(), JavaUtil.convertJavaToRuby(getRuntime(), value));
  17706. }
  17707. public Object get(Object key) {
  17708. return JavaUtil.convertRubyToJava(internalGet(JavaUtil.convertJavaToRuby(getRuntime(), key)));
  17709. }
  17710. public Object put(Object key, Object value) {
  17711. internalPut(JavaUtil.convertJavaToRuby(getRuntime(), key), JavaUtil.convertJavaToRuby(getRuntime(), value));
  17712. return value;
  17713. }
  17714. public Object remove(Object key) {
  17715. IRubyObject rubyKey = JavaUtil.convertJavaToRuby(getRuntime(), key);
  17716. return internalDelete(rubyKey).value;
  17717. }
  17718. public void putAll(Map map) {
  17719. Ruby runtime = getRuntime();
  17720. for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
  17721. Object key = iter.next();
  17722. internalPut(JavaUtil.convertJavaToRuby(runtime, key), JavaUtil.convertJavaToRuby(runtime, map.get(key)));
  17723. }
  17724. }
  17725. public void clear() {
  17726. rb_clear();
  17727. }
  17728. public boolean equals(Object other) {
  17729. if (!(other instanceof RubyHash)) return false;
  17730. if (this == other) return true;
  17731. return op_equal(getRuntime().getCurrentContext(), (RubyHash)other).isTrue() ? true : false;
  17732. }
  17733. public Set keySet() {
  17734. return new BaseSet(KEY_VIEW);
  17735. }
  17736. public Set directKeySet() {
  17737. return new BaseSet(DIRECT_KEY_VIEW);
  17738. }
  17739. public Collection values() {
  17740. return new BaseCollection(VALUE_VIEW);
  17741. }
  17742. public Collection directValues() {
  17743. return new BaseCollection(DIRECT_VALUE_VIEW);
  17744. }
  17745. public Set entrySet() {
  17746. return new BaseSet(ENTRY_VIEW);
  17747. }
  17748. public Set directEntrySet() {
  17749. return new BaseSet(DIRECT_ENTRY_VIEW);
  17750. }
  17751. private class BaseSet extends AbstractSet {
  17752. final EntryView view;
  17753. public BaseSet(EntryView view) {
  17754. this.view = view;
  17755. }
  17756. public Iterator iterator() {
  17757. return new BaseIterator(view);
  17758. }
  17759. public boolean contains(Object o) {
  17760. return view.contains(RubyHash.this, o);
  17761. }
  17762. public void clear() {
  17763. RubyHash.this.clear();
  17764. }
  17765. public int size() {
  17766. return RubyHash.this.size;
  17767. }
  17768. public boolean remove(Object o) {
  17769. return view.remove(RubyHash.this, o);
  17770. }
  17771. }
  17772. private class BaseCollection extends AbstractCollection {
  17773. final EntryView view;
  17774. public BaseCollection(EntryView view) {
  17775. this.view = view;
  17776. }
  17777. public Iterator iterator() {
  17778. return new BaseIterator(view);
  17779. }
  17780. public boolean contains(Object o) {
  17781. return view.contains(RubyHash.this, o);
  17782. }
  17783. public void clear() {
  17784. RubyHash.this.clear();
  17785. }
  17786. public int size() {
  17787. return RubyHash.this.size;
  17788. }
  17789. public boolean remove(Object o) {
  17790. return view.remove(RubyHash.this, o);
  17791. }
  17792. }
  17793. private class BaseIterator implements Iterator {
  17794. final private EntryView view;
  17795. private RubyHashEntry entry;
  17796. private boolean peeking;
  17797. private int startGeneration;
  17798. public BaseIterator(EntryView view) {
  17799. this.view = view;
  17800. this.entry = head;
  17801. this.startGeneration = generation;
  17802. }
  17803. private void advance(boolean consume) {
  17804. if (!peeking) {
  17805. do {
  17806. if (startGeneration != generation) {
  17807. startGeneration = generation;
  17808. entry = head;
  17809. }
  17810. entry = entry.nextAdded;
  17811. } while (entry != head && !entry.isLive());
  17812. }
  17813. peeking = !consume;
  17814. }
  17815. public Object next() {
  17816. advance(true);
  17817. if (entry == head) {
  17818. peeking = true; // remain where we are
  17819. throw new NoSuchElementException();
  17820. }
  17821. return view.convertEntry(getRuntime(), entry);
  17822. }
  17823. // once hasNext has been called, we commit to next() returning
  17824. // the entry it found, even if it were subsequently deleted
  17825. public boolean hasNext() {
  17826. advance(false);
  17827. return entry != head;
  17828. }
  17829. public void remove() {
  17830. if (entry == head) {
  17831. throw new IllegalStateException("Iterator out of range");
  17832. }
  17833. internalDeleteEntry(entry);
  17834. }
  17835. }
  17836. private static abstract class EntryView {
  17837. public abstract Object convertEntry(Ruby runtime, RubyHashEntry value);
  17838. public abstract boolean contains(RubyHash hash, Object o);
  17839. public abstract boolean remove(RubyHash hash, Object o);
  17840. }
  17841. private static final EntryView DIRECT_KEY_VIEW = new EntryView() {
  17842. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17843. return entry.key;
  17844. }
  17845. public boolean contains(RubyHash hash, Object o) {
  17846. if (!(o instanceof IRubyObject)) return false;
  17847. return hash.internalGet((IRubyObject)o) != null;
  17848. }
  17849. public boolean remove(RubyHash hash, Object o) {
  17850. if (!(o instanceof IRubyObject)) return false;
  17851. return hash.internalDelete((IRubyObject)o) != NO_ENTRY;
  17852. }
  17853. };
  17854. private static final EntryView KEY_VIEW = new EntryView() {
  17855. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17856. return JavaUtil.convertRubyToJava(entry.key, Object.class);
  17857. }
  17858. public boolean contains(RubyHash hash, Object o) {
  17859. return hash.containsKey(o);
  17860. }
  17861. public boolean remove(RubyHash hash, Object o) {
  17862. return hash.remove(o) != null;
  17863. }
  17864. };
  17865. private static final EntryView DIRECT_VALUE_VIEW = new EntryView() {
  17866. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17867. return entry.value;
  17868. }
  17869. public boolean contains(RubyHash hash, Object o) {
  17870. if (!(o instanceof IRubyObject)) return false;
  17871. IRubyObject obj = (IRubyObject)o;
  17872. return hash.hasValue(obj.getRuntime().getCurrentContext(), obj);
  17873. }
  17874. public boolean remove(RubyHash hash, Object o) {
  17875. if (!(o instanceof IRubyObject)) return false;
  17876. IRubyObject obj = (IRubyObject) o;
  17877. IRubyObject key = hash.internalIndex(obj.getRuntime().getCurrentContext(), obj);
  17878. if (key == null) return false;
  17879. return hash.internalDelete(key) != NO_ENTRY;
  17880. }
  17881. };
  17882. private final EntryView VALUE_VIEW = new EntryView() {
  17883. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17884. return JavaUtil.convertRubyToJava(entry.value, Object.class);
  17885. }
  17886. public boolean contains(RubyHash hash, Object o) {
  17887. return hash.containsValue(o);
  17888. }
  17889. public boolean remove(RubyHash hash, Object o) {
  17890. IRubyObject value = JavaUtil.convertJavaToRuby(hash.getRuntime(), o);
  17891. IRubyObject key = hash.internalIndex(hash.getRuntime().getCurrentContext(), value);
  17892. if (key == null) return false;
  17893. return hash.internalDelete(key) != NO_ENTRY;
  17894. }
  17895. };
  17896. private final EntryView DIRECT_ENTRY_VIEW = new EntryView() {
  17897. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17898. return entry;
  17899. }
  17900. public boolean contains(RubyHash hash, Object o) {
  17901. if (!(o instanceof RubyHashEntry)) return false;
  17902. RubyHashEntry entry = (RubyHashEntry)o;
  17903. RubyHashEntry candidate = internalGetEntry(entry.key);
  17904. return candidate != NO_ENTRY && entry.equals(candidate);
  17905. }
  17906. public boolean remove(RubyHash hash, Object o) {
  17907. if (!(o instanceof RubyHashEntry)) return false;
  17908. return hash.internalDeleteEntry((RubyHashEntry)o) != NO_ENTRY;
  17909. }
  17910. };
  17911. private final EntryView ENTRY_VIEW = new EntryView() {
  17912. public Object convertEntry(Ruby runtime, RubyHashEntry entry) {
  17913. return new ConvertingEntry(runtime, entry);
  17914. }
  17915. public boolean contains(RubyHash hash, Object o) {
  17916. if (!(o instanceof ConvertingEntry)) return false;
  17917. ConvertingEntry entry = (ConvertingEntry)o;
  17918. RubyHashEntry candidate = hash.internalGetEntry(entry.entry.key);
  17919. return candidate != NO_ENTRY && entry.entry.equals(candidate);
  17920. }
  17921. public boolean remove(RubyHash hash, Object o) {
  17922. if (!(o instanceof ConvertingEntry)) return false;
  17923. ConvertingEntry entry = (ConvertingEntry)o;
  17924. return hash.internalDeleteEntry(entry.entry) != NO_ENTRY;
  17925. }
  17926. };
  17927. private static class ConvertingEntry implements Map.Entry {
  17928. private final RubyHashEntry entry;
  17929. private final Ruby runtime;
  17930. public ConvertingEntry(Ruby runtime, RubyHashEntry entry) {
  17931. this.entry = entry;
  17932. this.runtime = runtime;
  17933. }
  17934. public Object getKey() {
  17935. return JavaUtil.convertRubyToJava(entry.key, Object.class);
  17936. }
  17937. public Object getValue() {
  17938. return JavaUtil.convertRubyToJava(entry.value, Object.class);
  17939. }
  17940. public Object setValue(Object o) {
  17941. return entry.setValue(JavaUtil.convertJavaToRuby(runtime, o));
  17942. }
  17943. public boolean equals(Object o) {
  17944. if (!(o instanceof ConvertingEntry)) {
  17945. return false;
  17946. }
  17947. ConvertingEntry other = (ConvertingEntry)o;
  17948. return entry.equals(other.entry);
  17949. }
  17950. public int hashCode() {
  17951. return entry.hashCode();
  17952. }
  17953. }
  17954. }
  17955. /***** BEGIN LICENSE BLOCK *****
  17956. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  17957. *
  17958. * The contents of this file are subject to the Common Public
  17959. * License Version 1.0 (the "License"); you may not use this file
  17960. * except in compliance with the License. You may obtain a copy of
  17961. * the License at http://www.eclipse.org/legal/cpl-v10.html
  17962. *
  17963. * Software distributed under the License is distributed on an "AS
  17964. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  17965. * implied. See the License for the specific language governing
  17966. * rights and limitations under the License.
  17967. *
  17968. * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
  17969. * Copyright (C) 2007 Koichiro Ohba <koichiro@meadowy.org>
  17970. *
  17971. * Alternatively, the contents of this file may be used under the terms of
  17972. * either of the GNU General Public License Version 2 or later (the "GPL"),
  17973. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  17974. * in which case the provisions of the GPL or the LGPL are applicable instead
  17975. * of those above. If you wish to allow use of your version of this file only
  17976. * under the terms of either the GPL or the LGPL, and not to allow others to
  17977. * use your version of this file under the terms of the CPL, indicate your
  17978. * decision by deleting the provisions above and replace them with the notice
  17979. * and other provisions required by the GPL or the LGPL. If you do not delete
  17980. * the provisions above, a recipient may use your version of this file under
  17981. * the terms of any one of the CPL, the GPL or the LGPL.
  17982. ***** END LICENSE BLOCK *****/
  17983. package org.jruby;
  17984. import java.nio.ByteBuffer;
  17985. import java.nio.CharBuffer;
  17986. import java.nio.charset.CharacterCodingException;
  17987. import java.nio.charset.Charset;
  17988. import java.nio.charset.CharsetDecoder;
  17989. import java.nio.charset.CharsetEncoder;
  17990. import java.nio.charset.CodingErrorAction;
  17991. import java.nio.charset.IllegalCharsetNameException;
  17992. import java.nio.charset.MalformedInputException;
  17993. import java.nio.charset.UnmappableCharacterException;
  17994. import java.nio.charset.UnsupportedCharsetException;
  17995. import org.jruby.anno.JRubyMethod;
  17996. import org.jruby.anno.JRubyModule;
  17997. import org.jruby.anno.JRubyClass;
  17998. import org.jruby.runtime.Arity;
  17999. import org.jruby.runtime.Block;
  18000. import org.jruby.runtime.ObjectAllocator;
  18001. import org.jruby.runtime.ThreadContext;
  18002. import org.jruby.runtime.builtin.IRubyObject;
  18003. import org.jruby.util.ByteList;
  18004. @JRubyClass(name="Iconv")
  18005. public class RubyIconv extends RubyObject {
  18006. //static private final String TRANSLIT = "//translit";
  18007. static private final String IGNORE = "//ignore";
  18008. private CharsetDecoder fromEncoding;
  18009. private CharsetEncoder toEncoding;
  18010. public RubyIconv(Ruby runtime, RubyClass type) {
  18011. super(runtime, type);
  18012. }
  18013. private static final ObjectAllocator ICONV_ALLOCATOR = new ObjectAllocator() {
  18014. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  18015. return new RubyIconv(runtime, klass);
  18016. }
  18017. };
  18018. @JRubyModule(name="Iconv::Failure")
  18019. public static class Failure {}
  18020. @JRubyClass(name="Iconv::IllegalSequence", parent="ArgumentError", include="Iconv::Failure")
  18021. public static class IllegalSequence {}
  18022. @JRubyClass(name="Iconv::InvalidCharacter", parent="ArgumentError", include="Iconv::Failure")
  18023. public static class InvalidCharacter {}
  18024. @JRubyClass(name="Iconv::InvalidEncoding", parent="ArgumentError", include="Iconv::Failure")
  18025. public static class InvalidEncoding {}
  18026. @JRubyClass(name="Iconv::OutOfRange", parent="ArgumentError", include="Iconv::Failure")
  18027. public static class OutOfRange {}
  18028. @JRubyClass(name="Iconv::BrokenLibrary", parent="ArgumentError", include="Iconv::Failure")
  18029. public static class BrokenLibrary {}
  18030. public static void createIconv(Ruby runtime) {
  18031. RubyClass iconvClass = runtime.defineClass("Iconv", runtime.getObject(), ICONV_ALLOCATOR);
  18032. iconvClass.defineAnnotatedMethods(RubyIconv.class);
  18033. RubyModule failure = iconvClass.defineModuleUnder("Failure");
  18034. RubyClass argumentError = runtime.getArgumentError();
  18035. String[] iconvErrors = {"IllegalSequence", "InvalidCharacter", "InvalidEncoding",
  18036. "OutOfRange", "BrokenLibrary"};
  18037. for (int i = 0; i < iconvErrors.length; i++) {
  18038. RubyClass subClass = iconvClass.defineClassUnder(iconvErrors[i], argumentError, RubyFailure.ICONV_FAILURE_ALLOCATOR);
  18039. subClass.defineAnnotatedMethods(RubyFailure.class);
  18040. subClass.includeModule(failure);
  18041. }
  18042. }
  18043. public static class RubyFailure extends RubyException {
  18044. private IRubyObject success;
  18045. private IRubyObject failed;
  18046. public static RubyFailure newInstance(Ruby runtime, RubyClass excptnClass, String msg) {
  18047. return new RubyFailure(runtime, excptnClass, msg);
  18048. }
  18049. protected static final ObjectAllocator ICONV_FAILURE_ALLOCATOR = new ObjectAllocator() {
  18050. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  18051. return new RubyFailure(runtime, klass);
  18052. }
  18053. };
  18054. protected RubyFailure(Ruby runtime, RubyClass rubyClass) {
  18055. this(runtime, rubyClass, null);
  18056. }
  18057. public RubyFailure(Ruby runtime, RubyClass rubyClass, String message) {
  18058. super(runtime, rubyClass, message);
  18059. }
  18060. @JRubyMethod(name = "initialize", required = 1, optional = 2, frame = true)
  18061. public IRubyObject initialize(IRubyObject[] args, Block block) {
  18062. super.initialize(args, block);
  18063. success = args.length >= 2 ? args[1] : getRuntime().getNil();
  18064. failed = args.length == 3 ? args[2] : getRuntime().getNil();
  18065. return this;
  18066. }
  18067. @JRubyMethod(name = "success")
  18068. public IRubyObject success() {
  18069. return success;
  18070. }
  18071. @JRubyMethod(name = "failed")
  18072. public IRubyObject failed() {
  18073. return failed;
  18074. }
  18075. @JRubyMethod(name = "inspect")
  18076. public IRubyObject inspect() {
  18077. RubyModule rubyClass = getMetaClass();
  18078. StringBuilder buffer = new StringBuilder("#<");
  18079. buffer.append(rubyClass.getName()).append(": ").append(success.inspect().toString());
  18080. buffer.append(", ").append(failed.inspect().toString()).append(">");
  18081. return getRuntime().newString(buffer.toString());
  18082. }
  18083. }
  18084. private static String getCharset(String encoding) {
  18085. int index = encoding.indexOf("//");
  18086. if (index == -1) return encoding;
  18087. return encoding.substring(0, index);
  18088. }
  18089. /* Currently dead code, but useful when we figure out how to actually perform translit.
  18090. private static boolean isTranslit(String encoding) {
  18091. return encoding.toLowerCase().indexOf(TRANSLIT) != -1 ? true : false;
  18092. }*/
  18093. private static boolean isIgnore(String encoding) {
  18094. return encoding.toLowerCase().indexOf(IGNORE) != -1 ? true : false;
  18095. }
  18096. @JRubyMethod(name = "open", required = 2, frame = true, meta = true)
  18097. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject to, IRubyObject from, Block block) {
  18098. Ruby runtime = context.getRuntime();
  18099. RubyIconv iconv = newIconv(context, recv, to, from);
  18100. if (!block.isGiven()) return iconv;
  18101. IRubyObject result = runtime.getNil();
  18102. try {
  18103. result = block.yield(context, iconv);
  18104. } finally {
  18105. iconv.close();
  18106. }
  18107. return result;
  18108. }
  18109. private static RubyIconv newIconv(ThreadContext context, IRubyObject recv,
  18110. IRubyObject to, IRubyObject from) {
  18111. RubyClass klazz = (RubyClass)recv;
  18112. return (RubyIconv) klazz.newInstance(
  18113. context, new IRubyObject[] {to, from}, Block.NULL_BLOCK);
  18114. }
  18115. @JRubyMethod(name = "initialize", required = 2, frame = true)
  18116. public IRubyObject initialize(IRubyObject arg1, IRubyObject arg2, Block unusedBlock) {
  18117. Ruby runtime = getRuntime();
  18118. if (!arg1.respondsTo("to_str")) {
  18119. throw runtime.newTypeError("can't convert " + arg1.getMetaClass() + " into String");
  18120. }
  18121. if (!arg2.respondsTo("to_str")) {
  18122. throw runtime.newTypeError("can't convert " + arg2.getMetaClass() + " into String");
  18123. }
  18124. String to = arg1.convertToString().toString();
  18125. String from = arg2.convertToString().toString();
  18126. try {
  18127. fromEncoding = Charset.forName(getCharset(from)).newDecoder();
  18128. toEncoding = Charset.forName(getCharset(to)).newEncoder();
  18129. if (!isIgnore(from)) fromEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
  18130. if (!isIgnore(to)) toEncoding.onUnmappableCharacter(CodingErrorAction.REPORT);
  18131. } catch (IllegalCharsetNameException e) {
  18132. throw runtime.newInvalidEncoding("invalid encoding");
  18133. } catch (UnsupportedCharsetException e) {
  18134. throw runtime.newInvalidEncoding("invalid encoding");
  18135. } catch (Exception e) {
  18136. throw runtime.newSystemCallError(e.toString());
  18137. }
  18138. return this;
  18139. }
  18140. @JRubyMethod(name = "close")
  18141. public IRubyObject close() {
  18142. toEncoding = null;
  18143. fromEncoding = null;
  18144. return RubyString.newEmptyString(getRuntime());
  18145. }
  18146. @JRubyMethod
  18147. public IRubyObject iconv(IRubyObject str) {
  18148. return iconv(str, 0, -1);
  18149. }
  18150. @JRubyMethod
  18151. public IRubyObject iconv(IRubyObject str, IRubyObject startArg) {
  18152. int start = 0;
  18153. if (!startArg.isNil()) start = RubyNumeric.fix2int(startArg);
  18154. return iconv(str, start, -1);
  18155. }
  18156. @JRubyMethod
  18157. public IRubyObject iconv(IRubyObject str, IRubyObject startArg, IRubyObject endArg) {
  18158. int start = 0;
  18159. int end = -1;
  18160. if (!startArg.isNil()) start = RubyNumeric.fix2int(startArg);
  18161. if (!endArg.isNil()) end = RubyNumeric.fix2int(endArg);
  18162. return iconv(str, start, end);
  18163. }
  18164. private IRubyObject iconv(IRubyObject str, int start, int end) {
  18165. if (str.isNil()) {
  18166. fromEncoding.reset();
  18167. toEncoding.reset();
  18168. return RubyString.newEmptyString(getRuntime());
  18169. }
  18170. return _iconv(str.convertToString(), start, end);
  18171. }
  18172. /**
  18173. * Variable-arity version for compatibility. Not bound to Ruby.
  18174. * @deprecated Use the versions with one, two or three arguments.
  18175. */
  18176. public IRubyObject iconv(IRubyObject[] args) {
  18177. switch (args.length) {
  18178. case 1:
  18179. return iconv(args[0]);
  18180. case 2:
  18181. return iconv(args[0], args[1]);
  18182. case 3:
  18183. return iconv(args[0], args[1], args[2]);
  18184. default:
  18185. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  18186. return null; // not reached
  18187. }
  18188. }
  18189. // FIXME: We are assuming that original string will be raw bytes. If -Ku is provided
  18190. // this will not be true, but that is ok for now. Deal with that when someone needs it.
  18191. private IRubyObject _iconv(RubyString str, int start, int end) {
  18192. if (fromEncoding == null) {
  18193. throw getRuntime().newArgumentError("closed iconv");
  18194. }
  18195. ByteList bytes = str.getByteList();
  18196. // treat start and end as start...end for end >= 0, start..end for end < 0
  18197. if (start < 0) {
  18198. start += bytes.length();
  18199. }
  18200. if (end < 0) {
  18201. end += 1 + bytes.length();
  18202. } else if (end > bytes.length()) {
  18203. end = bytes.length();
  18204. }
  18205. if (start < 0 || end < start) { // invalid ranges result in an empty string
  18206. return RubyString.newEmptyString(getRuntime());
  18207. }
  18208. ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin() + start, end - start);
  18209. try {
  18210. CharBuffer cbuf = fromEncoding.decode(buf);
  18211. buf = toEncoding.encode(cbuf);
  18212. } catch (MalformedInputException e) {
  18213. } catch (UnmappableCharacterException e) {
  18214. } catch (CharacterCodingException e) {
  18215. throw getRuntime().newInvalidEncoding("invalid sequence");
  18216. } catch (IllegalStateException e) {
  18217. }
  18218. byte[] arr = buf.array();
  18219. return getRuntime().newString(new ByteList(arr, 0, buf.limit()));
  18220. }
  18221. @JRubyMethod(name = "iconv", required = 2, rest = true, meta = true)
  18222. public static IRubyObject iconv(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  18223. return convertWithArgs(context, recv, args, "iconv");
  18224. }
  18225. @JRubyMethod(name = "conv", required = 3, rest = true, meta = true)
  18226. public static IRubyObject conv(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  18227. return convertWithArgs(context, recv, args, "conv").join(context, RubyString.newEmptyString(recv.getRuntime()));
  18228. }
  18229. @JRubyMethod(name = "charset_map", meta= true)
  18230. public static IRubyObject charset_map_get(IRubyObject recv) {
  18231. return recv.getRuntime().getCharsetMap();
  18232. }
  18233. private static String mapCharset(ThreadContext context, IRubyObject val) {
  18234. RubyHash charset = val.getRuntime().getCharsetMap();
  18235. if (charset.size() > 0) {
  18236. RubyString key = val.callMethod(context, "downcase").convertToString();
  18237. IRubyObject tryVal = charset.fastARef(key);
  18238. if (tryVal != null) val = tryVal;
  18239. }
  18240. return val.convertToString().toString();
  18241. }
  18242. public static RubyArray convertWithArgs(ThreadContext context, IRubyObject recv, IRubyObject[] args, String function) {
  18243. assert args.length >= 2;
  18244. RubyArray array = context.getRuntime().newArray(args.length - 2);
  18245. RubyIconv iconv = newIconv(context, recv, args[0], args[1]);
  18246. try {
  18247. for (int i = 2; i < args.length; i++) {
  18248. array.append(iconv.iconv(args[i]));
  18249. }
  18250. } finally {
  18251. iconv.close();
  18252. }
  18253. return array;
  18254. }
  18255. /*
  18256. private static IRubyObject convert(String fromEncoding, String toEncoding, RubyString original)
  18257. throws UnsupportedEncodingException {
  18258. // Get all bytes from PLAIN string pretend they are not encoded in any way.
  18259. byte[] string = original.getBytes();
  18260. // Now create a string pretending it is from fromEncoding
  18261. string = new String(string, fromEncoding).getBytes(toEncoding);
  18262. // Finally recode back to PLAIN
  18263. return RubyString.newString(original.getRuntime(), string);
  18264. }
  18265. */
  18266. }
  18267. /***** BEGIN LICENSE BLOCK *****
  18268. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  18269. *
  18270. * The contents of this file are subject to the Common Public
  18271. * License Version 1.0 (the "License"); you may not use this file
  18272. * except in compliance with the License. You may obtain a copy of
  18273. * the License at http://www.eclipse.org/legal/cpl-v10.html
  18274. *
  18275. * Software distributed under the License is distributed on an "AS
  18276. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  18277. * implied. See the License for the specific language governing
  18278. * rights and limitations under the License.
  18279. *
  18280. * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  18281. *
  18282. * Alternatively, the contents of this file may be used under the terms of
  18283. * either of the GNU General Public License Version 2 or later (the "GPL"),
  18284. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  18285. * in which case the provisions of the GPL or the LGPL are applicable instead
  18286. * of those above. If you wish to allow use of your version of this file only
  18287. * under the terms of either the GPL or the LGPL, and not to allow others to
  18288. * use your version of this file under the terms of the CPL, indicate your
  18289. * decision by deleting the provisions above and replace them with the notice
  18290. * and other provisions required by the GPL or the LGPL. If you do not delete
  18291. * the provisions above, a recipient may use your version of this file under
  18292. * the terms of any one of the CPL, the GPL or the LGPL.
  18293. ***** END LICENSE BLOCK *****/
  18294. package org.jruby;
  18295. import java.io.BufferedInputStream;
  18296. import java.io.ByteArrayInputStream;
  18297. import java.io.File;
  18298. import java.io.FileInputStream;
  18299. import java.io.IOException;
  18300. import java.io.InputStream;
  18301. import java.io.PrintStream;
  18302. import java.io.UnsupportedEncodingException;
  18303. import java.util.ArrayList;
  18304. import java.util.HashMap;
  18305. import java.util.HashSet;
  18306. import java.util.List;
  18307. import java.util.Map;
  18308. import java.util.Set;
  18309. import java.util.StringTokenizer;
  18310. import org.jruby.ast.executable.Script;
  18311. import org.jruby.exceptions.MainExitException;
  18312. import org.jruby.runtime.Constants;
  18313. import org.jruby.runtime.load.LoadService;
  18314. import org.jruby.util.ClassCache;
  18315. import org.jruby.util.JRubyFile;
  18316. import org.jruby.util.KCode;
  18317. import org.jruby.util.NormalizedFile;
  18318. import org.jruby.util.SafePropertyAccessor;
  18319. import org.objectweb.asm.Opcodes;
  18320. public class RubyInstanceConfig {
  18321. /**
  18322. * The max count of active methods eligible for JIT-compilation.
  18323. */
  18324. private static final int JIT_MAX_METHODS_LIMIT = 4096;
  18325. /**
  18326. * The max size of JIT-compiled methods (full class size) allowed.
  18327. */
  18328. private static final int JIT_MAX_SIZE_LIMIT = Integer.MAX_VALUE;
  18329. /**
  18330. * The JIT threshold to the specified method invocation count.
  18331. */
  18332. private static final int JIT_THRESHOLD = 50;
  18333. /** The version to use for generated classes. Set to current JVM version by default */
  18334. public static final int JAVA_VERSION;
  18335. /**
  18336. * Default size for chained compilation.
  18337. */
  18338. private static final int CHAINED_COMPILE_LINE_COUNT_DEFAULT = 500;
  18339. /**
  18340. * The number of lines at which a method, class, or block body is split into
  18341. * chained methods (to dodge 64k method-size limit in JVM).
  18342. */
  18343. public static final int CHAINED_COMPILE_LINE_COUNT
  18344. = SafePropertyAccessor.getInt("jruby.compile.chainsize", CHAINED_COMPILE_LINE_COUNT_DEFAULT);
  18345. public enum CompileMode {
  18346. JIT, FORCE, OFF;
  18347. public boolean shouldPrecompileCLI() {
  18348. switch (this) {
  18349. case JIT: case FORCE:
  18350. return true;
  18351. }
  18352. return false;
  18353. }
  18354. public boolean shouldJIT() {
  18355. switch (this) {
  18356. case JIT: case FORCE:
  18357. return true;
  18358. }
  18359. return false;
  18360. }
  18361. public boolean shouldPrecompileAll() {
  18362. return this == FORCE;
  18363. }
  18364. }
  18365. private InputStream input = System.in;
  18366. private PrintStream output = System.out;
  18367. private PrintStream error = System.err;
  18368. private Profile profile = Profile.DEFAULT;
  18369. private boolean objectSpaceEnabled
  18370. = SafePropertyAccessor.getBoolean("jruby.objectspace.enabled", false);
  18371. private CompileMode compileMode = CompileMode.JIT;
  18372. private boolean runRubyInProcess = true;
  18373. private String currentDirectory;
  18374. private Map environment;
  18375. private String[] argv = {};
  18376. private final boolean jitLogging;
  18377. private final boolean jitLoggingVerbose;
  18378. private final int jitLogEvery;
  18379. private final int jitThreshold;
  18380. private final int jitMax;
  18381. private final int jitMaxSize;
  18382. private final boolean samplingEnabled;
  18383. private CompatVersion compatVersion;
  18384. private ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
  18385. private ClassLoader loader = contextLoader == null ? RubyInstanceConfig.class.getClassLoader() : contextLoader;
  18386. private ClassCache<Script> classCache;
  18387. // from CommandlineParser
  18388. private List<String> loadPaths = new ArrayList<String>();
  18389. private Set<String> excludedMethods = new HashSet<String>();
  18390. private StringBuffer inlineScript = new StringBuffer();
  18391. private boolean hasInlineScript = false;
  18392. private String scriptFileName = null;
  18393. private List<String> requiredLibraries = new ArrayList<String>();
  18394. private boolean benchmarking = false;
  18395. private boolean argvGlobalsOn = false;
  18396. private boolean assumeLoop = false;
  18397. private boolean assumePrinting = false;
  18398. private Map optionGlobals = new HashMap();
  18399. private boolean processLineEnds = false;
  18400. private boolean split = false;
  18401. // This property is a Boolean, to allow three values, so it can match MRI's nil, false and true
  18402. private Boolean verbose = Boolean.FALSE;
  18403. private boolean debug = false;
  18404. private boolean showVersion = false;
  18405. private boolean showBytecode = false;
  18406. private boolean showCopyright = false;
  18407. private boolean endOfArguments = false;
  18408. private boolean shouldRunInterpreter = true;
  18409. private boolean shouldPrintUsage = false;
  18410. private boolean shouldPrintProperties=false;
  18411. private boolean yarv = false;
  18412. private boolean rubinius = false;
  18413. private boolean yarvCompile = false;
  18414. private KCode kcode = KCode.NONE;
  18415. private String recordSeparator = "\n";
  18416. private boolean shouldCheckSyntax = false;
  18417. private String inputFieldSeparator = null;
  18418. private boolean managementEnabled = true;
  18419. private int safeLevel = 0;
  18420. private String jrubyHome;
  18421. public static final boolean FASTEST_COMPILE_ENABLED
  18422. = SafePropertyAccessor.getBoolean("jruby.compile.fastest");
  18423. public static final boolean BOXED_COMPILE_ENABLED
  18424. = FASTEST_COMPILE_ENABLED
  18425. || SafePropertyAccessor.getBoolean("jruby.compile.boxed");
  18426. public static final boolean FASTOPS_COMPILE_ENABLED
  18427. = FASTEST_COMPILE_ENABLED
  18428. || SafePropertyAccessor.getBoolean("jruby.compile.fastops");
  18429. public static final boolean FRAMELESS_COMPILE_ENABLED
  18430. = FASTEST_COMPILE_ENABLED
  18431. || SafePropertyAccessor.getBoolean("jruby.compile.frameless");
  18432. public static final boolean POSITIONLESS_COMPILE_ENABLED
  18433. = FASTEST_COMPILE_ENABLED
  18434. || SafePropertyAccessor.getBoolean("jruby.compile.positionless");
  18435. public static final boolean THREADLESS_COMPILE_ENABLED
  18436. = FASTEST_COMPILE_ENABLED
  18437. || SafePropertyAccessor.getBoolean("jruby.compile.threadless");
  18438. public static final boolean LAZYHANDLES_COMPILE = SafePropertyAccessor.getBoolean("jruby.compile.lazyHandles", false);
  18439. public static final boolean FORK_ENABLED
  18440. = SafePropertyAccessor.getBoolean("jruby.fork.enabled");
  18441. public static final boolean POOLING_ENABLED
  18442. = SafePropertyAccessor.getBoolean("jruby.thread.pool.enabled");
  18443. public static final int POOL_MAX
  18444. = SafePropertyAccessor.getInt("jruby.thread.pool.max", Integer.MAX_VALUE);
  18445. public static final int POOL_MIN
  18446. = SafePropertyAccessor.getInt("jruby.thread.pool.min", 0);
  18447. public static final int POOL_TTL
  18448. = SafePropertyAccessor.getInt("jruby.thread.pool.ttl", 60);
  18449. public static final boolean NATIVE_NET_PROTOCOL
  18450. = SafePropertyAccessor.getBoolean("jruby.native.net.protocol", false);
  18451. public static boolean FULL_TRACE_ENABLED
  18452. = SafePropertyAccessor.getBoolean("jruby.debug.fullTrace", false);
  18453. public static final String COMPILE_EXCLUDE
  18454. = SafePropertyAccessor.getProperty("jruby.jit.exclude");
  18455. public static boolean nativeEnabled = true;
  18456. public static interface LoadServiceCreator {
  18457. LoadService create(Ruby runtime);
  18458. LoadServiceCreator DEFAULT = new LoadServiceCreator() {
  18459. public LoadService create(Ruby runtime) {
  18460. return new LoadService(runtime);
  18461. }
  18462. };
  18463. }
  18464. private LoadServiceCreator creator = LoadServiceCreator.DEFAULT;
  18465. static {
  18466. String specVersion = null;
  18467. try {
  18468. specVersion = System.getProperty("jruby.bytecode.version");
  18469. if (specVersion == null) {
  18470. specVersion = System.getProperty("java.specification.version");
  18471. }
  18472. if (System.getProperty("jruby.native.enabled") != null) {
  18473. nativeEnabled = Boolean.getBoolean("jruby.native.enabled");
  18474. }
  18475. } catch (SecurityException se) {
  18476. nativeEnabled = false;
  18477. specVersion = "1.5";
  18478. }
  18479. if (specVersion.equals("1.5")) {
  18480. JAVA_VERSION = Opcodes.V1_5;
  18481. } else {
  18482. JAVA_VERSION = Opcodes.V1_6;
  18483. }
  18484. }
  18485. public int characterIndex = 0;
  18486. public RubyInstanceConfig() {
  18487. if (Ruby.isSecurityRestricted())
  18488. currentDirectory = "/";
  18489. else {
  18490. currentDirectory = JRubyFile.getFileProperty("user.dir");
  18491. }
  18492. samplingEnabled = SafePropertyAccessor.getBoolean("jruby.sampling.enabled", false);
  18493. String compatString = SafePropertyAccessor.getProperty("jruby.compat.version", "RUBY1_8");
  18494. if (compatString.equalsIgnoreCase("RUBY1_8")) {
  18495. compatVersion = CompatVersion.RUBY1_8;
  18496. } else if (compatString.equalsIgnoreCase("RUBY1_9")) {
  18497. compatVersion = CompatVersion.RUBY1_9;
  18498. } else {
  18499. System.err.println("Compatibility version `" + compatString + "' invalid; use RUBY1_8 or RUBY1_9. Using RUBY1_8.");
  18500. compatVersion = CompatVersion.RUBY1_8;
  18501. }
  18502. if (Ruby.isSecurityRestricted()) {
  18503. compileMode = CompileMode.OFF;
  18504. jitLogging = false;
  18505. jitLoggingVerbose = false;
  18506. jitLogEvery = 0;
  18507. jitThreshold = -1;
  18508. jitMax = 0;
  18509. jitMaxSize = -1;
  18510. managementEnabled = false;
  18511. } else {
  18512. String threshold = SafePropertyAccessor.getProperty("jruby.jit.threshold");
  18513. String max = SafePropertyAccessor.getProperty("jruby.jit.max");
  18514. String maxSize = SafePropertyAccessor.getProperty("jruby.jit.maxsize");
  18515. if (COMPILE_EXCLUDE != null) {
  18516. String[] elements = COMPILE_EXCLUDE.split(",");
  18517. for (String element : elements) excludedMethods.add(element);
  18518. }
  18519. managementEnabled = SafePropertyAccessor.getBoolean("jruby.management.enabled", true);
  18520. runRubyInProcess = SafePropertyAccessor.getBoolean("jruby.launch.inproc", true);
  18521. boolean jitProperty = SafePropertyAccessor.getProperty("jruby.jit.enabled") != null;
  18522. if (jitProperty) {
  18523. error.print("jruby.jit.enabled property is deprecated; use jruby.compile.mode=(OFF|JIT|FORCE) for -C, default, and +C flags");
  18524. compileMode = SafePropertyAccessor.getBoolean("jruby.jit.enabled") ? CompileMode.JIT : CompileMode.OFF;
  18525. } else {
  18526. String jitModeProperty = SafePropertyAccessor.getProperty("jruby.compile.mode", "JIT");
  18527. if (jitModeProperty.equals("OFF")) {
  18528. compileMode = CompileMode.OFF;
  18529. } else if (jitModeProperty.equals("JIT")) {
  18530. compileMode = CompileMode.JIT;
  18531. } else if (jitModeProperty.equals("FORCE")) {
  18532. compileMode = CompileMode.FORCE;
  18533. } else {
  18534. error.print("jruby.compile.mode property must be OFF, JIT, FORCE, or unset; defaulting to JIT");
  18535. compileMode = CompileMode.JIT;
  18536. }
  18537. }
  18538. jitLogging = SafePropertyAccessor.getBoolean("jruby.jit.logging");
  18539. jitLoggingVerbose = SafePropertyAccessor.getBoolean("jruby.jit.logging.verbose");
  18540. String logEvery = SafePropertyAccessor.getProperty("jruby.jit.logEvery");
  18541. jitLogEvery = logEvery == null ? 0 : Integer.parseInt(logEvery);
  18542. jitThreshold = threshold == null ?
  18543. JIT_THRESHOLD : Integer.parseInt(threshold);
  18544. jitMax = max == null ?
  18545. JIT_MAX_METHODS_LIMIT : Integer.parseInt(max);
  18546. jitMaxSize = maxSize == null ?
  18547. JIT_MAX_SIZE_LIMIT : Integer.parseInt(maxSize);
  18548. }
  18549. // default ClassCache using jitMax as a soft upper bound
  18550. classCache = new ClassCache<Script>(loader, jitMax);
  18551. if (FORK_ENABLED) {
  18552. error.print("WARNING: fork is highly unlikely to be safe or stable on the JVM. Have fun!\n");
  18553. }
  18554. }
  18555. public LoadServiceCreator getLoadServiceCreator() {
  18556. return creator;
  18557. }
  18558. public void setLoadServiceCreator(LoadServiceCreator creator) {
  18559. this.creator = creator;
  18560. }
  18561. public LoadService createLoadService(Ruby runtime) {
  18562. return this.creator.create(runtime);
  18563. }
  18564. public String getBasicUsageHelp() {
  18565. StringBuilder sb = new StringBuilder();
  18566. sb
  18567. .append("Usage: jruby [switches] [--] [programfile] [arguments]\n")
  18568. .append(" -0[octal] specify record separator (\0, if no argument)\n")
  18569. .append(" -a autosplit mode with -n or -p (splits $_ into $F)\n")
  18570. .append(" -b benchmark mode, times the script execution\n")
  18571. .append(" -c check syntax only\n")
  18572. .append(" -Cdirectory cd to directory, before executing your script\n")
  18573. .append(" -d set debugging flags (set $DEBUG to true)\n")
  18574. .append(" -e 'command' one line of script. Several -e's allowed. Omit [programfile]\n")
  18575. .append(" -Fpattern split() pattern for autosplit (-a)\n")
  18576. //.append(" -i[extension] edit ARGV files in place (make backup if extension supplied)\n")
  18577. .append(" -Idirectory specify $LOAD_PATH directory (may be used more than once)\n")
  18578. .append(" -J[java option] pass an option on to the JVM (e.g. -J-Xmx512m)\n")
  18579. .append(" use --properties to list JRuby properties\n")
  18580. .append(" run 'java -help' for a list of other Java options\n")
  18581. .append(" -Kkcode specifies code-set (e.g. -Ku for Unicode\n")
  18582. .append(" -l enable line ending processing\n")
  18583. .append(" -n assume 'while gets(); ... end' loop around your script\n")
  18584. .append(" -p assume loop like -n but print line also like sed\n")
  18585. .append(" -rlibrary require the library, before executing your script\n")
  18586. .append(" -s enable some switch parsing for switches after script name\n")
  18587. .append(" -S look for the script in bin or using PATH environment variable\n")
  18588. .append(" -T[level] turn on tainting checks\n")
  18589. .append(" -v print version number, then turn on verbose mode\n")
  18590. .append(" -w turn warnings on for your script\n")
  18591. .append(" -W[level] set warning level; 0=silence, 1=medium, 2=verbose (default)\n")
  18592. //.append(" -x[directory] strip off text before #!ruby line and perhaps cd to directory\n")
  18593. .append(" -X[option] enable extended option (omit option to list)\n")
  18594. .append(" --copyright print the copyright\n")
  18595. .append(" --debug sets the execution mode most suitable for debugger functionality\n")
  18596. .append(" --jdb runs JRuby process under JDB\n")
  18597. .append(" --properties List all configuration Java properties (pass -J-Dproperty=value)\n")
  18598. .append(" --sample run with profiling using the JVM's sampling profiler\n")
  18599. .append(" --client use the non-optimizing \"client\" JVM (improves startup; default)\n")
  18600. .append(" --server use the optimizing \"server\" JVM (improves perf)\n")
  18601. .append(" --manage enable remote JMX management and monitoring of the VM and JRuby\n")
  18602. .append(" --1.8 specify Ruby 1.8.x compatibility (default)\n")
  18603. .append(" --1.9 specify Ruby 1.9.x compatibility\n")
  18604. .append(" --bytecode show the JVM bytecode produced by compiling specified code\n")
  18605. .append(" --version print the version\n");
  18606. return sb.toString();
  18607. }
  18608. public String getExtendedHelp() {
  18609. StringBuilder sb = new StringBuilder();
  18610. sb
  18611. .append("These flags are for extended JRuby options.\n")
  18612. .append("Specify them by passing -X<option>\n")
  18613. .append(" -O run with ObjectSpace disabled (default; improves performance)\n")
  18614. .append(" +O run with ObjectSpace enabled (reduces performance)\n")
  18615. .append(" -C disable all compilation\n")
  18616. .append(" +C force compilation of all scripts before they are run (except eval)\n")
  18617. .append(" -y read a YARV-compiled Ruby script and run that (EXPERIMENTAL)\n")
  18618. .append(" -Y compile a Ruby script into YARV bytecodes and run this (EXPERIMENTAL)\n")
  18619. .append(" -R read a Rubinius-compiled Ruby script and run that (EXPERIMENTAL)\n");
  18620. return sb.toString();
  18621. }
  18622. public String getPropertyHelp() {
  18623. StringBuilder sb = new StringBuilder();
  18624. sb
  18625. .append("These properties can be used to alter runtime behavior for perf or compatibility.\n")
  18626. .append("Specify them by passing -J-D<property>=<value>\n")
  18627. .append("\nCOMPILER SETTINGS:\n")
  18628. .append(" jruby.compile.mode=JIT|FORCE|OFF\n")
  18629. .append(" Set compilation mode. JIT is default; FORCE compiles all, OFF disables\n")
  18630. .append(" jruby.compile.fastest=true|false\n")
  18631. .append(" (EXPERIMENTAL) Turn on all experimental compiler optimizations\n")
  18632. .append(" jruby.compile.boxed=true|false\n")
  18633. .append(" (EXPERIMENTAL) Use boxed variables; this can speed up some methods. Default is false\n")
  18634. .append(" jruby.compile.frameless=true|false\n")
  18635. .append(" (EXPERIMENTAL) Turn on frameless compilation where possible\n")
  18636. .append(" jruby.compile.positionless=true|false\n")
  18637. .append(" (EXPERIMENTAL) Turn on compilation that avoids updating Ruby position info. Default is false\n")
  18638. .append(" jruby.compile.threadless=true|false\n")
  18639. .append(" (EXPERIMENTAL) Turn on compilation without polling for \"unsafe\" thread events. Default is false\n")
  18640. .append(" jruby.compile.fastops=true|false\n")
  18641. .append(" (EXPERIMENTAL) Turn on fast operators for Fixnum. Default is false\n")
  18642. .append(" jruby.compile.chainsize=<line count>\n")
  18643. .append(" Set the number of lines at which compiled bodies are \"chained\". Default is " + CHAINED_COMPILE_LINE_COUNT_DEFAULT + "\n")
  18644. .append(" jruby.compile.lazyHandles=true|false\n")
  18645. .append(" Generate method bindings (handles) for compiled methods lazily. Default is false.")
  18646. .append("\nJIT SETTINGS:\n")
  18647. .append(" jruby.jit.threshold=<invocation count>\n")
  18648. .append(" Set the JIT threshold to the specified method invocation count. Default is " + JIT_THRESHOLD + ".\n")
  18649. .append(" jruby.jit.max=<method count>\n")
  18650. .append(" Set the max count of active methods eligible for JIT-compilation.\n")
  18651. .append(" Default is " + JIT_MAX_METHODS_LIMIT + " per runtime. A value of 0 disables JIT, -1 disables max.\n")
  18652. .append(" jruby.jit.maxsize=<jitted method size (full .class)>\n")
  18653. .append(" Set the maximum full-class byte size allowed for jitted methods. Default is Integer.MAX_VALUE\n")
  18654. .append(" jruby.jit.logging=true|false\n")
  18655. .append(" Enable JIT logging (reports successful compilation). Default is false\n")
  18656. .append(" jruby.jit.logging.verbose=true|false\n")
  18657. .append(" Enable verbose JIT logging (reports failed compilation). Default is false\n")
  18658. .append(" jruby.jit.logEvery=<method count>\n")
  18659. .append(" Log a message every n methods JIT compiled. Default is 0 (off).\n")
  18660. .append(" jruby.jit.exclude=<ClsOrMod,ClsOrMod::method_name,-::method_name>\n")
  18661. .append(" Exclude methods from JIT by class/module short name, c/m::method_name,\n")
  18662. .append(" or -::method_name for anon/singleton classes/modules. Comma-delimited.\n")
  18663. .append("\nNATIVE SUPPORT:\n")
  18664. .append(" jruby.native.enabled=true|false\n")
  18665. .append(" Enable/disable native extensions (like JNA for non-Java APIs; Default is true\n")
  18666. .append(" (This affects all JRuby instances in a given JVM)\n")
  18667. .append(" jruby.native.verbose=true|false\n")
  18668. .append(" Enable verbose logging of native extension loading. Default is false.\n")
  18669. .append(" jruby.fork.enabled=true|false\n")
  18670. .append(" (EXPERIMENTAL, maybe dangerous) Enable fork(2) on platforms that support it.\n")
  18671. .append("\nTHREAD POOLING:\n")
  18672. .append(" jruby.thread.pool.enabled=true|false\n")
  18673. .append(" Enable reuse of native backing threads via a thread pool. Default is false.\n")
  18674. .append(" jruby.thread.pool.min=<min thread count>\n")
  18675. .append(" The minimum number of threads to keep alive in the pool. Default is 0.\n")
  18676. .append(" jruby.thread.pool.max=<max thread count>\n")
  18677. .append(" The maximum number of threads to allow in the pool. Default is unlimited.\n")
  18678. .append(" jruby.thread.pool.ttl=<time to live, in seconds>\n")
  18679. .append(" The maximum number of seconds to keep alive an idle thread. Default is 60.\n")
  18680. .append("\nMISCELLANY:\n")
  18681. .append(" jruby.compat.version=RUBY1_8|RUBY1_9\n")
  18682. .append(" Specify the major Ruby version to be compatible with; Default is RUBY1_8\n")
  18683. .append(" jruby.objectspace.enabled=true|false\n")
  18684. .append(" Enable or disable ObjectSpace.each_object (default is disabled)\n")
  18685. .append(" jruby.launch.inproc=true|false\n")
  18686. .append(" Set in-process launching of e.g. system('ruby ...'). Default is true\n")
  18687. .append(" jruby.bytecode.version=1.5|1.6\n")
  18688. .append(" Set bytecode version for JRuby to generate. Default is current JVM version.\n")
  18689. .append(" jruby.management.enabled=true|false\n")
  18690. .append(" Set whether JMX management is enabled. Default is true.\n")
  18691. .append(" jruby.debug.fullTrace=true|false\n")
  18692. .append(" Set whether full traces are enabled (c-call/c-return). Default is false.\n");
  18693. return sb.toString();
  18694. }
  18695. public String getVersionString() {
  18696. String ver = Constants.RUBY_VERSION;
  18697. switch (compatVersion) {
  18698. case RUBY1_8:
  18699. ver = Constants.RUBY_VERSION;
  18700. break;
  18701. case RUBY1_9:
  18702. ver = Constants.RUBY1_9_VERSION;
  18703. break;
  18704. }
  18705. String fullVersion = String.format(
  18706. "jruby %s (ruby %s patchlevel %s) (%s rev %s) [%s-java]\n",
  18707. Constants.VERSION, ver, Constants.RUBY_PATCHLEVEL,
  18708. Constants.COMPILE_DATE, Constants.REVISION,
  18709. SafePropertyAccessor.getProperty("os.arch", "unknown")
  18710. );
  18711. return fullVersion;
  18712. }
  18713. public String getCopyrightString() {
  18714. return "JRuby - Copyright (C) 2001-2008 The JRuby Community (and contribs)\n";
  18715. }
  18716. public void processArguments(String[] arguments) {
  18717. new ArgumentProcessor(arguments).processArguments();
  18718. }
  18719. public CompileMode getCompileMode() {
  18720. return compileMode;
  18721. }
  18722. public void setCompileMode(CompileMode compileMode) {
  18723. this.compileMode = compileMode;
  18724. }
  18725. public boolean isJitLogging() {
  18726. return jitLogging;
  18727. }
  18728. public boolean isJitLoggingVerbose() {
  18729. return jitLoggingVerbose;
  18730. }
  18731. public int getJitLogEvery() {
  18732. return jitLogEvery;
  18733. }
  18734. public boolean isSamplingEnabled() {
  18735. return samplingEnabled;
  18736. }
  18737. public int getJitThreshold() {
  18738. return jitThreshold;
  18739. }
  18740. public int getJitMax() {
  18741. return jitMax;
  18742. }
  18743. public int getJitMaxSize() {
  18744. return jitMaxSize;
  18745. }
  18746. public boolean isRunRubyInProcess() {
  18747. return runRubyInProcess;
  18748. }
  18749. public void setRunRubyInProcess(boolean flag) {
  18750. this.runRubyInProcess = flag;
  18751. }
  18752. public void setInput(InputStream newInput) {
  18753. input = newInput;
  18754. }
  18755. public InputStream getInput() {
  18756. return input;
  18757. }
  18758. public CompatVersion getCompatVersion() {
  18759. return compatVersion;
  18760. }
  18761. public void setOutput(PrintStream newOutput) {
  18762. output = newOutput;
  18763. }
  18764. public PrintStream getOutput() {
  18765. return output;
  18766. }
  18767. public void setError(PrintStream newError) {
  18768. error = newError;
  18769. }
  18770. public PrintStream getError() {
  18771. return error;
  18772. }
  18773. public void setCurrentDirectory(String newCurrentDirectory) {
  18774. currentDirectory = newCurrentDirectory;
  18775. }
  18776. public String getCurrentDirectory() {
  18777. return currentDirectory;
  18778. }
  18779. public void setProfile(Profile newProfile) {
  18780. profile = newProfile;
  18781. }
  18782. public Profile getProfile() {
  18783. return profile;
  18784. }
  18785. public void setObjectSpaceEnabled(boolean newObjectSpaceEnabled) {
  18786. objectSpaceEnabled = newObjectSpaceEnabled;
  18787. }
  18788. public boolean isObjectSpaceEnabled() {
  18789. return objectSpaceEnabled;
  18790. }
  18791. public void setEnvironment(Map newEnvironment) {
  18792. environment = newEnvironment;
  18793. }
  18794. public Map getEnvironment() {
  18795. return environment;
  18796. }
  18797. public ClassLoader getLoader() {
  18798. return loader;
  18799. }
  18800. public void setLoader(ClassLoader loader) {
  18801. // Setting the loader needs to reset the class cache
  18802. if(this.loader != loader) {
  18803. this.classCache = new ClassCache<Script>(loader, this.classCache.getMax());
  18804. }
  18805. this.loader = loader;
  18806. }
  18807. public String[] getArgv() {
  18808. return argv;
  18809. }
  18810. public void setArgv(String[] argv) {
  18811. this.argv = argv;
  18812. }
  18813. public String getJRubyHome() {
  18814. if (jrubyHome == null) {
  18815. if (Ruby.isSecurityRestricted()) {
  18816. return "SECURITY RESTRICTED";
  18817. }
  18818. jrubyHome = verifyHome(SafePropertyAccessor.getProperty("jruby.home",
  18819. SafePropertyAccessor.getProperty("user.home") + "/.jruby"));
  18820. try {
  18821. // This comment also in rbConfigLibrary
  18822. // Our shell scripts pass in non-canonicalized paths, but even if we didn't
  18823. // anyone who did would become unhappy because Ruby apps expect no relative
  18824. // operators in the pathname (rubygems, for example).
  18825. jrubyHome = new NormalizedFile(jrubyHome).getCanonicalPath();
  18826. } catch (IOException e) { }
  18827. jrubyHome = new NormalizedFile(jrubyHome).getAbsolutePath();
  18828. }
  18829. return jrubyHome;
  18830. }
  18831. public void setJRubyHome(String home) {
  18832. jrubyHome = verifyHome(home);
  18833. }
  18834. // We require the home directory to be absolute
  18835. private String verifyHome(String home) {
  18836. if (home.equals(".")) {
  18837. home = System.getProperty("user.dir");
  18838. }
  18839. if (!home.startsWith("file:")) {
  18840. NormalizedFile f = new NormalizedFile(home);
  18841. if (!f.isAbsolute()) {
  18842. home = f.getAbsolutePath();
  18843. }
  18844. f.mkdirs();
  18845. }
  18846. return home;
  18847. }
  18848. private class ArgumentProcessor {
  18849. private String[] arguments;
  18850. private int argumentIndex = 0;
  18851. public ArgumentProcessor(String[] arguments) {
  18852. this.arguments = arguments;
  18853. }
  18854. public void processArguments() {
  18855. while (argumentIndex < arguments.length && isInterpreterArgument(arguments[argumentIndex])) {
  18856. processArgument();
  18857. argumentIndex++;
  18858. }
  18859. if (!hasInlineScript && scriptFileName == null) {
  18860. if (argumentIndex < arguments.length) {
  18861. setScriptFileName(arguments[argumentIndex]); //consume the file name
  18862. argumentIndex++;
  18863. }
  18864. }
  18865. processArgv();
  18866. }
  18867. private void processArgv() {
  18868. List<String> arglist = new ArrayList<String>();
  18869. for (; argumentIndex < arguments.length; argumentIndex++) {
  18870. String arg = arguments[argumentIndex];
  18871. if (argvGlobalsOn && arg.startsWith("-")) {
  18872. arg = arg.substring(1);
  18873. if (arg.indexOf('=') > 0) {
  18874. String[] keyvalue = arg.split("=", 2);
  18875. optionGlobals.put(keyvalue[0], keyvalue[1]);
  18876. } else {
  18877. optionGlobals.put(arg, null);
  18878. }
  18879. } else {
  18880. argvGlobalsOn = false;
  18881. arglist.add(arg);
  18882. }
  18883. }
  18884. // Remaining arguments are for the script itself
  18885. argv = arglist.toArray(new String[arglist.size()]);
  18886. }
  18887. private boolean isInterpreterArgument(String argument) {
  18888. return (argument.charAt(0) == '-' || argument.charAt(0) == '+') && !endOfArguments;
  18889. }
  18890. private String getArgumentError(String additionalError) {
  18891. return "jruby: invalid argument\n" + additionalError + "\n";
  18892. }
  18893. private void processArgument() {
  18894. String argument = arguments[argumentIndex];
  18895. FOR : for (characterIndex = 1; characterIndex < argument.length(); characterIndex++) {
  18896. switch (argument.charAt(characterIndex)) {
  18897. case '0': {
  18898. String temp = grabOptionalValue();
  18899. if (null == temp) {
  18900. recordSeparator = "\u0000";
  18901. } else if (temp.equals("0")) {
  18902. recordSeparator = "\n\n";
  18903. } else if (temp.equals("777")) {
  18904. recordSeparator = "\uFFFF"; // Specify something that can't separate
  18905. } else {
  18906. try {
  18907. int val = Integer.parseInt(temp, 8);
  18908. recordSeparator = "" + (char) val;
  18909. } catch (Exception e) {
  18910. MainExitException mee = new MainExitException(1, getArgumentError(" -0 must be followed by either 0, 777, or a valid octal value"));
  18911. mee.setUsageError(true);
  18912. throw mee;
  18913. }
  18914. }
  18915. break FOR;
  18916. }
  18917. case 'a':
  18918. split = true;
  18919. break;
  18920. case 'b':
  18921. benchmarking = true;
  18922. break;
  18923. case 'c':
  18924. shouldCheckSyntax = true;
  18925. break;
  18926. case 'C':
  18927. try {
  18928. String saved = grabValue(getArgumentError(" -C must be followed by a directory expression"));
  18929. File base = new File(currentDirectory);
  18930. File newDir = new File(saved);
  18931. if (newDir.isAbsolute()) {
  18932. currentDirectory = newDir.getCanonicalPath();
  18933. } else {
  18934. currentDirectory = new File(base, newDir.getPath()).getCanonicalPath();
  18935. }
  18936. if (!(new File(currentDirectory).isDirectory())) {
  18937. MainExitException mee = new MainExitException(1, "jruby: Can't chdir to " + saved + " (fatal)");
  18938. mee.setUsageError(true);
  18939. throw mee;
  18940. }
  18941. } catch (IOException e) {
  18942. MainExitException mee = new MainExitException(1, getArgumentError(" -C must be followed by a valid directory"));
  18943. mee.setUsageError(true);
  18944. throw mee;
  18945. }
  18946. break;
  18947. case 'd':
  18948. debug = true;
  18949. verbose = Boolean.TRUE;
  18950. break;
  18951. case 'e':
  18952. inlineScript.append(grabValue(getArgumentError(" -e must be followed by an expression to evaluate")));
  18953. inlineScript.append('\n');
  18954. hasInlineScript = true;
  18955. break FOR;
  18956. case 'F':
  18957. inputFieldSeparator = grabValue(getArgumentError(" -F must be followed by a pattern for input field separation"));
  18958. break;
  18959. case 'h':
  18960. shouldPrintUsage = true;
  18961. shouldRunInterpreter = false;
  18962. break;
  18963. // FIXME: -i flag not supported
  18964. // case 'i' :
  18965. // break;
  18966. case 'I':
  18967. String s = grabValue(getArgumentError("-I must be followed by a directory name to add to lib path"));
  18968. String[] ls = s.split(java.io.File.pathSeparator);
  18969. for (int i = 0; i < ls.length; i++) {
  18970. loadPaths.add(ls[i]);
  18971. }
  18972. break FOR;
  18973. case 'K':
  18974. // FIXME: No argument seems to work for -K in MRI plus this should not
  18975. // siphon off additional args 'jruby -K ~/scripts/foo'. Also better error
  18976. // processing.
  18977. String eArg = grabValue(getArgumentError("provide a value for -K"));
  18978. kcode = KCode.create(null, eArg);
  18979. break;
  18980. case 'l':
  18981. processLineEnds = true;
  18982. break;
  18983. case 'n':
  18984. assumeLoop = true;
  18985. break;
  18986. case 'p':
  18987. assumePrinting = true;
  18988. assumeLoop = true;
  18989. break;
  18990. case 'r':
  18991. requiredLibraries.add(grabValue(getArgumentError("-r must be followed by a package to require")));
  18992. break FOR;
  18993. case 's' :
  18994. argvGlobalsOn = true;
  18995. break;
  18996. case 'S':
  18997. runBinScript();
  18998. break FOR;
  18999. case 'T' :{
  19000. String temp = grabOptionalValue();
  19001. int value = 1;
  19002. if(temp!=null) {
  19003. try {
  19004. value = Integer.parseInt(temp, 8);
  19005. } catch(Exception e) {
  19006. value = 1;
  19007. }
  19008. }
  19009. safeLevel = value;
  19010. break FOR;
  19011. }
  19012. case 'v':
  19013. verbose = Boolean.TRUE;
  19014. setShowVersion(true);
  19015. break;
  19016. case 'w':
  19017. verbose = Boolean.TRUE;
  19018. break;
  19019. case 'W': {
  19020. String temp = grabOptionalValue();
  19021. int value = 2;
  19022. if (null != temp) {
  19023. if (temp.equals("2")) {
  19024. value = 2;
  19025. } else if (temp.equals("1")) {
  19026. value = 1;
  19027. } else if (temp.equals("0")) {
  19028. value = 0;
  19029. } else {
  19030. MainExitException mee = new MainExitException(1, getArgumentError(" -W must be followed by either 0, 1, 2 or nothing"));
  19031. mee.setUsageError(true);
  19032. throw mee;
  19033. }
  19034. }
  19035. switch (value) {
  19036. case 0:
  19037. verbose = null;
  19038. break;
  19039. case 1:
  19040. verbose = Boolean.FALSE;
  19041. break;
  19042. case 2:
  19043. verbose = Boolean.TRUE;
  19044. break;
  19045. }
  19046. break FOR;
  19047. }
  19048. // FIXME: -x flag not supported
  19049. // case 'x' :
  19050. // break;
  19051. case 'X':
  19052. String extendedOption = grabOptionalValue();
  19053. if (extendedOption == null) {
  19054. throw new MainExitException(0, "jruby: missing extended option, listing available options\n" + getExtendedHelp());
  19055. } else if (extendedOption.equals("-O")) {
  19056. objectSpaceEnabled = false;
  19057. } else if (extendedOption.equals("+O")) {
  19058. objectSpaceEnabled = true;
  19059. } else if (extendedOption.equals("-C")) {
  19060. compileMode = CompileMode.OFF;
  19061. } else if (extendedOption.equals("+C")) {
  19062. compileMode = CompileMode.FORCE;
  19063. } else if (extendedOption.equals("-y")) {
  19064. yarv = true;
  19065. } else if (extendedOption.equals("-Y")) {
  19066. yarvCompile = true;
  19067. } else if (extendedOption.equals("-R")) {
  19068. rubinius = true;
  19069. } else {
  19070. MainExitException mee =
  19071. new MainExitException(1, "jruby: invalid extended option " + extendedOption + " (-X will list valid options)\n");
  19072. mee.setUsageError(true);
  19073. throw mee;
  19074. }
  19075. break FOR;
  19076. case '-':
  19077. if (argument.equals("--command") || argument.equals("--bin")) {
  19078. characterIndex = argument.length();
  19079. runBinScript();
  19080. break;
  19081. } else if (argument.equals("--compat")) {
  19082. characterIndex = argument.length();
  19083. compatVersion = CompatVersion.getVersionFromString(grabValue(getArgumentError("--compat must be RUBY1_8 or RUBY1_9")));
  19084. if (compatVersion == null) {
  19085. compatVersion = CompatVersion.RUBY1_8;
  19086. }
  19087. break FOR;
  19088. } else if (argument.equals("--copyright")) {
  19089. setShowCopyright(true);
  19090. shouldRunInterpreter = false;
  19091. break FOR;
  19092. } else if (argument.equals("--debug")) {
  19093. compileMode = CompileMode.OFF;
  19094. FULL_TRACE_ENABLED = true;
  19095. System.setProperty("jruby.reflection", "true");
  19096. break FOR;
  19097. } else if (argument.equals("--jdb")) {
  19098. debug = true;
  19099. verbose = Boolean.TRUE;
  19100. break;
  19101. } else if (argument.equals("--help")) {
  19102. shouldPrintUsage = true;
  19103. shouldRunInterpreter = false;
  19104. break;
  19105. } else if (argument.equals("--properties")) {
  19106. shouldPrintProperties = true;
  19107. shouldRunInterpreter = false;
  19108. break;
  19109. } else if (argument.equals("--version")) {
  19110. setShowVersion(true);
  19111. break FOR;
  19112. } else if (argument.equals("--bytecode")) {
  19113. setShowBytecode(true);
  19114. break FOR;
  19115. } else {
  19116. if (argument.equals("--")) {
  19117. // ruby interpreter compatibilty
  19118. // Usage: ruby [switches] [--] [programfile] [arguments])
  19119. endOfArguments = true;
  19120. break;
  19121. }
  19122. }
  19123. default:
  19124. throw new MainExitException(1, "jruby: unknown option " + argument);
  19125. }
  19126. }
  19127. }
  19128. private void runBinScript() {
  19129. String scriptName = grabValue("jruby: provide a bin script to execute");
  19130. if (scriptName.equals("irb")) {
  19131. scriptName = "jirb";
  19132. }
  19133. scriptFileName = scriptName;
  19134. if (!new File(scriptFileName).exists()) {
  19135. try {
  19136. String jrubyHome = JRubyFile.create(System.getProperty("user.dir"), JRubyFile.getFileProperty("jruby.home")).getCanonicalPath();
  19137. scriptFileName = JRubyFile.create(jrubyHome + JRubyFile.separator + "bin", scriptName).getCanonicalPath();
  19138. } catch (IOException io) {
  19139. MainExitException mee = new MainExitException(1, "jruby: Can't determine script filename");
  19140. mee.setUsageError(true);
  19141. throw mee;
  19142. }
  19143. }
  19144. // route 'gem' through ruby code in case we're running out of the complete jar
  19145. if (scriptName.equals("gem") || !new File(scriptFileName).exists()) {
  19146. requiredLibraries.add("jruby/commands");
  19147. inlineScript.append("JRuby::Commands." + scriptName);
  19148. inlineScript.append("\n");
  19149. hasInlineScript = true;
  19150. }
  19151. endOfArguments = true;
  19152. }
  19153. private String grabValue(String errorMessage) {
  19154. characterIndex++;
  19155. if (characterIndex < arguments[argumentIndex].length()) {
  19156. return arguments[argumentIndex].substring(characterIndex);
  19157. }
  19158. argumentIndex++;
  19159. if (argumentIndex < arguments.length) {
  19160. return arguments[argumentIndex];
  19161. }
  19162. MainExitException mee = new MainExitException(1, errorMessage);
  19163. mee.setUsageError(true);
  19164. throw mee;
  19165. }
  19166. private String grabOptionalValue() {
  19167. characterIndex++;
  19168. if (characterIndex < arguments[argumentIndex].length()) {
  19169. return arguments[argumentIndex].substring(characterIndex);
  19170. }
  19171. return null;
  19172. }
  19173. }
  19174. public byte[] inlineScript() {
  19175. return inlineScript.toString().getBytes();
  19176. }
  19177. public List<String> requiredLibraries() {
  19178. return requiredLibraries;
  19179. }
  19180. public List<String> loadPaths() {
  19181. return loadPaths;
  19182. }
  19183. public boolean shouldRunInterpreter() {
  19184. if(isShowVersion() && (hasInlineScript || scriptFileName != null)) {
  19185. return true;
  19186. }
  19187. return isShouldRunInterpreter();
  19188. }
  19189. public boolean shouldPrintUsage() {
  19190. return shouldPrintUsage;
  19191. }
  19192. public boolean shouldPrintProperties() {
  19193. return shouldPrintProperties;
  19194. }
  19195. private boolean isSourceFromStdin() {
  19196. return getScriptFileName() == null;
  19197. }
  19198. public boolean isInlineScript() {
  19199. return hasInlineScript;
  19200. }
  19201. public InputStream getScriptSource() {
  19202. try {
  19203. // KCode.NONE is used because KCODE does not affect parse in Ruby 1.8
  19204. // if Ruby 2.0 encoding pragmas are implemented, this will need to change
  19205. if (hasInlineScript) {
  19206. return new ByteArrayInputStream(inlineScript());
  19207. } else if (isSourceFromStdin()) {
  19208. // can't use -v and stdin
  19209. if (isShowVersion()) {
  19210. return null;
  19211. }
  19212. return getInput();
  19213. } else {
  19214. File file = JRubyFile.create(getCurrentDirectory(), getScriptFileName());
  19215. return new BufferedInputStream(new FileInputStream(file));
  19216. }
  19217. } catch (IOException e) {
  19218. throw new MainExitException(1, "Error opening script file: " + e.getMessage());
  19219. }
  19220. }
  19221. public String displayedFileName() {
  19222. if (hasInlineScript) {
  19223. if (scriptFileName != null) {
  19224. return scriptFileName;
  19225. } else {
  19226. return "-e";
  19227. }
  19228. } else if (isSourceFromStdin()) {
  19229. return "-";
  19230. } else {
  19231. return getScriptFileName();
  19232. }
  19233. }
  19234. private void setScriptFileName(String scriptFileName) {
  19235. this.scriptFileName = scriptFileName;
  19236. }
  19237. public String getScriptFileName() {
  19238. return scriptFileName;
  19239. }
  19240. public boolean isBenchmarking() {
  19241. return benchmarking;
  19242. }
  19243. public boolean isAssumeLoop() {
  19244. return assumeLoop;
  19245. }
  19246. public boolean isAssumePrinting() {
  19247. return assumePrinting;
  19248. }
  19249. public boolean isProcessLineEnds() {
  19250. return processLineEnds;
  19251. }
  19252. public boolean isSplit() {
  19253. return split;
  19254. }
  19255. public boolean isVerbose() {
  19256. return verbose == Boolean.TRUE;
  19257. }
  19258. public Boolean getVerbose() {
  19259. return verbose;
  19260. }
  19261. public boolean isDebug() {
  19262. return debug;
  19263. }
  19264. public boolean isShowVersion() {
  19265. return showVersion;
  19266. }
  19267. public boolean isShowBytecode() {
  19268. return showBytecode;
  19269. }
  19270. public boolean isShowCopyright() {
  19271. return showCopyright;
  19272. }
  19273. protected void setShowVersion(boolean showVersion) {
  19274. this.showVersion = showVersion;
  19275. }
  19276. protected void setShowBytecode(boolean showBytecode) {
  19277. this.showBytecode = showBytecode;
  19278. }
  19279. protected void setShowCopyright(boolean showCopyright) {
  19280. this.showCopyright = showCopyright;
  19281. }
  19282. public boolean isShouldRunInterpreter() {
  19283. return shouldRunInterpreter;
  19284. }
  19285. public boolean isShouldCheckSyntax() {
  19286. return shouldCheckSyntax;
  19287. }
  19288. public boolean isYARVEnabled() {
  19289. return yarv;
  19290. }
  19291. public String getInputFieldSeparator() {
  19292. return inputFieldSeparator;
  19293. }
  19294. public boolean isRubiniusEnabled() {
  19295. return rubinius;
  19296. }
  19297. public boolean isYARVCompileEnabled() {
  19298. return yarvCompile;
  19299. }
  19300. public KCode getKCode() {
  19301. return kcode;
  19302. }
  19303. public String getRecordSeparator() {
  19304. return recordSeparator;
  19305. }
  19306. public int getSafeLevel() {
  19307. return safeLevel;
  19308. }
  19309. public void setRecordSeparator(String recordSeparator) {
  19310. this.recordSeparator = recordSeparator;
  19311. }
  19312. public ClassCache getClassCache() {
  19313. return classCache;
  19314. }
  19315. public void setClassCache(ClassCache classCache) {
  19316. this.classCache = classCache;
  19317. }
  19318. public Map getOptionGlobals() {
  19319. return optionGlobals;
  19320. }
  19321. public boolean isManagementEnabled() {
  19322. return managementEnabled;
  19323. }
  19324. public Set getExcludedMethods() {
  19325. return excludedMethods;
  19326. }
  19327. }
  19328. /*
  19329. **** BEGIN LICENSE BLOCK *****
  19330. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  19331. *
  19332. * The contents of this file are subject to the Common Public
  19333. * License Version 1.0 (the "License"); you may not use this file
  19334. * except in compliance with the License. You may obtain a copy of
  19335. * the License at http://www.eclipse.org/legal/cpl-v10.html
  19336. *
  19337. * Software distributed under the License is distributed on an "AS
  19338. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  19339. * implied. See the License for the specific language governing
  19340. * rights and limitations under the License.
  19341. *
  19342. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  19343. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  19344. * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  19345. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  19346. * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  19347. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  19348. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  19349. *
  19350. * Alternatively, the contents of this file may be used under the terms of
  19351. * either of the GNU General Public License Version 2 or later (the "GPL"),
  19352. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19353. * in which case the provisions of the GPL or the LGPL are applicable instead
  19354. * of those above. If you wish to allow use of your version of this file only
  19355. * under the terms of either the GPL or the LGPL, and not to allow others to
  19356. * use your version of this file under the terms of the CPL, indicate your
  19357. * decision by deleting the provisions above and replace them with the notice
  19358. * and other provisions required by the GPL or the LGPL. If you do not delete
  19359. * the provisions above, a recipient may use your version of this file under
  19360. * the terms of any one of the CPL, the GPL or the LGPL.
  19361. ***** END LICENSE BLOCK *****/
  19362. package org.jruby;
  19363. import org.jruby.anno.JRubyMethod;
  19364. import org.jruby.anno.JRubyClass;
  19365. import org.jruby.runtime.Block;
  19366. import org.jruby.runtime.BlockBody;
  19367. import org.jruby.runtime.MethodIndex;
  19368. import org.jruby.runtime.ObjectAllocator;
  19369. import org.jruby.runtime.ThreadContext;
  19370. import org.jruby.runtime.builtin.IRubyObject;
  19371. import org.jruby.util.ByteList;
  19372. /** Implementation of the Integer class.
  19373. *
  19374. * @author jpetersen
  19375. */
  19376. @JRubyClass(name="Integer", parent="Numeric", include="Precision")
  19377. public abstract class RubyInteger extends RubyNumeric {
  19378. public static RubyClass createIntegerClass(Ruby runtime) {
  19379. RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
  19380. ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  19381. runtime.setInteger(integer);
  19382. integer.kindOf = new RubyModule.KindOf() {
  19383. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  19384. return obj instanceof RubyInteger;
  19385. }
  19386. };
  19387. integer.getSingletonClass().undefineMethod("new");
  19388. integer.includeModule(runtime.getPrecision());
  19389. integer.defineAnnotatedMethods(RubyInteger.class);
  19390. return integer;
  19391. }
  19392. public RubyInteger(Ruby runtime, RubyClass rubyClass) {
  19393. super(runtime, rubyClass);
  19394. }
  19395. public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
  19396. super(runtime, rubyClass, useObjectSpace);
  19397. }
  19398. public RubyInteger convertToInteger() {
  19399. return this;
  19400. }
  19401. // conversion
  19402. protected RubyFloat toFloat() {
  19403. return RubyFloat.newFloat(getRuntime(), getDoubleValue());
  19404. }
  19405. /* ================
  19406. * Instance Methods
  19407. * ================
  19408. */
  19409. /** int_int_p
  19410. *
  19411. */
  19412. @JRubyMethod(name = "integer?")
  19413. public IRubyObject integer_p() {
  19414. return getRuntime().getTrue();
  19415. }
  19416. /** int_upto
  19417. *
  19418. */
  19419. @JRubyMethod(name = "upto", frame = true)
  19420. public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
  19421. final Ruby runtime = getRuntime();
  19422. if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
  19423. RubyFixnum toFixnum = (RubyFixnum) to;
  19424. final long toValue = toFixnum.getLongValue();
  19425. final long fromValue = getLongValue();
  19426. if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
  19427. final IRubyObject nil = runtime.getNil();
  19428. for (long i = fromValue; i <= toValue; i++) {
  19429. block.yield(context, nil);
  19430. }
  19431. } else {
  19432. for (long i = fromValue; i <= toValue; i++) {
  19433. block.yield(context, RubyFixnum.newFixnum(runtime, i));
  19434. }
  19435. }
  19436. } else {
  19437. RubyNumeric i = this;
  19438. while (true) {
  19439. if (i.callMethod(context, MethodIndex.OP_GT, ">", to).isTrue()) {
  19440. break;
  19441. }
  19442. block.yield(context, i);
  19443. i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
  19444. }
  19445. }
  19446. return this;
  19447. }
  19448. /** int_downto
  19449. *
  19450. */
  19451. // TODO: Make callCoerced work in block context...then fix downto, step, and upto.
  19452. @JRubyMethod(name = "downto", frame = true)
  19453. public IRubyObject downto(ThreadContext context, IRubyObject to, Block block) {
  19454. final Ruby runtime = getRuntime();
  19455. if (this instanceof RubyFixnum && to instanceof RubyFixnum) {
  19456. RubyFixnum toFixnum = (RubyFixnum) to;
  19457. final long toValue = toFixnum.getLongValue();
  19458. if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
  19459. final IRubyObject nil = runtime.getNil();
  19460. for (long i = getLongValue(); i >= toValue; i--) {
  19461. block.yield(context, nil);
  19462. }
  19463. } else {
  19464. for (long i = getLongValue(); i >= toValue; i--) {
  19465. block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
  19466. }
  19467. }
  19468. } else {
  19469. RubyNumeric i = this;
  19470. while (true) {
  19471. if (i.callMethod(context, MethodIndex.OP_LT, "<", to).isTrue()) {
  19472. break;
  19473. }
  19474. block.yield(context, i);
  19475. i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_MINUS, "-", RubyFixnum.one(getRuntime()));
  19476. }
  19477. }
  19478. return this;
  19479. }
  19480. @JRubyMethod(name = "times", frame = true)
  19481. public IRubyObject times(ThreadContext context, Block block) {
  19482. final Ruby runtime = context.getRuntime();
  19483. if (this instanceof RubyFixnum) {
  19484. final long value = getLongValue();
  19485. if (block.getBody().getArgumentType() == BlockBody.ZERO_ARGS) {
  19486. final IRubyObject nil = runtime.getNil();
  19487. for (long i = 0; i < value; i++) {
  19488. block.yield(context, nil);
  19489. }
  19490. } else {
  19491. for (long i = 0; i < value; i++) {
  19492. block.yield(context, RubyFixnum.newFixnum(runtime, i));
  19493. }
  19494. }
  19495. } else {
  19496. RubyNumeric i = RubyFixnum.zero(runtime);
  19497. while (true) {
  19498. if (!i.callMethod(context, MethodIndex.OP_LT, "<", this).isTrue()) {
  19499. break;
  19500. }
  19501. block.yield(context, i);
  19502. i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(runtime));
  19503. }
  19504. }
  19505. return this;
  19506. }
  19507. /** int_succ
  19508. *
  19509. */
  19510. @JRubyMethod(name = {"succ", "next"})
  19511. public IRubyObject succ(ThreadContext context) {
  19512. if (this instanceof RubyFixnum) {
  19513. return RubyFixnum.newFixnum(getRuntime(), getLongValue() + 1L);
  19514. } else {
  19515. return callMethod(context, MethodIndex.OP_PLUS, "+", RubyFixnum.one(getRuntime()));
  19516. }
  19517. }
  19518. /** int_chr
  19519. *
  19520. */
  19521. @JRubyMethod(name = "chr")
  19522. public RubyString chr() {
  19523. if (getLongValue() < 0 || getLongValue() > 0xff) {
  19524. throw getRuntime().newRangeError(this.toString() + " out of char range");
  19525. }
  19526. return RubyString.newString(getRuntime(), new ByteList(new byte[]{(byte)getLongValue()}, false));
  19527. }
  19528. /** int_to_i
  19529. *
  19530. */
  19531. @JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "round", "truncate"})
  19532. public RubyInteger to_i() {
  19533. return this;
  19534. }
  19535. /** integer_to_r
  19536. *
  19537. */
  19538. @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
  19539. public IRubyObject to_r(ThreadContext context) {
  19540. return RubyRational.newRationalCanonicalize(context, this);
  19541. }
  19542. @JRubyMethod(name = {"odd?"})
  19543. public static RubyBoolean odd_p(ThreadContext context, IRubyObject recv) {
  19544. if(recv.callMethod(context, "%", recv.getRuntime().newFixnum(2)) != RubyFixnum.zero(recv.getRuntime())) {
  19545. return recv.getRuntime().getTrue();
  19546. }
  19547. return recv.getRuntime().getFalse();
  19548. }
  19549. @JRubyMethod(name = {"even?"})
  19550. public static RubyBoolean even_p(ThreadContext context, IRubyObject recv) {
  19551. if(recv.callMethod(context, "%", recv.getRuntime().newFixnum(2)) == RubyFixnum.zero(recv.getRuntime())) {
  19552. return recv.getRuntime().getTrue();
  19553. }
  19554. return recv.getRuntime().getFalse();
  19555. }
  19556. @JRubyMethod
  19557. public static IRubyObject pred(ThreadContext context, IRubyObject recv) {
  19558. return recv.callMethod(context, "-", recv.getRuntime().newFixnum(1));
  19559. }
  19560. /* ================
  19561. * Singleton Methods
  19562. * ================
  19563. */
  19564. /** rb_int_induced_from
  19565. *
  19566. */
  19567. @JRubyMethod(name = "induced_from", meta = true)
  19568. public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
  19569. if (other instanceof RubyFixnum || other instanceof RubyBignum) {
  19570. return other;
  19571. } else if (other instanceof RubyFloat || other instanceof RubyRational) {
  19572. return other.callMethod(context, MethodIndex.TO_I, "to_i");
  19573. } else {
  19574. throw recv.getRuntime().newTypeError(
  19575. "failed to convert " + other.getMetaClass().getName() + " into Integer");
  19576. }
  19577. }
  19578. }
  19579. /*
  19580. **** BEGIN LICENSE BLOCK *****
  19581. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  19582. *
  19583. * The contents of this file are subject to the Common Public
  19584. * License Version 1.0 (the "License"); you may not use this file
  19585. * except in compliance with the License. You may obtain a copy of
  19586. * the License at http://www.eclipse.org/legal/cpl-v10.html
  19587. *
  19588. * Software distributed under the License is distributed on an "AS
  19589. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  19590. * implied. See the License for the specific language governing
  19591. * rights and limitations under the License.
  19592. *
  19593. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  19594. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  19595. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  19596. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  19597. * Copyright (C) 2004-2006 Charles O Nutter <headius@headius.com>
  19598. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  19599. * Copyright (C) 2006 Evan Buswell <ebuswell@gmail.com>
  19600. * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
  19601. *
  19602. * Alternatively, the contents of this file may be used under the terms of
  19603. * either of the GNU General Public License Version 2 or later (the "GPL"),
  19604. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  19605. * in which case the provisions of the GPL or the LGPL are applicable instead
  19606. * of those above. If you wish to allow use of your version of this file only
  19607. * under the terms of either the GPL or the LGPL, and not to allow others to
  19608. * use your version of this file under the terms of the CPL, indicate your
  19609. * decision by deleting the provisions above and replace them with the notice
  19610. * and other provisions required by the GPL or the LGPL. If you do not delete
  19611. * the provisions above, a recipient may use your version of this file under
  19612. * the terms of any one of the CPL, the GPL or the LGPL.
  19613. ***** END LICENSE BLOCK *****/
  19614. package org.jruby;
  19615. import java.io.EOFException;
  19616. import java.io.FileDescriptor;
  19617. import java.io.FilterInputStream;
  19618. import java.io.FilterOutputStream;
  19619. import java.io.IOException;
  19620. import java.io.InputStream;
  19621. import java.io.OutputStream;
  19622. import java.lang.ref.Reference;
  19623. import java.lang.ref.WeakReference;
  19624. import java.nio.channels.Channel;
  19625. import java.nio.channels.Channels;
  19626. import java.nio.channels.FileChannel;
  19627. import java.nio.channels.Pipe;
  19628. import java.nio.channels.SelectableChannel;
  19629. import java.nio.channels.SelectionKey;
  19630. import java.nio.channels.Selector;
  19631. import java.util.ArrayList;
  19632. import java.util.HashSet;
  19633. import java.util.Iterator;
  19634. import java.util.List;
  19635. import java.util.Set;
  19636. import java.util.concurrent.atomic.AtomicInteger;
  19637. import org.jruby.anno.FrameField;
  19638. import org.jruby.anno.JRubyMethod;
  19639. import org.jruby.anno.JRubyClass;
  19640. import org.jruby.common.IRubyWarnings.ID;
  19641. import org.jruby.exceptions.RaiseException;
  19642. import org.jruby.ext.posix.util.FieldAccess;
  19643. import org.jruby.runtime.Arity;
  19644. import org.jruby.runtime.Block;
  19645. import org.jruby.runtime.CallType;
  19646. import org.jruby.runtime.MethodIndex;
  19647. import org.jruby.runtime.ObjectAllocator;
  19648. import org.jruby.runtime.ThreadContext;
  19649. import org.jruby.runtime.Visibility;
  19650. import org.jruby.runtime.builtin.IRubyObject;
  19651. import org.jruby.util.ByteList;
  19652. import org.jruby.util.io.Stream;
  19653. import org.jruby.util.io.ModeFlags;
  19654. import org.jruby.util.ShellLauncher;
  19655. import org.jruby.util.TypeConverter;
  19656. import org.jruby.util.io.BadDescriptorException;
  19657. import org.jruby.util.io.ChannelStream;
  19658. import org.jruby.util.io.InvalidValueException;
  19659. import org.jruby.util.io.PipeException;
  19660. import org.jruby.util.io.FileExistsException;
  19661. import org.jruby.util.io.STDIO;
  19662. import org.jruby.util.io.OpenFile;
  19663. import org.jruby.util.io.ChannelDescriptor;
  19664. import static org.jruby.CompatVersion.*;
  19665. /**
  19666. *
  19667. * @author jpetersen
  19668. */
  19669. @JRubyClass(name="IO", include="Enumerable")
  19670. public class RubyIO extends RubyObject {
  19671. protected OpenFile openFile;
  19672. protected List<RubyThread> blockingThreads;
  19673. public void registerDescriptor(ChannelDescriptor descriptor) {
  19674. getRuntime().getDescriptors().put(new Integer(descriptor.getFileno()), new WeakReference<ChannelDescriptor>(descriptor));
  19675. }
  19676. public void unregisterDescriptor(int aFileno) {
  19677. getRuntime().getDescriptors().remove(new Integer(aFileno));
  19678. }
  19679. public ChannelDescriptor getDescriptorByFileno(int aFileno) {
  19680. Reference<ChannelDescriptor> reference = getRuntime().getDescriptors().get(new Integer(aFileno));
  19681. if (reference == null) {
  19682. return null;
  19683. }
  19684. return reference.get();
  19685. }
  19686. // FIXME can't use static; would interfere with other runtimes in the same JVM
  19687. protected static AtomicInteger filenoIndex = new AtomicInteger(2);
  19688. public static int getNewFileno() {
  19689. return filenoIndex.incrementAndGet();
  19690. }
  19691. // This should only be called by this and RubyFile.
  19692. // It allows this object to be created without a IOHandler.
  19693. public RubyIO(Ruby runtime, RubyClass type) {
  19694. super(runtime, type);
  19695. openFile = new OpenFile();
  19696. }
  19697. public RubyIO(Ruby runtime, OutputStream outputStream) {
  19698. super(runtime, runtime.getIO());
  19699. // We only want IO objects with valid streams (better to error now).
  19700. if (outputStream == null) {
  19701. throw runtime.newIOError("Opening invalid stream");
  19702. }
  19703. openFile = new OpenFile();
  19704. try {
  19705. openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(outputStream), getNewFileno(), new FileDescriptor())));
  19706. } catch (InvalidValueException e) {
  19707. throw getRuntime().newErrnoEINVALError();
  19708. }
  19709. openFile.setMode(OpenFile.WRITABLE | OpenFile.APPEND);
  19710. registerDescriptor(openFile.getMainStream().getDescriptor());
  19711. }
  19712. public RubyIO(Ruby runtime, InputStream inputStream) {
  19713. super(runtime, runtime.getIO());
  19714. if (inputStream == null) {
  19715. throw runtime.newIOError("Opening invalid stream");
  19716. }
  19717. openFile = new OpenFile();
  19718. try {
  19719. openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(Channels.newChannel(inputStream), getNewFileno(), new FileDescriptor())));
  19720. } catch (InvalidValueException e) {
  19721. throw getRuntime().newErrnoEINVALError();
  19722. }
  19723. openFile.setMode(OpenFile.READABLE);
  19724. registerDescriptor(openFile.getMainStream().getDescriptor());
  19725. }
  19726. public RubyIO(Ruby runtime, Channel channel) {
  19727. super(runtime, runtime.getIO());
  19728. // We only want IO objects with valid streams (better to error now).
  19729. if (channel == null) {
  19730. throw runtime.newIOError("Opening invalid stream");
  19731. }
  19732. openFile = new OpenFile();
  19733. try {
  19734. openFile.setMainStream(new ChannelStream(runtime, new ChannelDescriptor(channel, getNewFileno(), new FileDescriptor())));
  19735. } catch (InvalidValueException e) {
  19736. throw getRuntime().newErrnoEINVALError();
  19737. }
  19738. openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
  19739. registerDescriptor(openFile.getMainStream().getDescriptor());
  19740. }
  19741. public RubyIO(Ruby runtime, ShellLauncher.POpenProcess process, ModeFlags modes) {
  19742. super(runtime, runtime.getIO());
  19743. openFile = new OpenFile();
  19744. openFile.setMode(modes.getOpenFileFlags() | OpenFile.SYNC);
  19745. openFile.setProcess(process);
  19746. try {
  19747. if (openFile.isReadable()) {
  19748. Channel inChannel;
  19749. if (process.getInput() != null) {
  19750. // NIO-based
  19751. inChannel = process.getInput();
  19752. } else {
  19753. // Stream-based
  19754. inChannel = Channels.newChannel(process.getInputStream());
  19755. }
  19756. ChannelDescriptor main = new ChannelDescriptor(
  19757. inChannel,
  19758. getNewFileno(),
  19759. new FileDescriptor());
  19760. main.setCanBeSeekable(false);
  19761. openFile.setMainStream(new ChannelStream(getRuntime(), main));
  19762. registerDescriptor(main);
  19763. }
  19764. if (openFile.isWritable()) {
  19765. Channel outChannel;
  19766. if (process.getOutput() != null) {
  19767. // NIO-based
  19768. outChannel = process.getOutput();
  19769. } else {
  19770. outChannel = Channels.newChannel(process.getOutputStream());
  19771. }
  19772. ChannelDescriptor pipe = new ChannelDescriptor(
  19773. outChannel,
  19774. getNewFileno(),
  19775. new FileDescriptor());
  19776. pipe.setCanBeSeekable(false);
  19777. if (openFile.getMainStream() != null) {
  19778. openFile.setPipeStream(new ChannelStream(getRuntime(), pipe));
  19779. } else {
  19780. openFile.setMainStream(new ChannelStream(getRuntime(), pipe));
  19781. }
  19782. registerDescriptor(pipe);
  19783. }
  19784. } catch (InvalidValueException e) {
  19785. throw getRuntime().newErrnoEINVALError();
  19786. }
  19787. }
  19788. public RubyIO(Ruby runtime, STDIO stdio) {
  19789. super(runtime, runtime.getIO());
  19790. openFile = new OpenFile();
  19791. try {
  19792. switch (stdio) {
  19793. case IN:
  19794. openFile.setMainStream(
  19795. new ChannelStream(
  19796. runtime,
  19797. // special constructor that accepts stream, not channel
  19798. new ChannelDescriptor(runtime.getIn(), 0, new ModeFlags(ModeFlags.RDONLY), FileDescriptor.in),
  19799. FileDescriptor.in));
  19800. break;
  19801. case OUT:
  19802. openFile.setMainStream(
  19803. new ChannelStream(
  19804. runtime,
  19805. new ChannelDescriptor(Channels.newChannel(runtime.getOut()), 1, new ModeFlags(ModeFlags.WRONLY | ModeFlags.APPEND), FileDescriptor.out),
  19806. FileDescriptor.out));
  19807. openFile.getMainStream().setSync(true);
  19808. break;
  19809. case ERR:
  19810. openFile.setMainStream(
  19811. new ChannelStream(
  19812. runtime,
  19813. new ChannelDescriptor(Channels.newChannel(runtime.getErr()), 2, new ModeFlags(ModeFlags.WRONLY | ModeFlags.APPEND), FileDescriptor.err),
  19814. FileDescriptor.err));
  19815. openFile.getMainStream().setSync(true);
  19816. break;
  19817. }
  19818. } catch (InvalidValueException ex) {
  19819. throw getRuntime().newErrnoEINVALError();
  19820. }
  19821. openFile.setMode(openFile.getMainStream().getModes().getOpenFileFlags());
  19822. registerDescriptor(openFile.getMainStream().getDescriptor());
  19823. }
  19824. public static RubyIO newIO(Ruby runtime, Channel channel) {
  19825. return new RubyIO(runtime, channel);
  19826. }
  19827. public OpenFile getOpenFile() {
  19828. return openFile;
  19829. }
  19830. protected OpenFile getOpenFileChecked() {
  19831. openFile.checkClosed(getRuntime());
  19832. return openFile;
  19833. }
  19834. private static ObjectAllocator IO_ALLOCATOR = new ObjectAllocator() {
  19835. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  19836. return new RubyIO(runtime, klass);
  19837. }
  19838. };
  19839. public static RubyClass createIOClass(Ruby runtime) {
  19840. RubyClass ioClass = runtime.defineClass("IO", runtime.getObject(), IO_ALLOCATOR);
  19841. ioClass.kindOf = new RubyModule.KindOf() {
  19842. @Override
  19843. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  19844. return obj instanceof RubyIO;
  19845. }
  19846. };
  19847. ioClass.includeModule(runtime.getEnumerable());
  19848. // TODO: Implement tty? and isatty. We have no real capability to
  19849. // determine this from java, but if we could set tty status, then
  19850. // we could invoke jruby differently to allow stdin to return true
  19851. // on this. This would allow things like cgi.rb to work properly.
  19852. ioClass.defineAnnotatedMethods(RubyIO.class);
  19853. // Constants for seek
  19854. ioClass.fastSetConstant("SEEK_SET", runtime.newFixnum(Stream.SEEK_SET));
  19855. ioClass.fastSetConstant("SEEK_CUR", runtime.newFixnum(Stream.SEEK_CUR));
  19856. ioClass.fastSetConstant("SEEK_END", runtime.newFixnum(Stream.SEEK_END));
  19857. return ioClass;
  19858. }
  19859. public OutputStream getOutStream() {
  19860. return getOpenFileChecked().getMainStream().newOutputStream();
  19861. }
  19862. public InputStream getInStream() {
  19863. return getOpenFileChecked().getMainStream().newInputStream();
  19864. }
  19865. public Channel getChannel() {
  19866. if (getOpenFileChecked().getMainStream() instanceof ChannelStream) {
  19867. return ((ChannelStream) openFile.getMainStream()).getDescriptor().getChannel();
  19868. } else {
  19869. return null;
  19870. }
  19871. }
  19872. public Stream getHandler() {
  19873. return getOpenFileChecked().getMainStream();
  19874. }
  19875. @JRubyMethod(name = "reopen", required = 1, optional = 1)
  19876. public IRubyObject reopen(ThreadContext context, IRubyObject[] args) throws InvalidValueException {
  19877. Ruby runtime = context.getRuntime();
  19878. if (args.length < 1) {
  19879. throw runtime.newArgumentError("wrong number of arguments");
  19880. }
  19881. IRubyObject tmp = TypeConverter.convertToTypeWithCheck(args[0], runtime.getIO(),
  19882. MethodIndex.getIndex("to_io"), "to_io");
  19883. if (!tmp.isNil()) {
  19884. try {
  19885. RubyIO ios = (RubyIO) tmp;
  19886. if (ios.openFile == this.openFile) {
  19887. return this;
  19888. }
  19889. OpenFile originalFile = ios.getOpenFileChecked();
  19890. OpenFile selfFile = getOpenFileChecked();
  19891. long pos = 0;
  19892. if (originalFile.isReadable()) {
  19893. pos = originalFile.getMainStream().fgetpos();
  19894. }
  19895. if (originalFile.getPipeStream() != null) {
  19896. originalFile.getPipeStream().fflush();
  19897. } else if (originalFile.isWritable()) {
  19898. originalFile.getMainStream().fflush();
  19899. }
  19900. if (selfFile.isWritable()) {
  19901. selfFile.getWriteStream().fflush();
  19902. }
  19903. selfFile.setMode(originalFile.getMode());
  19904. selfFile.setProcess(originalFile.getProcess());
  19905. selfFile.setLineNumber(originalFile.getLineNumber());
  19906. selfFile.setPath(originalFile.getPath());
  19907. selfFile.setFinalizer(originalFile.getFinalizer());
  19908. ChannelDescriptor selfDescriptor = selfFile.getMainStream().getDescriptor();
  19909. ChannelDescriptor originalDescriptor = originalFile.getMainStream().getDescriptor();
  19910. // confirm we're not reopening self's channel
  19911. if (selfDescriptor.getChannel() != originalDescriptor.getChannel()) {
  19912. // check if we're a stdio IO, and ensure we're not badly mutilated
  19913. if (selfDescriptor.getFileno() >=0 && selfDescriptor.getFileno() <= 2) {
  19914. selfFile.getMainStream().clearerr();
  19915. // dup2 new fd into self to preserve fileno and references to it
  19916. originalDescriptor.dup2Into(selfDescriptor);
  19917. // re-register, since fileno points at something new now
  19918. registerDescriptor(selfDescriptor);
  19919. } else {
  19920. Stream pipeFile = selfFile.getPipeStream();
  19921. int mode = selfFile.getMode();
  19922. selfFile.getMainStream().fclose();
  19923. selfFile.setPipeStream(null);
  19924. // TODO: turn off readable? am I reading this right?
  19925. // This only seems to be used while duping below, since modes gets
  19926. // reset to actual modes afterward
  19927. //fptr->mode &= (m & FMODE_READABLE) ? ~FMODE_READABLE : ~FMODE_WRITABLE;
  19928. if (pipeFile != null) {
  19929. selfFile.setMainStream(ChannelStream.fdopen(runtime, originalDescriptor, new ModeFlags()));
  19930. selfFile.setPipeStream(pipeFile);
  19931. } else {
  19932. selfFile.setMainStream(
  19933. new ChannelStream(
  19934. runtime,
  19935. originalDescriptor.dup2(selfDescriptor.getFileno())));
  19936. // re-register the descriptor
  19937. registerDescriptor(selfFile.getMainStream().getDescriptor());
  19938. // since we're not actually duping the incoming channel into our handler, we need to
  19939. // copy the original sync behavior from the other handler
  19940. selfFile.getMainStream().setSync(selfFile.getMainStream().isSync());
  19941. }
  19942. selfFile.setMode(mode);
  19943. }
  19944. // TODO: anything threads attached to original fd are notified of the close...
  19945. // see rb_thread_fd_close
  19946. if (originalFile.isReadable() && pos >= 0) {
  19947. selfFile.seek(pos, Stream.SEEK_SET);
  19948. originalFile.seek(pos, Stream.SEEK_SET);
  19949. }
  19950. }
  19951. if (selfFile.getPipeStream() != null && selfDescriptor.getFileno() != selfFile.getPipeStream().getDescriptor().getFileno()) {
  19952. int fd = selfFile.getPipeStream().getDescriptor().getFileno();
  19953. if (originalFile.getPipeStream() == null) {
  19954. selfFile.getPipeStream().fclose();
  19955. selfFile.setPipeStream(null);
  19956. } else if (fd != originalFile.getPipeStream().getDescriptor().getFileno()) {
  19957. selfFile.getPipeStream().fclose();
  19958. ChannelDescriptor newFD2 = originalFile.getPipeStream().getDescriptor().dup2(fd);
  19959. selfFile.setPipeStream(ChannelStream.fdopen(runtime, newFD2, getIOModes(runtime, "w")));
  19960. // re-register, since fileno points at something new now
  19961. registerDescriptor(newFD2);
  19962. }
  19963. }
  19964. // TODO: restore binary mode
  19965. // if (fptr->mode & FMODE_BINMODE) {
  19966. // rb_io_binmode(io);
  19967. // }
  19968. // TODO: set our metaclass to target's class (i.e. scary!)
  19969. } catch (IOException ex) { // TODO: better error handling
  19970. throw runtime.newIOError("could not reopen: " + ex.getMessage());
  19971. } catch (BadDescriptorException ex) {
  19972. throw runtime.newIOError("could not reopen: " + ex.getMessage());
  19973. } catch (PipeException ex) {
  19974. throw runtime.newIOError("could not reopen: " + ex.getMessage());
  19975. }
  19976. } else {
  19977. IRubyObject pathString = args[0].convertToString();
  19978. // TODO: check safe, taint on incoming string
  19979. if (openFile == null) {
  19980. openFile = new OpenFile();
  19981. }
  19982. try {
  19983. ModeFlags modes;
  19984. if (args.length > 1) {
  19985. IRubyObject modeString = args[1].convertToString();
  19986. modes = getIOModes(runtime, modeString.toString());
  19987. openFile.setMode(modes.getOpenFileFlags());
  19988. } else {
  19989. modes = getIOModes(runtime, "r");
  19990. }
  19991. String path = pathString.toString();
  19992. // Ruby code frequently uses a platform check to choose "NUL:" on windows
  19993. // but since that check doesn't work well on JRuby, we help it out
  19994. openFile.setPath(path);
  19995. if (openFile.getMainStream() == null) {
  19996. try {
  19997. openFile.setMainStream(ChannelStream.fopen(runtime, path, modes));
  19998. } catch (FileExistsException fee) {
  19999. throw runtime.newErrnoEEXISTError(path);
  20000. }
  20001. registerDescriptor(openFile.getMainStream().getDescriptor());
  20002. if (openFile.getPipeStream() != null) {
  20003. openFile.getPipeStream().fclose();
  20004. unregisterDescriptor(openFile.getPipeStream().getDescriptor().getFileno());
  20005. openFile.setPipeStream(null);
  20006. }
  20007. return this;
  20008. } else {
  20009. // TODO: This is an freopen in MRI, this is close, but not quite the same
  20010. openFile.getMainStream().freopen(path, getIOModes(runtime, openFile.getModeAsString(runtime)));
  20011. // re-register
  20012. registerDescriptor(openFile.getMainStream().getDescriptor());
  20013. if (openFile.getPipeStream() != null) {
  20014. // TODO: pipe handler to be reopened with path and "w" mode
  20015. }
  20016. }
  20017. } catch (PipeException pe) {
  20018. throw runtime.newErrnoEPIPEError();
  20019. } catch (IOException ex) {
  20020. throw runtime.newIOErrorFromException(ex);
  20021. } catch (BadDescriptorException ex) {
  20022. throw runtime.newErrnoEBADFError();
  20023. } catch (InvalidValueException e) {
  20024. throw runtime.newErrnoEINVALError();
  20025. }
  20026. }
  20027. // A potentially previously close IO is being 'reopened'.
  20028. return this;
  20029. }
  20030. public static ModeFlags getIOModes(Ruby runtime, String modesString) throws InvalidValueException {
  20031. return new ModeFlags(getIOModesIntFromString(runtime, modesString));
  20032. }
  20033. public static int getIOModesIntFromString(Ruby runtime, String modesString) {
  20034. int modes = 0;
  20035. int length = modesString.length();
  20036. if (length == 0) {
  20037. throw runtime.newArgumentError("illegal access mode");
  20038. }
  20039. switch (modesString.charAt(0)) {
  20040. case 'r' :
  20041. modes |= ModeFlags.RDONLY;
  20042. break;
  20043. case 'a' :
  20044. modes |= ModeFlags.APPEND | ModeFlags.WRONLY | ModeFlags.CREAT;
  20045. break;
  20046. case 'w' :
  20047. modes |= ModeFlags.WRONLY | ModeFlags.TRUNC | ModeFlags.CREAT;
  20048. break;
  20049. default :
  20050. throw runtime.newArgumentError("illegal access mode " + modes);
  20051. }
  20052. for (int n = 1; n < length; n++) {
  20053. switch (modesString.charAt(n)) {
  20054. case 'b':
  20055. modes |= ModeFlags.BINARY;
  20056. break;
  20057. case '+':
  20058. modes = (modes & ~ModeFlags.ACCMODE) | ModeFlags.RDWR;
  20059. break;
  20060. default:
  20061. throw runtime.newArgumentError("illegal access mode " + modes);
  20062. }
  20063. }
  20064. return modes;
  20065. }
  20066. private static ByteList getSeparatorFromArgs(Ruby runtime, IRubyObject[] args, int idx) {
  20067. IRubyObject sepVal;
  20068. if (args.length > idx) {
  20069. sepVal = args[idx];
  20070. } else {
  20071. sepVal = runtime.getRecordSeparatorVar().get();
  20072. }
  20073. ByteList separator = sepVal.isNil() ? null : sepVal.convertToString().getByteList();
  20074. if (separator != null && separator.realSize == 0) {
  20075. separator = Stream.PARAGRAPH_DELIMETER;
  20076. }
  20077. return separator;
  20078. }
  20079. private ByteList getSeparatorForGets(Ruby runtime, IRubyObject[] args) {
  20080. return getSeparatorFromArgs(runtime, args, 0);
  20081. }
  20082. public IRubyObject getline(Ruby runtime, ByteList separator) {
  20083. try {
  20084. OpenFile myOpenFile = getOpenFileChecked();
  20085. myOpenFile.checkReadable(runtime);
  20086. myOpenFile.setReadBuffered();
  20087. boolean isParagraph = separator == Stream.PARAGRAPH_DELIMETER;
  20088. separator = (separator == Stream.PARAGRAPH_DELIMETER) ?
  20089. Stream.PARAGRAPH_SEPARATOR : separator;
  20090. if (isParagraph) {
  20091. swallow('\n');
  20092. }
  20093. if (separator == null) {
  20094. IRubyObject str = readAll(null);
  20095. if (((RubyString)str).getByteList().length() == 0) {
  20096. return runtime.getNil();
  20097. }
  20098. incrementLineno(runtime, myOpenFile);
  20099. return str;
  20100. } else if (separator.length() == 1) {
  20101. return getlineFast(runtime, separator.get(0));
  20102. } else {
  20103. Stream readStream = myOpenFile.getMainStream();
  20104. int c = -1;
  20105. int n = -1;
  20106. int newline = separator.get(separator.length() - 1) & 0xFF;
  20107. ByteList buf = new ByteList(0);
  20108. boolean update = false;
  20109. while (true) {
  20110. do {
  20111. readCheck(readStream);
  20112. readStream.clearerr();
  20113. try {
  20114. n = readStream.getline(buf, (byte) newline);
  20115. c = buf.length() > 0 ? buf.get(buf.length() - 1) & 0xff : -1;
  20116. } catch (EOFException e) {
  20117. n = -1;
  20118. }
  20119. if (n == -1) {
  20120. if (!readStream.isBlocking() && (readStream instanceof ChannelStream)) {
  20121. if(!(waitReadable(((ChannelStream)readStream).getDescriptor()))) {
  20122. throw runtime.newIOError("bad file descriptor: " + openFile.getPath());
  20123. }
  20124. continue;
  20125. } else {
  20126. break;
  20127. }
  20128. }
  20129. update = true;
  20130. } while (c != newline); // loop until we see the nth separator char
  20131. // if we hit EOF, we're done
  20132. if (n == -1) {
  20133. break;
  20134. }
  20135. // if we've found the last char of the separator,
  20136. // and we've found at least as many characters as separator length,
  20137. // and the last n characters of our buffer match the separator, we're done
  20138. if (c == newline && buf.length() >= separator.length() &&
  20139. 0 == ByteList.memcmp(buf.unsafeBytes(), buf.begin + buf.realSize - separator.length(), separator.unsafeBytes(), separator.begin, separator.realSize)) {
  20140. break;
  20141. }
  20142. }
  20143. if (isParagraph) {
  20144. if (c != -1) {
  20145. swallow('\n');
  20146. }
  20147. }
  20148. if (!update) {
  20149. return runtime.getNil();
  20150. } else {
  20151. incrementLineno(runtime, myOpenFile);
  20152. RubyString str = RubyString.newString(runtime, buf);
  20153. str.setTaint(true);
  20154. return str;
  20155. }
  20156. }
  20157. } catch (PipeException ex) {
  20158. throw runtime.newErrnoEPIPEError();
  20159. } catch (InvalidValueException ex) {
  20160. throw runtime.newErrnoEINVALError();
  20161. } catch (EOFException e) {
  20162. return runtime.getNil();
  20163. } catch (BadDescriptorException e) {
  20164. throw runtime.newErrnoEBADFError();
  20165. } catch (IOException e) {
  20166. throw runtime.newIOError(e.getMessage());
  20167. }
  20168. }
  20169. private void incrementLineno(Ruby runtime, OpenFile myOpenFile) {
  20170. int lineno = myOpenFile.getLineNumber() + 1;
  20171. myOpenFile.setLineNumber(lineno);
  20172. runtime.getGlobalVariables().set("$.", runtime.newFixnum(lineno));
  20173. // this is for a range check, near as I can tell
  20174. RubyNumeric.int2fix(runtime, myOpenFile.getLineNumber());
  20175. }
  20176. protected boolean swallow(int term) throws IOException, BadDescriptorException {
  20177. Stream readStream = openFile.getMainStream();
  20178. int c;
  20179. do {
  20180. readCheck(readStream);
  20181. try {
  20182. c = readStream.fgetc();
  20183. } catch (EOFException e) {
  20184. c = -1;
  20185. }
  20186. if (c != term) {
  20187. readStream.ungetc(c);
  20188. return true;
  20189. }
  20190. } while (c != -1);
  20191. return false;
  20192. }
  20193. public IRubyObject getlineFast(Ruby runtime, int delim) throws IOException, BadDescriptorException {
  20194. Stream readStream = openFile.getMainStream();
  20195. int c = -1;
  20196. ByteList buf = new ByteList(0);
  20197. boolean update = false;
  20198. do {
  20199. readCheck(readStream);
  20200. readStream.clearerr();
  20201. int n;
  20202. try {
  20203. n = readStream.getline(buf, (byte) delim);
  20204. c = buf.length() > 0 ? buf.get(buf.length() - 1) & 0xff : -1;
  20205. } catch (EOFException e) {
  20206. n = -1;
  20207. }
  20208. if (n == -1) {
  20209. if (!readStream.isBlocking() && (readStream instanceof ChannelStream)) {
  20210. if(!(waitReadable(((ChannelStream)readStream).getDescriptor()))) {
  20211. throw runtime.newIOError("bad file descriptor: " + openFile.getPath());
  20212. }
  20213. continue;
  20214. } else {
  20215. break;
  20216. }
  20217. }
  20218. update = true;
  20219. } while (c != delim);
  20220. if (!update) {
  20221. return runtime.getNil();
  20222. } else {
  20223. incrementLineno(runtime, openFile);
  20224. RubyString str = RubyString.newString(runtime, buf);
  20225. str.setTaint(true);
  20226. return str;
  20227. }
  20228. }
  20229. // IO class methods.
  20230. @JRubyMethod(name = {"new", "for_fd"}, rest = true, frame = true, meta = true)
  20231. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  20232. RubyClass klass = (RubyClass)recv;
  20233. if (block.isGiven()) {
  20234. String className = klass.getName();
  20235. context.getRuntime().getWarnings().warn(
  20236. ID.BLOCK_NOT_ACCEPTED,
  20237. className + "::new() does not take block; use " + className + "::open() instead",
  20238. className + "::open()");
  20239. }
  20240. return klass.newInstance(context, args, block);
  20241. }
  20242. @JRubyMethod(name = "initialize", required = 1, optional = 1, frame = true, visibility = Visibility.PRIVATE)
  20243. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  20244. int argCount = args.length;
  20245. ModeFlags modes;
  20246. int fileno = RubyNumeric.fix2int(args[0]);
  20247. try {
  20248. ChannelDescriptor descriptor = getDescriptorByFileno(fileno);
  20249. if (descriptor == null) {
  20250. throw getRuntime().newErrnoEBADFError();
  20251. }
  20252. descriptor.checkOpen();
  20253. if (argCount == 2) {
  20254. if (args[1] instanceof RubyFixnum) {
  20255. modes = new ModeFlags(RubyFixnum.fix2long(args[1]));
  20256. } else {
  20257. modes = getIOModes(getRuntime(), args[1].convertToString().toString());
  20258. }
  20259. } else {
  20260. // use original modes
  20261. modes = descriptor.getOriginalModes();
  20262. }
  20263. openFile.setMode(modes.getOpenFileFlags());
  20264. openFile.setMainStream(fdopen(descriptor, modes));
  20265. } catch (BadDescriptorException ex) {
  20266. throw getRuntime().newErrnoEBADFError();
  20267. } catch (InvalidValueException ive) {
  20268. throw getRuntime().newErrnoEINVALError();
  20269. }
  20270. return this;
  20271. }
  20272. protected Stream fdopen(ChannelDescriptor existingDescriptor, ModeFlags modes) throws InvalidValueException {
  20273. // See if we already have this descriptor open.
  20274. // If so then we can mostly share the handler (keep open
  20275. // file, but possibly change the mode).
  20276. if (existingDescriptor == null) {
  20277. // redundant, done above as well
  20278. // this seems unlikely to happen unless it's a totally bogus fileno
  20279. // ...so do we even need to bother trying to create one?
  20280. // IN FACT, we should probably raise an error, yes?
  20281. throw getRuntime().newErrnoEBADFError();
  20282. // if (mode == null) {
  20283. // mode = "r";
  20284. // }
  20285. //
  20286. // try {
  20287. // openFile.setMainStream(streamForFileno(getRuntime(), fileno));
  20288. // } catch (BadDescriptorException e) {
  20289. // throw getRuntime().newErrnoEBADFError();
  20290. // } catch (IOException e) {
  20291. // throw getRuntime().newErrnoEBADFError();
  20292. // }
  20293. // //modes = new IOModes(getRuntime(), mode);
  20294. //
  20295. // registerStream(openFile.getMainStream());
  20296. } else {
  20297. // We are creating a new IO object that shares the same
  20298. // IOHandler (and fileno).
  20299. return ChannelStream.fdopen(getRuntime(), existingDescriptor, modes);
  20300. }
  20301. }
  20302. @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, meta = true)
  20303. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  20304. Ruby runtime = context.getRuntime();
  20305. RubyClass klass = (RubyClass)recv;
  20306. RubyIO io = (RubyIO)klass.newInstance(context, args, block);
  20307. if (block.isGiven()) {
  20308. try {
  20309. return block.yield(context, io);
  20310. } finally {
  20311. try {
  20312. io.getMetaClass().invoke(context, io, "close", IRubyObject.NULL_ARRAY, CallType.FUNCTIONAL, Block.NULL_BLOCK);
  20313. } catch (RaiseException re) {
  20314. RubyException rubyEx = re.getException();
  20315. if (rubyEx.kind_of_p(context, runtime.getStandardError()).isTrue()) {
  20316. // MRI behavior: swallow StandardErorrs
  20317. } else {
  20318. throw re;
  20319. }
  20320. }
  20321. }
  20322. }
  20323. return io;
  20324. }
  20325. // This appears to be some windows-only mode. On a java platform this is a no-op
  20326. @JRubyMethod(name = "binmode")
  20327. public IRubyObject binmode() {
  20328. return this;
  20329. }
  20330. /** @deprecated will be removed in 1.2 */
  20331. protected void checkInitialized() {
  20332. if (openFile == null) {
  20333. throw getRuntime().newIOError("uninitialized stream");
  20334. }
  20335. }
  20336. /** @deprecated will be removed in 1.2 */
  20337. protected void checkClosed() {
  20338. if (openFile.getMainStream() == null && openFile.getPipeStream() == null) {
  20339. throw getRuntime().newIOError("closed stream");
  20340. }
  20341. }
  20342. @JRubyMethod(name = "syswrite", required = 1)
  20343. public IRubyObject syswrite(ThreadContext context, IRubyObject obj) {
  20344. Ruby runtime = context.getRuntime();
  20345. try {
  20346. RubyString string = obj.asString();
  20347. OpenFile myOpenFile = getOpenFileChecked();
  20348. myOpenFile.checkWritable(runtime);
  20349. Stream writeStream = myOpenFile.getWriteStream();
  20350. if (myOpenFile.isWriteBuffered()) {
  20351. runtime.getWarnings().warn(ID.SYSWRITE_BUFFERED_IO, "syswrite for buffered IO");
  20352. }
  20353. if (!writeStream.getDescriptor().isWritable()) {
  20354. myOpenFile.checkClosed(runtime);
  20355. }
  20356. int read = writeStream.getDescriptor().write(string.getByteList());
  20357. if (read == -1) {
  20358. // TODO? I think this ends up propagating from normal Java exceptions
  20359. // sys_fail(openFile.getPath())
  20360. }
  20361. return runtime.newFixnum(read);
  20362. } catch (InvalidValueException ex) {
  20363. throw runtime.newErrnoEINVALError();
  20364. } catch (PipeException ex) {
  20365. throw runtime.newErrnoEPIPEError();
  20366. } catch (BadDescriptorException e) {
  20367. throw runtime.newErrnoEBADFError();
  20368. } catch (IOException e) {
  20369. throw runtime.newSystemCallError(e.getMessage());
  20370. }
  20371. }
  20372. @JRubyMethod(name = "write_nonblock", required = 1)
  20373. public IRubyObject write_nonblock(ThreadContext context, IRubyObject obj) {
  20374. // MRI behavior: always check whether the file is writable
  20375. // or not, even if we are to write 0 bytes.
  20376. OpenFile myOpenFile = getOpenFileChecked();
  20377. try {
  20378. myOpenFile.checkWritable(context.getRuntime());
  20379. } catch (IOException ex) {
  20380. throw context.getRuntime().newIOErrorFromException(ex);
  20381. } catch (BadDescriptorException ex) {
  20382. throw context.getRuntime().newErrnoEBADFError();
  20383. } catch (InvalidValueException ex) {
  20384. throw context.getRuntime().newErrnoEINVALError();
  20385. } catch (PipeException ex) {
  20386. throw context.getRuntime().newErrnoEPIPEError();
  20387. }
  20388. // TODO: Obviously, we're not doing a non-blocking write here
  20389. return write(context, obj);
  20390. }
  20391. /** io_write
  20392. *
  20393. */
  20394. @JRubyMethod(name = "write", required = 1)
  20395. public IRubyObject write(ThreadContext context, IRubyObject obj) {
  20396. Ruby runtime = context.getRuntime();
  20397. runtime.secure(4);
  20398. RubyString str = obj.asString();
  20399. // TODO: Ruby reuses this logic for other "write" behavior by checking if it's an IO and calling write again
  20400. if (str.getByteList().length() == 0) {
  20401. return runtime.newFixnum(0);
  20402. }
  20403. try {
  20404. OpenFile myOpenFile = getOpenFileChecked();
  20405. myOpenFile.checkWritable(runtime);
  20406. int written = fwrite(str.getByteList());
  20407. if (written == -1) {
  20408. // TODO: sys fail
  20409. }
  20410. // if not sync, we switch to write buffered mode
  20411. if (!myOpenFile.isSync()) {
  20412. myOpenFile.setWriteBuffered();
  20413. }
  20414. return runtime.newFixnum(written);
  20415. } catch (IOException ex) {
  20416. throw runtime.newIOErrorFromException(ex);
  20417. } catch (BadDescriptorException ex) {
  20418. throw runtime.newErrnoEBADFError();
  20419. } catch (InvalidValueException ex) {
  20420. throw runtime.newErrnoEINVALError();
  20421. } catch (PipeException ex) {
  20422. throw runtime.newErrnoEPIPEError();
  20423. }
  20424. }
  20425. protected boolean waitWritable(ChannelDescriptor descriptor) throws IOException {
  20426. Channel channel = descriptor.getChannel();
  20427. if (channel == null || !(channel instanceof SelectableChannel)) {
  20428. return false;
  20429. }
  20430. Selector selector = Selector.open();
  20431. ((SelectableChannel) channel).configureBlocking(false);
  20432. int real_ops = ((SelectableChannel) channel).validOps() & SelectionKey.OP_WRITE;
  20433. SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
  20434. if (key == null) {
  20435. ((SelectableChannel) channel).register(selector, real_ops, descriptor);
  20436. } else {
  20437. key.interestOps(key.interestOps()|real_ops);
  20438. }
  20439. while(selector.select() == 0);
  20440. for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
  20441. SelectionKey skey = (SelectionKey) i.next();
  20442. if ((skey.interestOps() & skey.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
  20443. if(skey.attachment() == descriptor) {
  20444. return true;
  20445. }
  20446. }
  20447. }
  20448. return false;
  20449. }
  20450. protected boolean waitReadable(ChannelDescriptor descriptor) throws IOException {
  20451. Channel channel = descriptor.getChannel();
  20452. if (channel == null || !(channel instanceof SelectableChannel)) {
  20453. return false;
  20454. }
  20455. Selector selector = Selector.open();
  20456. ((SelectableChannel) channel).configureBlocking(false);
  20457. int real_ops = ((SelectableChannel) channel).validOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT);
  20458. SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
  20459. if (key == null) {
  20460. ((SelectableChannel) channel).register(selector, real_ops, descriptor);
  20461. } else {
  20462. key.interestOps(key.interestOps()|real_ops);
  20463. }
  20464. while(selector.select() == 0);
  20465. for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
  20466. SelectionKey skey = (SelectionKey) i.next();
  20467. if ((skey.interestOps() & skey.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) {
  20468. if(skey.attachment() == descriptor) {
  20469. return true;
  20470. }
  20471. }
  20472. }
  20473. return false;
  20474. }
  20475. protected int fwrite(ByteList buffer) {
  20476. int n, r, l, offset = 0;
  20477. boolean eagain = false;
  20478. Stream writeStream = openFile.getWriteStream();
  20479. int len = buffer.length();
  20480. if ((n = len) <= 0) return n;
  20481. try {
  20482. if (openFile.isSync()) {
  20483. openFile.fflush(writeStream);
  20484. // TODO: why is this guarded?
  20485. // if (!rb_thread_fd_writable(fileno(f))) {
  20486. // rb_io_check_closed(fptr);
  20487. // }
  20488. while(offset<len) {
  20489. l = n;
  20490. // TODO: Something about pipe buffer length here
  20491. r = writeStream.getDescriptor().write(buffer,offset,l);
  20492. if(r == len) {
  20493. return len; //Everything written
  20494. }
  20495. if (0 <= r) {
  20496. offset += r;
  20497. n -= r;
  20498. eagain = true;
  20499. }
  20500. if(eagain && waitWritable(writeStream.getDescriptor())) {
  20501. openFile.checkClosed(getRuntime());
  20502. if(offset >= buffer.length()) {
  20503. return -1;
  20504. }
  20505. eagain = false;
  20506. } else {
  20507. return -1;
  20508. }
  20509. }
  20510. // TODO: all this stuff...some pipe logic, some async thread stuff
  20511. // retry:
  20512. // l = n;
  20513. // if (PIPE_BUF < l &&
  20514. // !rb_thread_critical &&
  20515. // !rb_thread_alone() &&
  20516. // wsplit_p(fptr)) {
  20517. // l = PIPE_BUF;
  20518. // }
  20519. // TRAP_BEG;
  20520. // r = write(fileno(f), RSTRING(str)->ptr+offset, l);
  20521. // TRAP_END;
  20522. // if (r == n) return len;
  20523. // if (0 <= r) {
  20524. // offset += r;
  20525. // n -= r;
  20526. // errno = EAGAIN;
  20527. // }
  20528. // if (rb_io_wait_writable(fileno(f))) {
  20529. // rb_io_check_closed(fptr);
  20530. // if (offset < RSTRING(str)->len)
  20531. // goto retry;
  20532. // }
  20533. // return -1L;
  20534. }
  20535. // TODO: handle errors in buffered write by retrying until finished or file is closed
  20536. return writeStream.fwrite(buffer);
  20537. // while (errno = 0, offset += (r = fwrite(RSTRING(str)->ptr+offset, 1, n, f)), (n -= r) > 0) {
  20538. // if (ferror(f)
  20539. // ) {
  20540. // if (rb_io_wait_writable(fileno(f))) {
  20541. // rb_io_check_closed(fptr);
  20542. // clearerr(f);
  20543. // if (offset < RSTRING(str)->len)
  20544. // continue;
  20545. // }
  20546. // return -1L;
  20547. // }
  20548. // }
  20549. // return len - n;
  20550. } catch (IOException ex) {
  20551. throw getRuntime().newIOErrorFromException(ex);
  20552. } catch (BadDescriptorException ex) {
  20553. throw getRuntime().newErrnoEBADFError();
  20554. }
  20555. }
  20556. /** rb_io_addstr
  20557. *
  20558. */
  20559. @JRubyMethod(name = "<<", required = 1)
  20560. public IRubyObject op_append(ThreadContext context, IRubyObject anObject) {
  20561. // Claims conversion is done via 'to_s' in docs.
  20562. callMethod(context, "write", anObject);
  20563. return this;
  20564. }
  20565. @JRubyMethod(name = "fileno", alias = "to_i")
  20566. public RubyFixnum fileno(ThreadContext context) {
  20567. return context.getRuntime().newFixnum(getOpenFileChecked().getMainStream().getDescriptor().getFileno());
  20568. }
  20569. /** Returns the current line number.
  20570. *
  20571. * @return the current line number.
  20572. */
  20573. @JRubyMethod(name = "lineno")
  20574. public RubyFixnum lineno(ThreadContext context) {
  20575. return context.getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
  20576. }
  20577. /** Sets the current line number.
  20578. *
  20579. * @param newLineNumber The new line number.
  20580. */
  20581. @JRubyMethod(name = "lineno=", required = 1)
  20582. public RubyFixnum lineno_set(ThreadContext context, IRubyObject newLineNumber) {
  20583. getOpenFileChecked().setLineNumber(RubyNumeric.fix2int(newLineNumber));
  20584. return context.getRuntime().newFixnum(getOpenFileChecked().getLineNumber());
  20585. }
  20586. /** Returns the current sync mode.
  20587. *
  20588. * @return the current sync mode.
  20589. */
  20590. @JRubyMethod(name = "sync")
  20591. public RubyBoolean sync(ThreadContext context) {
  20592. return context.getRuntime().newBoolean(getOpenFileChecked().getMainStream().isSync());
  20593. }
  20594. /**
  20595. * <p>Return the process id (pid) of the process this IO object
  20596. * spawned. If no process exists (popen was not called), then
  20597. * nil is returned. This is not how it appears to be defined
  20598. * but ruby 1.8 works this way.</p>
  20599. *
  20600. * @return the pid or nil
  20601. */
  20602. @JRubyMethod(name = "pid")
  20603. public IRubyObject pid(ThreadContext context) {
  20604. OpenFile myOpenFile = getOpenFileChecked();
  20605. if (myOpenFile.getProcess() == null) {
  20606. return context.getRuntime().getNil();
  20607. }
  20608. // Of course this isn't particularly useful.
  20609. int pid = myOpenFile.getProcess().hashCode();
  20610. return context.getRuntime().newFixnum(pid);
  20611. }
  20612. /**
  20613. * @deprecated
  20614. * @return
  20615. */
  20616. public boolean writeDataBuffered() {
  20617. return openFile.getMainStream().writeDataBuffered();
  20618. }
  20619. @JRubyMethod(name = {"pos", "tell"})
  20620. public RubyFixnum pos(ThreadContext context) {
  20621. try {
  20622. return context.getRuntime().newFixnum(getOpenFileChecked().getMainStream().fgetpos());
  20623. } catch (InvalidValueException ex) {
  20624. throw context.getRuntime().newErrnoEINVALError();
  20625. } catch (BadDescriptorException bde) {
  20626. throw context.getRuntime().newErrnoEBADFError();
  20627. } catch (PipeException e) {
  20628. throw context.getRuntime().newErrnoESPIPEError();
  20629. } catch (IOException e) {
  20630. throw context.getRuntime().newIOError(e.getMessage());
  20631. }
  20632. }
  20633. @JRubyMethod(name = "pos=", required = 1)
  20634. public RubyFixnum pos_set(ThreadContext context, IRubyObject newPosition) {
  20635. long offset = RubyNumeric.num2long(newPosition);
  20636. if (offset < 0) {
  20637. throw context.getRuntime().newSystemCallError("Negative seek offset");
  20638. }
  20639. OpenFile myOpenFile = getOpenFileChecked();
  20640. try {
  20641. myOpenFile.getMainStream().lseek(offset, Stream.SEEK_SET);
  20642. } catch (BadDescriptorException e) {
  20643. throw context.getRuntime().newErrnoEBADFError();
  20644. } catch (InvalidValueException e) {
  20645. throw context.getRuntime().newErrnoEINVALError();
  20646. } catch (PipeException e) {
  20647. throw context.getRuntime().newErrnoESPIPEError();
  20648. } catch (IOException e) {
  20649. throw context.getRuntime().newIOError(e.getMessage());
  20650. }
  20651. myOpenFile.getMainStream().clearerr();
  20652. return context.getRuntime().newFixnum(offset);
  20653. }
  20654. /** Print some objects to the stream.
  20655. *
  20656. */
  20657. @JRubyMethod(name = "print", rest = true, reads = FrameField.LASTLINE)
  20658. public IRubyObject print(ThreadContext context, IRubyObject[] args) {
  20659. if (args.length == 0) {
  20660. args = new IRubyObject[] { context.getCurrentFrame().getLastLine() };
  20661. }
  20662. Ruby runtime = context.getRuntime();
  20663. IRubyObject fs = runtime.getGlobalVariables().get("$,");
  20664. IRubyObject rs = runtime.getGlobalVariables().get("$\\");
  20665. for (int i = 0; i < args.length; i++) {
  20666. if (i > 0 && !fs.isNil()) {
  20667. callMethod(context, "write", fs);
  20668. }
  20669. if (args[i].isNil()) {
  20670. callMethod(context, "write", runtime.newString("nil"));
  20671. } else {
  20672. callMethod(context, "write", args[i]);
  20673. }
  20674. }
  20675. if (!rs.isNil()) {
  20676. callMethod(context, "write", rs);
  20677. }
  20678. return runtime.getNil();
  20679. }
  20680. @JRubyMethod(name = "printf", required = 1, rest = true)
  20681. public IRubyObject printf(ThreadContext context, IRubyObject[] args) {
  20682. callMethod(context, "write", RubyKernel.sprintf(context, this, args));
  20683. return context.getRuntime().getNil();
  20684. }
  20685. @JRubyMethod(name = "putc", required = 1, backtrace = true)
  20686. public IRubyObject putc(ThreadContext context, IRubyObject object) {
  20687. int c = RubyNumeric.num2chr(object);
  20688. try {
  20689. getOpenFileChecked().getMainStream().fputc(c);
  20690. } catch (BadDescriptorException e) {
  20691. return RubyFixnum.zero(context.getRuntime());
  20692. } catch (IOException e) {
  20693. return RubyFixnum.zero(context.getRuntime());
  20694. }
  20695. return object;
  20696. }
  20697. public RubyFixnum seek(ThreadContext context, IRubyObject[] args) {
  20698. long offset = RubyNumeric.num2long(args[0]);
  20699. int whence = Stream.SEEK_SET;
  20700. if (args.length > 1) {
  20701. whence = RubyNumeric.fix2int(args[1].convertToInteger());
  20702. }
  20703. return doSeek(context, offset, whence);
  20704. }
  20705. @JRubyMethod(name = "seek")
  20706. public RubyFixnum seek(ThreadContext context, IRubyObject arg0) {
  20707. long offset = RubyNumeric.num2long(arg0);
  20708. int whence = Stream.SEEK_SET;
  20709. return doSeek(context, offset, whence);
  20710. }
  20711. @JRubyMethod(name = "seek")
  20712. public RubyFixnum seek(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  20713. long offset = RubyNumeric.num2long(arg0);
  20714. int whence = RubyNumeric.fix2int(arg1.convertToInteger());
  20715. return doSeek(context, offset, whence);
  20716. }
  20717. private RubyFixnum doSeek(ThreadContext context, long offset, int whence) {
  20718. OpenFile myOpenFile = getOpenFileChecked();
  20719. try {
  20720. myOpenFile.seek(offset, whence);
  20721. } catch (BadDescriptorException ex) {
  20722. throw context.getRuntime().newErrnoEBADFError();
  20723. } catch (InvalidValueException e) {
  20724. throw context.getRuntime().newErrnoEINVALError();
  20725. } catch (PipeException e) {
  20726. throw context.getRuntime().newErrnoESPIPEError();
  20727. } catch (IOException e) {
  20728. throw context.getRuntime().newIOError(e.getMessage());
  20729. }
  20730. myOpenFile.getMainStream().clearerr();
  20731. return RubyFixnum.zero(context.getRuntime());
  20732. }
  20733. // This was a getOpt with one mandatory arg, but it did not work
  20734. // so I am parsing it for now.
  20735. @JRubyMethod(name = "sysseek", required = 1, optional = 1)
  20736. public RubyFixnum sysseek(ThreadContext context, IRubyObject[] args) {
  20737. long offset = RubyNumeric.num2long(args[0]);
  20738. long pos;
  20739. int whence = Stream.SEEK_SET;
  20740. if (args.length > 1) {
  20741. whence = RubyNumeric.fix2int(args[1].convertToInteger());
  20742. }
  20743. OpenFile myOpenFile = getOpenFileChecked();
  20744. try {
  20745. if (myOpenFile.isReadable() && myOpenFile.isReadBuffered()) {
  20746. throw context.getRuntime().newIOError("sysseek for buffered IO");
  20747. }
  20748. if (myOpenFile.isWritable() && myOpenFile.isWriteBuffered()) {
  20749. context.getRuntime().getWarnings().warn(ID.SYSSEEK_BUFFERED_IO, "sysseek for buffered IO");
  20750. }
  20751. pos = myOpenFile.getMainStream().getDescriptor().lseek(offset, whence);
  20752. } catch (BadDescriptorException ex) {
  20753. throw context.getRuntime().newErrnoEBADFError();
  20754. } catch (InvalidValueException e) {
  20755. throw context.getRuntime().newErrnoEINVALError();
  20756. } catch (PipeException e) {
  20757. throw context.getRuntime().newErrnoESPIPEError();
  20758. } catch (IOException e) {
  20759. throw context.getRuntime().newIOError(e.getMessage());
  20760. }
  20761. myOpenFile.getMainStream().clearerr();
  20762. return context.getRuntime().newFixnum(pos);
  20763. }
  20764. @JRubyMethod(name = "rewind")
  20765. public RubyFixnum rewind(ThreadContext context) {
  20766. OpenFile myOpenfile = getOpenFileChecked();
  20767. try {
  20768. myOpenfile.getMainStream().lseek(0L, Stream.SEEK_SET);
  20769. myOpenfile.getMainStream().clearerr();
  20770. // TODO: This is some goofy global file value from MRI..what to do?
  20771. // if (io == current_file) {
  20772. // gets_lineno -= fptr->lineno;
  20773. // }
  20774. } catch (BadDescriptorException e) {
  20775. throw context.getRuntime().newErrnoEBADFError();
  20776. } catch (InvalidValueException e) {
  20777. throw context.getRuntime().newErrnoEINVALError();
  20778. } catch (PipeException e) {
  20779. throw context.getRuntime().newErrnoESPIPEError();
  20780. } catch (IOException e) {
  20781. throw context.getRuntime().newIOError(e.getMessage());
  20782. }
  20783. // Must be back on first line on rewind.
  20784. myOpenfile.setLineNumber(0);
  20785. return RubyFixnum.zero(context.getRuntime());
  20786. }
  20787. @JRubyMethod(name = "fsync")
  20788. public RubyFixnum fsync(ThreadContext context) {
  20789. Ruby runtime = context.getRuntime();
  20790. try {
  20791. OpenFile myOpenFile = getOpenFileChecked();
  20792. myOpenFile.checkWritable(runtime);
  20793. myOpenFile.getWriteStream().sync();
  20794. } catch (InvalidValueException ex) {
  20795. throw runtime.newErrnoEINVALError();
  20796. } catch (PipeException ex) {
  20797. throw runtime.newErrnoEPIPEError();
  20798. } catch (IOException e) {
  20799. throw runtime.newIOError(e.getMessage());
  20800. } catch (BadDescriptorException e) {
  20801. throw runtime.newErrnoEBADFError();
  20802. }
  20803. return RubyFixnum.zero(runtime);
  20804. }
  20805. /** Sets the current sync mode.
  20806. *
  20807. * @param newSync The new sync mode.
  20808. */
  20809. @JRubyMethod(name = "sync=", required = 1)
  20810. public IRubyObject sync_set(IRubyObject newSync) {
  20811. getOpenFileChecked().setSync(newSync.isTrue());
  20812. getOpenFileChecked().getMainStream().setSync(newSync.isTrue());
  20813. return this;
  20814. }
  20815. @JRubyMethod(name = {"eof?", "eof"})
  20816. public RubyBoolean eof_p(ThreadContext context) {
  20817. Ruby runtime = context.getRuntime();
  20818. try {
  20819. OpenFile myOpenFile = getOpenFileChecked();
  20820. myOpenFile.checkReadable(runtime);
  20821. myOpenFile.setReadBuffered();
  20822. if (myOpenFile.getMainStream().feof()) {
  20823. return runtime.getTrue();
  20824. }
  20825. if (myOpenFile.getMainStream().readDataBuffered()) {
  20826. return runtime.getFalse();
  20827. }
  20828. readCheck(myOpenFile.getMainStream());
  20829. myOpenFile.getMainStream().clearerr();
  20830. int c = myOpenFile.getMainStream().fgetc();
  20831. if (c != -1) {
  20832. myOpenFile.getMainStream().ungetc(c);
  20833. return runtime.getFalse();
  20834. }
  20835. myOpenFile.checkClosed(runtime);
  20836. myOpenFile.getMainStream().clearerr();
  20837. return runtime.getTrue();
  20838. } catch (PipeException ex) {
  20839. throw runtime.newErrnoEPIPEError();
  20840. } catch (InvalidValueException ex) {
  20841. throw runtime.newErrnoEINVALError();
  20842. } catch (BadDescriptorException e) {
  20843. throw runtime.newErrnoEBADFError();
  20844. } catch (IOException e) {
  20845. throw runtime.newIOError(e.getMessage());
  20846. }
  20847. }
  20848. @JRubyMethod(name = {"tty?", "isatty"})
  20849. public RubyBoolean tty_p(ThreadContext context) {
  20850. return context.getRuntime().newBoolean(context.getRuntime().getPosix().isatty(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor()));
  20851. }
  20852. @JRubyMethod(name = "initialize_copy", required = 1)
  20853. @Override
  20854. public IRubyObject initialize_copy(IRubyObject original){
  20855. Ruby runtime = getRuntime();
  20856. if (this == original) return this;
  20857. RubyIO originalIO = (RubyIO) TypeConverter.convertToTypeWithCheck(original, runtime.getIO(), MethodIndex.TO_IO, "to_io");
  20858. OpenFile originalFile = originalIO.getOpenFileChecked();
  20859. OpenFile newFile = openFile;
  20860. try {
  20861. // TODO: I didn't see where MRI has this check, but it seems to be the right place
  20862. originalFile.checkClosed(runtime);
  20863. if (originalFile.getPipeStream() != null) {
  20864. originalFile.getPipeStream().fflush();
  20865. originalFile.getMainStream().lseek(0, Stream.SEEK_CUR);
  20866. } else if (originalFile.isWritable()) {
  20867. originalFile.getMainStream().fflush();
  20868. } else {
  20869. originalFile.getMainStream().lseek(0, Stream.SEEK_CUR);
  20870. }
  20871. newFile.setMode(originalFile.getMode());
  20872. newFile.setProcess(originalFile.getProcess());
  20873. newFile.setLineNumber(originalFile.getLineNumber());
  20874. newFile.setPath(originalFile.getPath());
  20875. newFile.setFinalizer(originalFile.getFinalizer());
  20876. ModeFlags modes;
  20877. if (newFile.isReadable()) {
  20878. if (newFile.isWritable()) {
  20879. if (newFile.getPipeStream() != null) {
  20880. modes = new ModeFlags(ModeFlags.RDONLY);
  20881. } else {
  20882. modes = new ModeFlags(ModeFlags.RDWR);
  20883. }
  20884. } else {
  20885. modes = new ModeFlags(ModeFlags.RDONLY);
  20886. }
  20887. } else {
  20888. if (newFile.isWritable()) {
  20889. modes = new ModeFlags(ModeFlags.WRONLY);
  20890. } else {
  20891. modes = originalFile.getMainStream().getModes();
  20892. }
  20893. }
  20894. ChannelDescriptor descriptor = originalFile.getMainStream().getDescriptor().dup();
  20895. newFile.setMainStream(ChannelStream.fdopen(runtime, descriptor, modes));
  20896. // TODO: the rest of this...seeking to same position is unnecessary since we share a channel
  20897. // but some of this may be needed?
  20898. // fseeko(fptr->f, ftello(orig->f), SEEK_SET);
  20899. // if (orig->f2) {
  20900. // if (fileno(orig->f) != fileno(orig->f2)) {
  20901. // fd = ruby_dup(fileno(orig->f2));
  20902. // }
  20903. // fptr->f2 = rb_fdopen(fd, "w");
  20904. // fseeko(fptr->f2, ftello(orig->f2), SEEK_SET);
  20905. // }
  20906. // if (fptr->mode & FMODE_BINMODE) {
  20907. // rb_io_binmode(dest);
  20908. // }
  20909. // Register the new descriptor
  20910. registerDescriptor(newFile.getMainStream().getDescriptor());
  20911. } catch (IOException ex) {
  20912. throw runtime.newIOError("could not init copy: " + ex);
  20913. } catch (BadDescriptorException ex) {
  20914. throw runtime.newIOError("could not init copy: " + ex);
  20915. } catch (PipeException ex) {
  20916. throw runtime.newIOError("could not init copy: " + ex);
  20917. } catch (InvalidValueException ex) {
  20918. throw runtime.newIOError("could not init copy: " + ex);
  20919. }
  20920. return this;
  20921. }
  20922. /** Closes the IO.
  20923. *
  20924. * @return The IO.
  20925. */
  20926. @JRubyMethod(name = "closed?")
  20927. public RubyBoolean closed_p(ThreadContext context) {
  20928. return context.getRuntime().newBoolean(openFile.getMainStream() == null && openFile.getPipeStream() == null);
  20929. }
  20930. /**
  20931. * <p>Closes all open resources for the IO. It also removes
  20932. * it from our magical all open file descriptor pool.</p>
  20933. *
  20934. * @return The IO.
  20935. */
  20936. @JRubyMethod(name = "close")
  20937. public IRubyObject close() {
  20938. Ruby runtime = getRuntime();
  20939. if (runtime.getSafeLevel() >= 4 && isTaint()) {
  20940. throw runtime.newSecurityError("Insecure: can't close");
  20941. }
  20942. openFile.checkClosed(runtime);
  20943. return close2(runtime);
  20944. }
  20945. protected IRubyObject close2(Ruby runtime) {
  20946. if (openFile == null) return runtime.getNil();
  20947. // These would be used when we notify threads...if we notify threads
  20948. interruptBlockingThreads();
  20949. ChannelDescriptor main, pipe;
  20950. if (openFile.getPipeStream() != null) {
  20951. pipe = openFile.getPipeStream().getDescriptor();
  20952. } else {
  20953. if (openFile.getMainStream() == null) {
  20954. return runtime.getNil();
  20955. }
  20956. pipe = null;
  20957. }
  20958. main = openFile.getMainStream().getDescriptor();
  20959. // cleanup, raising errors if any
  20960. openFile.cleanup(runtime, true);
  20961. // TODO: notify threads waiting on descriptors/IO? probably not...
  20962. if (openFile.getProcess() != null) {
  20963. try {
  20964. IRubyObject processResult = RubyProcess.RubyStatus.newProcessStatus(runtime, openFile.getProcess().waitFor());
  20965. runtime.getGlobalVariables().set("$?", processResult);
  20966. } catch (InterruptedException ie) {
  20967. // TODO: do something here?
  20968. }
  20969. }
  20970. return runtime.getNil();
  20971. }
  20972. @JRubyMethod(name = "close_write")
  20973. public IRubyObject close_write(ThreadContext context) throws BadDescriptorException {
  20974. try {
  20975. if (context.getRuntime().getSafeLevel() >= 4 && isTaint()) {
  20976. throw context.getRuntime().newSecurityError("Insecure: can't close");
  20977. }
  20978. OpenFile myOpenFile = getOpenFileChecked();
  20979. if (myOpenFile.getPipeStream() == null && myOpenFile.isReadable()) {
  20980. throw context.getRuntime().newIOError("closing non-duplex IO for writing");
  20981. }
  20982. if (myOpenFile.getPipeStream() == null) {
  20983. close();
  20984. } else{
  20985. myOpenFile.getPipeStream().fclose();
  20986. myOpenFile.setPipeStream(null);
  20987. myOpenFile.setMode(myOpenFile.getMode() & ~OpenFile.WRITABLE);
  20988. // TODO
  20989. // n is result of fclose; but perhaps having a SysError below is enough?
  20990. // if (n != 0) rb_sys_fail(fptr->path);
  20991. }
  20992. } catch (IOException ioe) {
  20993. // hmmmm
  20994. }
  20995. return this;
  20996. }
  20997. @JRubyMethod(name = "close_read")
  20998. public IRubyObject close_read(ThreadContext context) throws BadDescriptorException {
  20999. Ruby runtime = context.getRuntime();
  21000. try {
  21001. if (runtime.getSafeLevel() >= 4 && isTaint()) {
  21002. throw runtime.newSecurityError("Insecure: can't close");
  21003. }
  21004. OpenFile myOpenFile = getOpenFileChecked();
  21005. if (myOpenFile.getPipeStream() == null && myOpenFile.isWritable()) {
  21006. throw runtime.newIOError("closing non-duplex IO for reading");
  21007. }
  21008. if (myOpenFile.getPipeStream() == null) {
  21009. close();
  21010. } else{
  21011. myOpenFile.getMainStream().fclose();
  21012. myOpenFile.setMode(myOpenFile.getMode() & ~OpenFile.READABLE);
  21013. myOpenFile.setMainStream(myOpenFile.getPipeStream());
  21014. myOpenFile.setPipeStream(null);
  21015. // TODO
  21016. // n is result of fclose; but perhaps having a SysError below is enough?
  21017. // if (n != 0) rb_sys_fail(fptr->path);
  21018. }
  21019. } catch (IOException ioe) {
  21020. // I believe Ruby bails out with a "bug" if closing fails
  21021. throw runtime.newIOErrorFromException(ioe);
  21022. }
  21023. return this;
  21024. }
  21025. /** Flushes the IO output stream.
  21026. *
  21027. * @return The IO.
  21028. */
  21029. @JRubyMethod(name = "flush")
  21030. public RubyIO flush() {
  21031. try {
  21032. getOpenFileChecked().getWriteStream().fflush();
  21033. } catch (BadDescriptorException e) {
  21034. throw getRuntime().newErrnoEBADFError();
  21035. } catch (IOException e) {
  21036. throw getRuntime().newIOError(e.getMessage());
  21037. }
  21038. return this;
  21039. }
  21040. /** Read a line.
  21041. *
  21042. */
  21043. @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
  21044. public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
  21045. Ruby runtime = context.getRuntime();
  21046. ByteList separator = getSeparatorForGets(runtime, args);
  21047. IRubyObject result = getline(runtime, separator);
  21048. if (!result.isNil()) context.getCurrentFrame().setLastLine(result);
  21049. return result;
  21050. }
  21051. public boolean getBlocking() {
  21052. return ((ChannelStream) openFile.getMainStream()).isBlocking();
  21053. }
  21054. @JRubyMethod(name = "fcntl", required = 2)
  21055. public IRubyObject fcntl(ThreadContext context, IRubyObject cmd, IRubyObject arg) {
  21056. // TODO: This version differs from ioctl by checking whether fcntl exists
  21057. // and raising notimplemented if it doesn't; perhaps no difference for us?
  21058. return ctl(context.getRuntime(), cmd, arg);
  21059. }
  21060. @JRubyMethod(name = "ioctl", required = 1, optional = 1)
  21061. public IRubyObject ioctl(ThreadContext context, IRubyObject[] args) {
  21062. IRubyObject cmd = args[0];
  21063. IRubyObject arg;
  21064. if (args.length == 2) {
  21065. arg = args[1];
  21066. } else {
  21067. arg = context.getRuntime().getNil();
  21068. }
  21069. return ctl(context.getRuntime(), cmd, arg);
  21070. }
  21071. public IRubyObject ctl(Ruby runtime, IRubyObject cmd, IRubyObject arg) {
  21072. long realCmd = cmd.convertToInteger().getLongValue();
  21073. long nArg = 0;
  21074. // FIXME: Arg may also be true, false, and nil and still be valid. Strangely enough,
  21075. // protocol conversion is not happening in Ruby on this arg?
  21076. if (arg.isNil() || arg == runtime.getFalse()) {
  21077. nArg = 0;
  21078. } else if (arg instanceof RubyFixnum) {
  21079. nArg = RubyFixnum.fix2long(arg);
  21080. } else if (arg == runtime.getTrue()) {
  21081. nArg = 1;
  21082. } else {
  21083. throw runtime.newNotImplementedError("JRuby does not support string for second fcntl/ioctl argument yet");
  21084. }
  21085. OpenFile myOpenFile = getOpenFileChecked();
  21086. // Fixme: Only F_SETFL is current supported
  21087. if (realCmd == 1L) { // cmd is F_SETFL
  21088. boolean block = true;
  21089. if ((nArg & ModeFlags.NONBLOCK) == ModeFlags.NONBLOCK) {
  21090. block = false;
  21091. }
  21092. try {
  21093. myOpenFile.getMainStream().setBlocking(block);
  21094. } catch (IOException e) {
  21095. throw runtime.newIOError(e.getMessage());
  21096. }
  21097. } else {
  21098. throw runtime.newNotImplementedError("JRuby only supports F_SETFL for fcntl/ioctl currently");
  21099. }
  21100. return runtime.newFixnum(0);
  21101. }
  21102. private static final ByteList NIL_BYTELIST = ByteList.create("nil");
  21103. private static final ByteList RECURSIVE_BYTELIST = ByteList.create("[...]");
  21104. @JRubyMethod(name = "puts", rest = true)
  21105. public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
  21106. Ruby runtime = context.getRuntime();
  21107. assert runtime.getGlobalVariables().getDefaultSeparator() instanceof RubyString;
  21108. RubyString separator = (RubyString) runtime.getGlobalVariables().getDefaultSeparator();
  21109. if (args.length == 0) {
  21110. write(context, separator.getByteList());
  21111. return runtime.getNil();
  21112. }
  21113. for (int i = 0; i < args.length; i++) {
  21114. ByteList line;
  21115. if (args[i].isNil()) {
  21116. line = NIL_BYTELIST;
  21117. } else if (runtime.isInspecting(args[i])) {
  21118. line = RECURSIVE_BYTELIST;
  21119. } else if (args[i] instanceof RubyArray) {
  21120. inspectPuts(context, (RubyArray) args[i]);
  21121. continue;
  21122. } else {
  21123. line = args[i].asString().getByteList();
  21124. }
  21125. write(context, line);
  21126. if (line.length() == 0 || !line.endsWith(separator.getByteList())) {
  21127. write(context, separator.getByteList());
  21128. }
  21129. }
  21130. return runtime.getNil();
  21131. }
  21132. protected void write(ThreadContext context, ByteList byteList) {
  21133. callMethod(context, "write", RubyString.newStringShared(context.getRuntime(), byteList));
  21134. }
  21135. private IRubyObject inspectPuts(ThreadContext context, RubyArray array) {
  21136. try {
  21137. context.getRuntime().registerInspecting(array);
  21138. return puts(context, array.toJavaArray());
  21139. } finally {
  21140. context.getRuntime().unregisterInspecting(array);
  21141. }
  21142. }
  21143. /** Read a line.
  21144. *
  21145. */
  21146. @JRubyMethod(name = "readline", optional = 1, writes = FrameField.LASTLINE)
  21147. public IRubyObject readline(ThreadContext context, IRubyObject[] args) {
  21148. IRubyObject line = gets(context, args);
  21149. if (line.isNil()) throw context.getRuntime().newEOFError();
  21150. return line;
  21151. }
  21152. /** Read a byte. On EOF returns nil.
  21153. *
  21154. */
  21155. @JRubyMethod(name = "getc")
  21156. public IRubyObject getc() {
  21157. try {
  21158. OpenFile myOpenFile = getOpenFileChecked();
  21159. myOpenFile.checkReadable(getRuntime());
  21160. myOpenFile.setReadBuffered();
  21161. Stream stream = myOpenFile.getMainStream();
  21162. readCheck(stream);
  21163. stream.clearerr();
  21164. int c = myOpenFile.getMainStream().fgetc();
  21165. if (c == -1) {
  21166. // TODO: check for ferror, clear it, and try once more up above readCheck
  21167. // if (ferror(f)) {
  21168. // clearerr(f);
  21169. // if (!rb_io_wait_readable(fileno(f)))
  21170. // rb_sys_fail(fptr->path);
  21171. // goto retry;
  21172. // }
  21173. return getRuntime().getNil();
  21174. }
  21175. return getRuntime().newFixnum(c);
  21176. } catch (PipeException ex) {
  21177. throw getRuntime().newErrnoEPIPEError();
  21178. } catch (InvalidValueException ex) {
  21179. throw getRuntime().newErrnoEINVALError();
  21180. } catch (BadDescriptorException e) {
  21181. throw getRuntime().newErrnoEBADFError();
  21182. } catch (EOFException e) {
  21183. throw getRuntime().newEOFError();
  21184. } catch (IOException e) {
  21185. throw getRuntime().newIOError(e.getMessage());
  21186. }
  21187. }
  21188. private void readCheck(Stream stream) {
  21189. if (!stream.readDataBuffered()) {
  21190. openFile.checkClosed(getRuntime());
  21191. }
  21192. }
  21193. /**
  21194. * <p>Pushes char represented by int back onto IOS.</p>
  21195. *
  21196. * @param number to push back
  21197. */
  21198. @JRubyMethod(name = "ungetc", required = 1)
  21199. public IRubyObject ungetc(IRubyObject number) {
  21200. int ch = RubyNumeric.fix2int(number);
  21201. OpenFile myOpenFile = getOpenFileChecked();
  21202. if (!myOpenFile.isReadBuffered()) {
  21203. throw getRuntime().newIOError("unread stream");
  21204. }
  21205. try {
  21206. myOpenFile.checkReadable(getRuntime());
  21207. myOpenFile.setReadBuffered();
  21208. if (myOpenFile.getMainStream().ungetc(ch) == -1 && ch != -1) {
  21209. throw getRuntime().newIOError("ungetc failed");
  21210. }
  21211. } catch (PipeException ex) {
  21212. throw getRuntime().newErrnoEPIPEError();
  21213. } catch (InvalidValueException ex) {
  21214. throw getRuntime().newErrnoEINVALError();
  21215. } catch (BadDescriptorException e) {
  21216. throw getRuntime().newErrnoEBADFError();
  21217. } catch (EOFException e) {
  21218. throw getRuntime().newEOFError();
  21219. } catch (IOException e) {
  21220. throw getRuntime().newIOError(e.getMessage());
  21221. }
  21222. return getRuntime().getNil();
  21223. }
  21224. @JRubyMethod(name = "read_nonblock", required = 1, optional = 1)
  21225. public IRubyObject read_nonblock(ThreadContext context, IRubyObject[] args) {
  21226. Ruby runtime = context.getRuntime();
  21227. openFile.checkClosed(runtime);
  21228. if(!(openFile.getMainStream() instanceof ChannelStream)) {
  21229. // cryptic for the uninitiated...
  21230. throw runtime.newNotImplementedError("read_nonblock only works with Nio based handlers");
  21231. }
  21232. try {
  21233. int maxLength = RubyNumeric.fix2int(args[0]);
  21234. if (maxLength < 0) {
  21235. throw runtime.newArgumentError("negative length " + maxLength + " given");
  21236. }
  21237. ByteList buf = ((ChannelStream)openFile.getMainStream()).readnonblock(RubyNumeric.fix2int(args[0]));
  21238. IRubyObject strbuf = RubyString.newString(runtime, buf == null ? new ByteList(ByteList.NULL_ARRAY) : buf);
  21239. if(args.length > 1) {
  21240. args[1].callMethod(context, MethodIndex.OP_LSHIFT, "<<", strbuf);
  21241. return args[1];
  21242. }
  21243. return strbuf;
  21244. } catch (BadDescriptorException e) {
  21245. throw runtime.newErrnoEBADFError();
  21246. } catch (EOFException e) {
  21247. return runtime.getNil();
  21248. } catch (IOException e) {
  21249. throw runtime.newIOError(e.getMessage());
  21250. }
  21251. }
  21252. @JRubyMethod(name = "readpartial", required = 1, optional = 1)
  21253. public IRubyObject readpartial(ThreadContext context, IRubyObject[] args) {
  21254. Ruby runtime = context.getRuntime();
  21255. openFile.checkClosed(runtime);
  21256. if(!(openFile.getMainStream() instanceof ChannelStream)) {
  21257. // cryptic for the uninitiated...
  21258. throw runtime.newNotImplementedError("readpartial only works with Nio based handlers");
  21259. }
  21260. try {
  21261. int maxLength = RubyNumeric.fix2int(args[0]);
  21262. if (maxLength < 0) {
  21263. throw runtime.newArgumentError("negative length " + maxLength + " given");
  21264. }
  21265. ByteList buf = ((ChannelStream)openFile.getMainStream()).readpartial(RubyNumeric.fix2int(args[0]));
  21266. IRubyObject strbuf = RubyString.newString(runtime, buf == null ? new ByteList(ByteList.NULL_ARRAY) : buf);
  21267. if(args.length > 1) {
  21268. args[1].callMethod(context, MethodIndex.OP_LSHIFT, "<<", strbuf);
  21269. return args[1];
  21270. }
  21271. return strbuf;
  21272. } catch (BadDescriptorException e) {
  21273. throw runtime.newErrnoEBADFError();
  21274. } catch (EOFException e) {
  21275. return runtime.getNil();
  21276. } catch (IOException e) {
  21277. throw runtime.newIOError(e.getMessage());
  21278. }
  21279. }
  21280. @JRubyMethod(name = "sysread", required = 1, optional = 1)
  21281. public IRubyObject sysread(ThreadContext context, IRubyObject[] args) {
  21282. int len = (int)RubyNumeric.num2long(args[0]);
  21283. if (len < 0) throw getRuntime().newArgumentError("Negative size");
  21284. try {
  21285. RubyString str;
  21286. ByteList buffer;
  21287. if (args.length == 1 || args[1].isNil()) {
  21288. if (len == 0) {
  21289. return RubyString.newStringShared(getRuntime(), ByteList.EMPTY_BYTELIST);
  21290. }
  21291. buffer = new ByteList(len);
  21292. str = RubyString.newString(getRuntime(), buffer);
  21293. } else {
  21294. str = args[1].convertToString();
  21295. str.modify(len);
  21296. if (len == 0) {
  21297. return str;
  21298. }
  21299. buffer = str.getByteList();
  21300. }
  21301. OpenFile myOpenFile = getOpenFileChecked();
  21302. myOpenFile.checkReadable(getRuntime());
  21303. if (myOpenFile.getMainStream().readDataBuffered()) {
  21304. throw getRuntime().newIOError("sysread for buffered IO");
  21305. }
  21306. // TODO: Ruby locks the string here
  21307. context.getThread().beforeBlockingCall();
  21308. myOpenFile.checkClosed(getRuntime());
  21309. // TODO: Ruby re-checks that the buffer string hasn't been modified
  21310. int bytesRead = myOpenFile.getMainStream().getDescriptor().read(len, str.getByteList());
  21311. // TODO: Ruby unlocks the string here
  21312. // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
  21313. if (bytesRead == -1 || (bytesRead == 0 && len > 0)) {
  21314. throw getRuntime().newEOFError();
  21315. }
  21316. str.setTaint(true);
  21317. return str;
  21318. } catch (BadDescriptorException e) {
  21319. throw getRuntime().newErrnoEBADFError();
  21320. } catch (InvalidValueException e) {
  21321. throw getRuntime().newErrnoEINVALError();
  21322. } catch (PipeException e) {
  21323. throw getRuntime().newErrnoEPIPEError();
  21324. } catch (EOFException e) {
  21325. throw getRuntime().newEOFError();
  21326. } catch (IOException e) {
  21327. // All errors to sysread should be SystemCallErrors, but on a closed stream
  21328. // Ruby returns an IOError. Java throws same exception for all errors so
  21329. // we resort to this hack...
  21330. if ("File not open".equals(e.getMessage())) {
  21331. throw getRuntime().newIOError(e.getMessage());
  21332. }
  21333. throw getRuntime().newSystemCallError(e.getMessage());
  21334. } finally {
  21335. context.getThread().afterBlockingCall();
  21336. }
  21337. }
  21338. public IRubyObject read(IRubyObject[] args) {
  21339. ThreadContext context = getRuntime().getCurrentContext();
  21340. switch (args.length) {
  21341. case 0: return read(context);
  21342. case 1: return read(context, args[0]);
  21343. case 2: return read(context, args[0], args[1]);
  21344. default: throw getRuntime().newArgumentError(args.length, 2);
  21345. }
  21346. }
  21347. @JRubyMethod(name = "read")
  21348. public IRubyObject read(ThreadContext context) {
  21349. Ruby runtime = context.getRuntime();
  21350. OpenFile myOpenFile = getOpenFileChecked();
  21351. try {
  21352. myOpenFile.checkReadable(runtime);
  21353. myOpenFile.setReadBuffered();
  21354. return readAll(getRuntime().getNil());
  21355. } catch (PipeException ex) {
  21356. throw getRuntime().newErrnoEPIPEError();
  21357. } catch (InvalidValueException ex) {
  21358. throw getRuntime().newErrnoEINVALError();
  21359. } catch (EOFException ex) {
  21360. throw getRuntime().newEOFError();
  21361. } catch (IOException ex) {
  21362. throw getRuntime().newIOErrorFromException(ex);
  21363. } catch (BadDescriptorException ex) {
  21364. throw getRuntime().newErrnoEBADFError();
  21365. }
  21366. }
  21367. @JRubyMethod(name = "read")
  21368. public IRubyObject read(ThreadContext context, IRubyObject arg0) {
  21369. if (arg0.isNil()) {
  21370. return read(context);
  21371. }
  21372. OpenFile myOpenFile = getOpenFileChecked();
  21373. int length = RubyNumeric.num2int(arg0);
  21374. if (length < 0) {
  21375. throw getRuntime().newArgumentError("negative length " + length + " given");
  21376. }
  21377. RubyString str = null;
  21378. return readNotAll(context, myOpenFile, length, str);
  21379. }
  21380. @JRubyMethod(name = "read")
  21381. public IRubyObject read(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  21382. OpenFile myOpenFile = getOpenFileChecked();
  21383. if (arg0.isNil()) {
  21384. try {
  21385. myOpenFile.checkReadable(getRuntime());
  21386. myOpenFile.setReadBuffered();
  21387. return readAll(arg1);
  21388. } catch (PipeException ex) {
  21389. throw getRuntime().newErrnoEPIPEError();
  21390. } catch (InvalidValueException ex) {
  21391. throw getRuntime().newErrnoEINVALError();
  21392. } catch (EOFException ex) {
  21393. throw getRuntime().newEOFError();
  21394. } catch (IOException ex) {
  21395. throw getRuntime().newIOErrorFromException(ex);
  21396. } catch (BadDescriptorException ex) {
  21397. throw getRuntime().newErrnoEBADFError();
  21398. }
  21399. }
  21400. int length = RubyNumeric.num2int(arg0);
  21401. if (length < 0) {
  21402. throw getRuntime().newArgumentError("negative length " + length + " given");
  21403. }
  21404. RubyString str = null;
  21405. // ByteList buffer = null;
  21406. if (arg1.isNil()) {
  21407. // buffer = new ByteList(length);
  21408. // str = RubyString.newString(getRuntime(), buffer);
  21409. } else {
  21410. str = arg1.convertToString();
  21411. str.modify(length);
  21412. if (length == 0) {
  21413. return str;
  21414. }
  21415. // buffer = str.getByteList();
  21416. }
  21417. return readNotAll(context, myOpenFile, length, str);
  21418. }
  21419. private IRubyObject readNotAll(ThreadContext context, OpenFile myOpenFile, int length, RubyString str) {
  21420. Ruby runtime = context.getRuntime();
  21421. try {
  21422. myOpenFile.checkReadable(runtime);
  21423. myOpenFile.setReadBuffered();
  21424. if (myOpenFile.getMainStream().feof()) {
  21425. return runtime.getNil();
  21426. }
  21427. // TODO: Ruby locks the string here
  21428. // READ_CHECK from MRI io.c
  21429. readCheck(myOpenFile.getMainStream());
  21430. // TODO: check buffer length again?
  21431. // if (RSTRING(str)->len != len) {
  21432. // rb_raise(rb_eRuntimeError, "buffer string modified");
  21433. // }
  21434. // TODO: read into buffer using all the fread logic
  21435. // int read = openFile.getMainStream().fread(buffer);
  21436. ByteList newBuffer = myOpenFile.getMainStream().fread(length);
  21437. // TODO: Ruby unlocks the string here
  21438. // TODO: change this to check number read into buffer once that's working
  21439. // if (read == 0) {
  21440. if (newBuffer == null || newBuffer.length() == 0) {
  21441. if (myOpenFile.getMainStream() == null) {
  21442. return runtime.getNil();
  21443. }
  21444. if (myOpenFile.getMainStream().feof()) {
  21445. // truncate buffer string to zero, if provided
  21446. if (str != null) {
  21447. str.setValue(ByteList.EMPTY_BYTELIST.dup());
  21448. }
  21449. return runtime.getNil();
  21450. }
  21451. // Removed while working on JRUBY-2386, since fixes for that
  21452. // modified EOF logic such that this check is not really valid.
  21453. // We expect that an EOFException will be thrown now in EOF
  21454. // cases.
  21455. // if (length > 0) {
  21456. // // I think this is only partly correct; sys fail based on errno in Ruby
  21457. // throw getRuntime().newEOFError();
  21458. // }
  21459. }
  21460. // TODO: Ruby truncates string to specific size here, but our bytelist should handle this already?
  21461. // FIXME: I don't like the null checks here
  21462. if (str == null) {
  21463. if (newBuffer == null) {
  21464. str = RubyString.newEmptyString(runtime);
  21465. } else {
  21466. str = RubyString.newString(runtime, newBuffer);
  21467. }
  21468. } else {
  21469. if (newBuffer == null) {
  21470. str.empty();
  21471. } else {
  21472. str.setValue(newBuffer);
  21473. }
  21474. }
  21475. str.setTaint(true);
  21476. return str;
  21477. } catch (EOFException ex) {
  21478. throw runtime.newEOFError();
  21479. } catch (PipeException ex) {
  21480. throw runtime.newErrnoEPIPEError();
  21481. } catch (InvalidValueException ex) {
  21482. throw runtime.newErrnoEINVALError();
  21483. } catch (IOException ex) {
  21484. throw runtime.newIOErrorFromException(ex);
  21485. } catch (BadDescriptorException ex) {
  21486. throw runtime.newErrnoEBADFError();
  21487. }
  21488. }
  21489. protected IRubyObject readAll(IRubyObject buffer) throws BadDescriptorException, EOFException, IOException {
  21490. Ruby runtime = getRuntime();
  21491. // TODO: handle writing into original buffer better
  21492. RubyString str = null;
  21493. if (buffer instanceof RubyString) {
  21494. str = (RubyString)buffer;
  21495. }
  21496. // TODO: ruby locks the string here
  21497. // READ_CHECK from MRI io.c
  21498. if (openFile.getMainStream().readDataBuffered()) {
  21499. openFile.checkClosed(runtime);
  21500. }
  21501. ByteList newBuffer = openFile.getMainStream().readall();
  21502. // TODO same zero-length checks as file above
  21503. if (str == null) {
  21504. if (newBuffer == null) {
  21505. str = RubyString.newEmptyString(runtime);
  21506. } else {
  21507. str = RubyString.newString(runtime, newBuffer);
  21508. }
  21509. } else {
  21510. if (newBuffer == null) {
  21511. str.empty();
  21512. } else {
  21513. str.setValue(newBuffer);
  21514. }
  21515. }
  21516. str.taint(runtime.getCurrentContext());
  21517. return str;
  21518. // long bytes = 0;
  21519. // long n;
  21520. //
  21521. // if (siz == 0) siz = BUFSIZ;
  21522. // if (NIL_P(str)) {
  21523. // str = rb_str_new(0, siz);
  21524. // }
  21525. // else {
  21526. // rb_str_resize(str, siz);
  21527. // }
  21528. // for (;;) {
  21529. // rb_str_locktmp(str);
  21530. // READ_CHECK(fptr->f);
  21531. // n = io_fread(RSTRING(str)->ptr+bytes, siz-bytes, fptr);
  21532. // rb_str_unlocktmp(str);
  21533. // if (n == 0 && bytes == 0) {
  21534. // if (!fptr->f) break;
  21535. // if (feof(fptr->f)) break;
  21536. // if (!ferror(fptr->f)) break;
  21537. // rb_sys_fail(fptr->path);
  21538. // }
  21539. // bytes += n;
  21540. // if (bytes < siz) break;
  21541. // siz += BUFSIZ;
  21542. // rb_str_resize(str, siz);
  21543. // }
  21544. // if (bytes != siz) rb_str_resize(str, bytes);
  21545. // OBJ_TAINT(str);
  21546. //
  21547. // return str;
  21548. }
  21549. // TODO: There's a lot of complexity here due to error handling and
  21550. // nonblocking IO; much of this goes away, but for now I'm just
  21551. // having read call ChannelStream.fread directly.
  21552. // protected int fread(int len, ByteList buffer) {
  21553. // long n = len;
  21554. // int c;
  21555. // int saved_errno;
  21556. //
  21557. // while (n > 0) {
  21558. // c = read_buffered_data(ptr, n, fptr->f);
  21559. // if (c < 0) goto eof;
  21560. // if (c > 0) {
  21561. // ptr += c;
  21562. // if ((n -= c) <= 0) break;
  21563. // }
  21564. // rb_thread_wait_fd(fileno(fptr->f));
  21565. // rb_io_check_closed(fptr);
  21566. // clearerr(fptr->f);
  21567. // TRAP_BEG;
  21568. // c = getc(fptr->f);
  21569. // TRAP_END;
  21570. // if (c == EOF) {
  21571. // eof:
  21572. // if (ferror(fptr->f)) {
  21573. // switch (errno) {
  21574. // case EINTR:
  21575. // #if defined(ERESTART)
  21576. // case ERESTART:
  21577. // #endif
  21578. // clearerr(fptr->f);
  21579. // continue;
  21580. // case EAGAIN:
  21581. // #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
  21582. // case EWOULDBLOCK:
  21583. // #endif
  21584. // if (len > n) {
  21585. // clearerr(fptr->f);
  21586. // }
  21587. // saved_errno = errno;
  21588. // rb_warning("nonblocking IO#read is obsolete; use IO#readpartial or IO#sysread");
  21589. // errno = saved_errno;
  21590. // }
  21591. // if (len == n) return 0;
  21592. // }
  21593. // break;
  21594. // }
  21595. // *ptr++ = c;
  21596. // n--;
  21597. // }
  21598. // return len - n;
  21599. //
  21600. // }
  21601. /** Read a byte. On EOF throw EOFError.
  21602. *
  21603. */
  21604. @JRubyMethod(name = "readchar")
  21605. public IRubyObject readchar() {
  21606. IRubyObject c = getc();
  21607. if (c.isNil()) throw getRuntime().newEOFError();
  21608. return c;
  21609. }
  21610. @JRubyMethod
  21611. public IRubyObject stat(ThreadContext context) {
  21612. openFile.checkClosed(context.getRuntime());
  21613. return context.getRuntime().newFileStat(getOpenFileChecked().getMainStream().getDescriptor().getFileDescriptor());
  21614. }
  21615. /**
  21616. * <p>Invoke a block for each byte.</p>
  21617. */
  21618. @JRubyMethod(name = "each_byte", frame = true)
  21619. public IRubyObject each_byte(ThreadContext context, Block block) {
  21620. Ruby runtime = context.getRuntime();
  21621. try {
  21622. OpenFile myOpenFile = getOpenFileChecked();
  21623. while (true) {
  21624. myOpenFile.checkReadable(runtime);
  21625. myOpenFile.setReadBuffered();
  21626. // TODO: READ_CHECK from MRI
  21627. int c = myOpenFile.getMainStream().fgetc();
  21628. if (c == -1) {
  21629. // TODO: check for error, clear it, and wait until readable before trying once more
  21630. // if (ferror(f)) {
  21631. // clearerr(f);
  21632. // if (!rb_io_wait_readable(fileno(f)))
  21633. // rb_sys_fail(fptr->path);
  21634. // continue;
  21635. // }
  21636. break;
  21637. }
  21638. assert c < 256;
  21639. block.yield(context, getRuntime().newFixnum(c));
  21640. }
  21641. // TODO: one more check for error
  21642. // if (ferror(f)) rb_sys_fail(fptr->path);
  21643. return this;
  21644. } catch (PipeException ex) {
  21645. throw runtime.newErrnoEPIPEError();
  21646. } catch (InvalidValueException ex) {
  21647. throw runtime.newErrnoEINVALError();
  21648. } catch (BadDescriptorException e) {
  21649. throw runtime.newErrnoEBADFError();
  21650. } catch (EOFException e) {
  21651. return runtime.getNil();
  21652. } catch (IOException e) {
  21653. throw runtime.newIOError(e.getMessage());
  21654. }
  21655. }
  21656. /**
  21657. * <p>Invoke a block for each line.</p>
  21658. */
  21659. @JRubyMethod(name = {"each_line", "each"}, optional = 1, frame = true)
  21660. public RubyIO each_line(ThreadContext context, IRubyObject[] args, Block block) {
  21661. Ruby runtime = context.getRuntime();
  21662. ByteList separator = getSeparatorForGets(runtime, args);
  21663. for (IRubyObject line = getline(runtime, separator); !line.isNil();
  21664. line = getline(runtime, separator)) {
  21665. block.yield(context, line);
  21666. }
  21667. return this;
  21668. }
  21669. @JRubyMethod(name = "readlines", optional = 1)
  21670. public RubyArray readlines(ThreadContext context, IRubyObject[] args) {
  21671. Ruby runtime = context.getRuntime();
  21672. IRubyObject[] separatorArgs = args.length > 0 ? new IRubyObject[] { args[0] } : IRubyObject.NULL_ARRAY;
  21673. ByteList separator = getSeparatorForGets(runtime, separatorArgs);
  21674. RubyArray result = runtime.newArray();
  21675. IRubyObject line;
  21676. while (! (line = getline(runtime, separator)).isNil()) {
  21677. result.append(line);
  21678. }
  21679. return result;
  21680. }
  21681. @JRubyMethod(name = "to_io")
  21682. public RubyIO to_io() {
  21683. return this;
  21684. }
  21685. @Override
  21686. public String toString() {
  21687. return "RubyIO(" + openFile.getMode() + ", " + openFile.getMainStream().getDescriptor().getFileno() + ")";
  21688. }
  21689. /* class methods for IO */
  21690. /** rb_io_s_foreach
  21691. *
  21692. */
  21693. @JRubyMethod(name = "foreach", required = 1, optional = 1, frame = true, meta = true)
  21694. public static IRubyObject foreach(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  21695. Ruby runtime = context.getRuntime();
  21696. int count = args.length;
  21697. IRubyObject filename = args[0].convertToString();
  21698. runtime.checkSafeString(filename);
  21699. ByteList separator = getSeparatorFromArgs(runtime, args, 1);
  21700. RubyIO io = (RubyIO)RubyFile.open(context, runtime.getFile(), new IRubyObject[] { filename }, Block.NULL_BLOCK);
  21701. if (!io.isNil()) {
  21702. try {
  21703. IRubyObject str = io.getline(runtime, separator);
  21704. while (!str.isNil()) {
  21705. block.yield(context, str);
  21706. str = io.getline(runtime, separator);
  21707. }
  21708. } finally {
  21709. io.close();
  21710. }
  21711. }
  21712. return runtime.getNil();
  21713. }
  21714. private static RubyIO convertToIO(ThreadContext context, IRubyObject obj) {
  21715. return (RubyIO)TypeConverter.convertToType(obj, context.getRuntime().getIO(), MethodIndex.TO_IO, "to_io");
  21716. }
  21717. private static boolean registerSelect(ThreadContext context, Selector selector, IRubyObject obj, RubyIO ioObj, int ops) throws IOException {
  21718. Channel channel = ioObj.getChannel();
  21719. if (channel == null || !(channel instanceof SelectableChannel)) {
  21720. return false;
  21721. }
  21722. ((SelectableChannel) channel).configureBlocking(false);
  21723. int real_ops = ((SelectableChannel) channel).validOps() & ops;
  21724. SelectionKey key = ((SelectableChannel) channel).keyFor(selector);
  21725. if (key == null) {
  21726. ((SelectableChannel) channel).register(selector, real_ops, obj);
  21727. } else {
  21728. key.interestOps(key.interestOps()|real_ops);
  21729. }
  21730. return true;
  21731. }
  21732. @JRubyMethod(name = "select", required = 1, optional = 3, meta = true)
  21733. public static IRubyObject select(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  21734. return select_static(context, context.getRuntime(), args);
  21735. }
  21736. private static void checkArrayType(Ruby runtime, IRubyObject obj) {
  21737. if (!(obj instanceof RubyArray)) {
  21738. throw runtime.newTypeError("wrong argument type "
  21739. + obj.getMetaClass().getName() + " (expected Array)");
  21740. }
  21741. }
  21742. public static IRubyObject select_static(ThreadContext context, Ruby runtime, IRubyObject[] args) {
  21743. try {
  21744. Set pending = new HashSet();
  21745. Set unselectable_reads = new HashSet();
  21746. Set unselectable_writes = new HashSet();
  21747. Selector selector = Selector.open();
  21748. if (!args[0].isNil()) {
  21749. // read
  21750. checkArrayType(runtime, args[0]);
  21751. for (Iterator i = ((RubyArray) args[0]).getList().iterator(); i.hasNext(); ) {
  21752. IRubyObject obj = (IRubyObject) i.next();
  21753. RubyIO ioObj = convertToIO(context, obj);
  21754. if (registerSelect(context, selector, obj, ioObj, SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) {
  21755. if (ioObj.writeDataBuffered()) {
  21756. pending.add(obj);
  21757. }
  21758. } else {
  21759. if (( ioObj.openFile.getMode() & OpenFile.READABLE ) != 0) {
  21760. unselectable_reads.add(obj);
  21761. }
  21762. }
  21763. }
  21764. }
  21765. if (args.length > 1 && !args[1].isNil()) {
  21766. // write
  21767. checkArrayType(runtime, args[1]);
  21768. for (Iterator i = ((RubyArray) args[1]).getList().iterator(); i.hasNext(); ) {
  21769. IRubyObject obj = (IRubyObject) i.next();
  21770. RubyIO ioObj = convertToIO(context, obj);
  21771. if (!registerSelect(context, selector, obj, ioObj, SelectionKey.OP_WRITE)) {
  21772. if (( ioObj.openFile.getMode() & OpenFile.WRITABLE ) != 0) {
  21773. unselectable_writes.add(obj);
  21774. }
  21775. }
  21776. }
  21777. }
  21778. if (args.length > 2 && !args[2].isNil()) {
  21779. checkArrayType(runtime, args[2]);
  21780. // Java's select doesn't do anything about this, so we leave it be.
  21781. }
  21782. final boolean has_timeout = ( args.length > 3 && !args[3].isNil() );
  21783. long timeout = 0;
  21784. if(has_timeout) {
  21785. IRubyObject timeArg = args[3];
  21786. if (timeArg instanceof RubyFloat) {
  21787. timeout = Math.round(((RubyFloat) timeArg).getDoubleValue() * 1000);
  21788. } else if (timeArg instanceof RubyFixnum) {
  21789. timeout = Math.round(((RubyFixnum) timeArg).getDoubleValue() * 1000);
  21790. } else { // TODO: MRI also can hadle Bignum here
  21791. throw runtime.newTypeError("can't convert "
  21792. + timeArg.getMetaClass().getName() + " into time interval");
  21793. }
  21794. if (timeout < 0) {
  21795. throw runtime.newArgumentError("negative timeout given");
  21796. }
  21797. }
  21798. if (pending.isEmpty() && unselectable_reads.isEmpty() && unselectable_writes.isEmpty()) {
  21799. if (has_timeout) {
  21800. if (timeout==0) {
  21801. selector.selectNow();
  21802. } else {
  21803. selector.select(timeout);
  21804. }
  21805. } else {
  21806. selector.select();
  21807. }
  21808. } else {
  21809. selector.selectNow();
  21810. }
  21811. List r = new ArrayList();
  21812. List w = new ArrayList();
  21813. List e = new ArrayList();
  21814. for (Iterator i = selector.selectedKeys().iterator(); i.hasNext(); ) {
  21815. SelectionKey key = (SelectionKey) i.next();
  21816. if ((key.interestOps() & key.readyOps()
  21817. & (SelectionKey.OP_READ|SelectionKey.OP_ACCEPT|SelectionKey.OP_CONNECT)) != 0) {
  21818. r.add(key.attachment());
  21819. pending.remove(key.attachment());
  21820. }
  21821. if ((key.interestOps() & key.readyOps() & (SelectionKey.OP_WRITE)) != 0) {
  21822. w.add(key.attachment());
  21823. }
  21824. }
  21825. r.addAll(pending);
  21826. r.addAll(unselectable_reads);
  21827. w.addAll(unselectable_writes);
  21828. // make all sockets blocking as configured again
  21829. for (Iterator i = selector.keys().iterator(); i.hasNext(); ) {
  21830. SelectionKey key = (SelectionKey) i.next();
  21831. SelectableChannel channel = key.channel();
  21832. synchronized(channel.blockingLock()) {
  21833. RubyIO originalIO = (RubyIO) TypeConverter.convertToType(
  21834. (IRubyObject) key.attachment(), runtime.getIO(),
  21835. MethodIndex.TO_IO, "to_io");
  21836. boolean blocking = originalIO.getBlocking();
  21837. key.cancel();
  21838. channel.configureBlocking(blocking);
  21839. }
  21840. }
  21841. selector.close();
  21842. if (r.size() == 0 && w.size() == 0 && e.size() == 0) {
  21843. return runtime.getNil();
  21844. }
  21845. List ret = new ArrayList();
  21846. ret.add(RubyArray.newArray(runtime, r));
  21847. ret.add(RubyArray.newArray(runtime, w));
  21848. ret.add(RubyArray.newArray(runtime, e));
  21849. return RubyArray.newArray(runtime, ret);
  21850. } catch(IOException e) {
  21851. throw runtime.newIOError(e.getMessage());
  21852. }
  21853. }
  21854. public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  21855. switch (args.length) {
  21856. case 0: throw context.getRuntime().newArgumentError(0, 1);
  21857. case 1: return read(context, recv, args[0], block);
  21858. case 2: return read(context, recv, args[0], args[1], block);
  21859. case 3: return read(context, recv, args[0], args[1], args[2], block);
  21860. default: throw context.getRuntime().newArgumentError(args.length, 3);
  21861. }
  21862. }
  21863. @JRubyMethod(name = "read", meta = true)
  21864. public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
  21865. IRubyObject[] fileArguments = new IRubyObject[] {arg0};
  21866. RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
  21867. try {
  21868. return file.read(context);
  21869. } finally {
  21870. file.close();
  21871. }
  21872. }
  21873. @JRubyMethod(name = "read", meta = true)
  21874. public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
  21875. IRubyObject[] fileArguments = new IRubyObject[] {arg0};
  21876. RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
  21877. try {
  21878. if (!arg1.isNil()) {
  21879. return file.read(context, arg1);
  21880. } else {
  21881. return file.read(context);
  21882. }
  21883. } finally {
  21884. file.close();
  21885. }
  21886. }
  21887. @JRubyMethod(name = "read", meta = true)
  21888. public static IRubyObject read(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  21889. IRubyObject[] fileArguments = new IRubyObject[]{arg0};
  21890. RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
  21891. if (!arg2.isNil()) {
  21892. file.seek(context, arg2);
  21893. }
  21894. try {
  21895. if (!arg1.isNil()) {
  21896. return file.read(context, arg1);
  21897. } else {
  21898. return file.read(context);
  21899. }
  21900. } finally {
  21901. file.close();
  21902. }
  21903. }
  21904. @JRubyMethod(name = "readlines", required = 1, optional = 1, meta = true)
  21905. public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  21906. int count = args.length;
  21907. IRubyObject[] fileArguments = new IRubyObject[]{ args[0].convertToString() };
  21908. IRubyObject[] separatorArguments = count >= 2 ? new IRubyObject[]{args[1]} : IRubyObject.NULL_ARRAY;
  21909. RubyIO file = (RubyIO) RubyKernel.open(context, recv, fileArguments, block);
  21910. try {
  21911. return file.readlines(context, separatorArguments);
  21912. } finally {
  21913. file.close();
  21914. }
  21915. }
  21916. @JRubyMethod(name = "popen", required = 1, optional = 1, meta = true)
  21917. public static IRubyObject popen(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  21918. Ruby runtime = context.getRuntime();
  21919. int mode;
  21920. IRubyObject cmdObj = args[0].convertToString();
  21921. runtime.checkSafeString(cmdObj);
  21922. if ("-".equals(cmdObj.toString())) {
  21923. throw runtime.newNotImplementedError("popen(\"-\") is unimplemented");
  21924. }
  21925. try {
  21926. if (args.length == 1) {
  21927. mode = ModeFlags.RDONLY;
  21928. } else if (args[1] instanceof RubyFixnum) {
  21929. mode = RubyFixnum.num2int(args[1]);
  21930. } else {
  21931. mode = getIOModesIntFromString(runtime, args[1].convertToString().toString());
  21932. }
  21933. ModeFlags modes = new ModeFlags(mode);
  21934. ShellLauncher.POpenProcess process = ShellLauncher.popen(runtime, cmdObj, modes);
  21935. RubyIO io = new RubyIO(runtime, process, modes);
  21936. if (block.isGiven()) {
  21937. try {
  21938. return block.yield(context, io);
  21939. } finally {
  21940. if (io.openFile.isOpen()) {
  21941. io.close();
  21942. }
  21943. runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, (process.waitFor() * 256)));
  21944. }
  21945. }
  21946. return io;
  21947. } catch (InvalidValueException ex) {
  21948. throw runtime.newErrnoEINVALError();
  21949. } catch (IOException e) {
  21950. throw runtime.newIOErrorFromException(e);
  21951. } catch (InterruptedException e) {
  21952. throw runtime.newThreadError("unexpected interrupt");
  21953. }
  21954. }
  21955. // NIO based pipe
  21956. @JRubyMethod(name = "pipe", meta = true)
  21957. public static IRubyObject pipe(ThreadContext context, IRubyObject recv) throws Exception {
  21958. // TODO: This isn't an exact port of MRI's pipe behavior, so revisit
  21959. Ruby runtime = context.getRuntime();
  21960. Pipe pipe = Pipe.open();
  21961. RubyIO source = new RubyIO(runtime, pipe.source());
  21962. RubyIO sink = new RubyIO(runtime, pipe.sink());
  21963. sink.openFile.getMainStream().setSync(true);
  21964. return runtime.newArrayNoCopy(new IRubyObject[] { source, sink });
  21965. }
  21966. @JRubyMethod(name = "copy_stream", meta = true, compat = RUBY1_9)
  21967. public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv,
  21968. IRubyObject stream1, IRubyObject stream2) throws IOException {
  21969. RubyIO io1 = (RubyIO)stream1;
  21970. RubyIO io2 = (RubyIO)stream2;
  21971. ChannelDescriptor d1 = io1.openFile.getMainStream().getDescriptor();
  21972. if (!d1.isSeekable()) {
  21973. throw context.getRuntime().newTypeError("only supports file-to-file copy");
  21974. }
  21975. ChannelDescriptor d2 = io2.openFile.getMainStream().getDescriptor();
  21976. if (!d2.isSeekable()) {
  21977. throw context.getRuntime().newTypeError("only supports file-to-file copy");
  21978. }
  21979. FileChannel f1 = (FileChannel)d1.getChannel();
  21980. FileChannel f2 = (FileChannel)d2.getChannel();
  21981. long size = f1.size();
  21982. f1.transferTo(f2.position(), size, f2);
  21983. return context.getRuntime().newFixnum(size);
  21984. }
  21985. /**
  21986. * Add a thread to the list of blocking threads for this IO.
  21987. *
  21988. * @param thread A thread blocking on this IO
  21989. */
  21990. public synchronized void addBlockingThread(RubyThread thread) {
  21991. if (blockingThreads == null) {
  21992. blockingThreads = new ArrayList<RubyThread>(1);
  21993. }
  21994. blockingThreads.add(thread);
  21995. }
  21996. /**
  21997. * Remove a thread from the list of blocking threads for this IO.
  21998. *
  21999. * @param thread A thread blocking on this IO
  22000. */
  22001. public synchronized void removeBlockingThread(RubyThread thread) {
  22002. if (blockingThreads == null) {
  22003. return;
  22004. }
  22005. for (int i = 0; i < blockingThreads.size(); i++) {
  22006. if (blockingThreads.get(i) == thread) {
  22007. // not using remove(Object) here to avoid the equals() call
  22008. blockingThreads.remove(i);
  22009. }
  22010. }
  22011. }
  22012. /**
  22013. * Fire an IOError in all threads blocking on this IO object
  22014. */
  22015. protected synchronized void interruptBlockingThreads() {
  22016. if (blockingThreads == null) {
  22017. return;
  22018. }
  22019. for (int i = 0; i < blockingThreads.size(); i++) {
  22020. RubyThread thread = blockingThreads.get(i);
  22021. // raise will also wake the thread from selection
  22022. thread.raise(new IRubyObject[] {getRuntime().newIOError("stream closed").getException()}, Block.NULL_BLOCK);
  22023. }
  22024. }
  22025. }
  22026. /*
  22027. **** BEGIN LICENSE BLOCK *****
  22028. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  22029. *
  22030. * The contents of this file are subject to the Common Public
  22031. * License Version 1.0 (the "License"); you may not use this file
  22032. * except in compliance with the License. You may obtain a copy of
  22033. * the License at http://www.eclipse.org/legal/cpl-v10.html
  22034. *
  22035. * Software distributed under the License is distributed on an "AS
  22036. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  22037. * implied. See the License for the specific language governing
  22038. * rights and limitations under the License.
  22039. *
  22040. * Copyright (C) 2005 Thomas E Enebo <enebo@acm.org>
  22041. *
  22042. * Alternatively, the contents of this file may be used under the terms of
  22043. * either of the GNU General Public License Version 2 or later (the "GPL"),
  22044. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  22045. * in which case the provisions of the GPL or the LGPL are applicable instead
  22046. * of those above. If you wish to allow use of your version of this file only
  22047. * under the terms of either the GPL or the LGPL, and not to allow others to
  22048. * use your version of this file under the terms of the CPL, indicate your
  22049. * decision by deleting the provisions above and replace them with the notice
  22050. * and other provisions required by the GPL or the LGPL. If you do not delete
  22051. * the provisions above, a recipient may use your version of this file under
  22052. * the terms of any one of the CPL, the GPL or the LGPL.
  22053. ***** END LICENSE BLOCK *****/
  22054. package org.jruby;
  22055. import java.io.IOException;
  22056. import java.io.PrintWriter;
  22057. import java.io.StringWriter;
  22058. import org.jruby.anno.JRubyMethod;
  22059. import org.jruby.anno.JRubyModule;
  22060. import org.jruby.anno.JRubyClass;
  22061. import org.jruby.ast.ArgsNode;
  22062. import org.jruby.ast.ArgumentNode;
  22063. import org.jruby.ast.ListNode;
  22064. import org.jruby.ast.LocalAsgnNode;
  22065. import org.jruby.javasupport.Java;
  22066. import org.jruby.javasupport.JavaObject;
  22067. import org.jruby.runtime.Arity;
  22068. import org.jruby.runtime.Block;
  22069. import org.jruby.runtime.builtin.IRubyObject;
  22070. import org.jruby.runtime.load.Library;
  22071. import org.jruby.internal.runtime.methods.DynamicMethod;
  22072. import org.jruby.ast.Node;
  22073. import org.jruby.compiler.ASTInspector;
  22074. import org.jruby.compiler.ASTCompiler;
  22075. import org.jruby.compiler.impl.StandardASMCompiler;
  22076. import org.jruby.internal.runtime.methods.MethodArgs;
  22077. import org.jruby.javasupport.JavaUtil;
  22078. import org.jruby.runtime.InterpretedBlock;
  22079. import org.jruby.runtime.ThreadContext;
  22080. import org.jruby.util.TypeConverter;
  22081. import org.objectweb.asm.ClassReader;
  22082. import org.objectweb.asm.util.TraceClassVisitor;
  22083. /**
  22084. * Module which defines JRuby-specific methods for use.
  22085. */
  22086. @JRubyModule(name="JRuby")
  22087. public class RubyJRuby {
  22088. public static RubyModule createJRuby(Ruby runtime) {
  22089. ThreadContext context = runtime.getCurrentContext();
  22090. runtime.getKernel().callMethod(context, "require", runtime.newString("java"));
  22091. RubyModule jrubyModule = runtime.defineModule("JRuby");
  22092. jrubyModule.defineAnnotatedMethods(RubyJRuby.class);
  22093. RubyClass compiledScriptClass = jrubyModule.defineClassUnder("CompiledScript",runtime.getObject(), runtime.getObject().getAllocator());
  22094. compiledScriptClass.attr_accessor(context, new IRubyObject[]{runtime.newSymbol("name"), runtime.newSymbol("class_name"), runtime.newSymbol("original_script"), runtime.newSymbol("code")});
  22095. compiledScriptClass.defineAnnotatedMethods(JRubyCompiledScript.class);
  22096. return jrubyModule;
  22097. }
  22098. public static RubyModule createJRubyExt(Ruby runtime) {
  22099. runtime.getKernel().callMethod(runtime.getCurrentContext(),"require", runtime.newString("java"));
  22100. RubyModule mJRubyExt = runtime.getOrCreateModule("JRuby").defineModuleUnder("Extensions");
  22101. mJRubyExt.defineAnnotatedMethods(JRubyExtensions.class);
  22102. runtime.getObject().includeModule(mJRubyExt);
  22103. return mJRubyExt;
  22104. }
  22105. public static class ExtLibrary implements Library {
  22106. public void load(Ruby runtime, boolean wrap) throws IOException {
  22107. RubyJRuby.createJRubyExt(runtime);
  22108. runtime.getMethod().defineAnnotatedMethods(MethodExtensions.class);
  22109. }
  22110. }
  22111. public static class TypeLibrary implements Library {
  22112. public void load(Ruby runtime, boolean wrap) throws IOException {
  22113. RubyModule jrubyType = runtime.defineModule("Type");
  22114. jrubyType.defineAnnotatedMethods(TypeLibrary.class);
  22115. }
  22116. @JRubyMethod(module = true)
  22117. public static IRubyObject coerce_to(ThreadContext context, IRubyObject self, IRubyObject object, IRubyObject clazz, IRubyObject method) {
  22118. Ruby ruby = object.getRuntime();
  22119. if (!(clazz instanceof RubyClass)) {
  22120. throw ruby.newTypeError(clazz, ruby.getClassClass());
  22121. }
  22122. if (!(method instanceof RubySymbol)) {
  22123. throw ruby.newTypeError(method, ruby.getSymbol());
  22124. }
  22125. RubyClass rubyClass = (RubyClass)clazz;
  22126. RubySymbol methodSym = (RubySymbol)method;
  22127. return TypeConverter.convertToTypeOrRaise(object, rubyClass, methodSym.asJavaString());
  22128. }
  22129. }
  22130. @JRubyMethod(name = "runtime", frame = true, module = true)
  22131. public static IRubyObject runtime(IRubyObject recv, Block unusedBlock) {
  22132. return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), recv.getRuntime()), Block.NULL_BLOCK);
  22133. }
  22134. @JRubyMethod(name = "objectspace", frame = true, module = true)
  22135. public static IRubyObject getObjectSpaceEnabled(IRubyObject recv, Block b) {
  22136. Ruby runtime = recv.getRuntime();
  22137. return RubyBoolean.newBoolean(
  22138. runtime, runtime.isObjectSpaceEnabled());
  22139. }
  22140. @JRubyMethod(name = "objectspace=", required = 1, frame = true, module = true)
  22141. public static IRubyObject setObjectSpaceEnabled(
  22142. IRubyObject recv, IRubyObject arg, Block b) {
  22143. Ruby runtime = recv.getRuntime();
  22144. runtime.setObjectSpaceEnabled(arg.isTrue());
  22145. return runtime.getNil();
  22146. }
  22147. @JRubyMethod(name = {"parse", "ast_for"}, optional = 3, frame = true, module = true)
  22148. public static IRubyObject parse(IRubyObject recv, IRubyObject[] args, Block block) {
  22149. if(block.isGiven()) {
  22150. if(block.getBody() instanceof org.jruby.runtime.CompiledBlock) {
  22151. throw new RuntimeException("Cannot compile an already compiled block. Use -J-Djruby.jit.enabled=false to avoid this problem.");
  22152. }
  22153. Arity.checkArgumentCount(recv.getRuntime(),args,0,0);
  22154. return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), ((InterpretedBlock)block.getBody()).getIterNode().getBodyNode()), Block.NULL_BLOCK);
  22155. } else {
  22156. Arity.checkArgumentCount(recv.getRuntime(),args,1,3);
  22157. String filename = "-";
  22158. boolean extraPositionInformation = false;
  22159. RubyString content = args[0].convertToString();
  22160. if(args.length>1) {
  22161. filename = args[1].convertToString().toString();
  22162. if(args.length>2) {
  22163. extraPositionInformation = args[2].isTrue();
  22164. }
  22165. }
  22166. return Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(),
  22167. recv.getRuntime().parse(content.getByteList(), filename, null, 0, extraPositionInformation)), Block.NULL_BLOCK);
  22168. }
  22169. }
  22170. @JRubyMethod(name = "compile", optional = 3, frame = true, module = true)
  22171. public static IRubyObject compile(IRubyObject recv, IRubyObject[] args, Block block) {
  22172. Node node;
  22173. String filename;
  22174. RubyString content;
  22175. if(block.isGiven()) {
  22176. Arity.checkArgumentCount(recv.getRuntime(),args,0,0);
  22177. if(block.getBody() instanceof org.jruby.runtime.CompiledBlock) {
  22178. throw new RuntimeException("Cannot compile an already compiled block. Use -J-Djruby.jit.enabled=false to avoid this problem.");
  22179. }
  22180. content = RubyString.newEmptyString(recv.getRuntime());
  22181. Node bnode = ((InterpretedBlock)block.getBody()).getIterNode().getBodyNode();
  22182. node = new org.jruby.ast.RootNode(bnode.getPosition(), block.getBinding().getDynamicScope(), bnode);
  22183. filename = "__block_" + node.getPosition().getFile();
  22184. } else {
  22185. Arity.checkArgumentCount(recv.getRuntime(),args,1,3);
  22186. filename = "-";
  22187. boolean extraPositionInformation = false;
  22188. content = args[0].convertToString();
  22189. if(args.length>1) {
  22190. filename = args[1].convertToString().toString();
  22191. if(args.length>2) {
  22192. extraPositionInformation = args[2].isTrue();
  22193. }
  22194. }
  22195. node = recv.getRuntime().parse(content.getByteList(), filename, null, 0, extraPositionInformation);
  22196. }
  22197. String classname;
  22198. if (filename.equals("-e")) {
  22199. classname = "__dash_e__";
  22200. } else {
  22201. classname = filename.replace('\\', '/').replaceAll(".rb", "").replaceAll("-","_dash_");
  22202. }
  22203. ASTInspector inspector = new ASTInspector();
  22204. inspector.inspect(node);
  22205. StandardASMCompiler asmCompiler = new StandardASMCompiler(classname, filename);
  22206. ASTCompiler compiler = new ASTCompiler();
  22207. compiler.compileRoot(node, asmCompiler, inspector);
  22208. byte[] bts = asmCompiler.getClassByteArray();
  22209. IRubyObject compiledScript = ((RubyModule)recv).fastGetConstant("CompiledScript").callMethod(recv.getRuntime().getCurrentContext(),"new");
  22210. compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "name=", recv.getRuntime().newString(filename));
  22211. compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "class_name=", recv.getRuntime().newString(classname));
  22212. compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "original_script=", content);
  22213. compiledScript.callMethod(recv.getRuntime().getCurrentContext(), "code=", Java.java_to_ruby(recv, JavaObject.wrap(recv.getRuntime(), bts), Block.NULL_BLOCK));
  22214. return compiledScript;
  22215. }
  22216. @JRubyMethod(name = "reference", required = 1, module = true)
  22217. public static IRubyObject reference(IRubyObject recv, IRubyObject obj) {
  22218. return Java.wrap(recv.getRuntime().getJavaSupport().getJavaUtilitiesModule(),
  22219. JavaObject.wrap(recv.getRuntime(), obj));
  22220. }
  22221. @JRubyMethod(name = "dereference", required = 1, module = true)
  22222. public static IRubyObject dereference(ThreadContext context, IRubyObject recv, IRubyObject obj) {
  22223. if (!(obj.dataGetStruct() instanceof JavaObject)) {
  22224. throw context.getRuntime().newTypeError("got " + obj + ", expected wrapped Java object");
  22225. }
  22226. Object unwrapped = JavaUtil.unwrapJavaObject(obj);
  22227. if (!(unwrapped instanceof IRubyObject)) {
  22228. throw context.getRuntime().newTypeError("got " + obj + ", expected Java-wrapped Ruby object");
  22229. }
  22230. return (IRubyObject)unwrapped;
  22231. }
  22232. @JRubyClass(name="JRuby::CompiledScript")
  22233. public static class JRubyCompiledScript {
  22234. @JRubyMethod(name = "to_s")
  22235. public static IRubyObject compiled_script_to_s(IRubyObject recv) {
  22236. return recv.getInstanceVariables().fastGetInstanceVariable("@original_script");
  22237. }
  22238. @JRubyMethod(name = "inspect")
  22239. public static IRubyObject compiled_script_inspect(IRubyObject recv) {
  22240. return recv.getRuntime().newString("#<JRuby::CompiledScript " + recv.getInstanceVariables().fastGetInstanceVariable("@name") + ">");
  22241. }
  22242. @JRubyMethod(name = "inspect_bytecode")
  22243. public static IRubyObject compiled_script_inspect_bytecode(IRubyObject recv) {
  22244. StringWriter sw = new StringWriter();
  22245. ClassReader cr = new ClassReader((byte[])org.jruby.javasupport.JavaUtil.convertRubyToJava(recv.getInstanceVariables().fastGetInstanceVariable("@code"),byte[].class));
  22246. TraceClassVisitor cv = new TraceClassVisitor(new PrintWriter(sw));
  22247. cr.accept(cv, ClassReader.SKIP_DEBUG);
  22248. return recv.getRuntime().newString(sw.toString());
  22249. }
  22250. }
  22251. @JRubyModule(name="JRubyExtensions")
  22252. public static class JRubyExtensions {
  22253. @JRubyMethod(name = "steal_method", required = 2, module = true)
  22254. public static IRubyObject steal_method(IRubyObject recv, IRubyObject type, IRubyObject methodName) {
  22255. RubyModule to_add = null;
  22256. if(recv instanceof RubyModule) {
  22257. to_add = (RubyModule)recv;
  22258. } else {
  22259. to_add = recv.getSingletonClass();
  22260. }
  22261. String name = methodName.toString();
  22262. if(!(type instanceof RubyModule)) {
  22263. throw recv.getRuntime().newArgumentError("First argument must be a module/class");
  22264. }
  22265. DynamicMethod method = ((RubyModule)type).searchMethod(name);
  22266. if(method == null || method.isUndefined()) {
  22267. throw recv.getRuntime().newArgumentError("No such method " + name + " on " + type);
  22268. }
  22269. to_add.addMethod(name, method);
  22270. return recv.getRuntime().getNil();
  22271. }
  22272. @JRubyMethod(name = "steal_methods", required = 1, rest = true, module = true)
  22273. public static IRubyObject steal_methods(IRubyObject recv, IRubyObject[] args) {
  22274. IRubyObject type = args[0];
  22275. for(int i=1;i<args.length;i++) {
  22276. steal_method(recv, type, args[i]);
  22277. }
  22278. return recv.getRuntime().getNil();
  22279. }
  22280. }
  22281. public static class MethodExtensions {
  22282. @JRubyMethod(name = "args")
  22283. public static IRubyObject methodArgs(IRubyObject recv) {
  22284. Ruby ruby = recv.getRuntime();
  22285. RubyMethod rubyMethod = (RubyMethod)recv;
  22286. DynamicMethod method = rubyMethod.method;
  22287. if (method instanceof MethodArgs) {
  22288. MethodArgs interpMethod = (MethodArgs)method;
  22289. ArgsNode args = interpMethod.getArgsNode();
  22290. RubyArray argsArray = RubyArray.newArray(ruby);
  22291. RubyArray reqArray = RubyArray.newArray(ruby);
  22292. ListNode requiredArgs = args.getArgs();
  22293. for (int i = 0; requiredArgs != null && i < requiredArgs.size(); i++) {
  22294. ArgumentNode arg = (ArgumentNode)requiredArgs.get(i);
  22295. reqArray.append(RubySymbol.newSymbol(ruby, arg.getName()));
  22296. }
  22297. argsArray.append(reqArray);
  22298. RubyArray optArray = RubyArray.newArray(ruby);
  22299. ListNode optArgs = args.getOptArgs();
  22300. for (int i = 0; optArgs != null && i < optArgs.size(); i++) {
  22301. LocalAsgnNode arg = (LocalAsgnNode)optArgs.get(i);
  22302. optArray.append(RubySymbol.newSymbol(ruby, arg.getName()));
  22303. }
  22304. argsArray.append(optArray);
  22305. if (args.getRestArgNode() != null) {
  22306. argsArray.append(RubySymbol.newSymbol(ruby, args.getRestArgNode().getName()));
  22307. } else {
  22308. argsArray.append(ruby.getNil());
  22309. }
  22310. if (args.getBlockArgNode() != null) {
  22311. argsArray.append(RubySymbol.newSymbol(ruby, args.getBlockArgNode().getName()));
  22312. } else {
  22313. argsArray.append(ruby.getNil());
  22314. }
  22315. return argsArray;
  22316. }
  22317. throw ruby.newTypeError("Method args are only available for standard interpreted or jitted methods");
  22318. }
  22319. }
  22320. }
  22321. /*
  22322. ***** BEGIN LICENSE BLOCK *****
  22323. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  22324. *
  22325. * The contents of this file are subject to the Common Public
  22326. * License Version 1.0 (the "License"); you may not use this file
  22327. * except in compliance with the License. You may obtain a copy of
  22328. * the License at http://www.eclipse.org/legal/cpl-v10.html
  22329. *
  22330. * Software distributed under the License is distributed on an "AS
  22331. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  22332. * implied. See the License for the specific language governing
  22333. * rights and limitations under the License.
  22334. *
  22335. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  22336. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  22337. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  22338. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  22339. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  22340. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  22341. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  22342. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  22343. * Copyright (C) 2005 Kiel Hodges <jruby-devel@selfsosoft.com>
  22344. * Copyright (C) 2006 Evan Buswell <evan@heron.sytes.net>
  22345. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  22346. * Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
  22347. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  22348. * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  22349. *
  22350. * Alternatively, the contents of this file may be used under the terms of
  22351. * either of the GNU General Public License Version 2 or later (the "GPL"),
  22352. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  22353. * in which case the provisions of the GPL or the LGPL are applicable instead
  22354. * of those above. If you wish to allow use of your version of this file only
  22355. * under the terms of either the GPL or the LGPL, and not to allow others to
  22356. * use your version of this file under the terms of the CPL, indicate your
  22357. * decision by deleting the provisions above and replace them with the notice
  22358. * and other provisions required by the GPL or the LGPL. If you do not delete
  22359. * the provisions above, a recipient may use your version of this file under
  22360. * the terms of any one of the CPL, the GPL or the LGPL.
  22361. ***** END LICENSE BLOCK *****/
  22362. package org.jruby;
  22363. import java.io.ByteArrayOutputStream;
  22364. import java.math.BigInteger;
  22365. import java.util.ArrayList;
  22366. import static org.jruby.anno.FrameField.*;
  22367. import org.jruby.anno.JRubyMethod;
  22368. import org.jruby.anno.JRubyModule;
  22369. import org.jruby.ast.util.ArgsUtil;
  22370. import org.jruby.common.IRubyWarnings.ID;
  22371. import org.jruby.evaluator.ASTInterpreter;
  22372. import org.jruby.exceptions.JumpException;
  22373. import org.jruby.exceptions.MainExitException;
  22374. import org.jruby.exceptions.RaiseException;
  22375. import org.jruby.internal.runtime.JumpTarget;
  22376. import org.jruby.javasupport.util.RuntimeHelpers;
  22377. import org.jruby.runtime.Block;
  22378. import org.jruby.runtime.CallType;
  22379. import org.jruby.runtime.Frame;
  22380. import org.jruby.runtime.MethodIndex;
  22381. import org.jruby.runtime.ThreadContext;
  22382. import org.jruby.runtime.Visibility;
  22383. import static org.jruby.runtime.Visibility.*;
  22384. import org.jruby.runtime.builtin.IRubyObject;
  22385. import org.jruby.runtime.load.IAutoloadMethod;
  22386. import org.jruby.runtime.load.LoadService;
  22387. import org.jruby.util.IdUtil;
  22388. import org.jruby.util.ShellLauncher;
  22389. import org.jruby.util.TypeConverter;
  22390. /**
  22391. * Note: For CVS history, see KernelModule.java.
  22392. */
  22393. @JRubyModule(name="Kernel")
  22394. public class RubyKernel {
  22395. public final static Class<?> IRUBY_OBJECT = IRubyObject.class;
  22396. public static RubyModule createKernelModule(Ruby runtime) {
  22397. RubyModule module = runtime.defineModule("Kernel");
  22398. runtime.setKernel(module);
  22399. module.defineAnnotatedMethods(RubyKernel.class);
  22400. module.defineAnnotatedMethods(RubyObject.class);
  22401. runtime.setRespondToMethod(module.searchMethod("respond_to?"));
  22402. module.setFlag(RubyObject.USER7_F, false); //Kernel is the only Module that doesn't need an implementor
  22403. return module;
  22404. }
  22405. @JRubyMethod(name = "at_exit", frame = true, module = true, visibility = PRIVATE)
  22406. public static IRubyObject at_exit(ThreadContext context, IRubyObject recv, Block block) {
  22407. return context.getRuntime().pushExitBlock(context.getRuntime().newProc(Block.Type.PROC, block));
  22408. }
  22409. @JRubyMethod(name = "autoload?", required = 1, module = true, visibility = PRIVATE)
  22410. public static IRubyObject autoload_p(ThreadContext context, final IRubyObject recv, IRubyObject symbol) {
  22411. Ruby runtime = context.getRuntime();
  22412. RubyModule module = recv instanceof RubyModule ? (RubyModule) recv : runtime.getObject();
  22413. String name = module.getName() + "::" + symbol.asJavaString();
  22414. IAutoloadMethod autoloadMethod = runtime.getLoadService().autoloadFor(name);
  22415. if (autoloadMethod == null) return runtime.getNil();
  22416. return runtime.newString(autoloadMethod.file());
  22417. }
  22418. @JRubyMethod(name = "autoload", required = 2, frame = true, module = true, visibility = PRIVATE)
  22419. public static IRubyObject autoload(final IRubyObject recv, IRubyObject symbol, final IRubyObject file) {
  22420. Ruby runtime = recv.getRuntime();
  22421. final LoadService loadService = runtime.getLoadService();
  22422. String nonInternedName = symbol.asJavaString();
  22423. if (!IdUtil.isValidConstantName(nonInternedName)) {
  22424. throw runtime.newNameError("autoload must be constant name", nonInternedName);
  22425. }
  22426. RubyString fileString = file.convertToString();
  22427. if (fileString.isEmpty()) {
  22428. throw runtime.newArgumentError("empty file name");
  22429. }
  22430. final String baseName = symbol.asJavaString().intern(); // interned, OK for "fast" methods
  22431. final RubyModule module = recv instanceof RubyModule ? (RubyModule) recv : runtime.getObject();
  22432. String nm = module.getName() + "::" + baseName;
  22433. IRubyObject existingValue = module.fastFetchConstant(baseName);
  22434. if (existingValue != null && existingValue != RubyObject.UNDEF) return runtime.getNil();
  22435. module.fastStoreConstant(baseName, RubyObject.UNDEF);
  22436. loadService.addAutoload(nm, new IAutoloadMethod() {
  22437. public String file() {
  22438. return file.toString();
  22439. }
  22440. /**
  22441. * @see org.jruby.runtime.load.IAutoloadMethod#load(Ruby, String)
  22442. */
  22443. public IRubyObject load(Ruby runtime, String name) {
  22444. boolean required = loadService.require(file());
  22445. // File to be loaded by autoload has already been or is being loaded.
  22446. if (!required) return null;
  22447. return module.fastGetConstant(baseName);
  22448. }
  22449. });
  22450. return runtime.getNil();
  22451. }
  22452. @JRubyMethod(name = "method_missing", rest = true, frame = true, module = true, visibility = PRIVATE)
  22453. public static IRubyObject method_missing(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22454. Ruby runtime = context.getRuntime();
  22455. if (args.length == 0 || !(args[0] instanceof RubySymbol)) throw runtime.newArgumentError("no id given");
  22456. Visibility lastVis = context.getLastVisibility();
  22457. CallType lastCallType = context.getLastCallType();
  22458. // create a lightweight thunk
  22459. IRubyObject msg = new RubyNameError.RubyNameErrorMessage(runtime,
  22460. recv,
  22461. args[0],
  22462. lastVis,
  22463. lastCallType);
  22464. final IRubyObject[]exArgs;
  22465. final RubyClass exc;
  22466. if (lastCallType != CallType.VARIABLE) {
  22467. exc = runtime.getNoMethodError();
  22468. exArgs = new IRubyObject[]{msg, args[0], RubyArray.newArrayNoCopy(runtime, args, 1)};
  22469. } else {
  22470. exc = runtime.getNameError();
  22471. exArgs = new IRubyObject[]{msg, args[0]};
  22472. }
  22473. throw new RaiseException((RubyException)exc.newInstance(context, exArgs, Block.NULL_BLOCK));
  22474. }
  22475. @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, module = true, visibility = PRIVATE)
  22476. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22477. String arg = args[0].convertToString().toString();
  22478. Ruby runtime = context.getRuntime();
  22479. if (arg.startsWith("|")) {
  22480. String command = arg.substring(1);
  22481. // exec process, create IO with process
  22482. return RubyIO.popen(context, runtime.getIO(), new IRubyObject[] {runtime.newString(command)}, block);
  22483. }
  22484. return RubyFile.open(context, runtime.getFile(), args, block);
  22485. }
  22486. @JRubyMethod(name = "getc", module = true, visibility = PRIVATE)
  22487. public static IRubyObject getc(ThreadContext context, IRubyObject recv) {
  22488. context.getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "getc is obsolete; use STDIN.getc instead", "getc", "STDIN.getc");
  22489. IRubyObject defin = context.getRuntime().getGlobalVariables().get("$stdin");
  22490. return defin.callMethod(context, "getc");
  22491. }
  22492. @JRubyMethod(name = "gets", optional = 1, module = true, visibility = PRIVATE)
  22493. public static IRubyObject gets(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22494. return RubyArgsFile.gets(context, context.getRuntime().getGlobalVariables().get("$<"), args);
  22495. }
  22496. @JRubyMethod(name = "abort", optional = 1, module = true, visibility = PRIVATE)
  22497. public static IRubyObject abort(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22498. if(args.length == 1) {
  22499. context.getRuntime().getGlobalVariables().get("$stderr").callMethod(context,"puts",args[0]);
  22500. }
  22501. throw new MainExitException(1,true);
  22502. }
  22503. @JRubyMethod(name = "Array", required = 1, module = true, visibility = PRIVATE)
  22504. public static IRubyObject new_array(ThreadContext context, IRubyObject recv, IRubyObject object) {
  22505. IRubyObject value = object.checkArrayType();
  22506. if (value.isNil()) {
  22507. if (object.getMetaClass().searchMethod("to_a").getImplementationClass() != context.getRuntime().getKernel()) {
  22508. value = object.callMethod(context, MethodIndex.TO_A, "to_a");
  22509. if (!(value instanceof RubyArray)) throw context.getRuntime().newTypeError("`to_a' did not return Array");
  22510. return value;
  22511. } else {
  22512. return context.getRuntime().newArray(object);
  22513. }
  22514. }
  22515. return value;
  22516. }
  22517. @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22518. public static IRubyObject new_complex(ThreadContext context, IRubyObject recv) {
  22519. return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert");
  22520. }
  22521. @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22522. public static IRubyObject new_complex(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  22523. return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert", arg);
  22524. }
  22525. @JRubyMethod(name = "Complex", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22526. public static IRubyObject new_complex(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
  22527. return RuntimeHelpers.invoke(context, context.getRuntime().getComplex(), "convert", arg0, arg1);
  22528. }
  22529. @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22530. public static IRubyObject new_rational(ThreadContext context, IRubyObject recv) {
  22531. return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert");
  22532. }
  22533. @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22534. public static IRubyObject new_rational(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  22535. return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert", arg);
  22536. }
  22537. @JRubyMethod(name = "Rational", module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  22538. public static IRubyObject new_rational(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
  22539. return RuntimeHelpers.invoke(context, context.getRuntime().getRational(), "convert", arg0, arg1);
  22540. }
  22541. @JRubyMethod(name = "Float", module = true, visibility = PRIVATE)
  22542. public static IRubyObject new_float(IRubyObject recv, IRubyObject object) {
  22543. if(object instanceof RubyFixnum){
  22544. return RubyFloat.newFloat(object.getRuntime(), ((RubyFixnum)object).getDoubleValue());
  22545. }else if(object instanceof RubyFloat){
  22546. return object;
  22547. }else if(object instanceof RubyBignum){
  22548. return RubyFloat.newFloat(object.getRuntime(), RubyBignum.big2dbl((RubyBignum)object));
  22549. }else if(object instanceof RubyString){
  22550. if(((RubyString)object).getByteList().realSize == 0){ // rb_cstr_to_dbl case
  22551. throw recv.getRuntime().newArgumentError("invalid value for Float(): " + object.inspect());
  22552. }
  22553. return RubyNumeric.str2fnum(recv.getRuntime(),(RubyString)object,true);
  22554. }else if(object.isNil()){
  22555. throw recv.getRuntime().newTypeError("can't convert nil into Float");
  22556. } else {
  22557. RubyFloat rFloat = (RubyFloat)TypeConverter.convertToType(object, recv.getRuntime().getFloat(), MethodIndex.TO_F, "to_f");
  22558. if (Double.isNaN(rFloat.getDoubleValue())) throw recv.getRuntime().newArgumentError("invalid value for Float()");
  22559. return rFloat;
  22560. }
  22561. }
  22562. @JRubyMethod(name = "Integer", required = 1, module = true, visibility = PRIVATE)
  22563. public static IRubyObject new_integer(ThreadContext context, IRubyObject recv, IRubyObject object) {
  22564. if (object instanceof RubyFloat) {
  22565. double val = ((RubyFloat)object).getDoubleValue();
  22566. if (val > (double) RubyFixnum.MAX && val < (double) RubyFixnum.MIN) {
  22567. return RubyNumeric.dbl2num(context.getRuntime(),((RubyFloat)object).getDoubleValue());
  22568. }
  22569. } else if (object instanceof RubyFixnum || object instanceof RubyBignum) {
  22570. return object;
  22571. } else if (object instanceof RubyString) {
  22572. return RubyNumeric.str2inum(context.getRuntime(),(RubyString)object,0,true);
  22573. }
  22574. IRubyObject tmp = TypeConverter.convertToType(object, context.getRuntime().getInteger(), MethodIndex.TO_INT, "to_int", false);
  22575. if (tmp.isNil()) return object.convertToInteger(MethodIndex.TO_I, "to_i");
  22576. return tmp;
  22577. }
  22578. @JRubyMethod(name = "String", required = 1, module = true, visibility = PRIVATE)
  22579. public static IRubyObject new_string(ThreadContext context, IRubyObject recv, IRubyObject object) {
  22580. return TypeConverter.convertToType(object, context.getRuntime().getString(), MethodIndex.TO_S, "to_s");
  22581. }
  22582. @JRubyMethod(name = "p", rest = true, module = true, visibility = PRIVATE)
  22583. public static IRubyObject p(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22584. Ruby runtime = context.getRuntime();
  22585. IRubyObject defout = runtime.getGlobalVariables().get("$>");
  22586. for (int i = 0; i < args.length; i++) {
  22587. if (args[i] != null) {
  22588. defout.callMethod(context, "write", RubyObject.inspect(context, args[i]));
  22589. defout.callMethod(context, "write", runtime.newString("\n"));
  22590. }
  22591. }
  22592. if (defout instanceof RubyFile) {
  22593. ((RubyFile)defout).flush();
  22594. }
  22595. return context.getRuntime().getNil();
  22596. }
  22597. /** rb_f_putc
  22598. */
  22599. @JRubyMethod(name = "putc", required = 1, module = true, visibility = PRIVATE)
  22600. public static IRubyObject putc(ThreadContext context, IRubyObject recv, IRubyObject ch) {
  22601. IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
  22602. return defout.callMethod(context, "putc", ch);
  22603. }
  22604. @JRubyMethod(name = "puts", rest = true, module = true, visibility = PRIVATE)
  22605. public static IRubyObject puts(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22606. IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
  22607. defout.callMethod(context, "puts", args);
  22608. return context.getRuntime().getNil();
  22609. }
  22610. @JRubyMethod(name = "print", rest = true, module = true, visibility = PRIVATE)
  22611. public static IRubyObject print(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22612. IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
  22613. defout.callMethod(context, "print", args);
  22614. return context.getRuntime().getNil();
  22615. }
  22616. @JRubyMethod(name = "printf", rest = true, module = true, visibility = PRIVATE)
  22617. public static IRubyObject printf(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22618. if (args.length != 0) {
  22619. IRubyObject defout = context.getRuntime().getGlobalVariables().get("$>");
  22620. if (!(args[0] instanceof RubyString)) {
  22621. defout = args[0];
  22622. args = ArgsUtil.popArray(args);
  22623. }
  22624. defout.callMethod(context, "write", RubyKernel.sprintf(recv, args));
  22625. }
  22626. return context.getRuntime().getNil();
  22627. }
  22628. @JRubyMethod(name = "readline", optional = 1, module = true, visibility = PRIVATE)
  22629. public static IRubyObject readline(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22630. IRubyObject line = gets(context, recv, args);
  22631. if (line.isNil()) throw context.getRuntime().newEOFError();
  22632. return line;
  22633. }
  22634. @JRubyMethod(name = "readlines", optional = 1, module = true, visibility = PRIVATE)
  22635. public static RubyArray readlines(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22636. return RubyArgsFile.readlines(context, context.getRuntime().getGlobalVariables().get("$<"), args);
  22637. }
  22638. /** Returns value of $_.
  22639. *
  22640. * @throws TypeError if $_ is not a String or nil.
  22641. * @return value of $_ as String.
  22642. */
  22643. private static RubyString getLastlineString(ThreadContext context, Ruby runtime) {
  22644. IRubyObject line = context.getPreviousFrame().getLastLine();
  22645. if (line.isNil()) {
  22646. throw runtime.newTypeError("$_ value need to be String (nil given).");
  22647. } else if (!(line instanceof RubyString)) {
  22648. throw runtime.newTypeError("$_ value need to be String (" + line.getMetaClass().getName() + " given).");
  22649. } else {
  22650. return (RubyString) line;
  22651. }
  22652. }
  22653. /**
  22654. * Variable-arity version for compatibility. Not bound to Ruby.
  22655. * @deprecated Use the one or two-arg versions.
  22656. */
  22657. public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22658. return getLastlineString(context, context.getRuntime()).sub_bang(context, args, block);
  22659. }
  22660. @JRubyMethod(name = "sub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE)
  22661. public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
  22662. return getLastlineString(context, context.getRuntime()).sub_bang(context, arg0, block);
  22663. }
  22664. @JRubyMethod(name = "sub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE)
  22665. public static IRubyObject sub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
  22666. return getLastlineString(context, context.getRuntime()).sub_bang(context, arg0, arg1, block);
  22667. }
  22668. /**
  22669. * Variable-arity version for compatibility. Not bound to Ruby.
  22670. * @deprecated Use the one or two-arg versions.
  22671. */
  22672. public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22673. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22674. if (!str.sub_bang(context, args, block).isNil()) {
  22675. context.getPreviousFrame().setLastLine(str);
  22676. }
  22677. return str;
  22678. }
  22679. @JRubyMethod(name = "sub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22680. public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
  22681. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22682. if (!str.sub_bang(context, arg0, block).isNil()) {
  22683. context.getPreviousFrame().setLastLine(str);
  22684. }
  22685. return str;
  22686. }
  22687. @JRubyMethod(name = "sub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22688. public static IRubyObject sub(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
  22689. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22690. if (!str.sub_bang(context, arg0, arg1, block).isNil()) {
  22691. context.getPreviousFrame().setLastLine(str);
  22692. }
  22693. return str;
  22694. }
  22695. /**
  22696. * Variable-arity version for compatibility. Not bound to Ruby.
  22697. * @deprecated Use the one or two-arg versions.
  22698. */
  22699. public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22700. return getLastlineString(context, context.getRuntime()).gsub_bang(context, args, block);
  22701. }
  22702. @JRubyMethod(name = "gsub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22703. public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
  22704. return getLastlineString(context, context.getRuntime()).gsub_bang(context, arg0, block);
  22705. }
  22706. @JRubyMethod(name = "gsub!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22707. public static IRubyObject gsub_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
  22708. return getLastlineString(context, context.getRuntime()).gsub_bang(context, arg0, arg1, block);
  22709. }
  22710. /**
  22711. * Variable-arity version for compatibility. Not bound to Ruby.
  22712. * @deprecated Use the one or two-arg versions.
  22713. */
  22714. public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22715. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22716. if (!str.gsub_bang(context, args, block).isNil()) {
  22717. context.getPreviousFrame().setLastLine(str);
  22718. }
  22719. return str;
  22720. }
  22721. @JRubyMethod(name = "gsub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22722. public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject arg0, Block block) {
  22723. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22724. if (!str.gsub_bang(context, arg0, block).isNil()) {
  22725. context.getPreviousFrame().setLastLine(str);
  22726. }
  22727. return str;
  22728. }
  22729. @JRubyMethod(name = "gsub", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22730. public static IRubyObject gsub(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1, Block block) {
  22731. RubyString str = (RubyString) getLastlineString(context, context.getRuntime()).dup();
  22732. if (!str.gsub_bang(context, arg0, arg1, block).isNil()) {
  22733. context.getPreviousFrame().setLastLine(str);
  22734. }
  22735. return str;
  22736. }
  22737. @JRubyMethod(name = "chop!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22738. public static IRubyObject chop_bang(ThreadContext context, IRubyObject recv, Block block) {
  22739. return getLastlineString(context, context.getRuntime()).chop_bang();
  22740. }
  22741. @JRubyMethod(name = "chop", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22742. public static IRubyObject chop(ThreadContext context, IRubyObject recv, Block block) {
  22743. RubyString str = getLastlineString(context, context.getRuntime());
  22744. if (str.getByteList().realSize > 0) {
  22745. str = (RubyString) str.dup();
  22746. str.chop_bang();
  22747. context.getPreviousFrame().setLastLine(str);
  22748. }
  22749. return str;
  22750. }
  22751. /**
  22752. * Variable-arity version for compatibility. Not bound to Ruby.
  22753. * @deprecated Use the zero or one-arg versions.
  22754. */
  22755. public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22756. return getLastlineString(context, context.getRuntime()).chomp_bang(args);
  22757. }
  22758. @JRubyMethod(name = "chomp!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22759. public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv) {
  22760. return getLastlineString(context, context.getRuntime()).chomp_bang();
  22761. }
  22762. @JRubyMethod(name = "chomp!", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22763. public static IRubyObject chomp_bang(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
  22764. return getLastlineString(context, context.getRuntime()).chomp_bang(arg0);
  22765. }
  22766. /**
  22767. * Variable-arity version for compatibility. Not bound to Ruby.
  22768. * @deprecated Use the zero or one-arg versions.
  22769. */
  22770. public static IRubyObject chomp(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22771. RubyString str = getLastlineString(context, context.getRuntime());
  22772. RubyString dup = (RubyString) str.dup();
  22773. if (dup.chomp_bang(args).isNil()) {
  22774. return str;
  22775. }
  22776. context.getPreviousFrame().setLastLine(dup);
  22777. return dup;
  22778. }
  22779. @JRubyMethod(name = "chomp", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22780. public static IRubyObject chomp(ThreadContext context, IRubyObject recv) {
  22781. RubyString str = getLastlineString(context, context.getRuntime());
  22782. RubyString dup = (RubyString) str.dup();
  22783. if (dup.chomp_bang().isNil()) {
  22784. return str;
  22785. }
  22786. context.getPreviousFrame().setLastLine(dup);
  22787. return dup;
  22788. }
  22789. @JRubyMethod(name = "chomp", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = LASTLINE)
  22790. public static IRubyObject chomp(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
  22791. RubyString str = getLastlineString(context, context.getRuntime());
  22792. RubyString dup = (RubyString) str.dup();
  22793. if (dup.chomp_bang(arg0).isNil()) {
  22794. return str;
  22795. }
  22796. context.getPreviousFrame().setLastLine(dup);
  22797. return dup;
  22798. }
  22799. /**
  22800. * Variable arity version for compatibility. Not bound to a Ruby method.
  22801. *
  22802. * @param context The thread context for the current thread
  22803. * @param recv The receiver of the method (usually a class that has included Kernel)
  22804. * @return
  22805. * @deprecated Use the versions with zero, one, or two args.
  22806. */
  22807. public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22808. return getLastlineString(context, context.getRuntime()).split(context, args);
  22809. }
  22810. @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
  22811. public static IRubyObject split(ThreadContext context, IRubyObject recv) {
  22812. return getLastlineString(context, context.getRuntime()).split(context);
  22813. }
  22814. @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
  22815. public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
  22816. return getLastlineString(context, context.getRuntime()).split(context, arg0);
  22817. }
  22818. @JRubyMethod(name = "split", frame = true, module = true, visibility = PRIVATE, reads = LASTLINE, writes = {LASTLINE, BACKREF})
  22819. public static IRubyObject split(ThreadContext context, IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
  22820. return getLastlineString(context, context.getRuntime()).split(context, arg0, arg1);
  22821. }
  22822. @JRubyMethod(name = "scan", required = 1, frame = true, module = true, visibility = PRIVATE, reads = {LASTLINE, BACKREF}, writes = {LASTLINE, BACKREF})
  22823. public static IRubyObject scan(ThreadContext context, IRubyObject recv, IRubyObject pattern, Block block) {
  22824. return getLastlineString(context, context.getRuntime()).scan(context, pattern, block);
  22825. }
  22826. @JRubyMethod(name = "select", required = 1, optional = 3, module = true, visibility = PRIVATE)
  22827. public static IRubyObject select(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22828. return RubyIO.select_static(context, context.getRuntime(), args);
  22829. }
  22830. @JRubyMethod(name = "sleep", optional = 1, module = true, visibility = PRIVATE)
  22831. public static IRubyObject sleep(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22832. long milliseconds;
  22833. if (args.length == 0) {
  22834. // Zero sleeps forever
  22835. milliseconds = 0;
  22836. } else {
  22837. if (!(args[0] instanceof RubyNumeric)) {
  22838. throw context.getRuntime().newTypeError("can't convert " + args[0].getMetaClass().getName() + "into time interval");
  22839. }
  22840. milliseconds = (long) (args[0].convertToFloat().getDoubleValue() * 1000);
  22841. if (milliseconds < 0) {
  22842. throw context.getRuntime().newArgumentError("time interval must be positive");
  22843. } else if (milliseconds == 0) {
  22844. // Explicit zero in MRI returns immediately
  22845. return context.getRuntime().newFixnum(0);
  22846. }
  22847. }
  22848. long startTime = System.currentTimeMillis();
  22849. RubyThread rubyThread = context.getThread();
  22850. do {
  22851. long loopStartTime = System.currentTimeMillis();
  22852. try {
  22853. rubyThread.sleep(milliseconds);
  22854. } catch (InterruptedException iExcptn) {
  22855. }
  22856. milliseconds -= (System.currentTimeMillis() - loopStartTime);
  22857. } while (milliseconds > 0);
  22858. return context.getRuntime().newFixnum(Math.round((System.currentTimeMillis() - startTime) / 1000.0));
  22859. }
  22860. // FIXME: Add at_exit and finalizers to exit, then make exit_bang not call those.
  22861. @JRubyMethod(name = "exit", optional = 1, module = true, visibility = PRIVATE)
  22862. public static IRubyObject exit(IRubyObject recv, IRubyObject[] args) {
  22863. exit(recv.getRuntime(), args, false);
  22864. return recv.getRuntime().getNil(); // not reached
  22865. }
  22866. @JRubyMethod(name = "exit!", optional = 1, module = true, visibility = PRIVATE)
  22867. public static IRubyObject exit_bang(IRubyObject recv, IRubyObject[] args) {
  22868. exit(recv.getRuntime(), args, true);
  22869. return recv.getRuntime().getNil(); // not reached
  22870. }
  22871. private static void exit(Ruby runtime, IRubyObject[] args, boolean hard) {
  22872. runtime.secure(4);
  22873. int status = 1;
  22874. if (args.length > 0) {
  22875. RubyObject argument = (RubyObject)args[0];
  22876. if (argument instanceof RubyFixnum) {
  22877. status = RubyNumeric.fix2int(argument);
  22878. } else {
  22879. status = argument.isFalse() ? 1 : 0;
  22880. }
  22881. }
  22882. if (hard) {
  22883. throw new MainExitException(status, true);
  22884. } else {
  22885. throw runtime.newSystemExit(status);
  22886. }
  22887. }
  22888. /** Returns an Array with the names of all global variables.
  22889. *
  22890. */
  22891. @JRubyMethod(name = "global_variables", module = true, visibility = PRIVATE)
  22892. public static RubyArray global_variables(ThreadContext context, IRubyObject recv) {
  22893. Ruby runtime = context.getRuntime();
  22894. RubyArray globalVariables = runtime.newArray();
  22895. for (String globalVariableName : runtime.getGlobalVariables().getNames()) {
  22896. globalVariables.append(runtime.newString(globalVariableName));
  22897. }
  22898. return globalVariables;
  22899. }
  22900. /** Returns an Array with the names of all local variables.
  22901. *
  22902. */
  22903. @JRubyMethod(name = "local_variables", module = true, visibility = PRIVATE)
  22904. public static RubyArray local_variables(ThreadContext context, IRubyObject recv) {
  22905. final Ruby runtime = context.getRuntime();
  22906. RubyArray localVariables = runtime.newArray();
  22907. for (String name: context.getCurrentScope().getAllNamesInScope()) {
  22908. if (IdUtil.isLocal(name)) localVariables.append(runtime.newString(name));
  22909. }
  22910. return localVariables;
  22911. }
  22912. @JRubyMethod(name = "binding", frame = true, module = true, visibility = PRIVATE)
  22913. public static RubyBinding binding(ThreadContext context, IRubyObject recv, Block block) {
  22914. return RubyBinding.newBinding(context.getRuntime());
  22915. }
  22916. @JRubyMethod(name = {"block_given?", "iterator?"}, frame = true, module = true, visibility = PRIVATE)
  22917. public static RubyBoolean block_given_p(ThreadContext context, IRubyObject recv, Block block) {
  22918. return context.getRuntime().newBoolean(context.getPreviousFrame().getBlock().isGiven());
  22919. }
  22920. @Deprecated
  22921. public static IRubyObject sprintf(IRubyObject recv, IRubyObject[] args) {
  22922. return sprintf(recv.getRuntime().getCurrentContext(), recv, args);
  22923. }
  22924. @JRubyMethod(name = {"sprintf", "format"}, required = 1, rest = true, module = true, visibility = PRIVATE)
  22925. public static IRubyObject sprintf(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  22926. if (args.length == 0) {
  22927. throw context.getRuntime().newArgumentError("sprintf must have at least one argument");
  22928. }
  22929. RubyString str = RubyString.stringValue(args[0]);
  22930. RubyArray newArgs = context.getRuntime().newArrayNoCopy(args);
  22931. newArgs.shift();
  22932. return str.op_format(context, newArgs);
  22933. }
  22934. @JRubyMethod(name = {"raise", "fail"}, optional = 3, frame = true, module = true, visibility = PRIVATE)
  22935. public static IRubyObject raise(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  22936. // FIXME: Pass block down?
  22937. Ruby runtime = context.getRuntime();
  22938. if (args.length == 0) {
  22939. IRubyObject lastException = runtime.getGlobalVariables().get("$!");
  22940. if (lastException.isNil()) {
  22941. throw new RaiseException(runtime, runtime.getRuntimeError(), "", false);
  22942. }
  22943. throw new RaiseException((RubyException) lastException);
  22944. }
  22945. IRubyObject exception;
  22946. if (args.length == 1) {
  22947. if (args[0] instanceof RubyString) {
  22948. throw new RaiseException((RubyException)runtime.getRuntimeError().newInstance(context, args, block));
  22949. }
  22950. if (!args[0].respondsTo("exception")) {
  22951. throw runtime.newTypeError("exception class/object expected");
  22952. }
  22953. exception = args[0].callMethod(context, "exception");
  22954. } else {
  22955. if (!args[0].respondsTo("exception")) {
  22956. throw runtime.newTypeError("exception class/object expected");
  22957. }
  22958. exception = args[0].callMethod(context, "exception", args[1]);
  22959. }
  22960. if (!runtime.fastGetClass("Exception").isInstance(exception)) {
  22961. throw runtime.newTypeError("exception object expected");
  22962. }
  22963. if (args.length == 3) {
  22964. ((RubyException) exception).set_backtrace(args[2]);
  22965. }
  22966. if (runtime.getDebug().isTrue()) {
  22967. printExceptionSummary(context, runtime, (RubyException) exception);
  22968. }
  22969. throw new RaiseException((RubyException) exception);
  22970. }
  22971. private static void printExceptionSummary(ThreadContext context, Ruby runtime, RubyException rEx) {
  22972. Frame currentFrame = context.getCurrentFrame();
  22973. String msg = String.format("Exception `%s' at %s:%s - %s\n",
  22974. rEx.getMetaClass(),
  22975. currentFrame.getFile(), currentFrame.getLine() + 1,
  22976. rEx.to_s());
  22977. IRubyObject errorStream = runtime.getGlobalVariables().get("$stderr");
  22978. errorStream.callMethod(context, "write", runtime.newString(msg));
  22979. }
  22980. /**
  22981. * Require.
  22982. * MRI allows to require ever .rb files or ruby extension dll (.so or .dll depending on system).
  22983. * we allow requiring either .rb files or jars.
  22984. * @param recv ruby object used to call require (any object will do and it won't be used anyway).
  22985. * @param name the name of the file to require
  22986. **/
  22987. @JRubyMethod(name = "require", required = 1, frame = true, module = true, visibility = PRIVATE)
  22988. public static IRubyObject require(IRubyObject recv, IRubyObject name, Block block) {
  22989. Ruby runtime = recv.getRuntime();
  22990. if (runtime.getLoadService().require(name.convertToString().toString())) {
  22991. return runtime.getTrue();
  22992. }
  22993. return runtime.getFalse();
  22994. }
  22995. @JRubyMethod(name = "load", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
  22996. public static IRubyObject load(IRubyObject recv, IRubyObject[] args, Block block) {
  22997. Ruby runtime = recv.getRuntime();
  22998. RubyString file = args[0].convertToString();
  22999. boolean wrap = args.length == 2 ? args[1].isTrue() : false;
  23000. runtime.getLoadService().load(file.getByteList().toString(), wrap);
  23001. return runtime.getTrue();
  23002. }
  23003. @JRubyMethod(name = "eval", required = 1, optional = 3, frame = true, module = true, visibility = PRIVATE)
  23004. public static IRubyObject eval(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23005. Ruby runtime = context.getRuntime();
  23006. // string to eval
  23007. RubyString src = args[0].convertToString();
  23008. runtime.checkSafeString(src);
  23009. IRubyObject scope = args.length > 1 && !args[1].isNil() ? args[1] : null;
  23010. String file;
  23011. if (args.length > 2) {
  23012. file = args[2].convertToString().toString();
  23013. } else if (scope == null) {
  23014. file = "(eval)";
  23015. } else {
  23016. file = null;
  23017. }
  23018. int line;
  23019. if (args.length > 3) {
  23020. line = (int) args[3].convertToInteger().getLongValue();
  23021. } else if (scope == null) {
  23022. line = 0;
  23023. } else {
  23024. line = -1;
  23025. }
  23026. if (scope == null) scope = RubyBinding.newBindingForEval(context);
  23027. return ASTInterpreter.evalWithBinding(context, src, scope, file, line);
  23028. }
  23029. @JRubyMethod(name = "callcc", frame = true, module = true, visibility = PRIVATE)
  23030. public static IRubyObject callcc(ThreadContext context, IRubyObject recv, Block block) {
  23031. Ruby runtime = context.getRuntime();
  23032. runtime.getWarnings().warn(ID.EMPTY_IMPLEMENTATION, "Kernel#callcc: Continuations are not implemented in JRuby and will not work", "Kernel#callcc");
  23033. IRubyObject cc = runtime.getContinuation().callMethod(context, "new");
  23034. cc.dataWrapStruct(block);
  23035. return block.yield(context, cc);
  23036. }
  23037. @JRubyMethod(name = "caller", optional = 1, frame = true, module = true, visibility = PRIVATE)
  23038. public static IRubyObject caller(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23039. int level = args.length > 0 ? RubyNumeric.fix2int(args[0]) : 1;
  23040. if (level < 0) {
  23041. throw context.getRuntime().newArgumentError("negative level(" + level + ')');
  23042. }
  23043. return context.createCallerBacktrace(context.getRuntime(), level);
  23044. }
  23045. @JRubyMethod(name = "catch", required = 1, frame = true, module = true, visibility = PRIVATE)
  23046. public static IRubyObject rbCatch(ThreadContext context, IRubyObject recv, IRubyObject tag, Block block) {
  23047. CatchTarget target = new CatchTarget(tag.asJavaString());
  23048. try {
  23049. context.pushCatch(target);
  23050. return block.yield(context, tag);
  23051. } catch (JumpException.ThrowJump tj) {
  23052. if (tj.getTarget() == target) return (IRubyObject) tj.getValue();
  23053. throw tj;
  23054. } finally {
  23055. context.popCatch();
  23056. }
  23057. }
  23058. public static class CatchTarget implements JumpTarget {
  23059. private final String tag;
  23060. public CatchTarget(String tag) { this.tag = tag; }
  23061. public String getTag() { return tag; }
  23062. }
  23063. @JRubyMethod(name = "throw", required = 1, frame = true, optional = 1, module = true, visibility = PRIVATE)
  23064. public static IRubyObject rbThrow(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23065. Ruby runtime = context.getRuntime();
  23066. String tag = args[0].asJavaString();
  23067. CatchTarget[] catches = context.getActiveCatches();
  23068. String message = "uncaught throw `" + tag + "'";
  23069. // Ordering of array traversal not important, just intuitive
  23070. for (int i = catches.length - 1 ; i >= 0 ; i--) {
  23071. if (tag.equals(catches[i].getTag())) {
  23072. //Catch active, throw for catch to handle
  23073. throw new JumpException.ThrowJump(catches[i], args.length > 1 ? args[1] : runtime.getNil());
  23074. }
  23075. }
  23076. // No catch active for this throw
  23077. RubyThread currentThread = context.getThread();
  23078. if (currentThread == runtime.getThreadService().getMainThread()) {
  23079. throw runtime.newNameError(message, tag);
  23080. } else {
  23081. throw runtime.newThreadError(message + " in thread 0x" + Integer.toHexString(RubyInteger.fix2int(currentThread.id())));
  23082. }
  23083. }
  23084. @JRubyMethod(name = "trap", required = 1, frame = true, optional = 1, module = true, visibility = PRIVATE)
  23085. public static IRubyObject trap(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23086. context.getRuntime().getLoadService().require("jsignal");
  23087. return RuntimeHelpers.invoke(context, recv, "__jtrap", args, block);
  23088. }
  23089. @JRubyMethod(name = "warn", required = 1, module = true, visibility = PRIVATE)
  23090. public static IRubyObject warn(ThreadContext context, IRubyObject recv, IRubyObject message) {
  23091. Ruby runtime = context.getRuntime();
  23092. if (!runtime.getVerbose().isNil()) {
  23093. IRubyObject out = runtime.getGlobalVariables().get("$stderr");
  23094. RuntimeHelpers.invoke(context, out, "puts", message);
  23095. }
  23096. return runtime.getNil();
  23097. }
  23098. @JRubyMethod(name = "set_trace_func", required = 1, frame = true, module = true, visibility = PRIVATE)
  23099. public static IRubyObject set_trace_func(ThreadContext context, IRubyObject recv, IRubyObject trace_func, Block block) {
  23100. if (trace_func.isNil()) {
  23101. context.getRuntime().setTraceFunction(null);
  23102. } else if (!(trace_func instanceof RubyProc)) {
  23103. throw context.getRuntime().newTypeError("trace_func needs to be Proc.");
  23104. } else {
  23105. context.getRuntime().setTraceFunction((RubyProc) trace_func);
  23106. }
  23107. return trace_func;
  23108. }
  23109. @JRubyMethod(name = "trace_var", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
  23110. public static IRubyObject trace_var(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23111. if (args.length == 0) throw context.getRuntime().newArgumentError(0, 1);
  23112. RubyProc proc = null;
  23113. String var = args.length > 1 ? args[0].toString() : null;
  23114. // ignore if it's not a global var
  23115. if (var.charAt(0) != '$') return context.getRuntime().getNil();
  23116. if (args.length == 1) proc = RubyProc.newProc(context.getRuntime(), block, Block.Type.PROC);
  23117. if (args.length == 2) {
  23118. proc = (RubyProc)TypeConverter.convertToType(args[1], context.getRuntime().getProc(), 0, "to_proc", true);
  23119. }
  23120. context.getRuntime().getGlobalVariables().setTraceVar(var, proc);
  23121. return context.getRuntime().getNil();
  23122. }
  23123. @JRubyMethod(name = "untrace_var", required = 1, optional = 1, frame = true, module = true, visibility = PRIVATE)
  23124. public static IRubyObject untrace_var(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  23125. if (args.length == 0) throw context.getRuntime().newArgumentError(0, 1);
  23126. String var = args.length >= 1 ? args[0].toString() : null;
  23127. // ignore if it's not a global var
  23128. if (var.charAt(0) != '$') return context.getRuntime().getNil();
  23129. if (args.length > 1) {
  23130. ArrayList<IRubyObject> success = new ArrayList<IRubyObject>();
  23131. for (int i = 1; i < args.length; i++) {
  23132. if (context.getRuntime().getGlobalVariables().untraceVar(var, args[i])) {
  23133. success.add(args[i]);
  23134. }
  23135. }
  23136. return RubyArray.newArray(context.getRuntime(), success);
  23137. } else {
  23138. context.getRuntime().getGlobalVariables().untraceVar(var);
  23139. }
  23140. return context.getRuntime().getNil();
  23141. }
  23142. @JRubyMethod(name = "singleton_method_added", required = 1, frame = true, module = true, visibility = PRIVATE)
  23143. public static IRubyObject singleton_method_added(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
  23144. return context.getRuntime().getNil();
  23145. }
  23146. @JRubyMethod(name = "singleton_method_removed", required = 1, frame = true, module = true, visibility = PRIVATE)
  23147. public static IRubyObject singleton_method_removed(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
  23148. return context.getRuntime().getNil();
  23149. }
  23150. @JRubyMethod(name = "singleton_method_undefined", required = 1, frame = true, module = true, visibility = PRIVATE)
  23151. public static IRubyObject singleton_method_undefined(ThreadContext context, IRubyObject recv, IRubyObject symbolId, Block block) {
  23152. return context.getRuntime().getNil();
  23153. }
  23154. @JRubyMethod(name = {"proc", "lambda"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_8)
  23155. public static RubyProc proc(ThreadContext context, IRubyObject recv, Block block) {
  23156. return context.getRuntime().newProc(Block.Type.LAMBDA, block);
  23157. }
  23158. @Deprecated
  23159. public static RubyProc proc(IRubyObject recv, Block block) {
  23160. return recv.getRuntime().newProc(Block.Type.LAMBDA, block);
  23161. }
  23162. @JRubyMethod(name = {"lambda"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  23163. public static RubyProc lambda(ThreadContext context, IRubyObject recv, Block block) {
  23164. return context.getRuntime().newProc(Block.Type.LAMBDA, block);
  23165. }
  23166. @JRubyMethod(name = {"proc"}, frame = true, module = true, visibility = PRIVATE, compat = CompatVersion.RUBY1_9)
  23167. public static RubyProc proc_1_9(ThreadContext context, IRubyObject recv, Block block) {
  23168. return context.getRuntime().newProc(Block.Type.PROC, block);
  23169. }
  23170. @JRubyMethod(name = "loop", frame = true, module = true, visibility = PRIVATE)
  23171. public static IRubyObject loop(ThreadContext context, IRubyObject recv, Block block) {
  23172. while (true) {
  23173. block.yield(context, context.getRuntime().getNil());
  23174. context.pollThreadEvents();
  23175. }
  23176. }
  23177. @JRubyMethod(name = "test", required = 2, optional = 1, module = true, visibility = PRIVATE)
  23178. public static IRubyObject test(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23179. if (args.length == 0) throw context.getRuntime().newArgumentError("wrong number of arguments");
  23180. int cmd;
  23181. if (args[0] instanceof RubyFixnum) {
  23182. cmd = (int)((RubyFixnum) args[0]).getLongValue();
  23183. } else if (args[0] instanceof RubyString &&
  23184. ((RubyString) args[0]).getByteList().length() > 0) {
  23185. // MRI behavior: use first byte of string value if len > 0
  23186. cmd = ((RubyString) args[0]).getByteList().charAt(0);
  23187. } else {
  23188. cmd = (int) args[0].convertToInteger().getLongValue();
  23189. }
  23190. // MRI behavior: raise ArgumentError for 'unknown command' before
  23191. // checking number of args.
  23192. switch(cmd) {
  23193. case 'A': case 'b': case 'c': case 'C': case 'd': case 'e': case 'f': case 'g': case 'G':
  23194. case 'k': case 'M': case 'l': case 'o': case 'O': case 'p': case 'r': case 'R': case 's':
  23195. case 'S': case 'u': case 'w': case 'W': case 'x': case 'X': case 'z': case '=': case '<':
  23196. case '>': case '-':
  23197. break;
  23198. default:
  23199. throw context.getRuntime().newArgumentError("unknown command ?" + (char) cmd);
  23200. }
  23201. // MRI behavior: now check arg count
  23202. switch(cmd) {
  23203. case '-': case '=': case '<': case '>':
  23204. if (args.length != 3) throw context.getRuntime().newArgumentError(args.length, 3);
  23205. break;
  23206. default:
  23207. if (args.length != 2) throw context.getRuntime().newArgumentError(args.length, 2);
  23208. break;
  23209. }
  23210. switch (cmd) {
  23211. case 'A': // ?A | Time | Last access time for file1
  23212. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).atime();
  23213. case 'b': // ?b | boolean | True if file1 is a block device
  23214. return RubyFileTest.blockdev_p(recv, args[1]);
  23215. case 'c': // ?c | boolean | True if file1 is a character device
  23216. return RubyFileTest.chardev_p(recv, args[1]);
  23217. case 'C': // ?C | Time | Last change time for file1
  23218. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).ctime();
  23219. case 'd': // ?d | boolean | True if file1 exists and is a directory
  23220. return RubyFileTest.directory_p(recv, args[1]);
  23221. case 'e': // ?e | boolean | True if file1 exists
  23222. return RubyFileTest.exist_p(recv, args[1]);
  23223. case 'f': // ?f | boolean | True if file1 exists and is a regular file
  23224. return RubyFileTest.file_p(recv, args[1]);
  23225. case 'g': // ?g | boolean | True if file1 has the \CF{setgid} bit
  23226. return RubyFileTest.setgid_p(recv, args[1]);
  23227. case 'G': // ?G | boolean | True if file1 exists and has a group ownership equal to the caller's group
  23228. return RubyFileTest.grpowned_p(recv, args[1]);
  23229. case 'k': // ?k | boolean | True if file1 exists and has the sticky bit set
  23230. return RubyFileTest.sticky_p(recv, args[1]);
  23231. case 'M': // ?M | Time | Last modification time for file1
  23232. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtime();
  23233. case 'l': // ?l | boolean | True if file1 exists and is a symbolic link
  23234. return RubyFileTest.symlink_p(recv, args[1]);
  23235. case 'o': // ?o | boolean | True if file1 exists and is owned by the caller's effective uid
  23236. return RubyFileTest.owned_p(recv, args[1]);
  23237. case 'O': // ?O | boolean | True if file1 exists and is owned by the caller's real uid
  23238. return RubyFileTest.rowned_p(recv, args[1]);
  23239. case 'p': // ?p | boolean | True if file1 exists and is a fifo
  23240. return RubyFileTest.pipe_p(recv, args[1]);
  23241. case 'r': // ?r | boolean | True if file1 is readable by the effective uid/gid of the caller
  23242. return RubyFileTest.readable_p(recv, args[1]);
  23243. case 'R': // ?R | boolean | True if file is readable by the real uid/gid of the caller
  23244. // FIXME: Need to implement an readable_real_p in FileTest
  23245. return RubyFileTest.readable_p(recv, args[1]);
  23246. case 's': // ?s | int/nil | If file1 has nonzero size, return the size, otherwise nil
  23247. return RubyFileTest.size_p(recv, args[1]);
  23248. case 'S': // ?S | boolean | True if file1 exists and is a socket
  23249. return RubyFileTest.socket_p(recv, args[1]);
  23250. case 'u': // ?u | boolean | True if file1 has the setuid bit set
  23251. return RubyFileTest.setuid_p(recv, args[1]);
  23252. case 'w': // ?w | boolean | True if file1 exists and is writable by effective uid/gid
  23253. return RubyFileTest.writable_p(recv, args[1]);
  23254. case 'W': // ?W | boolean | True if file1 exists and is writable by the real uid/gid
  23255. // FIXME: Need to implement an writable_real_p in FileTest
  23256. return RubyFileTest.writable_p(recv, args[1]);
  23257. case 'x': // ?x | boolean | True if file1 exists and is executable by the effective uid/gid
  23258. return RubyFileTest.executable_p(recv, args[1]);
  23259. case 'X': // ?X | boolean | True if file1 exists and is executable by the real uid/gid
  23260. return RubyFileTest.executable_real_p(recv, args[1]);
  23261. case 'z': // ?z | boolean | True if file1 exists and has a zero length
  23262. return RubyFileTest.zero_p(recv, args[1]);
  23263. case '=': // ?= | boolean | True if the modification times of file1 and file2 are equal
  23264. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeEquals(args[2]);
  23265. case '<': // ?< | boolean | True if the modification time of file1 is prior to that of file2
  23266. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeLessThan(args[2]);
  23267. case '>': // ?> | boolean | True if the modification time of file1 is after that of file2
  23268. return context.getRuntime().newFileStat(args[1].convertToString().toString(), false).mtimeGreaterThan(args[2]);
  23269. case '-': // ?- | boolean | True if file1 and file2 are identical
  23270. return RubyFileTest.identical_p(recv, args[1], args[2]);
  23271. default:
  23272. throw new InternalError("unreachable code reached!");
  23273. }
  23274. }
  23275. @JRubyMethod(name = "`", required = 1, module = true, visibility = PRIVATE)
  23276. public static IRubyObject backquote(ThreadContext context, IRubyObject recv, IRubyObject aString) {
  23277. Ruby runtime = context.getRuntime();
  23278. ByteArrayOutputStream output = new ByteArrayOutputStream();
  23279. RubyString string = aString.convertToString();
  23280. int resultCode = ShellLauncher.runAndWait(runtime, new IRubyObject[] {string}, output);
  23281. runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, resultCode));
  23282. return RubyString.newString(runtime, output.toByteArray());
  23283. }
  23284. @JRubyMethod(name = "srand", optional = 1, module = true, visibility = PRIVATE)
  23285. public static RubyInteger srand(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23286. Ruby runtime = context.getRuntime();
  23287. long oldRandomSeed = runtime.getRandomSeed();
  23288. if (args.length > 0) {
  23289. RubyInteger integerSeed = args[0].convertToInteger(MethodIndex.TO_INT, "to_int");
  23290. runtime.setRandomSeed(integerSeed.getLongValue());
  23291. } else {
  23292. // Not sure how well this works, but it works much better than
  23293. // just currentTimeMillis by itself.
  23294. runtime.setRandomSeed(System.currentTimeMillis() ^
  23295. recv.hashCode() ^ runtime.incrementRandomSeedSequence() ^
  23296. runtime.getRandom().nextInt(Math.max(1, Math.abs((int)runtime.getRandomSeed()))));
  23297. }
  23298. runtime.getRandom().setSeed(runtime.getRandomSeed());
  23299. return runtime.newFixnum(oldRandomSeed);
  23300. }
  23301. @JRubyMethod(name = "rand", optional = 1, module = true, visibility = PRIVATE)
  23302. public static RubyNumeric rand(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23303. Ruby runtime = context.getRuntime();
  23304. long ceil;
  23305. if (args.length == 0) {
  23306. ceil = 0;
  23307. } else if (args.length == 1) {
  23308. if (args[0] instanceof RubyBignum) {
  23309. byte[] bigCeilBytes = ((RubyBignum) args[0]).getValue().toByteArray();
  23310. BigInteger bigCeil = new BigInteger(bigCeilBytes).abs();
  23311. byte[] randBytes = new byte[bigCeilBytes.length];
  23312. runtime.getRandom().nextBytes(randBytes);
  23313. BigInteger result = new BigInteger(randBytes).abs().mod(bigCeil);
  23314. return new RubyBignum(runtime, result);
  23315. }
  23316. RubyInteger integerCeil = (RubyInteger)RubyKernel.new_integer(context, recv, args[0]);
  23317. ceil = Math.abs(integerCeil.getLongValue());
  23318. } else {
  23319. throw runtime.newArgumentError("wrong # of arguments(" + args.length + " for 1)");
  23320. }
  23321. if (ceil == 0) {
  23322. return RubyFloat.newFloat(runtime, runtime.getRandom().nextDouble());
  23323. }
  23324. if (ceil > Integer.MAX_VALUE) {
  23325. return runtime.newFixnum(Math.abs(runtime.getRandom().nextLong()) % ceil);
  23326. }
  23327. return runtime.newFixnum(runtime.getRandom().nextInt((int) ceil));
  23328. }
  23329. @JRubyMethod(name = "syscall", required = 1, optional = 9, module = true, visibility = PRIVATE)
  23330. public static IRubyObject syscall(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23331. throw context.getRuntime().newNotImplementedError("Kernel#syscall is not implemented in JRuby");
  23332. }
  23333. @JRubyMethod(name = {"system"}, required = 1, rest = true, module = true, visibility = PRIVATE)
  23334. public static RubyBoolean system(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23335. Ruby runtime = context.getRuntime();
  23336. int resultCode;
  23337. try {
  23338. resultCode = ShellLauncher.runAndWait(runtime, args);
  23339. } catch (Exception e) {
  23340. resultCode = 127;
  23341. }
  23342. runtime.getGlobalVariables().set("$?", RubyProcess.RubyStatus.newProcessStatus(runtime, resultCode));
  23343. return runtime.newBoolean(resultCode == 0);
  23344. }
  23345. @JRubyMethod(name = {"exec"}, required = 1, rest = true, module = true, visibility = PRIVATE)
  23346. public static IRubyObject exec(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  23347. Ruby runtime = context.getRuntime();
  23348. int resultCode;
  23349. try {
  23350. // TODO: exec should replace the current process.
  23351. // This could be possible with JNA.
  23352. resultCode = ShellLauncher.execAndWait(runtime, args);
  23353. } catch (Exception e) {
  23354. throw runtime.newErrnoENOENTError("cannot execute");
  23355. }
  23356. return exit(recv, new IRubyObject[] {runtime.newFixnum(resultCode)});
  23357. }
  23358. @JRubyMethod(name = "fork", module = true, visibility = PRIVATE)
  23359. public static IRubyObject fork(ThreadContext context, IRubyObject recv, Block block) {
  23360. Ruby runtime = context.getRuntime();
  23361. if (!RubyInstanceConfig.FORK_ENABLED) {
  23362. throw runtime.newNotImplementedError("fork is unsafe and disabled by default on JRuby");
  23363. }
  23364. if (block.isGiven()) {
  23365. int pid = runtime.getPosix().fork();
  23366. if (pid == 0) {
  23367. try {
  23368. block.yield(context, runtime.getNil());
  23369. } catch (RaiseException re) {
  23370. if (re.getException() instanceof RubySystemExit) {
  23371. throw re;
  23372. }
  23373. return exit_bang(recv, new IRubyObject[] {RubyFixnum.minus_one(runtime)});
  23374. } catch (Throwable t) {
  23375. return exit_bang(recv, new IRubyObject[] {RubyFixnum.minus_one(runtime)});
  23376. }
  23377. return exit_bang(recv, new IRubyObject[] {RubyFixnum.zero(runtime)});
  23378. } else {
  23379. return runtime.newFixnum(pid);
  23380. }
  23381. } else {
  23382. int result = runtime.getPosix().fork();
  23383. if (result == -1) {
  23384. return runtime.getNil();
  23385. }
  23386. return runtime.newFixnum(result);
  23387. }
  23388. }
  23389. @JRubyMethod(frame = true, module = true)
  23390. public static IRubyObject tap(ThreadContext context, IRubyObject recv, Block block) {
  23391. block.yield(context, recv);
  23392. return recv;
  23393. }
  23394. }
  23395. /***** BEGIN LICENSE BLOCK *****
  23396. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  23397. *
  23398. * The contents of this file are subject to the Common Public
  23399. * License Version 1.0 (the "License"); you may not use this file
  23400. * except in compliance with the License. You may obtain a copy of
  23401. * the License at http://www.eclipse.org/legal/cpl-v10.html
  23402. *
  23403. * Software distributed under the License is distributed on an "AS
  23404. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  23405. * implied. See the License for the specific language governing
  23406. * rights and limitations under the License.
  23407. *
  23408. * Copyright (C) 2007 Charles O Nutter <headius@headius.com>
  23409. *
  23410. * Alternatively, the contents of this file may be used under the terms of
  23411. * either of the GNU General Public License Version 2 or later (the "GPL"),
  23412. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  23413. * in which case the provisions of the GPL or the LGPL are applicable instead
  23414. * of those above. If you wish to allow use of your version of this file only
  23415. * under the terms of either the GPL or the LGPL, and not to allow others to
  23416. * use your version of this file under the terms of the CPL, indicate your
  23417. * decision by deleting the provisions above and replace them with the notice
  23418. * and other provisions required by the GPL or the LGPL. If you do not delete
  23419. * the provisions above, a recipient may use your version of this file under
  23420. * the terms of any one of the CPL, the GPL or the LGPL.
  23421. ***** END LICENSE BLOCK *****/
  23422. package org.jruby;
  23423. import org.jruby.anno.JRubyMethod;
  23424. import org.jruby.anno.JRubyClass;
  23425. import org.jruby.runtime.ObjectAllocator;
  23426. import org.jruby.runtime.builtin.IRubyObject;
  23427. @JRubyClass(name="LocalJumpError",parent="StandardError")
  23428. public class RubyLocalJumpError extends RubyException {
  23429. private static ObjectAllocator LOCALJUMPERROR_ALLOCATOR = new ObjectAllocator() {
  23430. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  23431. return new RubyLocalJumpError(runtime, klass);
  23432. }
  23433. };
  23434. public static RubyClass createLocalJumpErrorClass(Ruby runtime, RubyClass standardErrorClass) {
  23435. RubyClass nameErrorClass = runtime.defineClass("LocalJumpError", standardErrorClass, LOCALJUMPERROR_ALLOCATOR);
  23436. nameErrorClass.defineAnnotatedMethods(RubyLocalJumpError.class);
  23437. return nameErrorClass;
  23438. }
  23439. private RubyLocalJumpError(Ruby runtime, RubyClass exceptionClass) {
  23440. super(runtime, exceptionClass);
  23441. }
  23442. public RubyLocalJumpError(Ruby runtime, RubyClass exceptionClass, String message, String reason, IRubyObject exitValue) {
  23443. super(runtime, exceptionClass, message);
  23444. fastSetInternalVariable("reason", runtime.newSymbol(reason));
  23445. fastSetInternalVariable("exit_value", exitValue);
  23446. }
  23447. @JRubyMethod(name = "reason")
  23448. public IRubyObject reason() {
  23449. return fastGetInternalVariable("reason");
  23450. }
  23451. @JRubyMethod(name = "exit_value")
  23452. public IRubyObject exit_value() {
  23453. return fastGetInternalVariable("exit_value");
  23454. }
  23455. }
  23456. /***** BEGIN LICENSE BLOCK *****
  23457. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  23458. *
  23459. * The contents of this file are subject to the Common Public
  23460. * License Version 1.0 (the "License"); you may not use this file
  23461. * except in compliance with the License. You may obtain a copy of
  23462. * the License at http://www.eclipse.org/legal/cpl-v10.html
  23463. *
  23464. * Software distributed under the License is distributed on an "AS
  23465. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  23466. * implied. See the License for the specific language governing
  23467. * rights and limitations under the License.
  23468. *
  23469. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  23470. * Copyright (C) 2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  23471. * Copyright (C) 2002-2007 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  23472. * Copyright (C) 2003 Thomas E Enebo <enebo@acm.org>
  23473. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  23474. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  23475. *
  23476. * Alternatively, the contents of this file may be used under the terms of
  23477. * either of the GNU General Public License Version 2 or later (the "GPL"),
  23478. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  23479. * in which case the provisions of the GPL or the LGPL are applicable instead
  23480. * of those above. If you wish to allow use of your version of this file only
  23481. * under the terms of either the GPL or the LGPL, and not to allow others to
  23482. * use your version of this file under the terms of the CPL, indicate your
  23483. * decision by deleting the provisions above and replace them with the notice
  23484. * and other provisions required by the GPL or the LGPL. If you do not delete
  23485. * the provisions above, a recipient may use your version of this file under
  23486. * the terms of any one of the CPL, the GPL or the LGPL.
  23487. ***** END LICENSE BLOCK *****/
  23488. package org.jruby;
  23489. import java.io.ByteArrayInputStream;
  23490. import java.io.ByteArrayOutputStream;
  23491. import java.io.EOFException;
  23492. import java.io.IOException;
  23493. import java.io.InputStream;
  23494. import java.io.OutputStream;
  23495. import org.jruby.anno.JRubyMethod;
  23496. import org.jruby.anno.JRubyModule;
  23497. import org.jruby.javasupport.util.RuntimeHelpers;
  23498. import org.jruby.runtime.Block;
  23499. import org.jruby.runtime.Constants;
  23500. import org.jruby.runtime.MethodIndex;
  23501. import org.jruby.runtime.ThreadContext;
  23502. import org.jruby.runtime.builtin.IRubyObject;
  23503. import org.jruby.runtime.marshal.MarshalStream;
  23504. import org.jruby.runtime.marshal.UnmarshalStream;
  23505. import org.jruby.util.ByteList;
  23506. import org.jruby.util.IOInputStream;
  23507. import org.jruby.util.IOOutputStream;
  23508. /**
  23509. * Marshal module
  23510. *
  23511. * @author Anders
  23512. */
  23513. @JRubyModule(name="Marshal")
  23514. public class RubyMarshal {
  23515. public static RubyModule createMarshalModule(Ruby runtime) {
  23516. RubyModule module = runtime.defineModule("Marshal");
  23517. runtime.setMarshal(module);
  23518. module.defineAnnotatedMethods(RubyMarshal.class);
  23519. module.defineConstant("MAJOR_VERSION", runtime.newFixnum(Constants.MARSHAL_MAJOR));
  23520. module.defineConstant("MINOR_VERSION", runtime.newFixnum(Constants.MARSHAL_MINOR));
  23521. return module;
  23522. }
  23523. @JRubyMethod(name = "dump", required = 1, optional = 2, frame = true, module = true)
  23524. public static IRubyObject dump(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  23525. if (args.length < 1) {
  23526. throw recv.getRuntime().newArgumentError("wrong # of arguments(at least 1)");
  23527. }
  23528. IRubyObject objectToDump = args[0];
  23529. IRubyObject io = null;
  23530. int depthLimit = -1;
  23531. if (args.length >= 2) {
  23532. if (args[1].respondsTo("write")) {
  23533. io = args[1];
  23534. } else if (args[1] instanceof RubyFixnum) {
  23535. depthLimit = (int) ((RubyFixnum) args[1]).getLongValue();
  23536. } else {
  23537. throw recv.getRuntime().newTypeError("Instance of IO needed");
  23538. }
  23539. if (args.length == 3) {
  23540. depthLimit = (int) ((RubyFixnum) args[2]).getLongValue();
  23541. }
  23542. }
  23543. try {
  23544. if (io != null) {
  23545. dumpToStream(objectToDump, outputStream(io), depthLimit);
  23546. return io;
  23547. }
  23548. ByteArrayOutputStream stringOutput = new ByteArrayOutputStream();
  23549. dumpToStream(objectToDump, stringOutput, depthLimit);
  23550. return RubyString.newString(recv.getRuntime(), new ByteList(stringOutput.toByteArray(),false));
  23551. } catch (IOException ioe) {
  23552. throw recv.getRuntime().newIOErrorFromException(ioe);
  23553. }
  23554. }
  23555. private static OutputStream outputStream(IRubyObject out) {
  23556. setBinmodeIfPossible(out);
  23557. if (out instanceof RubyIO) {
  23558. return ((RubyIO) out).getOutStream();
  23559. }
  23560. return new IOOutputStream(out);
  23561. }
  23562. private static void setBinmodeIfPossible(IRubyObject io) {
  23563. if (io.respondsTo("binmode")) {
  23564. io.callMethod(io.getRuntime().getCurrentContext(), "binmode");
  23565. }
  23566. }
  23567. @JRubyMethod(name = {"load", "restore"}, required = 1, optional = 1, frame = true, module = true)
  23568. public static IRubyObject load(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  23569. try {
  23570. if (args.length < 1) {
  23571. throw recv.getRuntime().newArgumentError("wrong number of arguments (0 for 1)");
  23572. }
  23573. if (args.length > 2) {
  23574. throw recv.getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 2)");
  23575. }
  23576. IRubyObject in = null;
  23577. IRubyObject proc = null;
  23578. switch (args.length) {
  23579. case 2:
  23580. proc = args[1];
  23581. case 1:
  23582. in = args[0];
  23583. }
  23584. InputStream rawInput;
  23585. if (in != null && in.respondsTo("read")) {
  23586. rawInput = inputStream(in);
  23587. } else if (in != null && in.respondsTo("to_str")) {
  23588. RubyString inString = (RubyString) RuntimeHelpers.invoke(context, in, "to_str");
  23589. ByteList bytes = inString.getByteList();
  23590. rawInput = new ByteArrayInputStream(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  23591. } else {
  23592. throw recv.getRuntime().newTypeError("instance of IO needed");
  23593. }
  23594. UnmarshalStream input = new UnmarshalStream(recv.getRuntime(), rawInput, proc);
  23595. return input.unmarshalObject();
  23596. } catch (EOFException ee) {
  23597. throw recv.getRuntime().newEOFError();
  23598. } catch (IOException ioe) {
  23599. throw recv.getRuntime().newIOErrorFromException(ioe);
  23600. }
  23601. }
  23602. private static InputStream inputStream(IRubyObject in) {
  23603. setBinmodeIfPossible(in);
  23604. if (in instanceof RubyIO) {
  23605. return ((RubyIO) in).getInStream();
  23606. }
  23607. return new IOInputStream(in);
  23608. }
  23609. private static void dumpToStream(IRubyObject object, OutputStream rawOutput, int depthLimit)
  23610. throws IOException
  23611. {
  23612. MarshalStream output = new MarshalStream(object.getRuntime(), rawOutput, depthLimit);
  23613. output.dumpObject(object);
  23614. }
  23615. }
  23616. /***** BEGIN LICENSE BLOCK *****
  23617. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  23618. *
  23619. * The contents of this file are subject to the Common Public
  23620. * License Version 1.0 (the "License"); you may not use this file
  23621. * except in compliance with the License. You may obtain a copy of
  23622. * the License at http://www.eclipse.org/legal/cpl-v10.html
  23623. *
  23624. * Software distributed under the License is distributed on an "AS
  23625. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  23626. * implied. See the License for the specific language governing
  23627. * rights and limitations under the License.
  23628. *
  23629. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  23630. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  23631. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  23632. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  23633. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  23634. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  23635. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  23636. *
  23637. * Alternatively, the contents of this file may be used under the terms of
  23638. * either of the GNU General Public License Version 2 or later (the "GPL"),
  23639. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  23640. * in which case the provisions of the GPL or the LGPL are applicable instead
  23641. * of those above. If you wish to allow use of your version of this file only
  23642. * under the terms of either the GPL or the LGPL, and not to allow others to
  23643. * use your version of this file under the terms of the CPL, indicate your
  23644. * decision by deleting the provisions above and replace them with the notice
  23645. * and other provisions required by the GPL or the LGPL. If you do not delete
  23646. * the provisions above, a recipient may use your version of this file under
  23647. * the terms of any one of the CPL, the GPL or the LGPL.
  23648. ***** END LICENSE BLOCK *****/
  23649. package org.jruby;
  23650. import java.util.Iterator;
  23651. import org.joni.NameEntry;
  23652. import org.joni.Regex;
  23653. import org.joni.Region;
  23654. import org.joni.exception.JOniException;
  23655. import org.jruby.anno.JRubyMethod;
  23656. import org.jruby.anno.JRubyClass;
  23657. import org.jruby.runtime.Arity;
  23658. import org.jruby.runtime.Block;
  23659. import org.jruby.runtime.ObjectAllocator;
  23660. import org.jruby.runtime.ThreadContext;
  23661. import org.jruby.runtime.builtin.IRubyObject;
  23662. import org.jruby.util.ByteList;
  23663. /**
  23664. * @author olabini
  23665. */
  23666. @JRubyClass(name="MatchData")
  23667. public class RubyMatchData extends RubyObject {
  23668. Region regs; // captures
  23669. int begin; // begin and end are used when not groups defined
  23670. int end;
  23671. RubyString str;
  23672. Regex pattern;
  23673. public static RubyClass createMatchDataClass(Ruby runtime) {
  23674. // TODO: Is NOT_ALLOCATABLE_ALLOCATOR ok here, since you can't actually instantiate MatchData directly?
  23675. RubyClass matchDataClass = runtime.defineClass("MatchData", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  23676. runtime.setMatchData(matchDataClass);
  23677. runtime.defineGlobalConstant("MatchingData", matchDataClass);
  23678. matchDataClass.kindOf = new RubyModule.KindOf() {
  23679. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  23680. return obj instanceof RubyMatchData;
  23681. }
  23682. };
  23683. matchDataClass.getMetaClass().undefineMethod("new");
  23684. matchDataClass.defineAnnotatedMethods(RubyMatchData.class);
  23685. return matchDataClass;
  23686. }
  23687. public RubyMatchData(Ruby runtime) {
  23688. super(runtime, runtime.getMatchData());
  23689. }
  23690. public final static int MATCH_BUSY = USER2_F;
  23691. // rb_match_busy
  23692. public final void use() {
  23693. flags |= MATCH_BUSY;
  23694. }
  23695. public final boolean used() {
  23696. return (flags & MATCH_BUSY) != 0;
  23697. }
  23698. private RubyArray match_array(Ruby runtime, int start) {
  23699. if (regs == null) {
  23700. if (start != 0) return runtime.newEmptyArray();
  23701. if (begin == -1) {
  23702. return getRuntime().newArray(runtime.getNil());
  23703. } else {
  23704. RubyString ss = str.makeShared(runtime, begin, end - begin);
  23705. if (isTaint()) ss.setTaint(true);
  23706. return getRuntime().newArray(ss);
  23707. }
  23708. } else {
  23709. RubyArray arr = getRuntime().newArray(regs.numRegs - start);
  23710. for (int i=start; i<regs.numRegs; i++) {
  23711. if (regs.beg[i] == -1) {
  23712. arr.append(getRuntime().getNil());
  23713. } else {
  23714. RubyString ss = str.makeShared(runtime, regs.beg[i], regs.end[i] - regs.beg[i]);
  23715. if (isTaint()) ss.setTaint(true);
  23716. arr.append(ss);
  23717. }
  23718. }
  23719. return arr;
  23720. }
  23721. }
  23722. public IRubyObject group(long n) {
  23723. return RubyRegexp.nth_match((int)n, this);
  23724. }
  23725. public IRubyObject group(int n) {
  23726. return RubyRegexp.nth_match(n, this);
  23727. }
  23728. @JRubyMethod(name = "inspect")
  23729. public IRubyObject inspect() {
  23730. if (pattern == null) return anyToString();
  23731. RubyString result = getRuntime().newString();
  23732. result.cat((byte)'#').cat((byte)'<');
  23733. result.append(getMetaClass().getRealClass().to_s());
  23734. NameEntry[]names = new NameEntry[regs == null ? 1 : regs.numRegs];
  23735. if (pattern.numberOfNames() > 0) {
  23736. for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
  23737. NameEntry e = i.next();
  23738. for (int num : e.getBackRefs()) names[num] = e;
  23739. }
  23740. }
  23741. for (int i=0; i<names.length; i++) {
  23742. result.cat((byte)' ');
  23743. if (i > 0) {
  23744. NameEntry e = names[i];
  23745. if (e != null) {
  23746. result.cat(e.name, e.nameP, e.nameEnd - e.nameP);
  23747. } else {
  23748. result.cat((byte)('0' + i));
  23749. }
  23750. result.cat((byte)':');
  23751. }
  23752. IRubyObject v = RubyRegexp.nth_match(i, this);
  23753. if (v.isNil()) {
  23754. result.cat("nil".getBytes());
  23755. } else {
  23756. result.append(v.inspect());
  23757. }
  23758. }
  23759. return result.cat((byte)'>');
  23760. }
  23761. /** match_to_a
  23762. *
  23763. */
  23764. @JRubyMethod(name = "to_a")
  23765. @Override
  23766. public RubyArray to_a() {
  23767. return match_array(getRuntime(), 0);
  23768. }
  23769. @JRubyMethod(name = "values_at", required = 1, rest = true)
  23770. public IRubyObject values_at(IRubyObject[] args) {
  23771. return to_a().values_at(args);
  23772. }
  23773. @JRubyMethod(name = "select", frame = true)
  23774. public IRubyObject select(ThreadContext context, Block block) {
  23775. return block.yield(context, to_a());
  23776. }
  23777. /** match_captures
  23778. *
  23779. */
  23780. @JRubyMethod(name = "captures")
  23781. public IRubyObject captures(ThreadContext context) {
  23782. return match_array(context.getRuntime(), 1);
  23783. }
  23784. private int nameToBackrefNumber(RubyString str) {
  23785. ByteList value = str.getByteList();
  23786. try {
  23787. return pattern.nameToBackrefNumber(value.bytes, value.begin, value.begin + value.realSize, regs);
  23788. } catch (JOniException je) {
  23789. throw getRuntime().newIndexError(je.getMessage());
  23790. }
  23791. }
  23792. final int backrefNumber(IRubyObject obj) {
  23793. if (obj instanceof RubySymbol) {
  23794. return nameToBackrefNumber((RubyString)((RubySymbol)obj).id2name());
  23795. } else if (obj instanceof RubyString) {
  23796. return nameToBackrefNumber((RubyString)obj);
  23797. } else {
  23798. return RubyNumeric.num2int(obj);
  23799. }
  23800. }
  23801. /**
  23802. * Variable arity version for compatibility. Not bound to a Ruby method.
  23803. * @deprecated Use the versions with zero, one, or two args.
  23804. */
  23805. public IRubyObject op_aref(IRubyObject[] args) {
  23806. switch (args.length) {
  23807. case 1:
  23808. return op_aref(args[0]);
  23809. case 2:
  23810. return op_aref(args[0], args[1]);
  23811. default:
  23812. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  23813. return null; // not reached
  23814. }
  23815. }
  23816. /** match_aref
  23817. *
  23818. */
  23819. @JRubyMethod(name = "[]")
  23820. public IRubyObject op_aref(IRubyObject idx) {
  23821. IRubyObject result = op_arefCommon(idx);
  23822. return result == null ? ((RubyArray)to_a()).aref(idx) : result;
  23823. }
  23824. /** match_aref
  23825. *
  23826. */
  23827. @JRubyMethod(name = "[]")
  23828. public IRubyObject op_aref(IRubyObject idx, IRubyObject rest) {
  23829. IRubyObject result;
  23830. return !rest.isNil() || (result = op_arefCommon(idx)) == null ? ((RubyArray)to_a()).aref(idx, rest) : result;
  23831. }
  23832. private IRubyObject op_arefCommon(IRubyObject idx) {
  23833. if (idx instanceof RubyFixnum) {
  23834. int num = RubyNumeric.fix2int(idx);
  23835. if (num >= 0) return RubyRegexp.nth_match(num, this);
  23836. } else {
  23837. if (idx instanceof RubySymbol) {
  23838. return RubyRegexp.nth_match(nameToBackrefNumber((RubyString)((RubySymbol)idx).id2name()), this);
  23839. } else if (idx instanceof RubyString) {
  23840. return RubyRegexp.nth_match(nameToBackrefNumber((RubyString)idx), this);
  23841. }
  23842. }
  23843. return null;
  23844. }
  23845. /** match_size
  23846. *
  23847. */
  23848. @JRubyMethod(name = {"size", "length"})
  23849. public IRubyObject size() {
  23850. return regs == null ? RubyFixnum.one(getRuntime()) : RubyFixnum.newFixnum(getRuntime(), regs.numRegs);
  23851. }
  23852. /** match_begin
  23853. *
  23854. */
  23855. @JRubyMethod(name = "begin", required = 1)
  23856. public IRubyObject begin(IRubyObject index) {
  23857. int i = backrefNumber(index);
  23858. if (regs == null) {
  23859. if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
  23860. if (begin < 0) return getRuntime().getNil();
  23861. return RubyFixnum.newFixnum(getRuntime(), begin);
  23862. } else {
  23863. if (i < 0 || regs.numRegs <= i) throw getRuntime().newIndexError("index " + i + " out of matches");
  23864. if (regs.beg[i] < 0) return getRuntime().getNil();
  23865. return RubyFixnum.newFixnum(getRuntime(), regs.beg[i]);
  23866. }
  23867. }
  23868. /** match_end
  23869. *
  23870. */
  23871. @JRubyMethod(name = "end", required = 1)
  23872. public IRubyObject end(IRubyObject index) {
  23873. int i = backrefNumber(index);
  23874. if (regs == null) {
  23875. if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
  23876. if (end < 0) return getRuntime().getNil();
  23877. return RubyFixnum.newFixnum(getRuntime(), end);
  23878. } else {
  23879. if (i < 0 || regs.numRegs <= i) throw getRuntime().newIndexError("index " + i + " out of matches");
  23880. if (regs.end[i] < 0) return getRuntime().getNil();
  23881. return RubyFixnum.newFixnum(getRuntime(), regs.end[i]);
  23882. }
  23883. }
  23884. /** match_offset
  23885. *
  23886. */
  23887. @JRubyMethod(name = "offset", required = 1)
  23888. public IRubyObject offset(IRubyObject index) {
  23889. int i = backrefNumber(index);
  23890. Ruby runtime = getRuntime();
  23891. if (regs == null) {
  23892. if (i != 0) throw getRuntime().newIndexError("index " + i + " out of matches");
  23893. if (begin < 0) return runtime.newArray(runtime.getNil(), runtime.getNil());
  23894. return runtime.newArray(RubyFixnum.newFixnum(runtime, begin),RubyFixnum.newFixnum(runtime, end));
  23895. } else {
  23896. if (i < 0 || regs.numRegs <= i) throw runtime.newIndexError("index " + i + " out of matches");
  23897. if (regs.beg[i] < 0) return runtime.newArray(runtime.getNil(), runtime.getNil());
  23898. return runtime.newArray(RubyFixnum.newFixnum(runtime, regs.beg[i]),RubyFixnum.newFixnum(runtime, regs.end[i]));
  23899. }
  23900. }
  23901. /** match_pre_match
  23902. *
  23903. */
  23904. @JRubyMethod(name = "pre_match")
  23905. public IRubyObject pre_match(ThreadContext context) {
  23906. RubyString ss;
  23907. if (regs == null) {
  23908. if(begin == -1) return context.getRuntime().getNil();
  23909. ss = str.makeShared(context.getRuntime(), 0, begin);
  23910. } else {
  23911. if(regs.beg[0] == -1) return context.getRuntime().getNil();
  23912. ss = str.makeShared(context.getRuntime(), 0, regs.beg[0]);
  23913. }
  23914. if (isTaint()) ss.setTaint(true);
  23915. return ss;
  23916. }
  23917. /** match_post_match
  23918. *
  23919. */
  23920. @JRubyMethod(name = "post_match")
  23921. public IRubyObject post_match(ThreadContext context) {
  23922. RubyString ss;
  23923. if (regs == null) {
  23924. if (begin == -1) return context.getRuntime().getNil();
  23925. ss = str.makeShared(context.getRuntime(), end, str.getByteList().length() - end);
  23926. } else {
  23927. if (regs.beg[0] == -1) return context.getRuntime().getNil();
  23928. ss = str.makeShared(context.getRuntime(), regs.end[0], str.getByteList().length() - regs.end[0]);
  23929. }
  23930. if(isTaint()) ss.setTaint(true);
  23931. return ss;
  23932. }
  23933. /** match_to_s
  23934. *
  23935. */
  23936. @JRubyMethod(name = "to_s")
  23937. public IRubyObject to_s() {
  23938. IRubyObject ss = RubyRegexp.last_match(this);
  23939. if (ss.isNil()) ss = RubyString.newEmptyString(getRuntime());
  23940. if (isTaint()) ss.setTaint(true);
  23941. return ss;
  23942. }
  23943. /** match_string
  23944. *
  23945. */
  23946. @JRubyMethod(name = "string")
  23947. public IRubyObject string() {
  23948. return str; //str is frozen
  23949. }
  23950. @JRubyMethod(name = "initialize_copy", required = 1)
  23951. public IRubyObject initialize_copy(IRubyObject original) {
  23952. if (this == original) return this;
  23953. if (!(getMetaClass() == original.getMetaClass())){ // MRI also does a pointer comparison here
  23954. throw getRuntime().newTypeError("wrong argument class");
  23955. }
  23956. RubyMatchData origMatchData = (RubyMatchData)original;
  23957. str = origMatchData.str;
  23958. regs = origMatchData.regs;
  23959. return this;
  23960. }
  23961. }
  23962. /***** BEGIN LICENSE BLOCK *****
  23963. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  23964. *
  23965. * The contents of this file are subject to the Common Public
  23966. * License Version 1.0 (the "License"); you may not use this file
  23967. * except in compliance with the License. You may obtain a copy of
  23968. * the License at http://www.eclipse.org/legal/cpl-v10.html
  23969. *
  23970. * Software distributed under the License is distributed on an "AS
  23971. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  23972. * implied. See the License for the specific language governing
  23973. * rights and limitations under the License.
  23974. *
  23975. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  23976. * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  23977. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  23978. * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  23979. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  23980. *
  23981. * Alternatively, the contents of this file may be used under the terms of
  23982. * either of the GNU General Public License Version 2 or later (the "GPL"),
  23983. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  23984. * in which case the provisions of the GPL or the LGPL are applicable instead
  23985. * of those above. If you wish to allow use of your version of this file only
  23986. * under the terms of either the GPL or the LGPL, and not to allow others to
  23987. * use your version of this file under the terms of the CPL, indicate your
  23988. * decision by deleting the provisions above and replace them with the notice
  23989. * and other provisions required by the GPL or the LGPL. If you do not delete
  23990. * the provisions above, a recipient may use your version of this file under
  23991. * the terms of any one of the CPL, the GPL or the LGPL.
  23992. ***** END LICENSE BLOCK *****/
  23993. package org.jruby;
  23994. import org.jruby.anno.JRubyMethod;
  23995. import org.jruby.anno.JRubyModule;
  23996. import org.jruby.runtime.Visibility;
  23997. import org.jruby.runtime.builtin.IRubyObject;
  23998. @JRubyModule(name="Math")
  23999. public class RubyMath {
  24000. /** Create the Math module and add it to the Ruby runtime.
  24001. *
  24002. */
  24003. public static RubyModule createMathModule(Ruby runtime) {
  24004. RubyModule result = runtime.defineModule("Math");
  24005. runtime.setMath(result);
  24006. result.defineConstant("E", RubyFloat.newFloat(runtime, Math.E));
  24007. result.defineConstant("PI", RubyFloat.newFloat(runtime, Math.PI));
  24008. result.defineAnnotatedMethods(RubyMath.class);
  24009. return result;
  24010. }
  24011. private static void domainCheck(IRubyObject recv, double value, String msg) {
  24012. if (Double.isNaN(value)) {
  24013. throw recv.getRuntime().newErrnoEDOMError(msg);
  24014. }
  24015. }
  24016. private static double chebylevSerie(double x, double coef[]) {
  24017. double b0, b1, b2, twox;
  24018. int i;
  24019. b1 = 0.0;
  24020. b0 = 0.0;
  24021. b2 = 0.0;
  24022. twox = 2.0 * x;
  24023. for (i = coef.length-1; i >= 0; i--) {
  24024. b2 = b1;
  24025. b1 = b0;
  24026. b0 = twox * b1 - b2 + coef[i];
  24027. }
  24028. return 0.5*(b0 - b2);
  24029. }
  24030. private static double sign(double x, double y) {
  24031. double abs = ((x < 0) ? -x : x);
  24032. return (y < 0.0) ? -abs : abs;
  24033. }
  24034. @JRubyMethod(name = "atan2", required = 2, module = true, visibility = Visibility.PRIVATE)
  24035. public static RubyFloat atan2(IRubyObject recv, IRubyObject x, IRubyObject y) {
  24036. double valuea = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24037. double valueb = ((RubyFloat)RubyKernel.new_float(recv,y)).getDoubleValue();
  24038. return RubyFloat.newFloat(recv.getRuntime(), Math.atan2(valuea, valueb));
  24039. }
  24040. @JRubyMethod(name = "cos", required = 1, module = true, visibility = Visibility.PRIVATE)
  24041. public static RubyFloat cos(IRubyObject recv, IRubyObject x) {
  24042. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24043. return RubyFloat.newFloat(recv.getRuntime(),Math.cos(value));
  24044. }
  24045. @JRubyMethod(name = "sin", required = 1, module = true, visibility = Visibility.PRIVATE)
  24046. public static RubyFloat sin(IRubyObject recv, IRubyObject x) {
  24047. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24048. return RubyFloat.newFloat(recv.getRuntime(),Math.sin(value));
  24049. }
  24050. @JRubyMethod(name = "tan", required = 1, module = true, visibility = Visibility.PRIVATE)
  24051. public static RubyFloat tan(IRubyObject recv, IRubyObject x) {
  24052. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24053. return RubyFloat.newFloat(recv.getRuntime(),Math.tan(value));
  24054. }
  24055. @JRubyMethod(name = "asin", required = 1, module = true, visibility = Visibility.PRIVATE)
  24056. public static RubyFloat asin(IRubyObject recv, IRubyObject x) {
  24057. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24058. double result = Math.asin(value);
  24059. domainCheck(recv, result, "asin");
  24060. return RubyFloat.newFloat(recv.getRuntime(),result);
  24061. }
  24062. @JRubyMethod(name = "acos", required = 1, module = true, visibility = Visibility.PRIVATE)
  24063. public static RubyFloat acos(IRubyObject recv, IRubyObject x) {
  24064. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24065. double result = Math.acos(value);
  24066. domainCheck(recv, result, "acos");
  24067. return RubyFloat.newFloat(recv.getRuntime(), result);
  24068. }
  24069. @JRubyMethod(name = "atan", required = 1, module = true, visibility = Visibility.PRIVATE)
  24070. public static RubyFloat atan(IRubyObject recv, IRubyObject x) {
  24071. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24072. return RubyFloat.newFloat(recv.getRuntime(),Math.atan(value));
  24073. }
  24074. @JRubyMethod(name = "cosh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24075. public static RubyFloat cosh(IRubyObject recv, IRubyObject x) {
  24076. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24077. return RubyFloat.newFloat(recv.getRuntime(),(Math.exp(value) + Math.exp(-value)) / 2.0);
  24078. }
  24079. @JRubyMethod(name = "sinh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24080. public static RubyFloat sinh(IRubyObject recv, IRubyObject x) {
  24081. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24082. return RubyFloat.newFloat(recv.getRuntime(),(Math.exp(value) - Math.exp(-value)) / 2.0);
  24083. }
  24084. @JRubyMethod(name = "tanh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24085. public static RubyFloat tanh(IRubyObject recv, IRubyObject x) {
  24086. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24087. return RubyFloat.newFloat(recv.getRuntime(), Math.tanh(value));
  24088. }
  24089. @JRubyMethod(name = "acosh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24090. public static RubyFloat acosh(IRubyObject recv, IRubyObject x) {
  24091. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24092. double result;
  24093. if (Double.isNaN(value) || value < 1) {
  24094. result = Double.NaN;
  24095. } else if (value < 94906265.62) {
  24096. result = Math.log(value + Math.sqrt(value * value - 1.0));
  24097. } else{
  24098. result = 0.69314718055994530941723212145818 + Math.log(value);
  24099. }
  24100. domainCheck(recv, result, "acosh");
  24101. return RubyFloat.newFloat(recv.getRuntime(),result);
  24102. }
  24103. private static final double ASINH_COEF[] = {
  24104. -.12820039911738186343372127359268e+0,
  24105. -.58811761189951767565211757138362e-1,
  24106. .47274654322124815640725249756029e-2,
  24107. -.49383631626536172101360174790273e-3,
  24108. .58506207058557412287494835259321e-4,
  24109. -.74669983289313681354755069217188e-5,
  24110. .10011693583558199265966192015812e-5,
  24111. -.13903543858708333608616472258886e-6,
  24112. .19823169483172793547317360237148e-7,
  24113. -.28847468417848843612747272800317e-8,
  24114. .42672965467159937953457514995907e-9,
  24115. -.63976084654366357868752632309681e-10,
  24116. .96991686089064704147878293131179e-11,
  24117. -.14844276972043770830246658365696e-11,
  24118. .22903737939027447988040184378983e-12,
  24119. -.35588395132732645159978942651310e-13,
  24120. .55639694080056789953374539088554e-14,
  24121. -.87462509599624678045666593520162e-15,
  24122. .13815248844526692155868802298129e-15,
  24123. -.21916688282900363984955142264149e-16,
  24124. .34904658524827565638313923706880e-17
  24125. };
  24126. @JRubyMethod(name = "asinh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24127. public static RubyFloat asinh(IRubyObject recv, IRubyObject x) {
  24128. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24129. double y = Math.abs(value);
  24130. double result;
  24131. if (Double.isNaN(value)) {
  24132. result = Double.NaN;
  24133. } else if (y <= 1.05367e-08) {
  24134. result = value;
  24135. } else if (y <= 1.0) {
  24136. result = value * (1.0 + chebylevSerie(2.0 * value * value - 1.0, ASINH_COEF));
  24137. } else if (y < 94906265.62) {
  24138. result = Math.log(value + Math.sqrt(value * value + 1.0));
  24139. } else {
  24140. result = 0.69314718055994530941723212145818 + Math.log(y);
  24141. if (value < 0) result *= -1;
  24142. }
  24143. return RubyFloat.newFloat(recv.getRuntime(),result);
  24144. }
  24145. private static final double ATANH_COEF[] = {
  24146. .9439510239319549230842892218633e-1,
  24147. .4919843705578615947200034576668e-1,
  24148. .2102593522455432763479327331752e-2,
  24149. .1073554449776116584640731045276e-3,
  24150. .5978267249293031478642787517872e-5,
  24151. .3505062030889134845966834886200e-6,
  24152. .2126374343765340350896219314431e-7,
  24153. .1321694535715527192129801723055e-8,
  24154. .8365875501178070364623604052959e-10,
  24155. .5370503749311002163881434587772e-11,
  24156. .3486659470157107922971245784290e-12,
  24157. .2284549509603433015524024119722e-13,
  24158. .1508407105944793044874229067558e-14,
  24159. .1002418816804109126136995722837e-15,
  24160. .6698674738165069539715526882986e-17,
  24161. .4497954546494931083083327624533e-18
  24162. };
  24163. @JRubyMethod(name = "atanh", required = 1, module = true, visibility = Visibility.PRIVATE)
  24164. public static RubyFloat atanh(IRubyObject recv, IRubyObject x) {
  24165. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24166. double y = Math.abs(value);
  24167. double result;
  24168. if (Double.isNaN(value)) {
  24169. result = Double.NaN;
  24170. } else if (y < 1.82501e-08) {
  24171. result = value;
  24172. } else if (y <= 0.5) {
  24173. result = value * (1.0 + chebylevSerie(8.0 * value * value - 1.0, ATANH_COEF));
  24174. } else if (y < 1.0) {
  24175. result = 0.5 * Math.log((1.0 + value) / (1.0 - value));
  24176. } else if (y == 1.0) {
  24177. result = value * Double.POSITIVE_INFINITY;
  24178. } else {
  24179. result = Double.NaN;
  24180. }
  24181. domainCheck(recv, result, "atanh");
  24182. return RubyFloat.newFloat(recv.getRuntime(),result);
  24183. }
  24184. @JRubyMethod(name = "exp", required = 1, module = true, visibility = Visibility.PRIVATE)
  24185. public static RubyFloat exp(IRubyObject recv, IRubyObject exponent) {
  24186. double value = ((RubyFloat)RubyKernel.new_float(recv,exponent)).getDoubleValue();
  24187. return RubyFloat.newFloat(recv.getRuntime(),Math.exp(value));
  24188. }
  24189. /** Returns the natural logarithm of x.
  24190. *
  24191. */
  24192. @JRubyMethod(name = "log", required = 1, module = true, visibility = Visibility.PRIVATE)
  24193. public static RubyFloat log(IRubyObject recv, IRubyObject x) {
  24194. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24195. double result = Math.log(value);
  24196. domainCheck(recv, result, "log");
  24197. return RubyFloat.newFloat(recv.getRuntime(),result);
  24198. }
  24199. /** Returns the base 10 logarithm of x.
  24200. *
  24201. */
  24202. @JRubyMethod(name = "log10", required = 1, module = true, visibility = Visibility.PRIVATE)
  24203. public static RubyFloat log10(IRubyObject recv, IRubyObject x) {
  24204. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24205. double result = Math.log(value) / Math.log(10);
  24206. domainCheck(recv, result, "log10");
  24207. return RubyFloat.newFloat(recv.getRuntime(),result);
  24208. }
  24209. @JRubyMethod(name = "sqrt", required = 1, module = true, visibility = Visibility.PRIVATE)
  24210. public static RubyFloat sqrt(IRubyObject recv, IRubyObject x) {
  24211. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24212. double result;
  24213. if (value < 0) {
  24214. result = Double.NaN;
  24215. } else{
  24216. result = Math.sqrt(value);
  24217. }
  24218. domainCheck(recv, result, "sqrt");
  24219. return RubyFloat.newFloat(recv.getRuntime(), result);
  24220. }
  24221. @JRubyMethod(name = "hypot", required = 2, module = true, visibility = Visibility.PRIVATE)
  24222. public static RubyFloat hypot(IRubyObject recv, IRubyObject x, IRubyObject y) {
  24223. double valuea = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24224. double valueb = ((RubyFloat)RubyKernel.new_float(recv,y)).getDoubleValue();
  24225. double result;
  24226. if (Math.abs(valuea) > Math.abs(valueb)) {
  24227. result = valueb / valuea;
  24228. result = Math.abs(valuea) * Math.sqrt(1 + result * result);
  24229. } else if (valueb != 0) {
  24230. result = valuea / valueb;
  24231. result = Math.abs(valueb) * Math.sqrt(1 + result * result);
  24232. } else {
  24233. result = 0;
  24234. }
  24235. return RubyFloat.newFloat(recv.getRuntime(),result);
  24236. }
  24237. /*
  24238. * x = mantissa * 2 ** exponent
  24239. *
  24240. * Where mantissa is in the range of [.5, 1)
  24241. *
  24242. */
  24243. @JRubyMethod(name = "frexp", required = 1, module = true, visibility = Visibility.PRIVATE)
  24244. public static RubyArray frexp(IRubyObject recv, IRubyObject other) {
  24245. double mantissa = ((RubyFloat)RubyKernel.new_float(recv,other)).getDoubleValue();
  24246. short sign = 1;
  24247. long exponent = 0;
  24248. if (mantissa != 0.0) {
  24249. // Make mantissa same sign so we only have one code path.
  24250. if (mantissa < 0) {
  24251. mantissa = -mantissa;
  24252. sign = -1;
  24253. }
  24254. // Increase value to hit lower range.
  24255. for (; mantissa < 0.5; mantissa *= 2.0, exponent -=1) { }
  24256. // Decrease value to hit upper range.
  24257. for (; mantissa >= 1.0; mantissa *= 0.5, exponent +=1) { }
  24258. }
  24259. return RubyArray.newArray(recv.getRuntime(),
  24260. RubyFloat.newFloat(recv.getRuntime(), sign * mantissa),
  24261. RubyNumeric.int2fix(recv.getRuntime(), exponent));
  24262. }
  24263. /*
  24264. * r = x * 2 ** y
  24265. */
  24266. @JRubyMethod(name = "ldexp", required = 2, module = true, visibility = Visibility.PRIVATE)
  24267. public static RubyFloat ldexp(IRubyObject recv, IRubyObject mantissa, IRubyObject exponent) {
  24268. double mantissaValue = ((RubyFloat)RubyKernel.new_float(recv, mantissa)).getDoubleValue();
  24269. return RubyFloat.newFloat(recv.getRuntime(),mantissaValue * Math.pow(2.0, RubyNumeric.num2int(exponent)));
  24270. }
  24271. private static final double ERFC_COEF[] = {
  24272. -.490461212346918080399845440334e-1,
  24273. -.142261205103713642378247418996e0,
  24274. .100355821875997955757546767129e-1,
  24275. -.576876469976748476508270255092e-3,
  24276. .274199312521960610344221607915e-4,
  24277. -.110431755073445076041353812959e-5,
  24278. .384887554203450369499613114982e-7,
  24279. -.118085825338754669696317518016e-8,
  24280. .323342158260509096464029309534e-10,
  24281. -.799101594700454875816073747086e-12,
  24282. .179907251139614556119672454866e-13,
  24283. -.371863548781869263823168282095e-15,
  24284. .710359900371425297116899083947e-17,
  24285. -.126124551191552258324954248533e-18
  24286. };
  24287. @JRubyMethod(name = "erf", required = 1, module = true, visibility = Visibility.PRIVATE)
  24288. public static RubyFloat erf(IRubyObject recv, IRubyObject x) {
  24289. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24290. double result;
  24291. double y = Math.abs(value);
  24292. if (y <= 1.49012e-08) {
  24293. result = 2 * value / 1.77245385090551602729816748334;
  24294. } else if (y <= 1) {
  24295. result = value * (1 + chebylevSerie(2 * value * value - 1, ERFC_COEF));
  24296. } else if (y < 6.013687357) {
  24297. result = sign(1 - erfc(recv, RubyFloat.newFloat(recv.getRuntime(),y)).getDoubleValue(), value);
  24298. } else {
  24299. result = sign(1, value);
  24300. }
  24301. return RubyFloat.newFloat(recv.getRuntime(),result);
  24302. }
  24303. private static final double ERFC2_COEF[] = {
  24304. -.69601346602309501127391508262e-1,
  24305. -.411013393626208934898221208467e-1,
  24306. .391449586668962688156114370524e-2,
  24307. -.490639565054897916128093545077e-3,
  24308. .715747900137703638076089414183e-4,
  24309. -.115307163413123283380823284791e-4,
  24310. .199467059020199763505231486771e-5,
  24311. -.364266647159922287393611843071e-6,
  24312. .694437261000501258993127721463e-7,
  24313. -.137122090210436601953460514121e-7,
  24314. .278838966100713713196386034809e-8,
  24315. -.581416472433116155186479105032e-9,
  24316. .123892049175275318118016881795e-9,
  24317. -.269063914530674343239042493789e-10,
  24318. .594261435084791098244470968384e-11,
  24319. -.133238673575811957928775442057e-11,
  24320. .30280468061771320171736972433e-12,
  24321. -.696664881494103258879586758895e-13,
  24322. .162085454105392296981289322763e-13,
  24323. -.380993446525049199987691305773e-14,
  24324. .904048781597883114936897101298e-15,
  24325. -.2164006195089607347809812047e-15,
  24326. .522210223399585498460798024417e-16,
  24327. -.126972960236455533637241552778e-16,
  24328. .310914550427619758383622741295e-17,
  24329. -.766376292032038552400956671481e-18,
  24330. .190081925136274520253692973329e-18
  24331. };
  24332. private static final double ERFCC_COEF[] = {
  24333. .715179310202924774503697709496e-1,
  24334. -.265324343376067157558893386681e-1,
  24335. .171115397792085588332699194606e-2,
  24336. -.163751663458517884163746404749e-3,
  24337. .198712935005520364995974806758e-4,
  24338. -.284371241276655508750175183152e-5,
  24339. .460616130896313036969379968464e-6,
  24340. -.822775302587920842057766536366e-7,
  24341. .159214187277090112989358340826e-7,
  24342. -.329507136225284321486631665072e-8,
  24343. .72234397604005554658126115389e-9,
  24344. -.166485581339872959344695966886e-9,
  24345. .401039258823766482077671768814e-10,
  24346. -.100481621442573113272170176283e-10,
  24347. .260827591330033380859341009439e-11,
  24348. -.699111056040402486557697812476e-12,
  24349. .192949233326170708624205749803e-12,
  24350. -.547013118875433106490125085271e-13,
  24351. .158966330976269744839084032762e-13,
  24352. -.47268939801975548392036958429e-14,
  24353. .14358733767849847867287399784e-14,
  24354. -.444951056181735839417250062829e-15,
  24355. .140481088476823343737305537466e-15,
  24356. -.451381838776421089625963281623e-16,
  24357. .147452154104513307787018713262e-16,
  24358. -.489262140694577615436841552532e-17,
  24359. .164761214141064673895301522827e-17,
  24360. -.562681717632940809299928521323e-18,
  24361. .194744338223207851429197867821e-18
  24362. };
  24363. @JRubyMethod(name = "erfc", required = 1, module = true, visibility = Visibility.PRIVATE)
  24364. public static RubyFloat erfc(IRubyObject recv, IRubyObject x) {
  24365. double value = ((RubyFloat)RubyKernel.new_float(recv,x)).getDoubleValue();
  24366. double result;
  24367. double y = Math.abs(value);
  24368. if (value <= -6.013687357) {
  24369. result = 2;
  24370. } else if (y < 1.49012e-08) {
  24371. result = 1 - 2 * value / 1.77245385090551602729816748334;
  24372. } else {
  24373. double ysq = y*y;
  24374. if (y < 1) {
  24375. result = 1 - value * (1 + chebylevSerie(2 * ysq - 1, ERFC_COEF));
  24376. } else if (y <= 4.0) {
  24377. result = Math.exp(-ysq)/y*(0.5+chebylevSerie((8.0 / ysq - 5.0) / 3.0, ERFC2_COEF));
  24378. if (value < 0) result = 2.0 - result;
  24379. if (value < 0) result = 2.0 - result;
  24380. if (value < 0) result = 2.0 - result;
  24381. } else {
  24382. result = Math.exp(-ysq) / y * (0.5 + chebylevSerie(8.0 / ysq - 1, ERFCC_COEF));
  24383. if (value < 0) result = 2.0 - result;
  24384. }
  24385. }
  24386. return RubyFloat.newFloat(recv.getRuntime(),result);
  24387. }
  24388. }
  24389. /***** BEGIN LICENSE BLOCK *****
  24390. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  24391. *
  24392. * The contents of this file are subject to the Common Public
  24393. * License Version 1.0 (the "License"); you may not use this file
  24394. * except in compliance with the License. You may obtain a copy of
  24395. * the License at http://www.eclipse.org/legal/cpl-v10.html
  24396. *
  24397. * Software distributed under the License is distributed on an "AS
  24398. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24399. * implied. See the License for the specific language governing
  24400. * rights and limitations under the License.
  24401. *
  24402. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  24403. * Copyright (C) 2001-2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  24404. * Copyright (C) 2002 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  24405. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  24406. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  24407. *
  24408. * Alternatively, the contents of this file may be used under the terms of
  24409. * either of the GNU General Public License Version 2 or later (the "GPL"),
  24410. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  24411. * in which case the provisions of the GPL or the LGPL are applicable instead
  24412. * of those above. If you wish to allow use of your version of this file only
  24413. * under the terms of either the GPL or the LGPL, and not to allow others to
  24414. * use your version of this file under the terms of the CPL, indicate your
  24415. * decision by deleting the provisions above and replace them with the notice
  24416. * and other provisions required by the GPL or the LGPL. If you do not delete
  24417. * the provisions above, a recipient may use your version of this file under
  24418. * the terms of any one of the CPL, the GPL or the LGPL.
  24419. ***** END LICENSE BLOCK *****/
  24420. package org.jruby;
  24421. import org.jruby.anno.JRubyMethod;
  24422. import org.jruby.anno.JRubyClass;
  24423. import org.jruby.exceptions.JumpException;
  24424. import org.jruby.internal.runtime.methods.DynamicMethod;
  24425. import org.jruby.runtime.Block;
  24426. import org.jruby.runtime.CallbackFactory;
  24427. import org.jruby.runtime.MethodBlock;
  24428. import org.jruby.runtime.ObjectAllocator;
  24429. import org.jruby.runtime.ThreadContext;
  24430. import org.jruby.runtime.builtin.IRubyObject;
  24431. /**
  24432. * The RubyMethod class represents a RubyMethod object.
  24433. *
  24434. * You can get such a method by calling the "method" method of an object.
  24435. *
  24436. * Note: This was renamed from Method.java
  24437. *
  24438. * @author jpetersen
  24439. * @since 0.2.3
  24440. */
  24441. @JRubyClass(name="Method")
  24442. public class RubyMethod extends RubyObject {
  24443. protected RubyModule implementationModule;
  24444. protected String methodName;
  24445. protected RubyModule originModule;
  24446. protected String originName;
  24447. protected DynamicMethod method;
  24448. protected IRubyObject receiver;
  24449. protected RubyMethod(Ruby runtime, RubyClass rubyClass) {
  24450. super(runtime, rubyClass);
  24451. }
  24452. /** Create the RubyMethod class and add it to the Ruby runtime.
  24453. *
  24454. */
  24455. public static RubyClass createMethodClass(Ruby runtime) {
  24456. // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
  24457. RubyClass methodClass = runtime.defineClass("Method", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  24458. runtime.setMethod(methodClass);
  24459. methodClass.defineAnnotatedMethods(RubyMethod.class);
  24460. return methodClass;
  24461. }
  24462. public static RubyMethod newMethod(
  24463. RubyModule implementationModule,
  24464. String methodName,
  24465. RubyModule originModule,
  24466. String originName,
  24467. DynamicMethod method,
  24468. IRubyObject receiver) {
  24469. Ruby runtime = implementationModule.getRuntime();
  24470. RubyMethod newMethod = new RubyMethod(runtime, runtime.getMethod());
  24471. newMethod.implementationModule = implementationModule;
  24472. newMethod.methodName = methodName;
  24473. newMethod.originModule = originModule;
  24474. newMethod.originName = originName;
  24475. newMethod.method = method.getRealMethod();
  24476. newMethod.receiver = receiver;
  24477. return newMethod;
  24478. }
  24479. /** Call the method.
  24480. *
  24481. */
  24482. @JRubyMethod(name = {"call", "[]"})
  24483. public IRubyObject call(ThreadContext context, Block block) {
  24484. return method.call(context, receiver, implementationModule, methodName, block);
  24485. }
  24486. @JRubyMethod(name = {"call", "[]"})
  24487. public IRubyObject call(ThreadContext context, IRubyObject arg, Block block) {
  24488. return method.call(context, receiver, implementationModule, methodName, arg, block);
  24489. }
  24490. @JRubyMethod(name = {"call", "[]"})
  24491. public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  24492. return method.call(context, receiver, implementationModule, methodName, arg0, arg1, block);
  24493. }
  24494. @JRubyMethod(name = {"call", "[]"})
  24495. public IRubyObject call(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  24496. return method.call(context, receiver, implementationModule, methodName, arg0, arg1, arg2, block);
  24497. }
  24498. @JRubyMethod(name = {"call", "[]"}, rest = true)
  24499. public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
  24500. return method.call(context, receiver, implementationModule, methodName, args, block);
  24501. }
  24502. /** Returns the number of arguments a method accepted.
  24503. *
  24504. * @return the number of arguments of a method.
  24505. */
  24506. @JRubyMethod(name = "arity")
  24507. public RubyFixnum arity() {
  24508. return getRuntime().newFixnum(method.getArity().getValue());
  24509. }
  24510. @JRubyMethod(name = "==", required = 1)
  24511. @Override
  24512. public RubyBoolean op_equal(ThreadContext context, IRubyObject other) {
  24513. if (!(other instanceof RubyMethod)) return context.getRuntime().getFalse();
  24514. RubyMethod otherMethod = (RubyMethod)other;
  24515. return context.getRuntime().newBoolean(implementationModule == otherMethod.implementationModule &&
  24516. originModule == otherMethod.originModule &&
  24517. receiver == otherMethod.receiver &&
  24518. method.getRealMethod() == otherMethod.method.getRealMethod());
  24519. }
  24520. @JRubyMethod(name = "clone")
  24521. @Override
  24522. public RubyMethod rbClone() {
  24523. return newMethod(implementationModule, methodName, originModule, originName, method, receiver);
  24524. }
  24525. /** Create a Proc object.
  24526. *
  24527. */
  24528. @JRubyMethod(name = "to_proc", frame = true)
  24529. public IRubyObject to_proc(ThreadContext context, Block unusedBlock) {
  24530. Ruby runtime = context.getRuntime();
  24531. CallbackFactory f = runtime.callbackFactory(RubyMethod.class);
  24532. Block block = MethodBlock.createMethodBlock(context, context.getCurrentScope(),
  24533. f.getBlockMethod("bmcall"), this, runtime.getTopSelf());
  24534. while (true) {
  24535. try {
  24536. // FIXME: We should not be regenerating this over and over
  24537. return mproc(context, block);
  24538. } catch (JumpException.BreakJump bj) {
  24539. return (IRubyObject) bj.getValue();
  24540. } catch (JumpException.ReturnJump rj) {
  24541. return (IRubyObject) rj.getValue();
  24542. } catch (JumpException.RetryJump rj) {
  24543. // Execute iterateMethod again.
  24544. }
  24545. }
  24546. }
  24547. /** Create a Proc object which is called like a ruby method.
  24548. *
  24549. * Used by the RubyMethod#to_proc method.
  24550. *
  24551. */
  24552. private IRubyObject mproc(ThreadContext context, Block block) {
  24553. try {
  24554. context.preMproc();
  24555. return RubyKernel.proc(context, context.getRuntime().getNil(), block);
  24556. } finally {
  24557. context.postMproc();
  24558. }
  24559. }
  24560. /** Delegate a block call to a bound method call.
  24561. *
  24562. * Used by the RubyMethod#to_proc method.
  24563. *
  24564. */
  24565. public static IRubyObject bmcall(IRubyObject blockArg, IRubyObject arg1,
  24566. IRubyObject self, Block unusedBlock) {
  24567. ThreadContext context = blockArg.getRuntime().getCurrentContext();
  24568. if (blockArg instanceof RubyArray) {
  24569. // ENEBO: Very wrong
  24570. return ((RubyMethod) arg1).call(context, ((RubyArray) blockArg).toJavaArray(), Block.NULL_BLOCK);
  24571. }
  24572. // ENEBO: Very wrong
  24573. return ((RubyMethod) arg1).call(context, new IRubyObject[] { blockArg }, Block.NULL_BLOCK);
  24574. }
  24575. @JRubyMethod(name = "unbind", frame = true)
  24576. public RubyUnboundMethod unbind(Block unusedBlock) {
  24577. RubyUnboundMethod unboundMethod =
  24578. RubyUnboundMethod.newUnboundMethod(implementationModule, methodName, originModule, originName, method);
  24579. unboundMethod.infectBy(this);
  24580. return unboundMethod;
  24581. }
  24582. @JRubyMethod(name = {"inspect", "to_s"})
  24583. @Override
  24584. public IRubyObject inspect() {
  24585. StringBuilder buf = new StringBuilder("#<");
  24586. char delimeter = '#';
  24587. buf.append(getMetaClass().getRealClass().getName()).append(": ");
  24588. if (implementationModule.isSingleton()) {
  24589. IRubyObject attached = ((MetaClass) implementationModule).getAttached();
  24590. if (receiver == null) {
  24591. buf.append(implementationModule.inspect().toString());
  24592. } else if (receiver == attached) {
  24593. buf.append(attached.inspect().toString());
  24594. delimeter = '.';
  24595. } else {
  24596. buf.append(receiver.inspect().toString());
  24597. buf.append('(').append(attached.inspect().toString()).append(')');
  24598. delimeter = '.';
  24599. }
  24600. } else {
  24601. buf.append(originModule.getName());
  24602. if (implementationModule != originModule) {
  24603. buf.append('(').append(implementationModule.getName()).append(')');
  24604. }
  24605. }
  24606. buf.append(delimeter).append(methodName).append('>');
  24607. RubyString str = getRuntime().newString(buf.toString());
  24608. str.setTaint(isTaint());
  24609. return str;
  24610. }
  24611. }
  24612. /*
  24613. **** BEGIN LICENSE BLOCK *****
  24614. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  24615. *
  24616. * The contents of this file are subject to the Common Public
  24617. * License Version 1.0 (the "License"); you may not use this file
  24618. * except in compliance with the License. You may obtain a copy of
  24619. * the License at http://www.eclipse.org/legal/cpl-v10.html
  24620. *
  24621. * Software distributed under the License is distributed on an "AS
  24622. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  24623. * implied. See the License for the specific language governing
  24624. * rights and limitations under the License.
  24625. *
  24626. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  24627. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  24628. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  24629. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  24630. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  24631. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  24632. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  24633. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  24634. * Copyright (C) 2006-2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
  24635. * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
  24636. *
  24637. * Alternatively, the contents of this file may be used under the terms of
  24638. * either of the GNU General Public License Version 2 or later (the "GPL"),
  24639. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  24640. * in which case the provisions of the GPL or the LGPL are applicable instead
  24641. * of those above. If you wish to allow use of your version of this file only
  24642. * under the terms of either the GPL or the LGPL, and not to allow others to
  24643. * use your version of this file under the terms of the CPL, indicate your
  24644. * decision by deleting the provisions above and replace them with the notice
  24645. * and other provisions required by the GPL or the LGPL. If you do not delete
  24646. * the provisions above, a recipient may use your version of this file under
  24647. * the terms of any one of the CPL, the GPL or the LGPL.
  24648. ***** END LICENSE BLOCK *****/
  24649. package org.jruby;
  24650. import java.lang.reflect.Field;
  24651. import java.lang.reflect.Method;
  24652. import java.lang.reflect.Modifier;
  24653. import java.util.ArrayList;
  24654. import java.util.Collections;
  24655. import java.util.HashMap;
  24656. import java.util.Iterator;
  24657. import java.util.HashSet;
  24658. import java.util.List;
  24659. import java.util.Map;
  24660. import java.util.Set;
  24661. import java.util.concurrent.ConcurrentHashMap;
  24662. import java.util.concurrent.locks.ReentrantLock;
  24663. import org.jruby.anno.JRubyMethod;
  24664. import org.jruby.anno.JRubyClass;
  24665. import org.jruby.anno.JRubyConstant;
  24666. import org.jruby.anno.JavaMethodDescriptor;
  24667. import org.jruby.anno.TypePopulator;
  24668. import org.jruby.common.IRubyWarnings.ID;
  24669. import org.jruby.compiler.ASTInspector;
  24670. import org.jruby.internal.runtime.methods.AliasMethod;
  24671. import org.jruby.internal.runtime.methods.DynamicMethod;
  24672. import org.jruby.internal.runtime.methods.FullFunctionCallbackMethod;
  24673. import org.jruby.internal.runtime.methods.SimpleCallbackMethod;
  24674. import org.jruby.internal.runtime.methods.MethodMethod;
  24675. import org.jruby.internal.runtime.methods.ProcMethod;
  24676. import org.jruby.internal.runtime.methods.UndefinedMethod;
  24677. import org.jruby.internal.runtime.methods.WrapperMethod;
  24678. import org.jruby.parser.StaticScope;
  24679. import org.jruby.runtime.Arity;
  24680. import org.jruby.runtime.Block;
  24681. import org.jruby.runtime.CacheMap;
  24682. import org.jruby.runtime.ObjectAllocator;
  24683. import org.jruby.runtime.ThreadContext;
  24684. import org.jruby.runtime.Visibility;
  24685. import static org.jruby.runtime.Visibility.*;
  24686. import static org.jruby.anno.FrameField.*;
  24687. import org.jruby.runtime.builtin.IRubyObject;
  24688. import org.jruby.runtime.builtin.Variable;
  24689. import org.jruby.runtime.callback.Callback;
  24690. import org.jruby.runtime.component.VariableEntry;
  24691. import org.jruby.runtime.marshal.MarshalStream;
  24692. import org.jruby.runtime.marshal.UnmarshalStream;
  24693. import org.jruby.util.ClassProvider;
  24694. import org.jruby.util.IdUtil;
  24695. import org.jruby.exceptions.RaiseException;
  24696. import org.jruby.internal.runtime.methods.JavaMethod;
  24697. import org.jruby.javasupport.util.RuntimeHelpers;
  24698. import org.jruby.runtime.ClassIndex;
  24699. import org.jruby.runtime.MethodFactory;
  24700. import org.jruby.runtime.MethodIndex;
  24701. /**
  24702. *
  24703. * @author jpetersen
  24704. */
  24705. @JRubyClass(name="Module")
  24706. public class RubyModule extends RubyObject {
  24707. private static final boolean DEBUG = false;
  24708. public static RubyClass createModuleClass(Ruby runtime, RubyClass moduleClass) {
  24709. moduleClass.index = ClassIndex.MODULE;
  24710. moduleClass.kindOf = new RubyModule.KindOf() {
  24711. @Override
  24712. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  24713. return obj instanceof RubyModule;
  24714. }
  24715. };
  24716. moduleClass.defineAnnotatedMethods(RubyModule.class);
  24717. moduleClass.defineAnnotatedMethods(ModuleKernelMethods.class);
  24718. return moduleClass;
  24719. }
  24720. public static class ModuleKernelMethods {
  24721. @JRubyMethod
  24722. public static IRubyObject autoload(IRubyObject recv, IRubyObject arg0, IRubyObject arg1) {
  24723. return RubyKernel.autoload(recv, arg0, arg1);
  24724. }
  24725. @JRubyMethod(name = "autoload?")
  24726. public static IRubyObject autoload_p(ThreadContext context, IRubyObject recv, IRubyObject arg0) {
  24727. return RubyKernel.autoload_p(context, recv, arg0);
  24728. }
  24729. }
  24730. static ObjectAllocator MODULE_ALLOCATOR = new ObjectAllocator() {
  24731. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  24732. return new RubyModule(runtime, klass);
  24733. }
  24734. };
  24735. @Override
  24736. public int getNativeTypeIndex() {
  24737. return ClassIndex.MODULE;
  24738. }
  24739. @Override
  24740. public boolean isModule() {
  24741. return true;
  24742. }
  24743. @Override
  24744. public boolean isClass() {
  24745. return false;
  24746. }
  24747. public boolean isSingleton() {
  24748. return false;
  24749. }
  24750. // superClass may be null.
  24751. protected RubyClass superClass;
  24752. public int index;
  24753. public static class KindOf {
  24754. public static final KindOf DEFAULT_KIND_OF = new KindOf();
  24755. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  24756. return obj.getMetaClass().hasModuleInHierarchy(type);
  24757. }
  24758. }
  24759. public boolean isInstance(IRubyObject object) {
  24760. return kindOf.isKindOf(object, this);
  24761. }
  24762. public KindOf kindOf = KindOf.DEFAULT_KIND_OF;
  24763. public final int id;
  24764. // Containing class...The parent of Object is null. Object should always be last in chain.
  24765. public RubyModule parent;
  24766. // ClassId is the name of the class/module sans where it is located.
  24767. // If it is null, then it an anonymous class.
  24768. protected String classId;
  24769. // CONSTANT TABLE
  24770. // Lock used for variableTable/constantTable writes. The RubyObject variableTable
  24771. // write methods are overridden here to use this lock rather than Java
  24772. // synchronization for faster concurrent writes for modules/classes.
  24773. protected final ReentrantLock variableWriteLock = new ReentrantLock();
  24774. protected transient volatile ConstantTableEntry[] constantTable =
  24775. new ConstantTableEntry[CONSTANT_TABLE_DEFAULT_CAPACITY];
  24776. protected transient int constantTableSize;
  24777. protected transient int constantTableThreshold =
  24778. (int)(CONSTANT_TABLE_DEFAULT_CAPACITY * CONSTANT_TABLE_LOAD_FACTOR);
  24779. private final Map<String, DynamicMethod> methods = new ConcurrentHashMap<String, DynamicMethod>(12, 0.75f, 1);
  24780. // ClassProviders return Java class/module (in #defineOrGetClassUnder and
  24781. // #defineOrGetModuleUnder) when class/module is opened using colon syntax.
  24782. private transient List<ClassProvider> classProviders;
  24783. /** separate path for MetaClass construction
  24784. *
  24785. */
  24786. protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
  24787. super(runtime, metaClass, objectSpace);
  24788. id = runtime.allocModuleId();
  24789. // if (parent == null) parent = runtime.getObject();
  24790. setFlag(USER7_F, !isClass());
  24791. }
  24792. /** used by MODULE_ALLOCATOR and RubyClass constructors
  24793. *
  24794. */
  24795. protected RubyModule(Ruby runtime, RubyClass metaClass) {
  24796. this(runtime, metaClass, runtime.isObjectSpaceEnabled());
  24797. }
  24798. /** standard path for Module construction
  24799. *
  24800. */
  24801. protected RubyModule(Ruby runtime) {
  24802. this(runtime, runtime.getModule());
  24803. }
  24804. public boolean needsImplementer() {
  24805. return getFlag(USER7_F);
  24806. }
  24807. /** rb_module_new
  24808. *
  24809. */
  24810. public static RubyModule newModule(Ruby runtime) {
  24811. return new RubyModule(runtime);
  24812. }
  24813. /** rb_module_new/rb_define_module_id/rb_name_class/rb_set_class_path
  24814. *
  24815. */
  24816. public static RubyModule newModule(Ruby runtime, String name, RubyModule parent, boolean setParent) {
  24817. RubyModule module = newModule(runtime);
  24818. module.setBaseName(name);
  24819. if (setParent) module.setParent(parent);
  24820. parent.setConstant(name, module);
  24821. return module;
  24822. }
  24823. // synchronized method per JRUBY-1173 (unsafe Double-Checked Locking)
  24824. // FIXME: synchronization is still wrong in CP code
  24825. public synchronized void addClassProvider(ClassProvider provider) {
  24826. if (classProviders == null) {
  24827. List<ClassProvider> cp = Collections.synchronizedList(new ArrayList<ClassProvider>());
  24828. cp.add(provider);
  24829. classProviders = cp;
  24830. } else {
  24831. synchronized(classProviders) {
  24832. if (!classProviders.contains(provider)) {
  24833. classProviders.add(provider);
  24834. }
  24835. }
  24836. }
  24837. }
  24838. public void removeClassProvider(ClassProvider provider) {
  24839. if (classProviders != null) {
  24840. classProviders.remove(provider);
  24841. }
  24842. }
  24843. private RubyClass searchProvidersForClass(String name, RubyClass superClazz) {
  24844. if (classProviders != null) {
  24845. synchronized(classProviders) {
  24846. RubyClass clazz;
  24847. for (ClassProvider classProvider: classProviders) {
  24848. if ((clazz = classProvider.defineClassUnder(this, name, superClazz)) != null) {
  24849. return clazz;
  24850. }
  24851. }
  24852. }
  24853. }
  24854. return null;
  24855. }
  24856. private RubyModule searchProvidersForModule(String name) {
  24857. if (classProviders != null) {
  24858. synchronized(classProviders) {
  24859. RubyModule module;
  24860. for (ClassProvider classProvider: classProviders) {
  24861. if ((module = classProvider.defineModuleUnder(this, name)) != null) {
  24862. return module;
  24863. }
  24864. }
  24865. }
  24866. }
  24867. return null;
  24868. }
  24869. /** Getter for property superClass.
  24870. * @return Value of property superClass.
  24871. */
  24872. public RubyClass getSuperClass() {
  24873. return superClass;
  24874. }
  24875. protected void setSuperClass(RubyClass superClass) {
  24876. this.superClass = superClass;
  24877. }
  24878. public RubyModule getParent() {
  24879. return parent;
  24880. }
  24881. public void setParent(RubyModule parent) {
  24882. this.parent = parent;
  24883. }
  24884. public Map<String, DynamicMethod> getMethods() {
  24885. return methods;
  24886. }
  24887. // note that addMethod now does its own put, so any change made to
  24888. // functionality here should be made there as well
  24889. private void putMethod(String name, DynamicMethod method) {
  24890. getMethods().put(name, method);
  24891. }
  24892. /**
  24893. * Is this module one that in an included one (e.g. an IncludedModuleWrapper).
  24894. */
  24895. public boolean isIncluded() {
  24896. return false;
  24897. }
  24898. public RubyModule getNonIncludedClass() {
  24899. return this;
  24900. }
  24901. public String getBaseName() {
  24902. return classId;
  24903. }
  24904. public void setBaseName(String name) {
  24905. classId = name;
  24906. }
  24907. private volatile String bareName;
  24908. private volatile String fullName;
  24909. /**
  24910. * Generate a fully-qualified class name or a #-style name for anonymous and singleton classes.
  24911. *
  24912. * Ruby C equivalent = "classname"
  24913. *
  24914. * @return The generated class name
  24915. */
  24916. public String getName() {
  24917. if (fullName == null) {
  24918. fullName = calculateFullName();
  24919. }
  24920. return fullName;
  24921. }
  24922. private String calculateFullName() {
  24923. if (getBaseName() == null) {
  24924. if (bareName == null) {
  24925. if (isClass()) {
  24926. bareName = "#<" + "Class" + ":01x" + Integer.toHexString(System.identityHashCode(this)) + ">";
  24927. } else {
  24928. bareName = "#<" + "Module" + ":01x" + Integer.toHexString(System.identityHashCode(this)) + ">";
  24929. }
  24930. }
  24931. return bareName;
  24932. }
  24933. String result = getBaseName();
  24934. RubyClass objectClass = getRuntime().getObject();
  24935. for (RubyModule p = this.getParent(); p != null && p != objectClass; p = p.getParent()) {
  24936. String pName = p.getBaseName();
  24937. // This is needed when the enclosing class or module is a singleton.
  24938. // In that case, we generated a name such as null::Foo, which broke
  24939. // Marshalling, among others. The correct thing to do in this situation
  24940. // is to insert the generate the name of form #<Class:01xasdfasd> if
  24941. // it's a singleton module/class, which this code accomplishes.
  24942. if(pName == null) {
  24943. pName = p.getName();
  24944. }
  24945. result = pName + "::" + result;
  24946. }
  24947. return result;
  24948. }
  24949. /**
  24950. * Create a wrapper to use for including the specified module into this one.
  24951. *
  24952. * Ruby C equivalent = "include_class_new"
  24953. *
  24954. * @return The module wrapper
  24955. */
  24956. public IncludedModuleWrapper newIncludeClass(RubyClass superClazz) {
  24957. IncludedModuleWrapper includedModule = new IncludedModuleWrapper(getRuntime(), superClazz, this);
  24958. // include its parent (and in turn that module's parents)
  24959. if (getSuperClass() != null) {
  24960. includedModule.includeModule(getSuperClass());
  24961. }
  24962. return includedModule;
  24963. }
  24964. /**
  24965. * Finds a class that is within the current module (or class).
  24966. *
  24967. * @param name to be found in this module (or class)
  24968. * @return the class or null if no such class
  24969. */
  24970. public RubyClass getClass(String name) {
  24971. IRubyObject module;
  24972. if ((module = getConstantAt(name)) instanceof RubyClass) {
  24973. return (RubyClass)module;
  24974. }
  24975. return null;
  24976. }
  24977. public RubyClass fastGetClass(String internedName) {
  24978. IRubyObject module;
  24979. if ((module = fastGetConstantAt(internedName)) instanceof RubyClass) {
  24980. return (RubyClass)module;
  24981. }
  24982. return null;
  24983. }
  24984. /**
  24985. * Include a new module in this module or class.
  24986. *
  24987. * @param arg The module to include
  24988. */
  24989. public synchronized void includeModule(IRubyObject arg) {
  24990. assert arg != null;
  24991. testFrozen("module");
  24992. if (!isTaint()) {
  24993. getRuntime().secure(4);
  24994. }
  24995. if (!(arg instanceof RubyModule)) {
  24996. throw getRuntime().newTypeError("Wrong argument type " + arg.getMetaClass().getName() +
  24997. " (expected Module).");
  24998. }
  24999. RubyModule module = (RubyModule) arg;
  25000. // Make sure the module we include does not already exist
  25001. if (isSame(module)) {
  25002. return;
  25003. }
  25004. infectBy(module);
  25005. doIncludeModule(module);
  25006. }
  25007. public void defineMethod(String name, Callback method) {
  25008. Visibility visibility = name.equals("initialize") ?
  25009. PRIVATE : PUBLIC;
  25010. addMethod(name, new FullFunctionCallbackMethod(this, method, visibility));
  25011. }
  25012. public void defineAnnotatedMethod(Class clazz, String name) {
  25013. // FIXME: This is probably not very efficient, since it loads all methods for each call
  25014. boolean foundMethod = false;
  25015. for (Method method : clazz.getDeclaredMethods()) {
  25016. if (method.getName().equals(name) && defineAnnotatedMethod(method, MethodFactory.createFactory(getRuntime().getJRubyClassLoader()))) {
  25017. foundMethod = true;
  25018. }
  25019. }
  25020. if (!foundMethod) {
  25021. throw new RuntimeException("No JRubyMethod present for method " + name + "on class " + clazz.getName());
  25022. }
  25023. }
  25024. public void defineAnnotatedConstants(Class clazz) {
  25025. Field[] declaredFields = clazz.getDeclaredFields();
  25026. for (Field field : declaredFields) {
  25027. if(Modifier.isStatic(field.getModifiers())) {
  25028. defineAnnotatedConstant(field);
  25029. }
  25030. }
  25031. }
  25032. public boolean defineAnnotatedConstant(Field field) {
  25033. JRubyConstant jrubyConstant = field.getAnnotation(JRubyConstant.class);
  25034. if (jrubyConstant == null) return false;
  25035. String[] names = jrubyConstant.value();
  25036. if(names.length == 0) {
  25037. names = new String[]{field.getName()};
  25038. }
  25039. Class tp = field.getType();
  25040. IRubyObject realVal;
  25041. try {
  25042. if(tp == Integer.class || tp == Integer.TYPE || tp == Short.class || tp == Short.TYPE || tp == Byte.class || tp == Byte.TYPE) {
  25043. realVal = RubyNumeric.int2fix(getRuntime(), field.getInt(null));
  25044. } else if(tp == Boolean.class || tp == Boolean.TYPE) {
  25045. realVal = field.getBoolean(null) ? getRuntime().getTrue() : getRuntime().getFalse();
  25046. } else {
  25047. realVal = getRuntime().getNil();
  25048. }
  25049. } catch(Exception e) {
  25050. realVal = getRuntime().getNil();
  25051. }
  25052. for(String name : names) {
  25053. this.fastSetConstant(name, realVal);
  25054. }
  25055. return true;
  25056. }
  25057. public void defineAnnotatedMethods(Class clazz) {
  25058. defineAnnotatedMethodsIndividually(clazz);
  25059. }
  25060. public static class MethodClumper {
  25061. Map<String, List<JavaMethodDescriptor>> annotatedMethods = new HashMap<String, List<JavaMethodDescriptor>>();
  25062. Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods = new HashMap<String, List<JavaMethodDescriptor>>();
  25063. Map<String, List<JavaMethodDescriptor>> annotatedMethods1_8 = new HashMap<String, List<JavaMethodDescriptor>>();
  25064. Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods1_8 = new HashMap<String, List<JavaMethodDescriptor>>();
  25065. Map<String, List<JavaMethodDescriptor>> annotatedMethods1_9 = new HashMap<String, List<JavaMethodDescriptor>>();
  25066. Map<String, List<JavaMethodDescriptor>> staticAnnotatedMethods1_9 = new HashMap<String, List<JavaMethodDescriptor>>();
  25067. public void clump(Class cls) {
  25068. Method[] declaredMethods = cls.getDeclaredMethods();
  25069. for (Method method: declaredMethods) {
  25070. JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
  25071. if (anno == null) continue;
  25072. JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
  25073. String name = anno.name().length == 0 ? method.getName() : anno.name()[0];
  25074. List<JavaMethodDescriptor> methodDescs;
  25075. Map<String, List<JavaMethodDescriptor>> methodsHash = null;
  25076. if (desc.isStatic) {
  25077. if (anno.compat() == CompatVersion.RUBY1_8) {
  25078. methodsHash = staticAnnotatedMethods1_8;
  25079. } else if (anno.compat() == CompatVersion.RUBY1_9) {
  25080. methodsHash = staticAnnotatedMethods1_9;
  25081. } else {
  25082. methodsHash = staticAnnotatedMethods;
  25083. }
  25084. } else {
  25085. if (anno.compat() == CompatVersion.RUBY1_8) {
  25086. methodsHash = annotatedMethods1_8;
  25087. } else if (anno.compat() == CompatVersion.RUBY1_9) {
  25088. methodsHash = annotatedMethods1_9;
  25089. } else {
  25090. methodsHash = annotatedMethods;
  25091. }
  25092. }
  25093. methodDescs = methodsHash.get(name);
  25094. if (methodDescs == null) {
  25095. methodDescs = new ArrayList<JavaMethodDescriptor>();
  25096. methodsHash.put(name, methodDescs);
  25097. }
  25098. methodDescs.add(desc);
  25099. }
  25100. }
  25101. public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods() {
  25102. return annotatedMethods;
  25103. }
  25104. public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods1_8() {
  25105. return annotatedMethods1_8;
  25106. }
  25107. public Map<String, List<JavaMethodDescriptor>> getAnnotatedMethods1_9() {
  25108. return annotatedMethods1_9;
  25109. }
  25110. public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods() {
  25111. return staticAnnotatedMethods;
  25112. }
  25113. public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods1_8() {
  25114. return staticAnnotatedMethods1_8;
  25115. }
  25116. public Map<String, List<JavaMethodDescriptor>> getStaticAnnotatedMethods1_9() {
  25117. return staticAnnotatedMethods1_9;
  25118. }
  25119. }
  25120. public void defineAnnotatedMethodsIndividually(Class clazz) {
  25121. String x = clazz.getSimpleName();
  25122. TypePopulator populator = null;
  25123. if (RubyInstanceConfig.FULL_TRACE_ENABLED) {
  25124. // we need full traces, use default (slow) populator
  25125. if (DEBUG) System.out.println("trace mode, using default populator");
  25126. populator = TypePopulator.DEFAULT;
  25127. } else {
  25128. try {
  25129. String qualifiedName = "org.jruby.gen." + clazz.getCanonicalName().replace('.', '$');
  25130. if (DEBUG) System.out.println("looking for " + qualifiedName + "$Populator");
  25131. Class populatorClass = Class.forName(qualifiedName + "$Populator");
  25132. populator = (TypePopulator)populatorClass.newInstance();
  25133. } catch (Throwable t) {
  25134. if (DEBUG) System.out.println("Could not find it, using default populator");
  25135. populator = TypePopulator.DEFAULT;
  25136. }
  25137. }
  25138. populator.populate(this, clazz);
  25139. }
  25140. @Deprecated
  25141. private void defineAnnotatedMethodsIndexed(Class clazz) {
  25142. MethodFactory methodFactory = MethodFactory.createFactory(getRuntime().getJRubyClassLoader());
  25143. methodFactory.defineIndexedAnnotatedMethods(this, clazz, methodDefiningCallback);
  25144. }
  25145. private static MethodFactory.MethodDefiningCallback methodDefiningCallback = new MethodFactory.MethodDefiningCallback() {
  25146. public void define(RubyModule module, JavaMethodDescriptor desc, DynamicMethod dynamicMethod) {
  25147. JRubyMethod jrubyMethod = desc.anno;
  25148. if (jrubyMethod.frame()) {
  25149. for (String name : jrubyMethod.name()) {
  25150. ASTInspector.FRAME_AWARE_METHODS.add(name);
  25151. }
  25152. }
  25153. if(jrubyMethod.compat() == CompatVersion.BOTH ||
  25154. module.getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
  25155. RubyModule singletonClass;
  25156. if (jrubyMethod.meta()) {
  25157. singletonClass = module.getSingletonClass();
  25158. dynamicMethod.setImplementationClass(singletonClass);
  25159. String baseName;
  25160. if (jrubyMethod.name().length == 0) {
  25161. baseName = desc.name;
  25162. singletonClass.addMethod(baseName, dynamicMethod);
  25163. } else {
  25164. baseName = jrubyMethod.name()[0];
  25165. for (String name : jrubyMethod.name()) {
  25166. singletonClass.addMethod(name, dynamicMethod);
  25167. }
  25168. }
  25169. if (jrubyMethod.alias().length > 0) {
  25170. for (String alias : jrubyMethod.alias()) {
  25171. singletonClass.defineAlias(alias, baseName);
  25172. }
  25173. }
  25174. } else {
  25175. String baseName;
  25176. if (jrubyMethod.name().length == 0) {
  25177. baseName = desc.name;
  25178. module.addMethod(baseName, dynamicMethod);
  25179. } else {
  25180. baseName = jrubyMethod.name()[0];
  25181. for (String name : jrubyMethod.name()) {
  25182. module.addMethod(name, dynamicMethod);
  25183. }
  25184. }
  25185. if (jrubyMethod.alias().length > 0) {
  25186. for (String alias : jrubyMethod.alias()) {
  25187. module.defineAlias(alias, baseName);
  25188. }
  25189. }
  25190. if (jrubyMethod.module()) {
  25191. singletonClass = module.getSingletonClass();
  25192. // module/singleton methods are all defined public
  25193. DynamicMethod moduleMethod = dynamicMethod.dup();
  25194. moduleMethod.setVisibility(PUBLIC);
  25195. if (jrubyMethod.name().length == 0) {
  25196. baseName = desc.name;
  25197. singletonClass.addMethod(desc.name, moduleMethod);
  25198. } else {
  25199. baseName = jrubyMethod.name()[0];
  25200. for (String name : jrubyMethod.name()) {
  25201. singletonClass.addMethod(name, moduleMethod);
  25202. }
  25203. }
  25204. if (jrubyMethod.alias().length > 0) {
  25205. for (String alias : jrubyMethod.alias()) {
  25206. singletonClass.defineAlias(alias, baseName);
  25207. }
  25208. }
  25209. }
  25210. }
  25211. }
  25212. }
  25213. };
  25214. public boolean defineAnnotatedMethod(String name, List<JavaMethodDescriptor> methods, MethodFactory methodFactory) {
  25215. JavaMethodDescriptor desc = methods.get(0);
  25216. if (methods.size() == 1) {
  25217. return defineAnnotatedMethod(desc, methodFactory);
  25218. } else {
  25219. DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, methods);
  25220. methodDefiningCallback.define(this, desc, dynamicMethod);
  25221. return true;
  25222. }
  25223. }
  25224. public boolean defineAnnotatedMethod(Method method, MethodFactory methodFactory) {
  25225. JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
  25226. if (jrubyMethod == null) return false;
  25227. if(jrubyMethod.compat() == CompatVersion.BOTH ||
  25228. getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
  25229. JavaMethodDescriptor desc = new JavaMethodDescriptor(method);
  25230. DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc);
  25231. methodDefiningCallback.define(this, desc, dynamicMethod);
  25232. return true;
  25233. }
  25234. return false;
  25235. }
  25236. public boolean defineAnnotatedMethod(JavaMethodDescriptor desc, MethodFactory methodFactory) {
  25237. JRubyMethod jrubyMethod = desc.anno;
  25238. if (jrubyMethod == null) return false;
  25239. if(jrubyMethod.compat() == CompatVersion.BOTH ||
  25240. getRuntime().getInstanceConfig().getCompatVersion() == jrubyMethod.compat()) {
  25241. DynamicMethod dynamicMethod = methodFactory.getAnnotatedMethod(this, desc);
  25242. methodDefiningCallback.define(this, desc, dynamicMethod);
  25243. return true;
  25244. }
  25245. return false;
  25246. }
  25247. public void defineFastMethod(String name, Callback method) {
  25248. Visibility visibility = name.equals("initialize") ?
  25249. PRIVATE : PUBLIC;
  25250. addMethod(name, new SimpleCallbackMethod(this, method, visibility));
  25251. }
  25252. public void defineFastMethod(String name, Callback method, Visibility visibility) {
  25253. addMethod(name, new SimpleCallbackMethod(this, method, visibility));
  25254. }
  25255. public void definePrivateMethod(String name, Callback method) {
  25256. addMethod(name, new FullFunctionCallbackMethod(this, method, PRIVATE));
  25257. }
  25258. public void defineFastPrivateMethod(String name, Callback method) {
  25259. addMethod(name, new SimpleCallbackMethod(this, method, PRIVATE));
  25260. }
  25261. public void defineFastProtectedMethod(String name, Callback method) {
  25262. addMethod(name, new SimpleCallbackMethod(this, method, PROTECTED));
  25263. }
  25264. public void undefineMethod(String name) {
  25265. addMethod(name, UndefinedMethod.getInstance());
  25266. }
  25267. /** rb_undef
  25268. *
  25269. */
  25270. public void undef(ThreadContext context, String name) {
  25271. Ruby runtime = context.getRuntime();
  25272. if (this == runtime.getObject()) runtime.secure(4);
  25273. if (runtime.getSafeLevel() >= 4 && !isTaint()) {
  25274. throw new SecurityException("Insecure: can't undef");
  25275. }
  25276. testFrozen("module");
  25277. if (name.equals("__id__") || name.equals("__send__")) {
  25278. runtime.getWarnings().warn(ID.UNDEFINING_BAD, "undefining `"+ name +"' may cause serious problem");
  25279. }
  25280. DynamicMethod method = searchMethod(name);
  25281. if (method.isUndefined()) {
  25282. String s0 = " class";
  25283. RubyModule c = this;
  25284. if (c.isSingleton()) {
  25285. IRubyObject obj = ((MetaClass)c).getAttached();
  25286. if (obj != null && obj instanceof RubyModule) {
  25287. c = (RubyModule) obj;
  25288. s0 = "";
  25289. }
  25290. } else if (c.isModule()) {
  25291. s0 = " module";
  25292. }
  25293. throw runtime.newNameError("Undefined method " + name + " for" + s0 + " '" + c.getName() + "'", name);
  25294. }
  25295. addMethod(name, UndefinedMethod.getInstance());
  25296. if (isSingleton()) {
  25297. IRubyObject singleton = ((MetaClass)this).getAttached();
  25298. singleton.callMethod(context, "singleton_method_undefined", runtime.newSymbol(name));
  25299. } else {
  25300. callMethod(context, "method_undefined", runtime.newSymbol(name));
  25301. }
  25302. }
  25303. @JRubyMethod(name = "include?", required = 1)
  25304. public IRubyObject include_p(ThreadContext context, IRubyObject arg) {
  25305. if (!arg.isModule()) {
  25306. throw context.getRuntime().newTypeError(arg, context.getRuntime().getModule());
  25307. }
  25308. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  25309. if ((p instanceof IncludedModuleWrapper) && ((IncludedModuleWrapper) p).getNonIncludedClass() == arg) {
  25310. return context.getRuntime().getTrue();
  25311. }
  25312. }
  25313. return context.getRuntime().getFalse();
  25314. }
  25315. // TODO: Consider a better way of synchronizing
  25316. public void addMethod(String name, DynamicMethod method) {
  25317. Ruby runtime = getRuntime();
  25318. if (this == runtime.getObject()) runtime.secure(4);
  25319. if (runtime.getSafeLevel() >= 4 && !isTaint()) {
  25320. throw runtime.newSecurityError("Insecure: can't define method");
  25321. }
  25322. testFrozen("class/module");
  25323. // We can safely reference methods here instead of doing getMethods() since if we
  25324. // are adding we are not using a IncludedModuleWrapper.
  25325. synchronized(getMethods()) {
  25326. // If we add a method which already is cached in this class, then we should update the
  25327. // cachemap so it stays up to date.
  25328. DynamicMethod existingMethod = getMethods().put(name, method);
  25329. if (existingMethod != null) {
  25330. runtime.getCacheMap().remove(existingMethod);
  25331. }
  25332. }
  25333. }
  25334. public void removeMethod(ThreadContext context, String name) {
  25335. Ruby runtime = context.getRuntime();
  25336. if (this == runtime.getObject()) runtime.secure(4);
  25337. if (runtime.getSafeLevel() >= 4 && !isTaint()) {
  25338. throw runtime.newSecurityError("Insecure: can't remove method");
  25339. }
  25340. testFrozen("class/module");
  25341. // We can safely reference methods here instead of doing getMethods() since if we
  25342. // are adding we are not using a IncludedModuleWrapper.
  25343. synchronized(getMethods()) {
  25344. DynamicMethod method = (DynamicMethod) getMethods().remove(name);
  25345. if (method == null) {
  25346. throw runtime.newNameError("method '" + name + "' not defined in " + getName(), name);
  25347. }
  25348. runtime.getCacheMap().remove(method);
  25349. }
  25350. if (isSingleton()) {
  25351. IRubyObject singleton = ((MetaClass)this).getAttached();
  25352. singleton.callMethod(context, "singleton_method_removed", runtime.newSymbol(name));
  25353. } else {
  25354. callMethod(context, "method_removed", runtime.newSymbol(name));
  25355. }
  25356. }
  25357. /**
  25358. * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
  25359. *
  25360. * @param name The name of the method to search for
  25361. * @return The method, or UndefinedMethod if not found
  25362. */
  25363. public DynamicMethod searchMethod(String name) {
  25364. DynamicMethod method = getMethods().get(name);
  25365. if (method != null) return method;
  25366. return superClass == null ? UndefinedMethod.getInstance() : superClass.searchMethod(name);
  25367. }
  25368. /**
  25369. * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
  25370. *
  25371. * @param name The name of the method to search for
  25372. * @return The method, or UndefinedMethod if not found
  25373. */
  25374. public DynamicMethod retrieveMethod(String name) {
  25375. return getMethods().get(name);
  25376. }
  25377. /**
  25378. * Search through this module and supermodules for method definitions. Cache superclass definitions in this class.
  25379. *
  25380. * @param name The name of the method to search for
  25381. * @return The method, or UndefinedMethod if not found
  25382. */
  25383. public RubyModule findImplementer(RubyModule clazz) {
  25384. for (RubyModule searchModule = this; searchModule != null; searchModule = searchModule.getSuperClass()) {
  25385. if (searchModule.isSame(clazz)) {
  25386. return searchModule;
  25387. }
  25388. }
  25389. return null;
  25390. }
  25391. public void addModuleFunction(String name, DynamicMethod method) {
  25392. addMethod(name, method);
  25393. getSingletonClass().addMethod(name, method);
  25394. }
  25395. /** rb_define_module_function
  25396. *
  25397. */
  25398. public void defineModuleFunction(String name, Callback method) {
  25399. definePrivateMethod(name, method);
  25400. getSingletonClass().defineMethod(name, method);
  25401. }
  25402. /** rb_define_module_function
  25403. *
  25404. */
  25405. public void definePublicModuleFunction(String name, Callback method) {
  25406. defineMethod(name, method);
  25407. getSingletonClass().defineMethod(name, method);
  25408. }
  25409. /** rb_define_module_function
  25410. *
  25411. */
  25412. public void defineFastModuleFunction(String name, Callback method) {
  25413. defineFastPrivateMethod(name, method);
  25414. getSingletonClass().defineFastMethod(name, method);
  25415. }
  25416. /** rb_define_module_function
  25417. *
  25418. */
  25419. public void defineFastPublicModuleFunction(String name, Callback method) {
  25420. defineFastMethod(name, method);
  25421. getSingletonClass().defineFastMethod(name, method);
  25422. }
  25423. /** rb_alias
  25424. *
  25425. */
  25426. public synchronized void defineAlias(String name, String oldName) {
  25427. testFrozen("module");
  25428. if (oldName.equals(name)) {
  25429. return;
  25430. }
  25431. Ruby runtime = getRuntime();
  25432. if (this == runtime.getObject()) {
  25433. runtime.secure(4);
  25434. }
  25435. DynamicMethod method = searchMethod(oldName);
  25436. DynamicMethod oldMethod = searchMethod(name);
  25437. if (method.isUndefined()) {
  25438. if (isModule()) {
  25439. method = runtime.getObject().searchMethod(oldName);
  25440. }
  25441. if (method.isUndefined()) {
  25442. throw runtime.newNameError("undefined method `" + oldName + "' for " +
  25443. (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
  25444. }
  25445. }
  25446. CacheMap cacheMap = runtime.getCacheMap();
  25447. cacheMap.remove(method);
  25448. cacheMap.remove(oldMethod);
  25449. if (oldMethod != oldMethod.getRealMethod()) {
  25450. cacheMap.remove(oldMethod.getRealMethod());
  25451. }
  25452. putMethod(name, new AliasMethod(this, method, oldName));
  25453. }
  25454. public synchronized void defineAliases(List<String> aliases, String oldName) {
  25455. testFrozen("module");
  25456. Ruby runtime = getRuntime();
  25457. if (this == runtime.getObject()) {
  25458. runtime.secure(4);
  25459. }
  25460. DynamicMethod method = searchMethod(oldName);
  25461. if (method.isUndefined()) {
  25462. if (isModule()) {
  25463. method = runtime.getObject().searchMethod(oldName);
  25464. }
  25465. if (method.isUndefined()) {
  25466. throw runtime.newNameError("undefined method `" + oldName + "' for " +
  25467. (isModule() ? "module" : "class") + " `" + getName() + "'", oldName);
  25468. }
  25469. }
  25470. CacheMap cacheMap = runtime.getCacheMap();
  25471. cacheMap.remove(method);
  25472. for (String name: aliases) {
  25473. if (oldName.equals(name)) continue;
  25474. DynamicMethod oldMethod = searchMethod(name);
  25475. cacheMap.remove(oldMethod);
  25476. if (oldMethod != oldMethod.getRealMethod()) {
  25477. cacheMap.remove(oldMethod.getRealMethod());
  25478. }
  25479. putMethod(name, new AliasMethod(this, method, oldName));
  25480. }
  25481. }
  25482. /** this method should be used only by interpreter or compiler
  25483. *
  25484. */
  25485. public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz) {
  25486. // This method is intended only for defining new classes in Ruby code,
  25487. // so it uses the allocator of the specified superclass or default to
  25488. // the Object allocator. It should NOT be used to define classes that require a native allocator.
  25489. Ruby runtime = getRuntime();
  25490. IRubyObject classObj = getConstantAt(name);
  25491. RubyClass clazz;
  25492. if (classObj != null) {
  25493. if (!(classObj instanceof RubyClass)) throw runtime.newTypeError(name + " is not a class");
  25494. clazz = (RubyClass)classObj;
  25495. if (superClazz != null) {
  25496. RubyClass tmp = clazz.getSuperClass();
  25497. while (tmp != null && tmp.isIncluded()) tmp = tmp.getSuperClass(); // need to skip IncludedModuleWrappers
  25498. if (tmp != null) tmp = tmp.getRealClass();
  25499. if (tmp != superClazz) throw runtime.newTypeError("superclass mismatch for class " + name);
  25500. // superClazz = null;
  25501. }
  25502. if (runtime.getSafeLevel() >= 4) throw runtime.newTypeError("extending class prohibited");
  25503. } else if (classProviders != null && (clazz = searchProvidersForClass(name, superClazz)) != null) {
  25504. // reopen a java class
  25505. } else {
  25506. if (superClazz == null) superClazz = runtime.getObject();
  25507. clazz = RubyClass.newClass(runtime, superClazz, name, superClazz.getAllocator(), this, true);
  25508. }
  25509. return clazz;
  25510. }
  25511. /** this method should be used only by interpreter or compiler
  25512. *
  25513. */
  25514. public RubyModule defineOrGetModuleUnder(String name) {
  25515. // This method is intended only for defining new modules in Ruby code
  25516. Ruby runtime = getRuntime();
  25517. IRubyObject moduleObj = getConstantAt(name);
  25518. RubyModule module;
  25519. if (moduleObj != null) {
  25520. if (!moduleObj.isModule()) throw runtime.newTypeError(name + " is not a module");
  25521. if (runtime.getSafeLevel() >= 4) throw runtime.newSecurityError("extending module prohibited");
  25522. module = (RubyModule)moduleObj;
  25523. } else if (classProviders != null && (module = searchProvidersForModule(name)) != null) {
  25524. // reopen a java module
  25525. } else {
  25526. module = RubyModule.newModule(runtime, name, this, true);
  25527. }
  25528. return module;
  25529. }
  25530. /** rb_define_class_under
  25531. * this method should be used only as an API to define/open nested classes
  25532. */
  25533. public RubyClass defineClassUnder(String name, RubyClass superClass, ObjectAllocator allocator) {
  25534. return getRuntime().defineClassUnder(name, superClass, allocator, this);
  25535. }
  25536. /** rb_define_module_under
  25537. * this method should be used only as an API to define/open nested module
  25538. */
  25539. public RubyModule defineModuleUnder(String name) {
  25540. return getRuntime().defineModuleUnder(name, this);
  25541. }
  25542. // FIXME: create AttrReaderMethod, AttrWriterMethod, for faster attr access
  25543. private void addAccessor(ThreadContext context, String internedName, boolean readable, boolean writeable) {
  25544. assert internedName == internedName.intern() : internedName + " is not interned";
  25545. final Ruby runtime = context.getRuntime();
  25546. // Check the visibility of the previous frame, which will be the frame in which the class is being eval'ed
  25547. Visibility attributeScope = context.getCurrentVisibility();
  25548. if (attributeScope == PRIVATE) {
  25549. //FIXME warning
  25550. } else if (attributeScope == MODULE_FUNCTION) {
  25551. attributeScope = PRIVATE;
  25552. // FIXME warning
  25553. }
  25554. final String variableName = ("@" + internedName).intern();
  25555. if (readable) {
  25556. // FIXME: should visibility be set to current visibility?
  25557. addMethod(internedName, new JavaMethod(this, PUBLIC) {
  25558. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  25559. if (args.length != 0) Arity.raiseArgumentError(runtime, args.length, 0, 0);
  25560. IRubyObject variable = self.getInstanceVariables().fastGetInstanceVariable(variableName);
  25561. return variable == null ? runtime.getNil() : variable;
  25562. }
  25563. @Override
  25564. public Arity getArity() {
  25565. return Arity.noArguments();
  25566. }
  25567. });
  25568. callMethod(context, "method_added", runtime.fastNewSymbol(internedName));
  25569. }
  25570. if (writeable) {
  25571. internedName = (internedName + "=").intern();
  25572. // FIXME: should visibility be set to current visibility?
  25573. addMethod(internedName, new JavaMethod(this, PUBLIC) {
  25574. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  25575. // ENEBO: Can anyone get args to be anything but length 1?
  25576. if (args.length != 1) Arity.raiseArgumentError(runtime, args.length, 1, 1);
  25577. return self.getInstanceVariables().fastSetInstanceVariable(variableName, args[0]);
  25578. }
  25579. @Override
  25580. public Arity getArity() {
  25581. return Arity.singleArgument();
  25582. }
  25583. });
  25584. callMethod(context, "method_added", runtime.fastNewSymbol(internedName));
  25585. }
  25586. }
  25587. /** set_method_visibility
  25588. *
  25589. */
  25590. public void setMethodVisibility(IRubyObject[] methods, Visibility visibility) {
  25591. if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
  25592. throw getRuntime().newSecurityError("Insecure: can't change method visibility");
  25593. }
  25594. for (int i = 0; i < methods.length; i++) {
  25595. exportMethod(methods[i].asJavaString(), visibility);
  25596. }
  25597. }
  25598. /** rb_export_method
  25599. *
  25600. */
  25601. public void exportMethod(String name, Visibility visibility) {
  25602. if (this == getRuntime().getObject()) {
  25603. getRuntime().secure(4);
  25604. }
  25605. DynamicMethod method = searchMethod(name);
  25606. if (method.isUndefined()) {
  25607. throw getRuntime().newNameError("undefined method '" + name + "' for " +
  25608. (isModule() ? "module" : "class") + " '" + getName() + "'", name);
  25609. }
  25610. if (method.getVisibility() != visibility) {
  25611. if (this == method.getImplementationClass()) {
  25612. method.setVisibility(visibility);
  25613. } else {
  25614. // FIXME: Why was this using a FullFunctionCallbackMethod before that did callSuper?
  25615. addMethod(name, new WrapperMethod(this, method, visibility));
  25616. }
  25617. }
  25618. }
  25619. /**
  25620. * MRI: rb_method_boundp
  25621. *
  25622. */
  25623. public boolean isMethodBound(String name, boolean checkVisibility) {
  25624. DynamicMethod method = searchMethod(name);
  25625. if (!method.isUndefined()) {
  25626. return !(checkVisibility && method.getVisibility() == PRIVATE);
  25627. }
  25628. return false;
  25629. }
  25630. public IRubyObject newMethod(IRubyObject receiver, String name, boolean bound) {
  25631. DynamicMethod method = searchMethod(name);
  25632. if (method.isUndefined()) {
  25633. throw getRuntime().newNameError("undefined method `" + name +
  25634. "' for class `" + this.getName() + "'", name);
  25635. }
  25636. RubyModule implementationModule = method.getImplementationClass();
  25637. RubyModule originModule = this;
  25638. while (originModule != implementationModule && originModule.isSingleton()) {
  25639. originModule = ((MetaClass)originModule).getRealClass();
  25640. }
  25641. RubyMethod newMethod = null;
  25642. if (bound) {
  25643. newMethod = RubyMethod.newMethod(implementationModule, name, originModule, name, method, receiver);
  25644. } else {
  25645. newMethod = RubyUnboundMethod.newUnboundMethod(implementationModule, name, originModule, name, method);
  25646. }
  25647. newMethod.infectBy(this);
  25648. return newMethod;
  25649. }
  25650. @JRubyMethod(name = "define_method", frame = true, visibility = PRIVATE, reads = VISIBILITY)
  25651. public IRubyObject define_method(ThreadContext context, IRubyObject arg0, Block block) {
  25652. Ruby runtime = context.getRuntime();
  25653. String name = arg0.asJavaString().intern();
  25654. DynamicMethod newMethod = null;
  25655. Visibility visibility = context.getCurrentVisibility();
  25656. if (visibility == MODULE_FUNCTION) visibility = PRIVATE;
  25657. RubyProc proc = runtime.newProc(Block.Type.LAMBDA, block);
  25658. // a normal block passed to define_method changes to do arity checking; make it a lambda
  25659. proc.getBlock().type = Block.Type.LAMBDA;
  25660. newMethod = createProcMethod(name, visibility, proc);
  25661. RuntimeHelpers.addInstanceMethod(this, name, newMethod, context.getPreviousVisibility(), context, runtime);
  25662. return proc;
  25663. }
  25664. @JRubyMethod(name = "define_method", frame = true, visibility = PRIVATE, reads = VISIBILITY)
  25665. public IRubyObject define_method(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  25666. Ruby runtime = context.getRuntime();
  25667. IRubyObject body;
  25668. String name = arg0.asJavaString().intern();
  25669. DynamicMethod newMethod = null;
  25670. Visibility visibility = context.getCurrentVisibility();
  25671. if (visibility == MODULE_FUNCTION) visibility = PRIVATE;
  25672. if (runtime.getProc().isInstance(arg1)) {
  25673. // double-testing args.length here, but it avoids duplicating the proc-setup code in two places
  25674. RubyProc proc = (RubyProc)arg1;
  25675. body = proc;
  25676. newMethod = createProcMethod(name, visibility, proc);
  25677. } else if (runtime.getMethod().isInstance(arg1)) {
  25678. RubyMethod method = (RubyMethod)arg1;
  25679. body = method;
  25680. newMethod = new MethodMethod(this, method.unbind(null), visibility);
  25681. } else {
  25682. throw runtime.newTypeError("wrong argument type " + arg1.getType().getName() + " (expected Proc/Method)");
  25683. }
  25684. RuntimeHelpers.addInstanceMethod(this, name, newMethod, context.getPreviousVisibility(), context, runtime);
  25685. return body;
  25686. }
  25687. @Deprecated
  25688. public IRubyObject define_method(ThreadContext context, IRubyObject[] args, Block block) {
  25689. switch (args.length) {
  25690. case 1:
  25691. return define_method(context, args[0], block);
  25692. case 2:
  25693. return define_method(context, args[0], args[1], block);
  25694. default:
  25695. throw context.getRuntime().newArgumentError("wrong # of arguments(" + args.length + " for 2)");
  25696. }
  25697. }
  25698. private DynamicMethod createProcMethod(String name, Visibility visibility, RubyProc proc) {
  25699. Block block = proc.getBlock();
  25700. block.getBinding().getFrame().setKlazz(this);
  25701. block.getBinding().getFrame().setName(name);
  25702. StaticScope scope = block.getBody().getStaticScope();
  25703. // for zsupers in define_method (blech!) we tell the proc scope to act as the "argument" scope
  25704. scope.setArgumentScope(true);
  25705. Arity arity = block.arity();
  25706. // just using required is broken...but no more broken than before zsuper refactoring
  25707. scope.setRequiredArgs(arity.required());
  25708. if(!arity.isFixed()) {
  25709. scope.setRestArg(arity.required());
  25710. }
  25711. return new ProcMethod(this, proc, visibility);
  25712. }
  25713. @Deprecated
  25714. public IRubyObject executeUnder(ThreadContext context, Callback method, IRubyObject[] args, Block block) {
  25715. context.preExecuteUnder(this, block);
  25716. try {
  25717. return method.execute(this, args, block);
  25718. } finally {
  25719. context.postExecuteUnder();
  25720. }
  25721. }
  25722. @JRubyMethod(name = "name")
  25723. public RubyString name() {
  25724. return getRuntime().newString(getBaseName() == null ? "" : getName());
  25725. }
  25726. protected IRubyObject cloneMethods(RubyModule clone) {
  25727. RubyModule realType = this.getNonIncludedClass();
  25728. for (Map.Entry<String, DynamicMethod> entry : getMethods().entrySet()) {
  25729. DynamicMethod method = entry.getValue();
  25730. // Do not clone cached methods
  25731. // FIXME: MRI copies all methods here
  25732. if (method.getImplementationClass() == realType || method instanceof UndefinedMethod) {
  25733. // A cloned method now belongs to a new class. Set it.
  25734. // TODO: Make DynamicMethod immutable
  25735. DynamicMethod clonedMethod = method.dup();
  25736. clonedMethod.setImplementationClass(clone);
  25737. clone.putMethod(entry.getKey(), clonedMethod);
  25738. }
  25739. }
  25740. return clone;
  25741. }
  25742. /** rb_mod_init_copy
  25743. *
  25744. */
  25745. @JRubyMethod(name = "initialize_copy", required = 1)
  25746. @Override
  25747. public IRubyObject initialize_copy(IRubyObject original) {
  25748. super.initialize_copy(original);
  25749. RubyModule originalModule = (RubyModule)original;
  25750. if (!getMetaClass().isSingleton()) setMetaClass(originalModule.getSingletonClassClone());
  25751. setSuperClass(originalModule.getSuperClass());
  25752. if (originalModule.hasVariables()){
  25753. syncVariables(originalModule.getVariableList());
  25754. }
  25755. originalModule.cloneMethods(this);
  25756. return this;
  25757. }
  25758. /** rb_mod_included_modules
  25759. *
  25760. */
  25761. @JRubyMethod(name = "included_modules")
  25762. public RubyArray included_modules(ThreadContext context) {
  25763. RubyArray ary = context.getRuntime().newArray();
  25764. for (RubyModule p = getSuperClass(); p != null; p = p.getSuperClass()) {
  25765. if (p.isIncluded()) {
  25766. ary.append(p.getNonIncludedClass());
  25767. }
  25768. }
  25769. return ary;
  25770. }
  25771. /** rb_mod_ancestors
  25772. *
  25773. */
  25774. @JRubyMethod(name = "ancestors")
  25775. public RubyArray ancestors(ThreadContext context) {
  25776. return context.getRuntime().newArray(getAncestorList());
  25777. }
  25778. @Deprecated
  25779. public RubyArray ancestors() {
  25780. return getRuntime().newArray(getAncestorList());
  25781. }
  25782. public List<IRubyObject> getAncestorList() {
  25783. ArrayList<IRubyObject> list = new ArrayList<IRubyObject>();
  25784. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  25785. if(!p.isSingleton()) {
  25786. list.add(p.getNonIncludedClass());
  25787. }
  25788. }
  25789. return list;
  25790. }
  25791. public boolean hasModuleInHierarchy(RubyModule type) {
  25792. // XXX: This check previously used callMethod("==") to check for equality between classes
  25793. // when scanning the hierarchy. However the == check may be safe; we should only ever have
  25794. // one instance bound to a given type/constant. If it's found to be unsafe, examine ways
  25795. // to avoid the == call.
  25796. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  25797. if (p.getNonIncludedClass() == type) return true;
  25798. }
  25799. return false;
  25800. }
  25801. @Override
  25802. public int hashCode() {
  25803. return id;
  25804. }
  25805. @JRubyMethod(name = "hash")
  25806. @Override
  25807. public RubyFixnum hash() {
  25808. return getRuntime().newFixnum(id);
  25809. }
  25810. /** rb_mod_to_s
  25811. *
  25812. */
  25813. @JRubyMethod(name = "to_s")
  25814. @Override
  25815. public IRubyObject to_s() {
  25816. if(isSingleton()){
  25817. IRubyObject attached = ((MetaClass)this).getAttached();
  25818. StringBuilder buffer = new StringBuilder("#<Class:");
  25819. if (attached != null) { // FIXME: figure out why we get null sometimes
  25820. if(attached instanceof RubyClass || attached instanceof RubyModule){
  25821. buffer.append(attached.inspect());
  25822. }else{
  25823. buffer.append(attached.anyToString());
  25824. }
  25825. }
  25826. buffer.append(">");
  25827. return getRuntime().newString(buffer.toString());
  25828. }
  25829. return getRuntime().newString(getName());
  25830. }
  25831. /** rb_mod_eqq
  25832. *
  25833. */
  25834. @JRubyMethod(name = "===", required = 1)
  25835. @Override
  25836. public RubyBoolean op_eqq(ThreadContext context, IRubyObject obj) {
  25837. return context.getRuntime().newBoolean(isInstance(obj));
  25838. }
  25839. @JRubyMethod(name = "==", required = 1)
  25840. @Override
  25841. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  25842. return super.op_equal(context, other);
  25843. }
  25844. /** rb_mod_freeze
  25845. *
  25846. */
  25847. @JRubyMethod(name = "freeze")
  25848. @Override
  25849. public IRubyObject freeze(ThreadContext context) {
  25850. to_s();
  25851. return super.freeze(context);
  25852. }
  25853. /** rb_mod_le
  25854. *
  25855. */
  25856. @JRubyMethod(name = "<=", required = 1)
  25857. public IRubyObject op_le(IRubyObject obj) {
  25858. if (!(obj instanceof RubyModule)) {
  25859. throw getRuntime().newTypeError("compared with non class/module");
  25860. }
  25861. if (isKindOfModule((RubyModule) obj)) {
  25862. return getRuntime().getTrue();
  25863. } else if (((RubyModule) obj).isKindOfModule(this)) {
  25864. return getRuntime().getFalse();
  25865. }
  25866. return getRuntime().getNil();
  25867. }
  25868. /** rb_mod_lt
  25869. *
  25870. */
  25871. @JRubyMethod(name = "<", required = 1)
  25872. public IRubyObject op_lt(IRubyObject obj) {
  25873. return obj == this ? getRuntime().getFalse() : op_le(obj);
  25874. }
  25875. /** rb_mod_ge
  25876. *
  25877. */
  25878. @JRubyMethod(name = ">=", required = 1)
  25879. public IRubyObject op_ge(IRubyObject obj) {
  25880. if (!(obj instanceof RubyModule)) {
  25881. throw getRuntime().newTypeError("compared with non class/module");
  25882. }
  25883. return ((RubyModule) obj).op_le(this);
  25884. }
  25885. /** rb_mod_gt
  25886. *
  25887. */
  25888. @JRubyMethod(name = ">", required = 1)
  25889. public IRubyObject op_gt(IRubyObject obj) {
  25890. return this == obj ? getRuntime().getFalse() : op_ge(obj);
  25891. }
  25892. /** rb_mod_cmp
  25893. *
  25894. */
  25895. @JRubyMethod(name = "<=>", required = 1)
  25896. public IRubyObject op_cmp(IRubyObject obj) {
  25897. if (this == obj) return getRuntime().newFixnum(0);
  25898. if (!(obj instanceof RubyModule)) return getRuntime().getNil();
  25899. RubyModule module = (RubyModule) obj;
  25900. if (module.isKindOfModule(this)) {
  25901. return getRuntime().newFixnum(1);
  25902. } else if (this.isKindOfModule(module)) {
  25903. return getRuntime().newFixnum(-1);
  25904. }
  25905. return getRuntime().getNil();
  25906. }
  25907. public boolean isKindOfModule(RubyModule type) {
  25908. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  25909. if (p.isSame(type)) {
  25910. return true;
  25911. }
  25912. }
  25913. return false;
  25914. }
  25915. protected boolean isSame(RubyModule module) {
  25916. return this == module;
  25917. }
  25918. /** rb_mod_initialize
  25919. *
  25920. */
  25921. @JRubyMethod(name = "initialize", frame = true, visibility = PRIVATE)
  25922. public IRubyObject initialize(Block block) {
  25923. if (block.isGiven()) {
  25924. // class and module bodies default to public, so make the block's visibility public. JRUBY-1185.
  25925. block.getBinding().setVisibility(PUBLIC);
  25926. block.yield(getRuntime().getCurrentContext(), this, this, this, false);
  25927. }
  25928. return getRuntime().getNil();
  25929. }
  25930. public void addReadWriteAttribute(ThreadContext context, String name) {
  25931. addAccessor(context, name.intern(), true, true);
  25932. }
  25933. public void addReadAttribute(ThreadContext context, String name) {
  25934. addAccessor(context, name.intern(), true, false);
  25935. }
  25936. public void addWriteAttribute(ThreadContext context, String name) {
  25937. addAccessor(context, name.intern(), false, true);
  25938. }
  25939. /** rb_mod_attr
  25940. *
  25941. */
  25942. @JRubyMethod(name = "attr", required = 1, optional = 1, visibility = PRIVATE, reads = VISIBILITY)
  25943. public IRubyObject attr(ThreadContext context, IRubyObject[] args) {
  25944. boolean writeable = args.length > 1 ? args[1].isTrue() : false;
  25945. addAccessor(context, args[0].asJavaString().intern(), true, writeable);
  25946. return getRuntime().getNil();
  25947. }
  25948. /**
  25949. * @deprecated
  25950. */
  25951. public IRubyObject attr_reader(IRubyObject[] args) {
  25952. return attr_reader(getRuntime().getCurrentContext(), args);
  25953. }
  25954. /** rb_mod_attr_reader
  25955. *
  25956. */
  25957. @JRubyMethod(name = "attr_reader", rest = true, visibility = PRIVATE, reads = VISIBILITY)
  25958. public IRubyObject attr_reader(ThreadContext context, IRubyObject[] args) {
  25959. for (int i = 0; i < args.length; i++) {
  25960. addAccessor(context, args[i].asJavaString().intern(), true, false);
  25961. }
  25962. return context.getRuntime().getNil();
  25963. }
  25964. /** rb_mod_attr_writer
  25965. *
  25966. */
  25967. @JRubyMethod(name = "attr_writer", rest = true, visibility = PRIVATE, reads = VISIBILITY)
  25968. public IRubyObject attr_writer(ThreadContext context, IRubyObject[] args) {
  25969. for (int i = 0; i < args.length; i++) {
  25970. addAccessor(context, args[i].asJavaString().intern(), false, true);
  25971. }
  25972. return context.getRuntime().getNil();
  25973. }
  25974. /**
  25975. * @deprecated
  25976. */
  25977. public IRubyObject attr_accessor(IRubyObject[] args) {
  25978. return attr_accessor(getRuntime().getCurrentContext(), args);
  25979. }
  25980. /** rb_mod_attr_accessor
  25981. *
  25982. */
  25983. @JRubyMethod(name = "attr_accessor", rest = true, visibility = PRIVATE, reads = VISIBILITY)
  25984. public IRubyObject attr_accessor(ThreadContext context, IRubyObject[] args) {
  25985. for (int i = 0; i < args.length; i++) {
  25986. // This is almost always already interned, since it will be called with a symbol in most cases
  25987. // but when created from Java code, we might get an argument that needs to be interned.
  25988. // addAccessor has as a precondition that the string MUST be interned
  25989. addAccessor(context, args[i].asJavaString().intern(), true, true);
  25990. }
  25991. return context.getRuntime().getNil();
  25992. }
  25993. /**
  25994. * Get a list of all instance methods names of the provided visibility unless not is true, then
  25995. * get all methods which are not the provided
  25996. *
  25997. * @param args passed into one of the Ruby instance_method methods
  25998. * @param visibility to find matching instance methods against
  25999. * @param not if true only find methods not matching supplied visibility
  26000. * @return a RubyArray of instance method names
  26001. */
  26002. private RubyArray instance_methods(IRubyObject[] args, final Visibility visibility, boolean not) {
  26003. boolean includeSuper = args.length > 0 ? args[0].isTrue() : true;
  26004. RubyArray ary = getRuntime().newArray();
  26005. Set<String> seen = new HashSet<String>();
  26006. for (RubyModule type = this; type != null; type = type.getSuperClass()) {
  26007. RubyModule realType = type.getNonIncludedClass();
  26008. for (Iterator iter = type.getMethods().entrySet().iterator(); iter.hasNext();) {
  26009. Map.Entry entry = (Map.Entry) iter.next();
  26010. DynamicMethod method = (DynamicMethod) entry.getValue();
  26011. String methodName = (String) entry.getKey();
  26012. if (! seen.contains(methodName)) {
  26013. seen.add(methodName);
  26014. if (method.getImplementationClass() == realType &&
  26015. (!not && method.getVisibility() == visibility || (not && method.getVisibility() != visibility)) &&
  26016. ! method.isUndefined()) {
  26017. ary.append(getRuntime().newString(methodName));
  26018. }
  26019. }
  26020. }
  26021. if (!includeSuper) {
  26022. break;
  26023. }
  26024. }
  26025. return ary;
  26026. }
  26027. @JRubyMethod(name = "instance_methods", optional = 1)
  26028. public RubyArray instance_methods(IRubyObject[] args) {
  26029. return instance_methods(args, PRIVATE, true);
  26030. }
  26031. @JRubyMethod(name = "public_instance_methods", optional = 1)
  26032. public RubyArray public_instance_methods(IRubyObject[] args) {
  26033. return instance_methods(args, PUBLIC, false);
  26034. }
  26035. @JRubyMethod(name = "instance_method", required = 1)
  26036. public IRubyObject instance_method(IRubyObject symbol) {
  26037. return newMethod(null, symbol.asJavaString(), false);
  26038. }
  26039. /** rb_class_protected_instance_methods
  26040. *
  26041. */
  26042. @JRubyMethod(name = "protected_instance_methods", optional = 1)
  26043. public RubyArray protected_instance_methods(IRubyObject[] args) {
  26044. return instance_methods(args, PROTECTED, false);
  26045. }
  26046. /** rb_class_private_instance_methods
  26047. *
  26048. */
  26049. @JRubyMethod(name = "private_instance_methods", optional = 1)
  26050. public RubyArray private_instance_methods(IRubyObject[] args) {
  26051. return instance_methods(args, PRIVATE, false);
  26052. }
  26053. /** rb_mod_append_features
  26054. *
  26055. */
  26056. @JRubyMethod(name = "append_features", required = 1, visibility = PRIVATE)
  26057. public RubyModule append_features(IRubyObject module) {
  26058. if (!(module instanceof RubyModule)) {
  26059. // MRI error message says Class, even though Module is ok
  26060. throw getRuntime().newTypeError(module,getRuntime().getClassClass());
  26061. }
  26062. ((RubyModule) module).includeModule(this);
  26063. return this;
  26064. }
  26065. /** rb_mod_extend_object
  26066. *
  26067. */
  26068. @JRubyMethod(name = "extend_object", required = 1, visibility = PRIVATE)
  26069. public IRubyObject extend_object(IRubyObject obj) {
  26070. obj.getSingletonClass().includeModule(this);
  26071. return obj;
  26072. }
  26073. /** rb_mod_include
  26074. *
  26075. */
  26076. @JRubyMethod(name = "include", required = 1, rest = true, visibility = PRIVATE)
  26077. public RubyModule include(IRubyObject[] modules) {
  26078. ThreadContext context = getRuntime().getCurrentContext();
  26079. // MRI checks all types first:
  26080. for (int i = modules.length; --i >= 0; ) {
  26081. IRubyObject obj = modules[i];
  26082. if (!obj.isModule()) throw context.getRuntime().newTypeError(obj, context.getRuntime().getModule());
  26083. }
  26084. for (int i = modules.length - 1; i >= 0; i--) {
  26085. modules[i].callMethod(context, "append_features", this);
  26086. modules[i].callMethod(context, "included", this);
  26087. }
  26088. return this;
  26089. }
  26090. @JRubyMethod(name = "included", required = 1)
  26091. public IRubyObject included(ThreadContext context, IRubyObject other) {
  26092. return context.getRuntime().getNil();
  26093. }
  26094. @JRubyMethod(name = "extended", required = 1, frame = true)
  26095. public IRubyObject extended(ThreadContext context, IRubyObject other, Block block) {
  26096. return context.getRuntime().getNil();
  26097. }
  26098. private void setVisibility(ThreadContext context, IRubyObject[] args, Visibility visibility) {
  26099. if (context.getRuntime().getSafeLevel() >= 4 && !isTaint()) {
  26100. throw context.getRuntime().newSecurityError("Insecure: can't change method visibility");
  26101. }
  26102. if (args.length == 0) {
  26103. // Note: we change current frames visibility here because the methods which call
  26104. // this method are all "fast" (e.g. they do not created their own frame).
  26105. context.setCurrentVisibility(visibility);
  26106. } else {
  26107. setMethodVisibility(args, visibility);
  26108. }
  26109. }
  26110. /** rb_mod_public
  26111. *
  26112. */
  26113. @JRubyMethod(name = "public", rest = true, visibility = PRIVATE, writes = VISIBILITY)
  26114. public RubyModule rbPublic(ThreadContext context, IRubyObject[] args) {
  26115. setVisibility(context, args, PUBLIC);
  26116. return this;
  26117. }
  26118. /** rb_mod_protected
  26119. *
  26120. */
  26121. @JRubyMethod(name = "protected", rest = true, visibility = PRIVATE, writes = VISIBILITY)
  26122. public RubyModule rbProtected(ThreadContext context, IRubyObject[] args) {
  26123. setVisibility(context, args, PROTECTED);
  26124. return this;
  26125. }
  26126. /** rb_mod_private
  26127. *
  26128. */
  26129. @JRubyMethod(name = "private", rest = true, visibility = PRIVATE, writes = VISIBILITY)
  26130. public RubyModule rbPrivate(ThreadContext context, IRubyObject[] args) {
  26131. setVisibility(context, args, PRIVATE);
  26132. return this;
  26133. }
  26134. /** rb_mod_modfunc
  26135. *
  26136. */
  26137. @JRubyMethod(name = "module_function", rest = true, visibility = PRIVATE, writes = VISIBILITY)
  26138. public RubyModule module_function(ThreadContext context, IRubyObject[] args) {
  26139. if (context.getRuntime().getSafeLevel() >= 4 && !isTaint()) {
  26140. throw context.getRuntime().newSecurityError("Insecure: can't change method visibility");
  26141. }
  26142. if (args.length == 0) {
  26143. context.setCurrentVisibility(MODULE_FUNCTION);
  26144. } else {
  26145. setMethodVisibility(args, PRIVATE);
  26146. for (int i = 0; i < args.length; i++) {
  26147. String name = args[i].asJavaString().intern();
  26148. DynamicMethod method = searchMethod(name);
  26149. assert !method.isUndefined() : "undefined method '" + name + "'";
  26150. getSingletonClass().addMethod(name, new WrapperMethod(getSingletonClass(), method, PUBLIC));
  26151. callMethod(context, "singleton_method_added", context.getRuntime().fastNewSymbol(name));
  26152. }
  26153. }
  26154. return this;
  26155. }
  26156. @JRubyMethod(name = "method_added", required = 1, visibility = PRIVATE)
  26157. public IRubyObject method_added(ThreadContext context, IRubyObject nothing) {
  26158. return context.getRuntime().getNil();
  26159. }
  26160. @JRubyMethod(name = "method_removed", required = 1, visibility = PRIVATE)
  26161. public IRubyObject method_removed(ThreadContext context, IRubyObject nothing) {
  26162. return context.getRuntime().getNil();
  26163. }
  26164. @JRubyMethod(name = "method_undefined", required = 1, visibility = PRIVATE)
  26165. public IRubyObject method_undefined(ThreadContext context, IRubyObject nothing) {
  26166. return context.getRuntime().getNil();
  26167. }
  26168. @JRubyMethod(name = "method_defined?", required = 1)
  26169. public RubyBoolean method_defined_p(ThreadContext context, IRubyObject symbol) {
  26170. return isMethodBound(symbol.asJavaString(), true) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  26171. }
  26172. @JRubyMethod(name = "public_method_defined?", required = 1)
  26173. public IRubyObject public_method_defined(ThreadContext context, IRubyObject symbol) {
  26174. DynamicMethod method = searchMethod(symbol.asJavaString());
  26175. return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PUBLIC);
  26176. }
  26177. @JRubyMethod(name = "protected_method_defined?", required = 1)
  26178. public IRubyObject protected_method_defined(ThreadContext context, IRubyObject symbol) {
  26179. DynamicMethod method = searchMethod(symbol.asJavaString());
  26180. return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PROTECTED);
  26181. }
  26182. @JRubyMethod(name = "private_method_defined?", required = 1)
  26183. public IRubyObject private_method_defined(ThreadContext context, IRubyObject symbol) {
  26184. DynamicMethod method = searchMethod(symbol.asJavaString());
  26185. return context.getRuntime().newBoolean(!method.isUndefined() && method.getVisibility() == PRIVATE);
  26186. }
  26187. @JRubyMethod(name = "public_class_method", rest = true)
  26188. public RubyModule public_class_method(IRubyObject[] args) {
  26189. getMetaClass().setMethodVisibility(args, PUBLIC);
  26190. return this;
  26191. }
  26192. @JRubyMethod(name = "private_class_method", rest = true)
  26193. public RubyModule private_class_method(IRubyObject[] args) {
  26194. getMetaClass().setMethodVisibility(args, PRIVATE);
  26195. return this;
  26196. }
  26197. @JRubyMethod(name = "alias_method", required = 2, visibility = PRIVATE)
  26198. public RubyModule alias_method(ThreadContext context, IRubyObject newId, IRubyObject oldId) {
  26199. String newName = newId.asJavaString();
  26200. defineAlias(newName, oldId.asJavaString());
  26201. RubySymbol newSym = newId instanceof RubySymbol ? (RubySymbol)newId :
  26202. context.getRuntime().newSymbol(newName);
  26203. if (isSingleton()) {
  26204. ((MetaClass)this).getAttached().callMethod(context, "singleton_method_added", newSym);
  26205. } else {
  26206. callMethod(context, "method_added", newSym);
  26207. }
  26208. return this;
  26209. }
  26210. @JRubyMethod(name = "undef_method", required = 1, rest = true, visibility = PRIVATE)
  26211. public RubyModule undef_method(ThreadContext context, IRubyObject[] args) {
  26212. for (int i=0; i<args.length; i++) {
  26213. undef(context, args[i].asJavaString());
  26214. }
  26215. return this;
  26216. }
  26217. @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
  26218. public IRubyObject module_eval(ThreadContext context, Block block) {
  26219. return specificEval(context, this, block);
  26220. }
  26221. @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
  26222. public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, Block block) {
  26223. return specificEval(context, this, arg0, block);
  26224. }
  26225. @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
  26226. public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  26227. return specificEval(context, this, arg0, arg1, block);
  26228. }
  26229. @JRubyMethod(name = {"module_eval", "class_eval"}, frame = true)
  26230. public IRubyObject module_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  26231. return specificEval(context, this, arg0, arg1, arg2, block);
  26232. }
  26233. @Deprecated
  26234. public IRubyObject module_eval(ThreadContext context, IRubyObject[] args, Block block) {
  26235. return specificEval(context, this, args, block);
  26236. }
  26237. @JRubyMethod(name = "remove_method", required = 1, rest = true, visibility = PRIVATE)
  26238. public RubyModule remove_method(ThreadContext context, IRubyObject[] args) {
  26239. for(int i=0;i<args.length;i++) {
  26240. removeMethod(context, args[i].asJavaString());
  26241. }
  26242. return this;
  26243. }
  26244. public static void marshalTo(RubyModule module, MarshalStream output) throws java.io.IOException {
  26245. output.registerLinkTarget(module);
  26246. output.writeString(MarshalStream.getPathFromClass(module));
  26247. }
  26248. public static RubyModule unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  26249. String name = RubyString.byteListToString(input.unmarshalString());
  26250. RubyModule result = UnmarshalStream.getModuleFromPath(input.getRuntime(), name);
  26251. input.registerLinkTarget(result);
  26252. return result;
  26253. }
  26254. /* Module class methods */
  26255. /**
  26256. * Return an array of nested modules or classes.
  26257. */
  26258. @JRubyMethod(name = "nesting", frame = true, meta = true)
  26259. public static RubyArray nesting(ThreadContext context, IRubyObject recv, Block block) {
  26260. Ruby runtime = context.getRuntime();
  26261. RubyModule object = runtime.getObject();
  26262. StaticScope scope = context.getCurrentScope().getStaticScope();
  26263. RubyArray result = runtime.newArray();
  26264. for (StaticScope current = scope; current.getModule() != object; current = current.getPreviousCRefScope()) {
  26265. result.append(current.getModule());
  26266. }
  26267. return result;
  26268. }
  26269. private void doIncludeModule(RubyModule includedModule) {
  26270. boolean skip = false;
  26271. RubyModule currentModule = this;
  26272. while (includedModule != null) {
  26273. if (getNonIncludedClass() == includedModule.getNonIncludedClass()) {
  26274. throw getRuntime().newArgumentError("cyclic include detected");
  26275. }
  26276. boolean superclassSeen = false;
  26277. // scan class hierarchy for module
  26278. for (RubyModule superClass = this.getSuperClass(); superClass != null; superClass = superClass.getSuperClass()) {
  26279. if (superClass instanceof IncludedModuleWrapper) {
  26280. if (superClass.getNonIncludedClass() == includedModule.getNonIncludedClass()) {
  26281. if (!superclassSeen) {
  26282. currentModule = superClass;
  26283. }
  26284. skip = true;
  26285. break;
  26286. }
  26287. } else {
  26288. superclassSeen = true;
  26289. }
  26290. }
  26291. if (!skip) {
  26292. // blow away caches for any methods that are redefined by module
  26293. getRuntime().getCacheMap().moduleIncluded(currentModule, includedModule);
  26294. // In the current logic, if we get here we know that module is not an
  26295. // IncludedModuleWrapper, so there's no need to fish out the delegate. But just
  26296. // in case the logic should change later, let's do it anyway:
  26297. currentModule.setSuperClass(new IncludedModuleWrapper(getRuntime(), currentModule.getSuperClass(),
  26298. includedModule.getNonIncludedClass()));
  26299. currentModule = currentModule.getSuperClass();
  26300. }
  26301. includedModule = includedModule.getSuperClass();
  26302. skip = false;
  26303. }
  26304. }
  26305. //
  26306. ////////////////// CLASS VARIABLE RUBY METHODS ////////////////
  26307. //
  26308. @JRubyMethod(name = "class_variable_defined?", required = 1)
  26309. public IRubyObject class_variable_defined_p(ThreadContext context, IRubyObject var) {
  26310. String internedName = validateClassVariable(var.asJavaString().intern());
  26311. RubyModule module = this;
  26312. do {
  26313. if (module.fastHasClassVariable(internedName)) {
  26314. return context.getRuntime().getTrue();
  26315. }
  26316. } while ((module = module.getSuperClass()) != null);
  26317. return context.getRuntime().getFalse();
  26318. }
  26319. /** rb_mod_cvar_get
  26320. *
  26321. */
  26322. @JRubyMethod(name = "class_variable_get", required = 1, visibility = PRIVATE)
  26323. public IRubyObject class_variable_get(IRubyObject var) {
  26324. return fastGetClassVar(validateClassVariable(var.asJavaString()).intern());
  26325. }
  26326. /** rb_mod_cvar_set
  26327. *
  26328. */
  26329. @JRubyMethod(name = "class_variable_set", required = 2, visibility = PRIVATE)
  26330. public IRubyObject class_variable_set(IRubyObject var, IRubyObject value) {
  26331. return fastSetClassVar(validateClassVariable(var.asJavaString()).intern(), value);
  26332. }
  26333. /** rb_mod_remove_cvar
  26334. *
  26335. */
  26336. @JRubyMethod(name = "remove_class_variable", required = 1, visibility = PRIVATE)
  26337. public IRubyObject remove_class_variable(ThreadContext context, IRubyObject name) {
  26338. String javaName = validateClassVariable(name.asJavaString());
  26339. IRubyObject value;
  26340. if ((value = deleteClassVariable(javaName)) != null) {
  26341. return value;
  26342. }
  26343. if (fastIsClassVarDefined(javaName)) {
  26344. throw cannotRemoveError(javaName);
  26345. }
  26346. throw context.getRuntime().newNameError("class variable " + javaName + " not defined for " + getName(), javaName);
  26347. }
  26348. /** rb_mod_class_variables
  26349. *
  26350. */
  26351. @JRubyMethod(name = "class_variables")
  26352. public RubyArray class_variables(ThreadContext context) {
  26353. Set<String> names = new HashSet<String>();
  26354. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  26355. for (String name : p.getClassVariableNameList()) {
  26356. names.add(name);
  26357. }
  26358. }
  26359. Ruby runtime = context.getRuntime();
  26360. RubyArray ary = runtime.newArray();
  26361. for (String name : names) {
  26362. ary.append(runtime.newString(name));
  26363. }
  26364. return ary;
  26365. }
  26366. //
  26367. ////////////////// CONSTANT RUBY METHODS ////////////////
  26368. //
  26369. /** rb_mod_const_defined
  26370. *
  26371. */
  26372. @JRubyMethod(name = "const_defined?", required = 1)
  26373. public RubyBoolean const_defined_p(ThreadContext context, IRubyObject symbol) {
  26374. // Note: includes part of fix for JRUBY-1339
  26375. return context.getRuntime().newBoolean(fastIsConstantDefined(validateConstant(symbol.asJavaString()).intern()));
  26376. }
  26377. /** rb_mod_const_get
  26378. *
  26379. */
  26380. @JRubyMethod(name = "const_get", required = 1)
  26381. public IRubyObject const_get(IRubyObject symbol) {
  26382. return fastGetConstant(validateConstant(symbol.asJavaString()).intern());
  26383. }
  26384. /** rb_mod_const_set
  26385. *
  26386. */
  26387. @JRubyMethod(name = "const_set", required = 2)
  26388. public IRubyObject const_set(IRubyObject symbol, IRubyObject value) {
  26389. return fastSetConstant(validateConstant(symbol.asJavaString()).intern(), value);
  26390. }
  26391. @JRubyMethod(name = "remove_const", required = 1, visibility = PRIVATE)
  26392. public IRubyObject remove_const(ThreadContext context, IRubyObject name) {
  26393. String id = validateConstant(name.asJavaString());
  26394. IRubyObject value;
  26395. if ((value = deleteConstant(id)) != null) {
  26396. if (value != UNDEF) {
  26397. return value;
  26398. }
  26399. context.getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + id);
  26400. // FIXME: I'm not sure this is right, but the old code returned
  26401. // the undef, which definitely isn't right...
  26402. return context.getRuntime().getNil();
  26403. }
  26404. if (hasConstantInHierarchy(id)) {
  26405. throw cannotRemoveError(id);
  26406. }
  26407. throw context.getRuntime().newNameError("constant " + id + " not defined for " + getName(), id);
  26408. }
  26409. private boolean hasConstantInHierarchy(final String name) {
  26410. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  26411. if (p.hasConstant(name)) {
  26412. return true;
  26413. }
  26414. }
  26415. return false;
  26416. }
  26417. /**
  26418. * Base implementation of Module#const_missing, throws NameError for specific missing constant.
  26419. *
  26420. * @param name The constant name which was found to be missing
  26421. * @return Nothing! Absolutely nothing! (though subclasses might choose to return something)
  26422. */
  26423. @JRubyMethod(name = "const_missing", required = 1, frame = true)
  26424. public IRubyObject const_missing(ThreadContext context, IRubyObject name, Block block) {
  26425. /* Uninitialized constant */
  26426. if (this != context.getRuntime().getObject()) {
  26427. throw context.getRuntime().newNameError("uninitialized constant " + getName() + "::" + name.asJavaString(), "" + getName() + "::" + name.asJavaString());
  26428. }
  26429. throw context.getRuntime().newNameError("uninitialized constant " + name.asJavaString(), name.asJavaString());
  26430. }
  26431. /** rb_mod_constants
  26432. *
  26433. */
  26434. @JRubyMethod(name = "constants")
  26435. public RubyArray constants(ThreadContext context) {
  26436. Ruby runtime = context.getRuntime();
  26437. RubyArray array = runtime.newArray();
  26438. RubyModule objectClass = runtime.getObject();
  26439. if (getRuntime().getModule() == this) {
  26440. for (String name : objectClass.getStoredConstantNameList()) {
  26441. array.append(runtime.newString(name));
  26442. }
  26443. } else if (objectClass == this) {
  26444. for (String name : getStoredConstantNameList()) {
  26445. array.append(runtime.newString(name));
  26446. }
  26447. } else {
  26448. Set<String> names = new HashSet<String>();
  26449. for (RubyModule p = this; p != null; p = p.getSuperClass()) {
  26450. if (objectClass != p) {
  26451. for (String name : p.getStoredConstantNameList()) {
  26452. names.add(name);
  26453. }
  26454. }
  26455. }
  26456. for (String name : names) {
  26457. array.append(runtime.newString(name));
  26458. }
  26459. }
  26460. return array;
  26461. }
  26462. //
  26463. ////////////////// CLASS VARIABLE API METHODS ////////////////
  26464. //
  26465. /**
  26466. * Set the named class variable to the given value, provided taint and freeze allow setting it.
  26467. *
  26468. * Ruby C equivalent = "rb_cvar_set"
  26469. *
  26470. * @param name The variable name to set
  26471. * @param value The value to set it to
  26472. */
  26473. public IRubyObject setClassVar(String name, IRubyObject value) {
  26474. RubyModule module = this;
  26475. do {
  26476. if (module.hasClassVariable(name)) {
  26477. return module.storeClassVariable(name, value);
  26478. }
  26479. } while ((module = module.getSuperClass()) != null);
  26480. return storeClassVariable(name, value);
  26481. }
  26482. public IRubyObject fastSetClassVar(final String internedName, final IRubyObject value) {
  26483. assert internedName == internedName.intern() : internedName + " is not interned";
  26484. RubyModule module = this;
  26485. do {
  26486. if (module.fastHasClassVariable(internedName)) {
  26487. return module.fastStoreClassVariable(internedName, value);
  26488. }
  26489. } while ((module = module.getSuperClass()) != null);
  26490. return fastStoreClassVariable(internedName, value);
  26491. }
  26492. /**
  26493. * Retrieve the specified class variable, searching through this module, included modules, and supermodules.
  26494. *
  26495. * Ruby C equivalent = "rb_cvar_get"
  26496. *
  26497. * @param name The name of the variable to retrieve
  26498. * @return The variable's value, or throws NameError if not found
  26499. */
  26500. public IRubyObject getClassVar(String name) {
  26501. assert IdUtil.isClassVariable(name);
  26502. IRubyObject value;
  26503. RubyModule module = this;
  26504. do {
  26505. if ((value = module.variableTableFetch(name)) != null) return value;
  26506. } while ((module = module.getSuperClass()) != null);
  26507. throw getRuntime().newNameError("uninitialized class variable " + name + " in " + getName(), name);
  26508. }
  26509. public IRubyObject fastGetClassVar(String internedName) {
  26510. assert internedName == internedName.intern() : internedName + " is not interned";
  26511. assert IdUtil.isClassVariable(internedName);
  26512. IRubyObject value;
  26513. RubyModule module = this;
  26514. do {
  26515. if ((value = module.variableTableFastFetch(internedName)) != null) return value;
  26516. } while ((module = module.getSuperClass()) != null);
  26517. throw getRuntime().newNameError("uninitialized class variable " + internedName + " in " + getName(), internedName);
  26518. }
  26519. /**
  26520. * Is class var defined?
  26521. *
  26522. * Ruby C equivalent = "rb_cvar_defined"
  26523. *
  26524. * @param name The class var to determine "is defined?"
  26525. * @return true if true, false if false
  26526. */
  26527. public boolean isClassVarDefined(String name) {
  26528. RubyModule module = this;
  26529. do {
  26530. if (module.hasClassVariable(name)) return true;
  26531. } while ((module = module.getSuperClass()) != null);
  26532. return false;
  26533. }
  26534. public boolean fastIsClassVarDefined(String internedName) {
  26535. assert internedName == internedName.intern() : internedName + " is not interned";
  26536. RubyModule module = this;
  26537. do {
  26538. if (module.fastHasClassVariable(internedName)) return true;
  26539. } while ((module = module.getSuperClass()) != null);
  26540. return false;
  26541. }
  26542. /** rb_mod_remove_cvar
  26543. *
  26544. * FIXME: any good reason to have two identical methods? (same as remove_class_variable)
  26545. */
  26546. public IRubyObject removeCvar(IRubyObject name) { // Wrong Parameter ?
  26547. String internedName = validateClassVariable(name.asJavaString());
  26548. IRubyObject value;
  26549. if ((value = deleteClassVariable(internedName)) != null) {
  26550. return value;
  26551. }
  26552. if (fastIsClassVarDefined(internedName)) {
  26553. throw cannotRemoveError(internedName);
  26554. }
  26555. throw getRuntime().newNameError("class variable " + internedName + " not defined for " + getName(), internedName);
  26556. }
  26557. //
  26558. ////////////////// CONSTANT API METHODS ////////////////
  26559. //
  26560. public IRubyObject getConstantAt(String name) {
  26561. IRubyObject value;
  26562. if ((value = fetchConstant(name)) != UNDEF) {
  26563. return value;
  26564. }
  26565. deleteConstant(name);
  26566. return getRuntime().getLoadService().autoload(getName() + "::" + name);
  26567. }
  26568. public IRubyObject fastGetConstantAt(String internedName) {
  26569. assert internedName == internedName.intern() : internedName + " is not interned";
  26570. IRubyObject value;
  26571. if ((value = fastFetchConstant(internedName)) != UNDEF) {
  26572. return value;
  26573. }
  26574. deleteConstant(internedName);
  26575. return getRuntime().getLoadService().autoload(getName() + "::" + internedName);
  26576. }
  26577. /**
  26578. * Retrieve the named constant, invoking 'const_missing' should that be appropriate.
  26579. *
  26580. * @param name The constant to retrieve
  26581. * @return The value for the constant, or null if not found
  26582. */
  26583. public IRubyObject getConstant(String name) {
  26584. assert IdUtil.isConstant(name);
  26585. boolean retryForModule = false;
  26586. IRubyObject value;
  26587. RubyModule p = this;
  26588. retry: while (true) {
  26589. while (p != null) {
  26590. if ((value = p.constantTableFetch(name)) != null) {
  26591. if (value != UNDEF) {
  26592. return value;
  26593. }
  26594. p.deleteConstant(name);
  26595. if (getRuntime().getLoadService().autoload(
  26596. p.getName() + "::" + name) == null) {
  26597. break;
  26598. }
  26599. continue;
  26600. }
  26601. p = p.getSuperClass();
  26602. }
  26603. if (!retryForModule && !isClass()) {
  26604. retryForModule = true;
  26605. p = getRuntime().getObject();
  26606. continue retry;
  26607. }
  26608. break;
  26609. }
  26610. return callMethod(getRuntime().getCurrentContext(),
  26611. "const_missing", getRuntime().newSymbol(name));
  26612. }
  26613. public IRubyObject fastGetConstant(String internedName) {
  26614. assert internedName == internedName.intern() : internedName + " is not interned";
  26615. assert IdUtil.isConstant(internedName);
  26616. boolean retryForModule = false;
  26617. IRubyObject value;
  26618. RubyModule p = this;
  26619. retry: while (true) {
  26620. while (p != null) {
  26621. if ((value = p.constantTableFastFetch(internedName)) != null) {
  26622. if (value != UNDEF) {
  26623. return value;
  26624. }
  26625. p.deleteConstant(internedName);
  26626. if (getRuntime().getLoadService().autoload(
  26627. p.getName() + "::" + internedName) == null) {
  26628. break;
  26629. }
  26630. continue;
  26631. }
  26632. p = p.getSuperClass();
  26633. }
  26634. if (!retryForModule && !isClass()) {
  26635. retryForModule = true;
  26636. p = getRuntime().getObject();
  26637. continue retry;
  26638. }
  26639. break;
  26640. }
  26641. return callMethod(getRuntime().getCurrentContext(),
  26642. "const_missing", getRuntime().fastNewSymbol(internedName));
  26643. }
  26644. // not actually called anywhere (all known uses call the fast version)
  26645. public IRubyObject getConstantFrom(String name) {
  26646. return fastGetConstantFrom(name.intern());
  26647. }
  26648. public IRubyObject fastGetConstantFrom(String internedName) {
  26649. assert internedName == internedName.intern() : internedName + " is not interned";
  26650. assert IdUtil.isConstant(internedName);
  26651. RubyClass objectClass = getRuntime().getObject();
  26652. IRubyObject value;
  26653. RubyModule p = this;
  26654. while (p != null) {
  26655. if ((value = p.constantTableFastFetch(internedName)) != null) {
  26656. if (value != UNDEF) {
  26657. if (p == objectClass && this != objectClass) {
  26658. String badCName = getName() + "::" + internedName;
  26659. getRuntime().getWarnings().warn(ID.CONSTANT_BAD_REFERENCE, "toplevel constant " +
  26660. internedName + " referenced by " + badCName, badCName);
  26661. }
  26662. return value;
  26663. }
  26664. p.deleteConstant(internedName);
  26665. if (getRuntime().getLoadService().autoload(
  26666. p.getName() + "::" + internedName) == null) {
  26667. break;
  26668. }
  26669. continue;
  26670. }
  26671. p = p.getSuperClass();
  26672. }
  26673. return callMethod(getRuntime().getCurrentContext(),
  26674. "const_missing", getRuntime().fastNewSymbol(internedName));
  26675. }
  26676. /**
  26677. * Set the named constant on this module. Also, if the value provided is another Module and
  26678. * that module has not yet been named, assign it the specified name.
  26679. *
  26680. * @param name The name to assign
  26681. * @param value The value to assign to it; if an unnamed Module, also set its basename to name
  26682. * @return The result of setting the variable.
  26683. */
  26684. public IRubyObject setConstant(String name, IRubyObject value) {
  26685. IRubyObject oldValue;
  26686. if ((oldValue = fetchConstant(name)) != null) {
  26687. if (oldValue == UNDEF) {
  26688. getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + name);
  26689. } else {
  26690. getRuntime().getWarnings().warn(ID.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + name, name);
  26691. }
  26692. }
  26693. storeConstant(name, value);
  26694. // if adding a module under a constant name, set that module's basename to the constant name
  26695. if (value instanceof RubyModule) {
  26696. RubyModule module = (RubyModule)value;
  26697. if (module.getBaseName() == null) {
  26698. module.setBaseName(name);
  26699. module.setParent(this);
  26700. }
  26701. /*
  26702. module.setParent(this);
  26703. */
  26704. }
  26705. return value;
  26706. }
  26707. public IRubyObject fastSetConstant(String internedName, IRubyObject value) {
  26708. assert internedName == internedName.intern() : internedName + " is not interned";
  26709. IRubyObject oldValue;
  26710. if ((oldValue = fastFetchConstant(internedName)) != null) {
  26711. if (oldValue == UNDEF) {
  26712. getRuntime().getLoadService().removeAutoLoadFor(getName() + "::" + internedName);
  26713. } else {
  26714. getRuntime().getWarnings().warn(ID.CONSTANT_ALREADY_INITIALIZED, "already initialized constant " + internedName, internedName);
  26715. }
  26716. }
  26717. fastStoreConstant(internedName, value);
  26718. // if adding a module under a constant name, set that module's basename to the constant name
  26719. if (value instanceof RubyModule) {
  26720. RubyModule module = (RubyModule)value;
  26721. if (module.getBaseName() == null) {
  26722. module.setBaseName(internedName);
  26723. module.setParent(this);
  26724. }
  26725. /*
  26726. module.setParent(this);
  26727. */
  26728. }
  26729. return value;
  26730. }
  26731. /** rb_define_const
  26732. *
  26733. */
  26734. public void defineConstant(String name, IRubyObject value) {
  26735. assert value != null;
  26736. if (this == getRuntime().getClassClass()) {
  26737. getRuntime().secure(4);
  26738. }
  26739. if (!IdUtil.isValidConstantName(name)) {
  26740. throw getRuntime().newNameError("bad constant name " + name, name);
  26741. }
  26742. setConstant(name, value);
  26743. }
  26744. // Fix for JRUBY-1339 - search hierarchy for constant
  26745. /** rb_const_defined_at
  26746. *
  26747. */
  26748. public boolean isConstantDefined(String name) {
  26749. assert IdUtil.isConstant(name);
  26750. boolean isObject = this == getRuntime().getObject();
  26751. RubyModule module = this;
  26752. do {
  26753. Object value;
  26754. if ((value = module.constantTableFetch(name)) != null) {
  26755. if (value != UNDEF) return true;
  26756. return getRuntime().getLoadService().autoloadFor(
  26757. module.getName() + "::" + name) != null;
  26758. }
  26759. } while (isObject && (module = module.getSuperClass()) != null );
  26760. return false;
  26761. }
  26762. public boolean fastIsConstantDefined(String internedName) {
  26763. assert internedName == internedName.intern() : internedName + " is not interned";
  26764. assert IdUtil.isConstant(internedName);
  26765. boolean isObject = this == getRuntime().getObject();
  26766. RubyModule module = this;
  26767. do {
  26768. Object value;
  26769. if ((value = module.constantTableFastFetch(internedName)) != null) {
  26770. if (value != UNDEF) return true;
  26771. return getRuntime().getLoadService().autoloadFor(
  26772. module.getName() + "::" + internedName) != null;
  26773. }
  26774. } while (isObject && (module = module.getSuperClass()) != null );
  26775. return false;
  26776. }
  26777. //
  26778. ////////////////// COMMON CONSTANT / CVAR METHODS ////////////////
  26779. //
  26780. private RaiseException cannotRemoveError(String id) {
  26781. return getRuntime().newNameError("cannot remove " + id + " for " + getName(), id);
  26782. }
  26783. //
  26784. ////////////////// INTERNAL MODULE VARIABLE API METHODS ////////////////
  26785. //
  26786. /**
  26787. * Behaves similarly to {@link #getClassVar(String)}. Searches this
  26788. * class/module <em>and its ancestors</em> for the specified internal
  26789. * variable.
  26790. *
  26791. * @param name the internal variable name
  26792. * @return the value of the specified internal variable if found, else null
  26793. * @see #setInternalModuleVariable(String, IRubyObject)
  26794. */
  26795. public boolean hasInternalModuleVariable(final String name) {
  26796. RubyModule module = this;
  26797. do {
  26798. if (module.hasInternalVariable(name)) {
  26799. return true;
  26800. }
  26801. } while ((module = module.getSuperClass()) != null);
  26802. return false;
  26803. }
  26804. /**
  26805. * Behaves similarly to {@link #getClassVar(String)}. Searches this
  26806. * class/module <em>and its ancestors</em> for the specified internal
  26807. * variable.
  26808. *
  26809. * @param name the internal variable name
  26810. * @return the value of the specified internal variable if found, else null
  26811. * @see #setInternalModuleVariable(String, IRubyObject)
  26812. */
  26813. public IRubyObject searchInternalModuleVariable(final String name) {
  26814. RubyModule module = this;
  26815. IRubyObject value;
  26816. do {
  26817. if ((value = module.getInternalVariable(name)) != null) {
  26818. return value;
  26819. }
  26820. } while ((module = module.getSuperClass()) != null);
  26821. return null;
  26822. }
  26823. /**
  26824. * Behaves similarly to {@link #setClassVar(String, IRubyObject)}. If the
  26825. * specified internal variable is found in this class/module <em>or an ancestor</em>,
  26826. * it is set where found. Otherwise it is set in this module.
  26827. *
  26828. * @param name the internal variable name
  26829. * @param value the internal variable value
  26830. * @see #searchInternalModuleVariable(String)
  26831. */
  26832. public void setInternalModuleVariable(final String name, final IRubyObject value) {
  26833. RubyModule module = this;
  26834. do {
  26835. if (module.hasInternalVariable(name)) {
  26836. module.setInternalVariable(name, value);
  26837. return;
  26838. }
  26839. } while ((module = module.getSuperClass()) != null);
  26840. setInternalVariable(name, value);
  26841. }
  26842. //
  26843. ////////////////// LOW-LEVEL CLASS VARIABLE INTERFACE ////////////////
  26844. //
  26845. // fetch/store/list class variables for this module
  26846. //
  26847. public boolean hasClassVariable(String name) {
  26848. assert IdUtil.isClassVariable(name);
  26849. return variableTableContains(name);
  26850. }
  26851. public boolean fastHasClassVariable(String internedName) {
  26852. assert IdUtil.isClassVariable(internedName);
  26853. return variableTableFastContains(internedName);
  26854. }
  26855. public IRubyObject fetchClassVariable(String name) {
  26856. assert IdUtil.isClassVariable(name);
  26857. return variableTableFetch(name);
  26858. }
  26859. public IRubyObject fastFetchClassVariable(String internedName) {
  26860. assert IdUtil.isClassVariable(internedName);
  26861. return variableTableFastFetch(internedName);
  26862. }
  26863. public IRubyObject storeClassVariable(String name, IRubyObject value) {
  26864. assert IdUtil.isClassVariable(name) && value != null;
  26865. ensureClassVariablesSettable();
  26866. return variableTableStore(name, value);
  26867. }
  26868. public IRubyObject fastStoreClassVariable(String internedName, IRubyObject value) {
  26869. assert IdUtil.isClassVariable(internedName) && value != null;
  26870. ensureClassVariablesSettable();
  26871. return variableTableFastStore(internedName, value);
  26872. }
  26873. public IRubyObject deleteClassVariable(String name) {
  26874. assert IdUtil.isClassVariable(name);
  26875. ensureClassVariablesSettable();
  26876. return variableTableRemove(name);
  26877. }
  26878. public List<Variable<IRubyObject>> getClassVariableList() {
  26879. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  26880. VariableTableEntry[] table = variableTableGetTable();
  26881. IRubyObject readValue;
  26882. for (int i = table.length; --i >= 0; ) {
  26883. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  26884. if (IdUtil.isClassVariable(e.name)) {
  26885. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  26886. list.add(new VariableEntry<IRubyObject>(e.name, readValue));
  26887. }
  26888. }
  26889. }
  26890. return list;
  26891. }
  26892. public List<String> getClassVariableNameList() {
  26893. ArrayList<String> list = new ArrayList<String>();
  26894. VariableTableEntry[] table = variableTableGetTable();
  26895. for (int i = table.length; --i >= 0; ) {
  26896. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  26897. if (IdUtil.isClassVariable(e.name)) {
  26898. list.add(e.name);
  26899. }
  26900. }
  26901. }
  26902. return list;
  26903. }
  26904. protected static final String ERR_INSECURE_SET_CLASS_VAR = "Insecure: can't modify class variable";
  26905. protected static final String ERR_FROZEN_CVAR_TYPE = "class/module ";
  26906. protected final String validateClassVariable(String name) {
  26907. if (IdUtil.isValidClassVariableName(name)) {
  26908. return name;
  26909. }
  26910. throw getRuntime().newNameError("`" + name + "' is not allowed as a class variable name", name);
  26911. }
  26912. protected final void ensureClassVariablesSettable() {
  26913. Ruby runtime = getRuntime();
  26914. if (!isFrozen() && (runtime.getSafeLevel() < 4 || isTaint())) {
  26915. return;
  26916. }
  26917. if (runtime.getSafeLevel() >= 4 && !isTaint()) {
  26918. throw runtime.newSecurityError(ERR_INSECURE_SET_CONSTANT);
  26919. }
  26920. if (isFrozen()) {
  26921. if (this instanceof RubyModule) {
  26922. throw runtime.newFrozenError(ERR_FROZEN_CONST_TYPE);
  26923. } else {
  26924. throw runtime.newFrozenError("");
  26925. }
  26926. }
  26927. }
  26928. //
  26929. ////////////////// LOW-LEVEL CONSTANT INTERFACE ////////////////
  26930. //
  26931. // fetch/store/list constants for this module
  26932. //
  26933. public boolean hasConstant(String name) {
  26934. assert IdUtil.isConstant(name);
  26935. return constantTableContains(name);
  26936. }
  26937. public boolean fastHasConstant(String internedName) {
  26938. assert IdUtil.isConstant(internedName);
  26939. return constantTableFastContains(internedName);
  26940. }
  26941. // returns the stored value without processing undefs (autoloads)
  26942. public IRubyObject fetchConstant(String name) {
  26943. assert IdUtil.isConstant(name);
  26944. return constantTableFetch(name);
  26945. }
  26946. // returns the stored value without processing undefs (autoloads)
  26947. public IRubyObject fastFetchConstant(String internedName) {
  26948. assert IdUtil.isConstant(internedName);
  26949. return constantTableFastFetch(internedName);
  26950. }
  26951. public IRubyObject storeConstant(String name, IRubyObject value) {
  26952. assert IdUtil.isConstant(name) && value != null;
  26953. ensureConstantsSettable();
  26954. return constantTableStore(name, value);
  26955. }
  26956. public IRubyObject fastStoreConstant(String internedName, IRubyObject value) {
  26957. assert IdUtil.isConstant(internedName) && value != null;
  26958. ensureConstantsSettable();
  26959. return constantTableFastStore(internedName, value);
  26960. }
  26961. // removes and returns the stored value without processing undefs (autoloads)
  26962. public IRubyObject deleteConstant(String name) {
  26963. assert IdUtil.isConstant(name);
  26964. ensureConstantsSettable();
  26965. return constantTableRemove(name);
  26966. }
  26967. public List<Variable<IRubyObject>> getStoredConstantList() {
  26968. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  26969. ConstantTableEntry[] table = constantTableGetTable();
  26970. for (int i = table.length; --i >= 0; ) {
  26971. for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
  26972. list.add(e);
  26973. }
  26974. }
  26975. return list;
  26976. }
  26977. public List<String> getStoredConstantNameList() {
  26978. ArrayList<String> list = new ArrayList<String>();
  26979. ConstantTableEntry[] table = constantTableGetTable();
  26980. for (int i = table.length; --i >= 0; ) {
  26981. for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
  26982. list.add(e.name);
  26983. }
  26984. }
  26985. return list;
  26986. }
  26987. protected static final String ERR_INSECURE_SET_CONSTANT = "Insecure: can't modify constant";
  26988. protected static final String ERR_FROZEN_CONST_TYPE = "class/module ";
  26989. protected final String validateConstant(String name) {
  26990. if (IdUtil.isValidConstantName(name)) {
  26991. return name;
  26992. }
  26993. throw getRuntime().newNameError("wrong constant name " + name, name);
  26994. }
  26995. protected final void ensureConstantsSettable() {
  26996. Ruby runtime = getRuntime();
  26997. if (!isFrozen() && (runtime.getSafeLevel() < 4 || isTaint())) {
  26998. return;
  26999. }
  27000. if (runtime.getSafeLevel() >= 4 && !isTaint()) {
  27001. throw runtime.newSecurityError(ERR_INSECURE_SET_CONSTANT);
  27002. }
  27003. if (isFrozen()) {
  27004. if (this instanceof RubyModule) {
  27005. throw runtime.newFrozenError(ERR_FROZEN_CONST_TYPE);
  27006. } else {
  27007. throw runtime.newFrozenError("");
  27008. }
  27009. }
  27010. }
  27011. //
  27012. ////////////////// VARIABLE TABLE METHODS ////////////////
  27013. //
  27014. // Overridden to use variableWriteLock in place of synchronization
  27015. //
  27016. @Override
  27017. protected IRubyObject variableTableStore(String name, IRubyObject value) {
  27018. int hash = name.hashCode();
  27019. ReentrantLock lock;
  27020. (lock = variableWriteLock).lock();
  27021. try {
  27022. VariableTableEntry[] table;
  27023. VariableTableEntry e;
  27024. if ((table = variableTable) == null) {
  27025. table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  27026. e = new VariableTableEntry(hash, name.intern(), value, null);
  27027. table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
  27028. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  27029. variableTableSize = 1;
  27030. variableTable = table;
  27031. return value;
  27032. }
  27033. int potentialNewSize;
  27034. if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
  27035. table = variableTableRehash();
  27036. }
  27037. int index;
  27038. for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  27039. if (hash == e.hash && name.equals(e.name)) {
  27040. e.value = value;
  27041. return value;
  27042. }
  27043. }
  27044. // external volatile value initialization intended to obviate the need for
  27045. // readValueUnderLock technique used in ConcurrentHashMap. may be a little
  27046. // slower, but better to pay a price on first write rather than all reads.
  27047. e = new VariableTableEntry(hash, name.intern(), value, table[index]);
  27048. table[index] = e;
  27049. variableTableSize = potentialNewSize;
  27050. variableTable = table; // write-volatile
  27051. } finally {
  27052. lock.unlock();
  27053. }
  27054. return value;
  27055. }
  27056. @Override
  27057. protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
  27058. assert internedName == internedName.intern() : internedName + " not interned";
  27059. int hash = internedName.hashCode();
  27060. ReentrantLock lock;
  27061. (lock = variableWriteLock).lock();
  27062. try {
  27063. VariableTableEntry[] table;
  27064. VariableTableEntry e;
  27065. if ((table = variableTable) == null) {
  27066. table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  27067. e = new VariableTableEntry(hash, internedName, value, null);
  27068. table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
  27069. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  27070. variableTableSize = 1;
  27071. variableTable = table;
  27072. return value;
  27073. }
  27074. int potentialNewSize;
  27075. if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
  27076. table = variableTableRehash();
  27077. }
  27078. int index;
  27079. for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  27080. if (internedName == e.name) {
  27081. e.value = value;
  27082. return value;
  27083. }
  27084. }
  27085. // external volatile value initialization intended to obviate the need for
  27086. // readValueUnderLock technique used in ConcurrentHashMap. may be a little
  27087. // slower, but better to pay a price on first write rather than all reads.
  27088. e = new VariableTableEntry(hash, internedName, value, table[index]);
  27089. table[index] = e;
  27090. variableTableSize = potentialNewSize;
  27091. variableTable = table; // write-volatile
  27092. } finally {
  27093. lock.unlock();
  27094. }
  27095. return value;
  27096. }
  27097. @Override
  27098. protected IRubyObject variableTableRemove(String name) {
  27099. ReentrantLock lock;
  27100. (lock = variableWriteLock).lock();
  27101. try {
  27102. VariableTableEntry[] table;
  27103. if ((table = variableTable) != null) {
  27104. int hash = name.hashCode();
  27105. int index = hash & (table.length - 1);
  27106. VariableTableEntry first = table[index];
  27107. VariableTableEntry e;
  27108. for (e = first; e != null; e = e.next) {
  27109. if (hash == e.hash && name.equals(e.name)) {
  27110. IRubyObject oldValue = e.value;
  27111. // All entries following removed node can stay
  27112. // in list, but all preceding ones need to be
  27113. // cloned.
  27114. VariableTableEntry newFirst = e.next;
  27115. for (VariableTableEntry p = first; p != e; p = p.next) {
  27116. newFirst = new VariableTableEntry(p.hash, p.name, p.value, newFirst);
  27117. }
  27118. table[index] = newFirst;
  27119. variableTableSize--;
  27120. variableTable = table; // write-volatile
  27121. return oldValue;
  27122. }
  27123. }
  27124. }
  27125. } finally {
  27126. lock.unlock();
  27127. }
  27128. return null;
  27129. }
  27130. @Override
  27131. protected IRubyObject variableTableReadLocked(VariableTableEntry entry) {
  27132. ReentrantLock lock;
  27133. (lock = variableWriteLock).lock();
  27134. try {
  27135. return entry.value;
  27136. } finally {
  27137. lock.unlock();
  27138. }
  27139. }
  27140. @Override
  27141. protected void variableTableSync(List<Variable<IRubyObject>> vars) {
  27142. ReentrantLock lock;
  27143. (lock = variableWriteLock).lock();
  27144. try {
  27145. variableTableSize = 0;
  27146. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  27147. variableTable = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  27148. for (Variable<IRubyObject> var : vars) {
  27149. assert !var.isConstant() && var.getValue() != null;
  27150. variableTableStore(var.getName(), var.getValue());
  27151. }
  27152. } finally {
  27153. lock.unlock();
  27154. }
  27155. }
  27156. @Override
  27157. public void syncVariables(List<Variable<IRubyObject>> variables) {
  27158. ArrayList<Variable<IRubyObject>> constants = new ArrayList<Variable<IRubyObject>>(variables.size());
  27159. Variable<IRubyObject> var;
  27160. for (Iterator<Variable<IRubyObject>> iter = variables.iterator(); iter.hasNext(); ) {
  27161. if ((var = iter.next()).isConstant()) {
  27162. constants.add(var);
  27163. iter.remove();
  27164. }
  27165. }
  27166. ReentrantLock lock;
  27167. (lock = variableWriteLock).lock();
  27168. try {
  27169. variableTableSync(variables);
  27170. constantTableSync(constants);
  27171. } finally {
  27172. lock.unlock();
  27173. }
  27174. }
  27175. @Override
  27176. @SuppressWarnings("unchecked")
  27177. @Deprecated // born deprecated
  27178. public Map getVariableMap() {
  27179. Map map = variableTableGetMap();
  27180. constantTableGetMap(map);
  27181. return map;
  27182. }
  27183. @Override
  27184. public boolean hasVariables() {
  27185. return variableTableGetSize() > 0 || constantTableGetSize() > 0;
  27186. }
  27187. @Override
  27188. public int getVariableCount() {
  27189. return variableTableGetSize() + constantTableGetSize();
  27190. }
  27191. @Override
  27192. public List<Variable<IRubyObject>> getVariableList() {
  27193. VariableTableEntry[] vtable = variableTableGetTable();
  27194. ConstantTableEntry[] ctable = constantTableGetTable();
  27195. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  27196. IRubyObject readValue;
  27197. for (int i = vtable.length; --i >= 0; ) {
  27198. for (VariableTableEntry e = vtable[i]; e != null; e = e.next) {
  27199. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  27200. list.add(new VariableEntry<IRubyObject>(e.name, readValue));
  27201. }
  27202. }
  27203. for (int i = ctable.length; --i >= 0; ) {
  27204. for (ConstantTableEntry e = ctable[i]; e != null; e = e.next) {
  27205. list.add(e);
  27206. }
  27207. }
  27208. return list;
  27209. }
  27210. @Override
  27211. public List<String> getVariableNameList() {
  27212. VariableTableEntry[] vtable = variableTableGetTable();
  27213. ConstantTableEntry[] ctable = constantTableGetTable();
  27214. ArrayList<String> list = new ArrayList<String>();
  27215. for (int i = vtable.length; --i >= 0; ) {
  27216. for (VariableTableEntry e = vtable[i]; e != null; e = e.next) {
  27217. list.add(e.name);
  27218. }
  27219. }
  27220. for (int i = ctable.length; --i >= 0; ) {
  27221. for (ConstantTableEntry e = ctable[i]; e != null; e = e.next) {
  27222. list.add(e.name);
  27223. }
  27224. }
  27225. return list;
  27226. }
  27227. //
  27228. ////////////////// CONSTANT TABLE METHODS, ETC. ////////////////
  27229. //
  27230. protected static final int CONSTANT_TABLE_DEFAULT_CAPACITY = 8; // MUST be power of 2!
  27231. protected static final int CONSTANT_TABLE_MAXIMUM_CAPACITY = 1 << 30;
  27232. protected static final float CONSTANT_TABLE_LOAD_FACTOR = 0.75f;
  27233. protected static final class ConstantTableEntry implements Variable<IRubyObject> {
  27234. final int hash;
  27235. final String name;
  27236. final IRubyObject value;
  27237. final ConstantTableEntry next;
  27238. // constant table entry values are final; if a constant is redefined, the
  27239. // entry will be removed and replaced with a new entry.
  27240. ConstantTableEntry(
  27241. int hash,
  27242. String name,
  27243. IRubyObject value,
  27244. ConstantTableEntry next) {
  27245. this.hash = hash;
  27246. this.name = name;
  27247. this.value = value;
  27248. this.next = next;
  27249. }
  27250. public String getName() {
  27251. return name;
  27252. }
  27253. public IRubyObject getValue() {
  27254. return value;
  27255. }
  27256. public final boolean isClassVariable() {
  27257. return false;
  27258. }
  27259. public final boolean isConstant() {
  27260. return true;
  27261. }
  27262. public final boolean isInstanceVariable() {
  27263. return false;
  27264. }
  27265. public final boolean isRubyVariable() {
  27266. return true;
  27267. }
  27268. }
  27269. protected boolean constantTableContains(String name) {
  27270. int hash = name.hashCode();
  27271. ConstantTableEntry[] table;
  27272. for (ConstantTableEntry e = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
  27273. if (hash == e.hash && name.equals(e.name)) {
  27274. return true;
  27275. }
  27276. }
  27277. return false;
  27278. }
  27279. protected boolean constantTableFastContains(String internedName) {
  27280. ConstantTableEntry[] table;
  27281. for (ConstantTableEntry e = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
  27282. if (internedName == e.name) {
  27283. return true;
  27284. }
  27285. }
  27286. return false;
  27287. }
  27288. protected IRubyObject constantTableFetch(String name) {
  27289. int hash = name.hashCode();
  27290. ConstantTableEntry[] table;
  27291. for (ConstantTableEntry e = (table = constantTable)[hash & (table.length - 1)]; e != null; e = e.next) {
  27292. if (hash == e.hash && name.equals(e.name)) {
  27293. return e.value;
  27294. }
  27295. }
  27296. return null;
  27297. }
  27298. protected IRubyObject constantTableFastFetch(String internedName) {
  27299. ConstantTableEntry[] table;
  27300. for (ConstantTableEntry e = (table = constantTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
  27301. if (internedName == e.name) {
  27302. return e.value;
  27303. }
  27304. }
  27305. return null;
  27306. }
  27307. protected IRubyObject constantTableStore(String name, IRubyObject value) {
  27308. int hash = name.hashCode();
  27309. ReentrantLock lock;
  27310. (lock = variableWriteLock).lock();
  27311. try {
  27312. ConstantTableEntry[] table;
  27313. ConstantTableEntry e;
  27314. ConstantTableEntry first;
  27315. int potentialNewSize;
  27316. if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
  27317. table = constantTableRehash();
  27318. } else {
  27319. table = constantTable;
  27320. }
  27321. int index;
  27322. for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  27323. if (hash == e.hash && name.equals(e.name)) {
  27324. // if value is unchanged, do nothing
  27325. if (value == e.value) {
  27326. return value;
  27327. }
  27328. // create new entry, prepend to any trailing entries
  27329. ConstantTableEntry newFirst = new ConstantTableEntry(e.hash, e.name, value, e.next);
  27330. // all entries before this one must be cloned
  27331. for (ConstantTableEntry n = first; n != e; n = n.next) {
  27332. newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
  27333. }
  27334. table[index] = newFirst;
  27335. constantTable = table; // write-volatile
  27336. return value;
  27337. }
  27338. }
  27339. table[index] = new ConstantTableEntry(hash, name.intern(), value, table[index]);
  27340. constantTableSize = potentialNewSize;
  27341. constantTable = table; // write-volatile
  27342. } finally {
  27343. lock.unlock();
  27344. }
  27345. return value;
  27346. }
  27347. protected IRubyObject constantTableFastStore(String internedName, IRubyObject value) {
  27348. assert internedName == internedName.intern() : internedName + " not interned";
  27349. int hash = internedName.hashCode();
  27350. ReentrantLock lock;
  27351. (lock = variableWriteLock).lock();
  27352. try {
  27353. ConstantTableEntry[] table;
  27354. ConstantTableEntry e;
  27355. ConstantTableEntry first;
  27356. int potentialNewSize;
  27357. if ((potentialNewSize = constantTableSize + 1) > constantTableThreshold) {
  27358. table = constantTableRehash();
  27359. } else {
  27360. table = constantTable;
  27361. }
  27362. int index;
  27363. for (e = first = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  27364. if (internedName == e.name) {
  27365. // if value is unchanged, do nothing
  27366. if (value == e.value) {
  27367. return value;
  27368. }
  27369. // create new entry, prepend to any trailing entries
  27370. ConstantTableEntry newFirst = new ConstantTableEntry(e.hash, e.name, value, e.next);
  27371. // all entries before this one must be cloned
  27372. for (ConstantTableEntry n = first; n != e; n = n.next) {
  27373. newFirst = new ConstantTableEntry(n.hash, n.name, n.value, newFirst);
  27374. }
  27375. table[index] = newFirst;
  27376. constantTable = table; // write-volatile
  27377. return value;
  27378. }
  27379. }
  27380. table[index] = new ConstantTableEntry(hash, internedName, value, table[index]);
  27381. constantTableSize = potentialNewSize;
  27382. constantTable = table; // write-volatile
  27383. } finally {
  27384. lock.unlock();
  27385. }
  27386. return value;
  27387. }
  27388. protected IRubyObject constantTableRemove(String name) {
  27389. ReentrantLock lock;
  27390. (lock = variableWriteLock).lock();
  27391. try {
  27392. ConstantTableEntry[] table;
  27393. if ((table = constantTable) != null) {
  27394. int hash = name.hashCode();
  27395. int index = hash & (table.length - 1);
  27396. ConstantTableEntry first = table[index];
  27397. ConstantTableEntry e;
  27398. for (e = first; e != null; e = e.next) {
  27399. if (hash == e.hash && name.equals(e.name)) {
  27400. IRubyObject oldValue = e.value;
  27401. // All entries following removed node can stay
  27402. // in list, but all preceding ones need to be
  27403. // cloned.
  27404. ConstantTableEntry newFirst = e.next;
  27405. for (ConstantTableEntry p = first; p != e; p = p.next) {
  27406. newFirst = new ConstantTableEntry(p.hash, p.name, p.value, newFirst);
  27407. }
  27408. table[index] = newFirst;
  27409. constantTableSize--;
  27410. constantTable = table; // write-volatile
  27411. return oldValue;
  27412. }
  27413. }
  27414. }
  27415. } finally {
  27416. lock.unlock();
  27417. }
  27418. return null;
  27419. }
  27420. protected ConstantTableEntry[] constantTableGetTable() {
  27421. return constantTable;
  27422. }
  27423. protected int constantTableGetSize() {
  27424. if (constantTable != null) {
  27425. return constantTableSize;
  27426. }
  27427. return 0;
  27428. }
  27429. protected void constantTableSync(List<Variable<IRubyObject>> vars) {
  27430. ReentrantLock lock;
  27431. (lock = variableWriteLock).lock();
  27432. try {
  27433. constantTableSize = 0;
  27434. constantTableThreshold = (int)(CONSTANT_TABLE_DEFAULT_CAPACITY * CONSTANT_TABLE_LOAD_FACTOR);
  27435. constantTable = new ConstantTableEntry[CONSTANT_TABLE_DEFAULT_CAPACITY];
  27436. for (Variable<IRubyObject> var : vars) {
  27437. assert var.isConstant() && var.getValue() != null;
  27438. constantTableStore(var.getName(), var.getValue());
  27439. }
  27440. } finally {
  27441. lock.unlock();
  27442. }
  27443. }
  27444. // MUST be called from synchronized/locked block!
  27445. // should only be called by constantTableStore/constantTableFastStore
  27446. private final ConstantTableEntry[] constantTableRehash() {
  27447. ConstantTableEntry[] oldTable = constantTable;
  27448. int oldCapacity;
  27449. if ((oldCapacity = oldTable.length) >= CONSTANT_TABLE_MAXIMUM_CAPACITY) {
  27450. return oldTable;
  27451. }
  27452. int newCapacity = oldCapacity << 1;
  27453. ConstantTableEntry[] newTable = new ConstantTableEntry[newCapacity];
  27454. constantTableThreshold = (int)(newCapacity * CONSTANT_TABLE_LOAD_FACTOR);
  27455. int sizeMask = newCapacity - 1;
  27456. ConstantTableEntry e;
  27457. for (int i = oldCapacity; --i >= 0; ) {
  27458. // We need to guarantee that any existing reads of old Map can
  27459. // proceed. So we cannot yet null out each bin.
  27460. e = oldTable[i];
  27461. if (e != null) {
  27462. ConstantTableEntry next = e.next;
  27463. int idx = e.hash & sizeMask;
  27464. // Single node on list
  27465. if (next == null)
  27466. newTable[idx] = e;
  27467. else {
  27468. // Reuse trailing consecutive sequence at same slot
  27469. ConstantTableEntry lastRun = e;
  27470. int lastIdx = idx;
  27471. for (ConstantTableEntry last = next;
  27472. last != null;
  27473. last = last.next) {
  27474. int k = last.hash & sizeMask;
  27475. if (k != lastIdx) {
  27476. lastIdx = k;
  27477. lastRun = last;
  27478. }
  27479. }
  27480. newTable[lastIdx] = lastRun;
  27481. // Clone all remaining nodes
  27482. for (ConstantTableEntry p = e; p != lastRun; p = p.next) {
  27483. int k = p.hash & sizeMask;
  27484. ConstantTableEntry m = new ConstantTableEntry(p.hash, p.name, p.value, newTable[k]);
  27485. newTable[k] = m;
  27486. }
  27487. }
  27488. }
  27489. }
  27490. constantTable = newTable;
  27491. return newTable;
  27492. }
  27493. /**
  27494. * Method to help ease transition to new variables implementation.
  27495. * Will likely be deprecated in the near future.
  27496. */
  27497. @SuppressWarnings("unchecked")
  27498. protected Map constantTableGetMap() {
  27499. HashMap map = new HashMap();
  27500. ConstantTableEntry[] table;
  27501. if ((table = constantTable) != null) {
  27502. for (int i = table.length; --i >= 0; ) {
  27503. for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
  27504. map.put(e.name, e.value);
  27505. }
  27506. }
  27507. }
  27508. return map;
  27509. }
  27510. /**
  27511. * Method to help ease transition to new variables implementation.
  27512. * Will likely be deprecated in the near future.
  27513. */
  27514. @SuppressWarnings("unchecked")
  27515. protected Map constantTableGetMap(Map map) {
  27516. ConstantTableEntry[] table;
  27517. if ((table = constantTable) != null) {
  27518. for (int i = table.length; --i >= 0; ) {
  27519. for (ConstantTableEntry e = table[i]; e != null; e = e.next) {
  27520. map.put(e.name, e.value);
  27521. }
  27522. }
  27523. }
  27524. return map;
  27525. }
  27526. }
  27527. /***** BEGIN LICENSE BLOCK *****
  27528. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  27529. *
  27530. * The contents of this file are subject to the Common Public
  27531. * License Version 1.0 (the "License"); you may not use this file
  27532. * except in compliance with the License. You may obtain a copy of
  27533. * the License at http://www.eclipse.org/legal/cpl-v10.html
  27534. *
  27535. * Software distributed under the License is distributed on an "AS
  27536. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  27537. * implied. See the License for the specific language governing
  27538. * rights and limitations under the License.
  27539. *
  27540. * Copyright (C) 2006 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  27541. *
  27542. * Alternatively, the contents of this file may be used under the terms of
  27543. * either of the GNU General Public License Version 2 or later (the "GPL"),
  27544. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27545. * in which case the provisions of the GPL or the LGPL are applicable instead
  27546. * of those above. If you wish to allow use of your version of this file only
  27547. * under the terms of either the GPL or the LGPL, and not to allow others to
  27548. * use your version of this file under the terms of the CPL, indicate your
  27549. * decision by deleting the provisions above and replace them with the notice
  27550. * and other provisions required by the GPL or the LGPL. If you do not delete
  27551. * the provisions above, a recipient may use your version of this file under
  27552. * the terms of any one of the CPL, the GPL or the LGPL.
  27553. ***** END LICENSE BLOCK *****/
  27554. package org.jruby;
  27555. import static org.jruby.runtime.Visibility.PRIVATE;
  27556. import static org.jruby.runtime.Visibility.PROTECTED;
  27557. import org.jruby.anno.JRubyMethod;
  27558. import org.jruby.anno.JRubyClass;
  27559. import org.jruby.exceptions.JumpException;
  27560. import org.jruby.runtime.Block;
  27561. import org.jruby.runtime.CallType;
  27562. import org.jruby.runtime.ObjectAllocator;
  27563. import org.jruby.runtime.ThreadContext;
  27564. import org.jruby.runtime.Visibility;
  27565. import org.jruby.runtime.builtin.IRubyObject;
  27566. import org.jruby.util.Sprintf;
  27567. /**
  27568. * @author Anders Bengtsson
  27569. */
  27570. @JRubyClass(name="NameError", parent="StandardError")
  27571. public class RubyNameError extends RubyException {
  27572. private IRubyObject name;
  27573. /**
  27574. * Nested class whose instances act as thunks reacting to to_str method
  27575. * called from (Exception#to_str, Exception#message)
  27576. * MRI equivalent: rb_cNameErrorMesg, class name: "message", construction method: "!",
  27577. * to_str implementation: "name_err_mesg_to_str"
  27578. *
  27579. * TODO: this class should not be lookupable
  27580. */
  27581. @JRubyClass(name = "NameError::Message", parent = "Object")
  27582. public static final class RubyNameErrorMessage extends RubyObject {
  27583. static ObjectAllocator NAMEERRORMESSAGE_ALLOCATOR = new ObjectAllocator() {
  27584. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  27585. IRubyObject dummy = new RubyObject(runtime, runtime.getObject());
  27586. return new RubyNameErrorMessage(runtime, dummy, dummy, Visibility.PRIVATE, CallType.VARIABLE);
  27587. }
  27588. };
  27589. private final IRubyObject object;
  27590. private final IRubyObject method;
  27591. private final Visibility visibility;
  27592. private final CallType callType;
  27593. RubyNameErrorMessage(Ruby runtime, IRubyObject object, IRubyObject method, Visibility visibility, CallType callType) {
  27594. super(runtime, runtime.getNameErrorMessage(), false);
  27595. this.object = object;
  27596. this.method = method;
  27597. this.visibility = visibility;
  27598. this.callType = callType;
  27599. }
  27600. @JRubyMethod(name = "_load", meta = true)
  27601. public static IRubyObject load(IRubyObject recv, IRubyObject arg) {
  27602. return arg;
  27603. }
  27604. @JRubyMethod(name = "_dump")
  27605. public IRubyObject dump(ThreadContext context, IRubyObject arg) {
  27606. return to_str(context);
  27607. }
  27608. @JRubyMethod(name = "to_str")
  27609. public IRubyObject to_str(ThreadContext context) {
  27610. String format = null;
  27611. if (visibility == PRIVATE) {
  27612. format = "private method `%s' called for %s";
  27613. } else if (visibility == PROTECTED) {
  27614. format = "protected method `%s' called for %s";
  27615. } else if (callType == CallType.VARIABLE) {
  27616. format = "undefined local variable or method `%s' for %s";
  27617. } else if (callType == CallType.SUPER) {
  27618. format = "super: no superclass method `%s'";
  27619. }
  27620. if (format == null) format = "undefined method `%s' for %s";
  27621. String description = null;
  27622. if (object.isNil()) {
  27623. description = "nil";
  27624. } else if (object instanceof RubyBoolean && object.isTrue()) {
  27625. description = "true";
  27626. } else if (object instanceof RubyBoolean && !object.isTrue()) {
  27627. description = "false";
  27628. } else {
  27629. try {
  27630. description = RubyObject.inspect(context, object).toString();
  27631. } catch(JumpException e) {}
  27632. if (description == null || description.length() > 65) description = object.anyToString().toString();
  27633. }
  27634. if (description.length() == 0 || (description.length() > 0 && description.charAt(0) != '#')) {
  27635. description = description + ":" + object.getMetaClass().getRealClass().getName();
  27636. }
  27637. Ruby runtime = getRuntime();
  27638. RubyArray arr = runtime.newArray(method, runtime.newString(description));
  27639. RubyString msg = runtime.newString(Sprintf.sprintf(runtime.newString(format), arr).toString());
  27640. if (object.isTaint()) msg.setTaint(true);
  27641. return msg;
  27642. }
  27643. }
  27644. private static ObjectAllocator NAMEERROR_ALLOCATOR = new ObjectAllocator() {
  27645. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  27646. return new RubyNameError(runtime, klass);
  27647. }
  27648. };
  27649. public static RubyClass createNameErrorClass(Ruby runtime, RubyClass standardErrorClass) {
  27650. RubyClass nameErrorClass = runtime.defineClass("NameError", standardErrorClass, NAMEERROR_ALLOCATOR);
  27651. nameErrorClass.defineAnnotatedMethods(RubyNameError.class);
  27652. return nameErrorClass;
  27653. }
  27654. public static RubyClass createNameErrorMessageClass(Ruby runtime, RubyClass nameErrorClass) {
  27655. RubyClass messageClass = nameErrorClass.defineClassUnder("Message", runtime.getObject(), RubyNameErrorMessage.NAMEERRORMESSAGE_ALLOCATOR);
  27656. messageClass.defineAnnotatedMethods(RubyNameErrorMessage.class);
  27657. return messageClass;
  27658. }
  27659. protected RubyNameError(Ruby runtime, RubyClass exceptionClass) {
  27660. this(runtime, exceptionClass, exceptionClass.getName());
  27661. }
  27662. public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message) {
  27663. this(runtime, exceptionClass, message, null);
  27664. }
  27665. public RubyNameError(Ruby runtime, RubyClass exceptionClass, String message, String name) {
  27666. super(runtime, exceptionClass, message);
  27667. this.name = name == null ? runtime.getNil() : runtime.newString(name);
  27668. }
  27669. @JRubyMethod(name = "exception", rest = true, meta = true)
  27670. public static RubyException newRubyNameError(IRubyObject recv, IRubyObject[] args) {
  27671. RubyClass klass = (RubyClass)recv;
  27672. RubyException newError = (RubyException) klass.allocate();
  27673. newError.callInit(args, Block.NULL_BLOCK);
  27674. return newError;
  27675. }
  27676. @JRubyMethod(name = "initialize", optional = 2, frame = true)
  27677. @Override
  27678. public IRubyObject initialize(IRubyObject[] args, Block block) {
  27679. if (args.length > 1) {
  27680. name = args[args.length - 1];
  27681. int newLength = args.length > 2 ? args.length - 2 : args.length - 1;
  27682. IRubyObject []tmpArgs = new IRubyObject[newLength];
  27683. System.arraycopy(args, 0, tmpArgs, 0, newLength);
  27684. args = tmpArgs;
  27685. } else {
  27686. name = getRuntime().getNil();
  27687. }
  27688. super.initialize(args, block);
  27689. return this;
  27690. }
  27691. @JRubyMethod(name = "to_s")
  27692. @Override
  27693. public IRubyObject to_s() {
  27694. if (message.isNil()) return getRuntime().newString(message.getMetaClass().getName());
  27695. RubyString str = message.convertToString();
  27696. if (str != message) message = str;
  27697. if (isTaint()) message.setTaint(true);
  27698. return message;
  27699. }
  27700. @JRubyMethod(name = "name")
  27701. public IRubyObject name() {
  27702. return name;
  27703. }
  27704. }
  27705. /***** BEGIN LICENSE BLOCK *****
  27706. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  27707. *
  27708. * The contents of this file are subject to the Common Public
  27709. * License Version 1.0 (the "License"); you may not use this file
  27710. * except in compliance with the License. You may obtain a copy of
  27711. * the License at http://www.eclipse.org/legal/cpl-v10.html
  27712. *
  27713. * Software distributed under the License is distributed on an "AS
  27714. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  27715. * implied. See the License for the specific language governing
  27716. * rights and limitations under the License.
  27717. *
  27718. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  27719. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  27720. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  27721. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  27722. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  27723. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  27724. *
  27725. * Alternatively, the contents of this file may be used under the terms of
  27726. * either of the GNU General Public License Version 2 or later (the "GPL"),
  27727. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27728. * in which case the provisions of the GPL or the LGPL are applicable instead
  27729. * of those above. If you wish to allow use of your version of this file only
  27730. * under the terms of either the GPL or the LGPL, and not to allow others to
  27731. * use your version of this file under the terms of the CPL, indicate your
  27732. * decision by deleting the provisions above and replace them with the notice
  27733. * and other provisions required by the GPL or the LGPL. If you do not delete
  27734. * the provisions above, a recipient may use your version of this file under
  27735. * the terms of any one of the CPL, the GPL or the LGPL.
  27736. ***** END LICENSE BLOCK *****/
  27737. package org.jruby;
  27738. import org.jruby.anno.JRubyMethod;
  27739. import org.jruby.anno.JRubyClass;
  27740. import org.jruby.runtime.ClassIndex;
  27741. import org.jruby.runtime.ObjectAllocator;
  27742. import org.jruby.runtime.ThreadContext;
  27743. import org.jruby.runtime.builtin.IRubyObject;
  27744. /**
  27745. *
  27746. * @author jpetersen
  27747. */
  27748. @JRubyClass(name="NilClass")
  27749. public class RubyNil extends RubyObject {
  27750. public RubyNil(Ruby runtime) {
  27751. super(runtime, runtime.getNilClass(), false);
  27752. flags |= NIL_F | FALSE_F;
  27753. }
  27754. public static final ObjectAllocator NIL_ALLOCATOR = new ObjectAllocator() {
  27755. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  27756. return runtime.getNil();
  27757. }
  27758. };
  27759. public static RubyClass createNilClass(Ruby runtime) {
  27760. RubyClass nilClass = runtime.defineClass("NilClass", runtime.getObject(), NIL_ALLOCATOR);
  27761. runtime.setNilClass(nilClass);
  27762. nilClass.index = ClassIndex.NIL;
  27763. nilClass.defineAnnotatedMethods(RubyNil.class);
  27764. nilClass.getMetaClass().undefineMethod("new");
  27765. // FIXME: This is causing a verification error for some reason
  27766. //nilClass.dispatcher = callbackFactory.createDispatcher(nilClass);
  27767. return nilClass;
  27768. }
  27769. @Override
  27770. public int getNativeTypeIndex() {
  27771. return ClassIndex.NIL;
  27772. }
  27773. @Override
  27774. public boolean isImmediate() {
  27775. return true;
  27776. }
  27777. @Override
  27778. public RubyClass getSingletonClass() {
  27779. return metaClass;
  27780. }
  27781. @Override
  27782. public Class<?> getJavaClass() {
  27783. return void.class;
  27784. }
  27785. // Methods of the Nil Class (nil_*):
  27786. /** nil_to_i
  27787. *
  27788. */
  27789. @JRubyMethod(name = "to_i")
  27790. public static RubyFixnum to_i(IRubyObject recv) {
  27791. return RubyFixnum.zero(recv.getRuntime());
  27792. }
  27793. /**
  27794. * nil_to_f
  27795. *
  27796. */
  27797. @JRubyMethod(name = "to_f")
  27798. public static RubyFloat to_f(IRubyObject recv) {
  27799. return RubyFloat.newFloat(recv.getRuntime(), 0.0D);
  27800. }
  27801. /** nil_to_s
  27802. *
  27803. */
  27804. @JRubyMethod(name = "to_s")
  27805. public static RubyString to_s(IRubyObject recv) {
  27806. return RubyString.newEmptyString(recv.getRuntime());
  27807. }
  27808. /** nil_to_a
  27809. *
  27810. */
  27811. @JRubyMethod(name = "to_a")
  27812. public static RubyArray to_a(IRubyObject recv) {
  27813. return recv.getRuntime().newEmptyArray();
  27814. }
  27815. /** nil_inspect
  27816. *
  27817. */
  27818. @JRubyMethod(name = "inspect")
  27819. public static RubyString inspect(IRubyObject recv) {
  27820. return recv.getRuntime().newString("nil");
  27821. }
  27822. /** nil_type
  27823. *
  27824. */
  27825. @JRubyMethod(name = "type")
  27826. public static RubyClass type(IRubyObject recv) {
  27827. return recv.getRuntime().getNilClass();
  27828. }
  27829. /** nil_and
  27830. *
  27831. */
  27832. @JRubyMethod(name = "&", required = 1)
  27833. public static RubyBoolean op_and(IRubyObject recv, IRubyObject obj) {
  27834. return recv.getRuntime().getFalse();
  27835. }
  27836. /** nil_or
  27837. *
  27838. */
  27839. @JRubyMethod(name = "|", required = 1)
  27840. public static RubyBoolean op_or(IRubyObject recv, IRubyObject obj) {
  27841. return recv.getRuntime().newBoolean(obj.isTrue());
  27842. }
  27843. /** nil_xor
  27844. *
  27845. */
  27846. @JRubyMethod(name = "^", required = 1)
  27847. public static RubyBoolean op_xor(IRubyObject recv, IRubyObject obj) {
  27848. return recv.getRuntime().newBoolean(obj.isTrue());
  27849. }
  27850. @JRubyMethod(name = "nil?")
  27851. public IRubyObject nil_p() {
  27852. return getRuntime().getTrue();
  27853. }
  27854. @Override
  27855. public RubyFixnum id() {
  27856. return getRuntime().newFixnum(4);
  27857. }
  27858. @Override
  27859. public IRubyObject taint(ThreadContext context) {
  27860. return this;
  27861. }
  27862. @Override
  27863. public IRubyObject freeze(ThreadContext context) {
  27864. return this;
  27865. }
  27866. /** nilclass_to_c
  27867. *
  27868. */
  27869. @JRubyMethod(name = "to_c", compat = CompatVersion.RUBY1_9)
  27870. public static IRubyObject to_c(ThreadContext context, IRubyObject recv) {
  27871. return RubyComplex.newComplexCanonicalize(context, RubyFixnum.zero(context.getRuntime()));
  27872. }
  27873. /** nilclass_to_r
  27874. *
  27875. */
  27876. @JRubyMethod(name = "to_r", compat = CompatVersion.RUBY1_9)
  27877. public static IRubyObject to_r(ThreadContext context, IRubyObject recv) {
  27878. return RubyRational.newRationalCanonicalize(context, RubyFixnum.zero(context.getRuntime()));
  27879. }
  27880. }
  27881. /***** BEGIN LICENSE BLOCK *****
  27882. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  27883. *
  27884. * The contents of this file are subject to the Common Public
  27885. * License Version 1.0 (the "License"); you may not use this file
  27886. * except in compliance with the License. You may obtain a copy of
  27887. * the License at http://www.eclipse.org/legal/cpl-v10.html
  27888. *
  27889. * Software distributed under the License is distributed on an "AS
  27890. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  27891. * implied. See the License for the specific language governing
  27892. * rights and limitations under the License.
  27893. *
  27894. * Copyright (C) 2007 Koichiro Ohba <koichiro@meadowy.org>
  27895. *
  27896. * Alternatively, the contents of this file may be used under the terms of
  27897. * either of the GNU General Public License Version 2 or later (the "GPL"),
  27898. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27899. * in which case the provisions of the GPL or the LGPL are applicable instead
  27900. * of those above. If you wish to allow use of your version of this file only
  27901. * under the terms of either the GPL or the LGPL, and not to allow others to
  27902. * use your version of this file under the terms of the CPL, indicate your
  27903. * decision by deleting the provisions above and replace them with the notice
  27904. * and other provisions required by the GPL or the LGPL. If you do not delete
  27905. * the provisions above, a recipient may use your version of this file under
  27906. * the terms of any one of the CPL, the GPL or the LGPL.
  27907. ***** END LICENSE BLOCK *****/
  27908. package org.jruby;
  27909. import java.nio.ByteBuffer;
  27910. import java.nio.CharBuffer;
  27911. import java.nio.charset.CharacterCodingException;
  27912. import java.nio.charset.Charset;
  27913. import java.nio.charset.CharsetDecoder;
  27914. import java.nio.charset.CharsetEncoder;
  27915. import java.nio.charset.UnsupportedCharsetException;
  27916. import java.util.HashMap;
  27917. import java.util.Map;
  27918. import org.jruby.anno.JRubyMethod;
  27919. import org.jruby.anno.JRubyModule;
  27920. import org.jruby.runtime.ThreadContext;
  27921. import org.jruby.runtime.builtin.IRubyObject;
  27922. import org.jruby.util.ByteList;
  27923. import org.jruby.util.KCode;
  27924. @JRubyModule(name="NKF")
  27925. public class RubyNKF {
  27926. public static final NKFCharset AUTO = new NKFCharset(0, "x-JISAutoDetect");
  27927. public static final NKFCharset JIS = new NKFCharset(1, "iso-2022-jp");
  27928. public static final NKFCharset EUC = new NKFCharset(2, "EUC-JP");
  27929. public static final NKFCharset SJIS = new NKFCharset(3, "Windows-31J");
  27930. public static final NKFCharset BINARY = new NKFCharset(4, null);
  27931. public static final NKFCharset NOCONV = new NKFCharset(4, null);
  27932. public static final NKFCharset UNKNOWN = new NKFCharset(0, null);
  27933. public static final NKFCharset ASCII = new NKFCharset(5, "iso-8859-1");
  27934. public static final NKFCharset UTF8 = new NKFCharset(6, "UTF-8");
  27935. public static final NKFCharset UTF16 = new NKFCharset(8, "UTF-16");
  27936. public static final NKFCharset UTF32 = new NKFCharset(12, "UTF-32");
  27937. public static final NKFCharset OTHER = new NKFCharset(16, null);
  27938. public static class NKFCharset {
  27939. private final int value;
  27940. private final String charset;
  27941. public NKFCharset(int v, String c) {
  27942. value = v;
  27943. charset = c;
  27944. }
  27945. public int getValue() {
  27946. return value;
  27947. }
  27948. public String getCharset() {
  27949. return charset;
  27950. }
  27951. }
  27952. public static void createNKF(Ruby runtime) {
  27953. RubyModule nkfModule = runtime.defineModule("NKF");
  27954. nkfModule.defineConstant("AUTO", RubyFixnum.newFixnum(runtime, AUTO.getValue()));
  27955. nkfModule.defineConstant("JIS", RubyFixnum.newFixnum(runtime, JIS.getValue()));
  27956. nkfModule.defineConstant("EUC", RubyFixnum.newFixnum(runtime, EUC.getValue()));
  27957. nkfModule.defineConstant("SJIS", RubyFixnum.newFixnum(runtime, SJIS.getValue()));
  27958. nkfModule.defineConstant("BINARY", RubyFixnum.newFixnum(runtime, BINARY.getValue()));
  27959. nkfModule.defineConstant("NOCONV", RubyFixnum.newFixnum(runtime, NOCONV.getValue()));
  27960. nkfModule.defineConstant("UNKNOWN", RubyFixnum.newFixnum(runtime, UNKNOWN.getValue()));
  27961. nkfModule.defineConstant("ASCII", RubyFixnum.newFixnum(runtime, ASCII.getValue()));
  27962. nkfModule.defineConstant("UTF8", RubyFixnum.newFixnum(runtime, UTF8.getValue()));
  27963. nkfModule.defineConstant("UTF16", RubyFixnum.newFixnum(runtime, UTF16.getValue()));
  27964. nkfModule.defineConstant("UTF32", RubyFixnum.newFixnum(runtime, UTF32.getValue()));
  27965. nkfModule.defineConstant("OTHER", RubyFixnum.newFixnum(runtime, OTHER.getValue()));
  27966. RubyString version = runtime.newString("2.0.7 (JRuby 2007-05-11)");
  27967. RubyString nkfVersion = runtime.newString("2.0.7");
  27968. RubyString nkfDate = runtime.newString("2007-05-11");
  27969. ThreadContext context = runtime.getCurrentContext();
  27970. version.freeze(context);
  27971. nkfVersion.freeze(context);
  27972. nkfDate.freeze(context);
  27973. nkfModule.defineAnnotatedMethods(RubyNKF.class);
  27974. }
  27975. @JRubyMethod(name = "guess", required = 1, module = true)
  27976. public static IRubyObject guess(ThreadContext context, IRubyObject recv, IRubyObject s) {
  27977. Ruby runtime = context.getRuntime();
  27978. if (!s.respondsTo("to_str")) {
  27979. throw runtime.newTypeError("can't convert " + s.getMetaClass() + " into String");
  27980. }
  27981. ByteList bytes = s.convertToString().getByteList();
  27982. ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  27983. CharsetDecoder decoder = Charset.forName("x-JISAutoDetect").newDecoder();
  27984. try {
  27985. decoder.decode(buf);
  27986. } catch (CharacterCodingException e) {
  27987. return runtime.newFixnum(UNKNOWN.getValue());
  27988. }
  27989. if (!decoder.isCharsetDetected()) {
  27990. return runtime.newFixnum(UNKNOWN.getValue());
  27991. }
  27992. Charset charset = decoder.detectedCharset();
  27993. String name = charset.name();
  27994. // System.out.println("detect: " + name + "\n");
  27995. if ("Shift_JIS".equals(name))
  27996. return runtime.newFixnum(SJIS.getValue());
  27997. if ("windows-31j".equals(name))
  27998. return runtime.newFixnum(SJIS.getValue());
  27999. else if ("EUC-JP".equals(name))
  28000. return runtime.newFixnum(EUC.getValue());
  28001. else if ("ISO-2022-JP".equals(name))
  28002. return runtime.newFixnum(JIS.getValue());
  28003. else
  28004. return runtime.newFixnum(UNKNOWN.getValue());
  28005. }
  28006. @JRubyMethod(name = "guess1", required = 1, module = true)
  28007. public static IRubyObject guess1(ThreadContext context, IRubyObject recv, IRubyObject str) {
  28008. return guess(context, recv, str);
  28009. }
  28010. @JRubyMethod(name = "guess2", required = 1, module = true)
  28011. public static IRubyObject guess2(ThreadContext context, IRubyObject recv, IRubyObject str) {
  28012. return guess(context, recv, str);
  28013. }
  28014. @JRubyMethod(name = "nkf", required = 2, module = true)
  28015. public static IRubyObject nkf(ThreadContext context, IRubyObject recv, IRubyObject opt, IRubyObject str) {
  28016. Ruby runtime = context.getRuntime();
  28017. if (!opt.respondsTo("to_str")) {
  28018. throw runtime.newTypeError("can't convert " + opt.getMetaClass() + " into String");
  28019. }
  28020. if (!str.respondsTo("to_str")) {
  28021. throw runtime.newTypeError("can't convert " + str.getMetaClass() + " into String");
  28022. }
  28023. Map<String, NKFCharset> options = parseOpt(opt.convertToString().toString());
  28024. NKFCharset nc = options.get("input");
  28025. if (nc.getValue() == AUTO.getValue()) {
  28026. KCode kcode = runtime.getKCode();
  28027. if (kcode == KCode.SJIS) {
  28028. nc = SJIS;
  28029. } else if (kcode == KCode.EUC) {
  28030. nc = EUC;
  28031. } else if (kcode == KCode.UTF8) {
  28032. nc = UTF8;
  28033. }
  28034. }
  28035. String decodeCharset = nc.getCharset();
  28036. String encodeCharset = options.get("output").getCharset();
  28037. return convert(context, decodeCharset, encodeCharset, str);
  28038. }
  28039. private static IRubyObject convert(ThreadContext context, String decodeCharset,
  28040. String encodeCharset, IRubyObject str) {
  28041. Ruby runtime = context.getRuntime();
  28042. CharsetDecoder decoder;
  28043. CharsetEncoder encoder;
  28044. try {
  28045. decoder = Charset.forName(decodeCharset).newDecoder();
  28046. encoder = Charset.forName(encodeCharset).newEncoder();
  28047. } catch (UnsupportedCharsetException e) {
  28048. throw runtime.newArgumentError("invalid encoding");
  28049. }
  28050. ByteList bytes = str.convertToString().getByteList();
  28051. ByteBuffer buf = ByteBuffer.wrap(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  28052. try {
  28053. CharBuffer cbuf = decoder.decode(buf);
  28054. buf = encoder.encode(cbuf);
  28055. } catch (CharacterCodingException e) {
  28056. throw runtime.newArgumentError("invalid encoding");
  28057. }
  28058. byte[] arr = buf.array();
  28059. return runtime.newString(new ByteList(arr, 0, buf.limit()));
  28060. }
  28061. private static int optionUTF(String s, int i) {
  28062. int n = 8;
  28063. if (i+1 < s.length() && Character.isDigit(s.charAt(i+1))) {
  28064. n = Character.digit(s.charAt(i+1), 10);
  28065. if (i+2 < s.length() && Character.isDigit(s.charAt(i+2))) {
  28066. n *= 10;
  28067. n += Character.digit(s.charAt(i+2), 10);
  28068. }
  28069. }
  28070. return n;
  28071. }
  28072. private static Map<String, NKFCharset> parseOpt(String s) {
  28073. Map<String, NKFCharset> options = new HashMap<String, NKFCharset>();
  28074. // default options
  28075. options.put("input", AUTO);
  28076. options.put("output", JIS);
  28077. for (int i = 0; i < s.length(); i++) {
  28078. switch (s.charAt(i)) {
  28079. case 'b':
  28080. break;
  28081. case 'u':
  28082. break;
  28083. case 'j': // iso-2022-jp
  28084. options.put("output", JIS);
  28085. break;
  28086. case 's': // Shift_JIS
  28087. options.put("output", SJIS);
  28088. break;
  28089. case 'e': // EUC-JP
  28090. options.put("output", EUC);
  28091. break;
  28092. case 'w': // UTF-8
  28093. {
  28094. int n = optionUTF(s, i);
  28095. if (n == 32)
  28096. options.put("output", UTF32);
  28097. else if (n == 16)
  28098. options.put("output", UTF16);
  28099. else
  28100. options.put("output", UTF8);
  28101. }
  28102. break;
  28103. case 'J': // iso-2022-jp
  28104. options.put("input", JIS);
  28105. break;
  28106. case 'S': // Shift_JIS
  28107. options.put("input", SJIS);
  28108. break;
  28109. case 'E': // EUC-JP
  28110. options.put("input", EUC);
  28111. break;
  28112. case 'W': // UTF-8
  28113. {
  28114. int n = optionUTF(s, i);
  28115. if (n == 32)
  28116. options.put("input", UTF32);
  28117. else if (n == 16)
  28118. options.put("input", UTF16);
  28119. else
  28120. options.put("input", UTF8);
  28121. }
  28122. break;
  28123. case 't':
  28124. break;
  28125. case 'r':
  28126. break;
  28127. case 'h':
  28128. break;
  28129. case 'm':
  28130. break;
  28131. case 'M':
  28132. break;
  28133. case 'l':
  28134. break;
  28135. case 'f':
  28136. break;
  28137. case 'F':
  28138. break;
  28139. case 'Z':
  28140. break;
  28141. case 'X':
  28142. break;
  28143. case 'x':
  28144. break;
  28145. case 'B':
  28146. break;
  28147. case 'T':
  28148. break;
  28149. case 'd':
  28150. break;
  28151. case 'c':
  28152. break;
  28153. case 'I':
  28154. break;
  28155. case 'L':
  28156. break;
  28157. case '-':
  28158. if (s.charAt(i+1) == '-') {
  28159. // long name option
  28160. }
  28161. default:
  28162. }
  28163. }
  28164. return options;
  28165. }
  28166. }
  28167. /***** BEGIN LICENSE BLOCK *****
  28168. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  28169. *
  28170. * The contents of this file are subject to the Common Public
  28171. * License Version 1.0 (the "License"); you may not use this file
  28172. * except in compliance with the License. You may obtain a copy of
  28173. * the License at http://www.eclipse.org/legal/cpl-v10.html
  28174. *
  28175. * Software distributed under the License is distributed on an "AS
  28176. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  28177. * implied. See the License for the specific language governing
  28178. * rights and limitations under the License.
  28179. *
  28180. * Alternatively, the contents of this file may be used under the terms of
  28181. * either of the GNU General Public License Version 2 or later (the "GPL"),
  28182. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28183. * in which case the provisions of the GPL or the LGPL are applicable instead
  28184. * of those above. If you wish to allow use of your version of this file only
  28185. * under the terms of either the GPL or the LGPL, and not to allow others to
  28186. * use your version of this file under the terms of the CPL, indicate your
  28187. * decision by deleting the provisions above and replace them with the notice
  28188. * and other provisions required by the GPL or the LGPL. If you do not delete
  28189. * the provisions above, a recipient may use your version of this file under
  28190. * the terms of any one of the CPL, the GPL or the LGPL.
  28191. ***** END LICENSE BLOCK *****/
  28192. package org.jruby;
  28193. import org.jruby.anno.JRubyMethod;
  28194. import org.jruby.anno.JRubyClass;
  28195. import org.jruby.runtime.Block;
  28196. import org.jruby.runtime.ObjectAllocator;
  28197. import org.jruby.runtime.builtin.IRubyObject;
  28198. @JRubyClass(name="NoMethodError", parent="NameError")
  28199. public class RubyNoMethodError extends RubyNameError {
  28200. private IRubyObject args;
  28201. private static final ObjectAllocator NOMETHODERROR_ALLOCATOR = new ObjectAllocator() {
  28202. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  28203. return new RubyNoMethodError(runtime, klass);
  28204. }
  28205. };
  28206. public static RubyClass createNoMethodErrorClass(Ruby runtime, RubyClass nameErrorClass) {
  28207. RubyClass noMethodErrorClass = runtime.defineClass("NoMethodError", nameErrorClass, NOMETHODERROR_ALLOCATOR);
  28208. noMethodErrorClass.defineAnnotatedMethods(RubyNoMethodError.class);
  28209. return noMethodErrorClass;
  28210. }
  28211. protected RubyNoMethodError(Ruby runtime, RubyClass exceptionClass) {
  28212. super(runtime, exceptionClass, exceptionClass.getName());
  28213. this.args = runtime.getNil();
  28214. }
  28215. public RubyNoMethodError(Ruby runtime, RubyClass exceptionClass, String message, String name, IRubyObject args) {
  28216. super(runtime, exceptionClass, message, name);
  28217. this.args = args;
  28218. }
  28219. @JRubyMethod(name = "initialize", optional = 3, frame = true)
  28220. public IRubyObject initialize(IRubyObject[] args, Block block) {
  28221. if (args.length > 2) {
  28222. this.args = args[args.length - 1];
  28223. IRubyObject []tmpArgs = new IRubyObject[args.length - 1];
  28224. System.arraycopy(args, 0, tmpArgs, 0, tmpArgs.length);
  28225. args = tmpArgs;
  28226. } else {
  28227. this.args = getRuntime().getNil();
  28228. }
  28229. super.initialize(args, block);
  28230. return this;
  28231. }
  28232. @JRubyMethod(name = "args")
  28233. public IRubyObject args() {
  28234. return args;
  28235. }
  28236. }
  28237. /***** BEGIN LICENSE BLOCK *****
  28238. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  28239. *
  28240. * The contents of this file are subject to the Common Public
  28241. * License Version 1.0 (the "License"); you may not use this file
  28242. * except in compliance with the License. You may obtain a copy of
  28243. * the License at http://www.eclipse.org/legal/cpl-v10.html
  28244. *
  28245. * Software distributed under the License is distributed on an "AS
  28246. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  28247. * implied. See the License for the specific language governing
  28248. * rights and limitations under the License.
  28249. *
  28250. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  28251. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  28252. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  28253. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  28254. * Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
  28255. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  28256. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  28257. * Copyright (C) 2006 Antti Karanta <Antti.Karanta@napa.fi>
  28258. *
  28259. * Alternatively, the contents of this file may be used under the terms of
  28260. * either of the GNU General Public License Version 2 or later (the "GPL"),
  28261. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28262. * in which case the provisions of the GPL or the LGPL are applicable instead
  28263. * of those above. If you wish to allow use of your version of this file only
  28264. * under the terms of either the GPL or the LGPL, and not to allow others to
  28265. * use your version of this file under the terms of the CPL, indicate your
  28266. * decision by deleting the provisions above and replace them with the notice
  28267. * and other provisions required by the GPL or the LGPL. If you do not delete
  28268. * the provisions above, a recipient may use your version of this file under
  28269. * the terms of any one of the CPL, the GPL or the LGPL.
  28270. ***** END LICENSE BLOCK *****/
  28271. package org.jruby;
  28272. import static org.jruby.util.Numeric.f_abs;
  28273. import static org.jruby.util.Numeric.f_arg;
  28274. import static org.jruby.util.Numeric.f_negative_p;
  28275. import java.math.BigInteger;
  28276. import org.jruby.anno.JRubyClass;
  28277. import org.jruby.anno.JRubyMethod;
  28278. import org.jruby.exceptions.RaiseException;
  28279. import org.jruby.javasupport.util.RuntimeHelpers;
  28280. import org.jruby.runtime.Block;
  28281. import org.jruby.runtime.MethodIndex;
  28282. import org.jruby.runtime.ObjectAllocator;
  28283. import org.jruby.runtime.ThreadContext;
  28284. import org.jruby.runtime.Visibility;
  28285. import org.jruby.runtime.builtin.IRubyObject;
  28286. import org.jruby.util.ByteList;
  28287. import org.jruby.util.Convert;
  28288. /**
  28289. * Base class for all numerical types in ruby.
  28290. */
  28291. // TODO: Numeric.new works in Ruby and it does here too. However trying to use
  28292. // that instance in a numeric operation should generate an ArgumentError. Doing
  28293. // this seems so pathological I do not see the need to fix this now.
  28294. @JRubyClass(name="Numeric", include="Comparable")
  28295. public class RubyNumeric extends RubyObject {
  28296. public static RubyClass createNumericClass(Ruby runtime) {
  28297. RubyClass numeric = runtime.defineClass("Numeric", runtime.getObject(), NUMERIC_ALLOCATOR);
  28298. runtime.setNumeric(numeric);
  28299. numeric.kindOf = new RubyModule.KindOf() {
  28300. @Override
  28301. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  28302. return obj instanceof RubyNumeric;
  28303. }
  28304. };
  28305. numeric.includeModule(runtime.getComparable());
  28306. numeric.defineAnnotatedMethods(RubyNumeric.class);
  28307. return numeric;
  28308. }
  28309. protected static final ObjectAllocator NUMERIC_ALLOCATOR = new ObjectAllocator() {
  28310. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  28311. return new RubyNumeric(runtime, klass);
  28312. }
  28313. };
  28314. public static double DBL_EPSILON=2.2204460492503131e-16;
  28315. private static IRubyObject convertToNum(double val, Ruby runtime) {
  28316. if (val >= (double) RubyFixnum.MAX || val < (double) RubyFixnum.MIN) {
  28317. return RubyBignum.newBignum(runtime, val);
  28318. }
  28319. return RubyFixnum.newFixnum(runtime, (long) val);
  28320. }
  28321. public RubyNumeric(Ruby runtime, RubyClass metaClass) {
  28322. super(runtime, metaClass);
  28323. }
  28324. public RubyNumeric(Ruby runtime, RubyClass metaClass, boolean useObjectSpace) {
  28325. super(runtime, metaClass, useObjectSpace);
  28326. }
  28327. // The implementations of these are all bonus (see TODO above) I was going
  28328. // to throw an error from these, but it appears to be the wrong place to
  28329. // do it.
  28330. public double getDoubleValue() {
  28331. return 0;
  28332. }
  28333. public long getLongValue() {
  28334. return 0;
  28335. }
  28336. public static RubyNumeric newNumeric(Ruby runtime) {
  28337. return new RubyNumeric(runtime, runtime.getNumeric());
  28338. }
  28339. /* ================
  28340. * Utility Methods
  28341. * ================
  28342. */
  28343. /** rb_num2int, NUM2INT
  28344. *
  28345. */
  28346. public static int num2int(IRubyObject arg) {
  28347. long num = num2long(arg);
  28348. checkInt(arg, num);
  28349. return (int)num;
  28350. }
  28351. /** check_int
  28352. *
  28353. */
  28354. public static void checkInt(IRubyObject arg, long num){
  28355. String s;
  28356. if (num < Integer.MIN_VALUE) {
  28357. s = "small";
  28358. } else if (num > Integer.MAX_VALUE) {
  28359. s = "big";
  28360. } else {
  28361. return;
  28362. }
  28363. throw arg.getRuntime().newRangeError("integer " + num + " too " + s + " to convert to `int'");
  28364. }
  28365. /**
  28366. * NUM2CHR
  28367. */
  28368. public static byte num2chr(IRubyObject arg) {
  28369. if (arg instanceof RubyString) {
  28370. String value = ((RubyString) arg).toString();
  28371. if (value != null && value.length() > 0) return (byte) value.charAt(0);
  28372. }
  28373. return (byte) num2int(arg);
  28374. }
  28375. /** rb_num2long and FIX2LONG (numeric.c)
  28376. *
  28377. */
  28378. public static long num2long(IRubyObject arg) {
  28379. if (arg instanceof RubyFixnum) {
  28380. return ((RubyFixnum) arg).getLongValue();
  28381. }
  28382. if (arg.isNil()) {
  28383. throw arg.getRuntime().newTypeError("no implicit conversion from nil to integer");
  28384. }
  28385. if (arg instanceof RubyFloat) {
  28386. double aFloat = ((RubyFloat) arg).getDoubleValue();
  28387. if (aFloat <= (double) Long.MAX_VALUE && aFloat >= (double) Long.MIN_VALUE) {
  28388. return (long) aFloat;
  28389. } else {
  28390. // TODO: number formatting here, MRI uses "%-.10g", 1.4 API is a must?
  28391. throw arg.getRuntime().newRangeError("float " + aFloat + " out of range of integer");
  28392. }
  28393. } else if (arg instanceof RubyBignum) {
  28394. return RubyBignum.big2long((RubyBignum) arg);
  28395. }
  28396. return arg.convertToInteger().getLongValue();
  28397. }
  28398. /** rb_dbl2big + LONG2FIX at once (numeric.c)
  28399. *
  28400. */
  28401. public static IRubyObject dbl2num(Ruby runtime, double val) {
  28402. if (Double.isInfinite(val)) {
  28403. throw runtime.newFloatDomainError(val < 0 ? "-Infinity" : "Infinity");
  28404. }
  28405. if (Double.isNaN(val)) {
  28406. throw runtime.newFloatDomainError("NaN");
  28407. }
  28408. return convertToNum(val,runtime);
  28409. }
  28410. /** rb_num2dbl and NUM2DBL
  28411. *
  28412. */
  28413. public static double num2dbl(IRubyObject arg) {
  28414. if (arg instanceof RubyFloat) {
  28415. return ((RubyFloat) arg).getDoubleValue();
  28416. } else if (arg instanceof RubyString) {
  28417. throw arg.getRuntime().newTypeError("no implicit conversion to float from string");
  28418. } else if (arg == arg.getRuntime().getNil()) {
  28419. throw arg.getRuntime().newTypeError("no implicit conversion to float from nil");
  28420. }
  28421. return arg.convertToFloat().getDoubleValue();
  28422. }
  28423. /** rb_dbl_cmp (numeric.c)
  28424. *
  28425. */
  28426. public static IRubyObject dbl_cmp(Ruby runtime, double a, double b) {
  28427. if (Double.isNaN(a) || Double.isNaN(b)) {
  28428. return runtime.getNil();
  28429. }
  28430. if (a > b) {
  28431. return RubyFixnum.one(runtime);
  28432. }
  28433. if (a < b) {
  28434. return RubyFixnum.minus_one(runtime);
  28435. }
  28436. return RubyFixnum.zero(runtime);
  28437. }
  28438. public static long fix2long(IRubyObject arg) {
  28439. return ((RubyFixnum) arg).getLongValue();
  28440. }
  28441. public static int fix2int(IRubyObject arg) {
  28442. long num = arg instanceof RubyFixnum ? fix2long(arg) : num2long(arg);
  28443. checkInt(arg, num);
  28444. return (int) num;
  28445. }
  28446. public static RubyInteger str2inum(Ruby runtime, RubyString str, int base) {
  28447. return str2inum(runtime,str,base,false);
  28448. }
  28449. public static RubyNumeric int2fix(Ruby runtime, long val) {
  28450. return RubyFixnum.newFixnum(runtime,val);
  28451. }
  28452. /** rb_num2fix
  28453. *
  28454. */
  28455. public static IRubyObject num2fix(IRubyObject val) {
  28456. if (val instanceof RubyFixnum) {
  28457. return val;
  28458. }
  28459. if (val instanceof RubyBignum) {
  28460. // any BigInteger is bigger than Fixnum and we don't have FIXABLE
  28461. throw val.getRuntime().newRangeError("integer " + val + " out of range of fixnum");
  28462. }
  28463. return RubyFixnum.newFixnum(val.getRuntime(), num2long(val));
  28464. }
  28465. /**
  28466. * Converts a string representation of an integer to the integer value.
  28467. * Parsing starts at the beginning of the string (after leading and
  28468. * trailing whitespace have been removed), and stops at the end or at the
  28469. * first character that can't be part of an integer. Leading signs are
  28470. * allowed. If <code>base</code> is zero, strings that begin with '0[xX]',
  28471. * '0[bB]', or '0' (optionally preceded by a sign) will be treated as hex,
  28472. * binary, or octal numbers, respectively. If a non-zero base is given,
  28473. * only the prefix (if any) that is appropriate to that base will be
  28474. * parsed correctly. For example, if the base is zero or 16, the string
  28475. * "0xff" will be converted to 256, but if the base is 10, it will come out
  28476. * as zero, since 'x' is not a valid decimal digit. If the string fails
  28477. * to parse as a number, zero is returned.
  28478. *
  28479. * @param runtime the ruby runtime
  28480. * @param str the string to be converted
  28481. * @param base the expected base of the number (for example, 2, 8, 10, 16),
  28482. * or 0 if the method should determine the base automatically
  28483. * (defaults to 10). Values 0 and 2-36 are permitted. Any other
  28484. * value will result in an ArgumentError.
  28485. * @param strict if true, enforce the strict criteria for String encoding of
  28486. * numeric values, as required by Integer('n'), and raise an
  28487. * exception when those criteria are not met. Otherwise, allow
  28488. * lax expression of values, as permitted by String#to_i, and
  28489. * return a value in almost all cases (excepting illegal radix).
  28490. * TODO: describe the rules/criteria
  28491. * @return a RubyFixnum or (if necessary) a RubyBignum representing
  28492. * the result of the conversion, which will be zero if the
  28493. * conversion failed.
  28494. */
  28495. public static RubyInteger str2inum(Ruby runtime, RubyString str, int base, boolean strict) {
  28496. if (base != 0 && (base < 2 || base > 36)) {
  28497. throw runtime.newArgumentError("illegal radix " + base);
  28498. }
  28499. ByteList bytes = str.getByteList();
  28500. try {
  28501. return runtime.newFixnum(Convert.byteListToLong(bytes, base, strict));
  28502. } catch (InvalidIntegerException e) {
  28503. return str2inumIIE(strict, runtime, str);
  28504. } catch (NumberTooLargeException e) {
  28505. return str2inumNTLE(strict, runtime, str, bytes, base);
  28506. }
  28507. }
  28508. private static RubyInteger str2inumIIE(boolean strict, Ruby runtime, RubyString str) throws RaiseException {
  28509. if (strict) {
  28510. throw runtime.newArgumentError("invalid value for Integer: " + str.callMethod(runtime.getCurrentContext(), "inspect").toString());
  28511. }
  28512. return RubyFixnum.zero(runtime);
  28513. }
  28514. private static RubyInteger str2inumNTLE(boolean strict, Ruby runtime, RubyString str, ByteList bytes, int base) {
  28515. try {
  28516. BigInteger bi = Convert.byteListToBigInteger(bytes, base, strict);
  28517. return new RubyBignum(runtime, bi);
  28518. } catch (InvalidIntegerException e2) {
  28519. return str2inumIIE(strict, runtime, str);
  28520. }
  28521. }
  28522. public static RubyFloat str2fnum(Ruby runtime, RubyString arg) {
  28523. return str2fnum(runtime,arg,false);
  28524. }
  28525. /**
  28526. * Converts a string representation of a floating-point number to the
  28527. * numeric value. Parsing starts at the beginning of the string (after
  28528. * leading and trailing whitespace have been removed), and stops at the
  28529. * end or at the first character that can't be part of a number. If
  28530. * the string fails to parse as a number, 0.0 is returned.
  28531. *
  28532. * @param runtime the ruby runtime
  28533. * @param arg the string to be converted
  28534. * @param strict if true, enforce the strict criteria for String encoding of
  28535. * numeric values, as required by Float('n'), and raise an
  28536. * exception when those criteria are not met. Otherwise, allow
  28537. * lax expression of values, as permitted by String#to_f, and
  28538. * return a value in all cases.
  28539. * TODO: describe the rules/criteria
  28540. * @return a RubyFloat representing the result of the conversion, which
  28541. * will be 0.0 if the conversion failed.
  28542. */
  28543. public static RubyFloat str2fnum(Ruby runtime, RubyString arg, boolean strict) {
  28544. final double ZERO = 0.0;
  28545. try {
  28546. return new RubyFloat(runtime,Convert.byteListToDouble(arg.getByteList(),strict));
  28547. } catch (NumberFormatException e) {
  28548. if (strict) {
  28549. throw runtime.newArgumentError("invalid value for Float(): "
  28550. + arg.callMethod(runtime.getCurrentContext(), "inspect").toString());
  28551. }
  28552. return new RubyFloat(runtime,ZERO);
  28553. }
  28554. }
  28555. /** Numeric methods. (num_*)
  28556. *
  28557. */
  28558. protected IRubyObject[] getCoerced(ThreadContext context, IRubyObject other, boolean error) {
  28559. IRubyObject result;
  28560. try {
  28561. result = other.callMethod(context, "coerce", this);
  28562. } catch (RaiseException e) {
  28563. if (error) {
  28564. throw getRuntime().newTypeError(
  28565. other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
  28566. }
  28567. return null;
  28568. }
  28569. if (!(result instanceof RubyArray) || ((RubyArray)result).getLength() != 2) {
  28570. throw getRuntime().newTypeError("coerce must return [x, y]");
  28571. }
  28572. return ((RubyArray)result).toJavaArray();
  28573. }
  28574. protected IRubyObject callCoerced(ThreadContext context, String method, IRubyObject other, boolean err) {
  28575. IRubyObject[] args = getCoerced(context, other, err);
  28576. if(args == null) {
  28577. return getRuntime().getNil();
  28578. }
  28579. return args[0].callMethod(context, method, args[1]);
  28580. }
  28581. protected IRubyObject callCoerced(ThreadContext context, String method, IRubyObject other) {
  28582. IRubyObject[] args = getCoerced(context, other, false);
  28583. if(args == null) {
  28584. return getRuntime().getNil();
  28585. }
  28586. return args[0].callMethod(context, method, args[1]);
  28587. }
  28588. // beneath are rewritten coercions that reflect MRI logic, the aboves are used only by RubyBigDecimal
  28589. /** coerce_body
  28590. *
  28591. */
  28592. protected final IRubyObject coerceBody(ThreadContext context, IRubyObject other) {
  28593. return other.callMethod(context, "coerce", this);
  28594. }
  28595. /** do_coerce
  28596. *
  28597. */
  28598. protected final RubyArray doCoerce(ThreadContext context, IRubyObject other, boolean err) {
  28599. IRubyObject result;
  28600. try {
  28601. result = coerceBody(context, other);
  28602. } catch (RaiseException e) {
  28603. if (err) {
  28604. throw getRuntime().newTypeError(
  28605. other.getMetaClass().getName() + " can't be coerced into " + getMetaClass().getName());
  28606. }
  28607. return null;
  28608. }
  28609. if (!(result instanceof RubyArray) || ((RubyArray) result).getLength() != 2) {
  28610. throw getRuntime().newTypeError("coerce must return [x, y]");
  28611. }
  28612. return (RubyArray) result;
  28613. }
  28614. /** rb_num_coerce_bin
  28615. * coercion taking two arguments
  28616. */
  28617. protected final IRubyObject coerceBin(ThreadContext context, String method, IRubyObject other) {
  28618. RubyArray ary = doCoerce(context, other, true);
  28619. return (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
  28620. }
  28621. /** rb_num_coerce_cmp
  28622. * coercion used for comparisons
  28623. */
  28624. protected final IRubyObject coerceCmp(ThreadContext context, String method, IRubyObject other) {
  28625. RubyArray ary = doCoerce(context, other, false);
  28626. if (ary == null) {
  28627. return getRuntime().getNil(); // MRI does it!
  28628. }
  28629. return (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
  28630. }
  28631. /** rb_num_coerce_relop
  28632. * coercion used for relative operators
  28633. */
  28634. protected final IRubyObject coerceRelOp(ThreadContext context, String method, IRubyObject other) {
  28635. RubyArray ary = doCoerce(context, other, false);
  28636. if (ary == null) {
  28637. return RubyComparable.cmperr(this, other);
  28638. }
  28639. return unwrapCoerced(context, method, other, ary);
  28640. }
  28641. private final IRubyObject unwrapCoerced(ThreadContext context, String method, IRubyObject other, RubyArray ary) {
  28642. IRubyObject result = (ary.eltInternal(0)).callMethod(context, method, ary.eltInternal(1));
  28643. if (result.isNil()) {
  28644. return RubyComparable.cmperr(this, other);
  28645. }
  28646. return result;
  28647. }
  28648. public RubyNumeric asNumeric() {
  28649. return this;
  28650. }
  28651. /* ================
  28652. * Instance Methods
  28653. * ================
  28654. */
  28655. /** num_sadded
  28656. *
  28657. */
  28658. @JRubyMethod(name = "singleton_method_added")
  28659. public IRubyObject sadded(IRubyObject name) {
  28660. throw getRuntime().newTypeError("can't define singleton method " + name + " for " + getType().getName());
  28661. }
  28662. /** num_init_copy
  28663. *
  28664. */
  28665. @Override
  28666. @JRubyMethod(name = "initialize_copy", visibility = Visibility.PRIVATE)
  28667. public IRubyObject initialize_copy(IRubyObject arg) {
  28668. throw getRuntime().newTypeError("can't copy " + getType().getName());
  28669. }
  28670. /** num_coerce
  28671. *
  28672. */
  28673. @JRubyMethod(name = "coerce")
  28674. public IRubyObject coerce(IRubyObject other) {
  28675. if (getClass() == other.getClass()) return getRuntime().newArray(other, this);
  28676. IRubyObject cdr = RubyKernel.new_float(this, this);
  28677. IRubyObject car = RubyKernel.new_float(this, other);
  28678. return getRuntime().newArray(car, cdr);
  28679. }
  28680. /** num_uplus
  28681. *
  28682. */
  28683. @JRubyMethod(name = "+@")
  28684. public IRubyObject op_uplus() {
  28685. return this;
  28686. }
  28687. /** num_uminus
  28688. *
  28689. */
  28690. @JRubyMethod(name = "-@")
  28691. public IRubyObject op_uminus(ThreadContext context) {
  28692. RubyFixnum zero = RubyFixnum.zero(getRuntime());
  28693. RubyArray ary = zero.doCoerce(context, this, true);
  28694. return ary.eltInternal(0).callMethod(context, MethodIndex.OP_MINUS, "-", ary.eltInternal(1));
  28695. }
  28696. /** num_cmp
  28697. *
  28698. */
  28699. @JRubyMethod(name = "<=>")
  28700. public IRubyObject op_cmp(IRubyObject other) {
  28701. if (this == other) { // won't hurt fixnums
  28702. return RubyFixnum.zero(getRuntime());
  28703. }
  28704. return getRuntime().getNil();
  28705. }
  28706. /** num_eql
  28707. *
  28708. */
  28709. @JRubyMethod(name = "eql?")
  28710. public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
  28711. if (getClass() != other.getClass()) return getRuntime().getFalse();
  28712. return equalInternal(context, this, other) ? getRuntime().getTrue() : getRuntime().getFalse();
  28713. }
  28714. /** num_quo
  28715. *
  28716. */
  28717. @JRubyMethod(name = "quo")
  28718. public IRubyObject quo(ThreadContext context, IRubyObject other) {
  28719. return callMethod(context, "/", other);
  28720. }
  28721. /** num_quo
  28722. *
  28723. */
  28724. @JRubyMethod(name = "quo", compat = CompatVersion.RUBY1_9)
  28725. public IRubyObject quo_19(ThreadContext context, IRubyObject other) {
  28726. return RubyRational.newRationalRaw(context.getRuntime(), this).callMethod(context, "/", other);
  28727. }
  28728. /** num_div
  28729. *
  28730. */
  28731. @JRubyMethod(name = "div")
  28732. public IRubyObject div(ThreadContext context, IRubyObject other) {
  28733. return callMethod(context, "/", other).convertToFloat().floor();
  28734. }
  28735. /** num_divmod
  28736. *
  28737. */
  28738. @JRubyMethod(name = "divmod")
  28739. public IRubyObject divmod(ThreadContext context, IRubyObject other) {
  28740. return RubyArray.newArray(getRuntime(), div(context, other), modulo(context, other));
  28741. }
  28742. /** num_fdiv (1.9) */
  28743. @JRubyMethod(name = "fdiv", compat = CompatVersion.RUBY1_9)
  28744. public IRubyObject fdiv(ThreadContext context, IRubyObject other) {
  28745. return RuntimeHelpers.invoke(context, this.convertToFloat(), "/", other);
  28746. }
  28747. /** num_modulo
  28748. *
  28749. */
  28750. @JRubyMethod(name = "modulo")
  28751. public IRubyObject modulo(ThreadContext context, IRubyObject other) {
  28752. return callMethod(context, "%", other);
  28753. }
  28754. /** num_remainder
  28755. *
  28756. */
  28757. @JRubyMethod(name = "remainder")
  28758. public IRubyObject remainder(ThreadContext context, IRubyObject dividend) {
  28759. IRubyObject z = callMethod(context, "%", dividend);
  28760. IRubyObject x = this;
  28761. RubyFixnum zero = RubyFixnum.zero(getRuntime());
  28762. if (!equalInternal(context, z, zero) &&
  28763. ((x.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue() &&
  28764. dividend.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue()) ||
  28765. (x.callMethod(context, MethodIndex.OP_GT, ">", zero).isTrue() &&
  28766. dividend.callMethod(context, MethodIndex.OP_LT, "<", zero).isTrue()))) {
  28767. return z.callMethod(context, MethodIndex.OP_MINUS, "-", dividend);
  28768. } else {
  28769. return z;
  28770. }
  28771. }
  28772. /** num_abs
  28773. *
  28774. */
  28775. @JRubyMethod(name = "abs")
  28776. public IRubyObject abs(ThreadContext context) {
  28777. if (callMethod(context, MethodIndex.OP_LT, "<", RubyFixnum.zero(getRuntime())).isTrue()) {
  28778. return callMethod(context, "-@");
  28779. }
  28780. return this;
  28781. }
  28782. /** num_to_int
  28783. *
  28784. */
  28785. @JRubyMethod(name = "to_int")
  28786. public IRubyObject to_int(ThreadContext context) {
  28787. return RuntimeHelpers.invoke(context, this, "to_i");
  28788. }
  28789. /** num_scalar_p
  28790. *
  28791. */
  28792. @JRubyMethod(name = "scalar?", compat = CompatVersion.RUBY1_9)
  28793. public IRubyObject scalar_p() {
  28794. return getRuntime().getTrue();
  28795. }
  28796. /** num_int_p
  28797. *
  28798. */
  28799. @JRubyMethod(name = "integer?")
  28800. public IRubyObject integer_p() {
  28801. return getRuntime().getFalse();
  28802. }
  28803. /** num_zero_p
  28804. *
  28805. */
  28806. @JRubyMethod(name = "zero?")
  28807. public IRubyObject zero_p(ThreadContext context) {
  28808. return equalInternal(context, this, RubyFixnum.zero(getRuntime())) ? getRuntime().getTrue() : getRuntime().getFalse();
  28809. }
  28810. /** num_nonzero_p
  28811. *
  28812. */
  28813. @JRubyMethod(name = "nonzero?")
  28814. public IRubyObject nonzero_p(ThreadContext context) {
  28815. if (callMethod(context, "zero?").isTrue()) {
  28816. return getRuntime().getNil();
  28817. }
  28818. return this;
  28819. }
  28820. /** num_floor
  28821. *
  28822. */
  28823. @JRubyMethod(name = "floor")
  28824. public IRubyObject floor() {
  28825. return convertToFloat().floor();
  28826. }
  28827. /** num_ceil
  28828. *
  28829. */
  28830. @JRubyMethod(name = "ceil")
  28831. public IRubyObject ceil() {
  28832. return convertToFloat().ceil();
  28833. }
  28834. /** num_round
  28835. *
  28836. */
  28837. @JRubyMethod(name = "round")
  28838. public IRubyObject round() {
  28839. return convertToFloat().round();
  28840. }
  28841. /** num_truncate
  28842. *
  28843. */
  28844. @JRubyMethod(name = "truncate")
  28845. public IRubyObject truncate() {
  28846. return convertToFloat().truncate();
  28847. }
  28848. @JRubyMethod(name = "step", required = 1, optional = 1, frame = true)
  28849. public IRubyObject step(ThreadContext context, IRubyObject[] args, Block block) {
  28850. switch (args.length) {
  28851. case 0: throw context.getRuntime().newArgumentError(0, 1);
  28852. case 1: return step(context, args[0], block);
  28853. case 2: return step(context, args[0], args[1], block);
  28854. default: throw context.getRuntime().newArgumentError(args.length, 2);
  28855. }
  28856. }
  28857. @JRubyMethod(name = "step", frame = true)
  28858. public IRubyObject step(ThreadContext context, IRubyObject arg0, Block block) {
  28859. return step(context, arg0, RubyFixnum.one(context.getRuntime()), block);
  28860. }
  28861. @JRubyMethod(name = "step", frame = true)
  28862. public IRubyObject step(ThreadContext context, IRubyObject to, IRubyObject step, Block block) {
  28863. if (this instanceof RubyFixnum && to instanceof RubyFixnum && step instanceof RubyFixnum) {
  28864. long value = getLongValue();
  28865. long end = ((RubyFixnum) to).getLongValue();
  28866. long diff = ((RubyFixnum) step).getLongValue();
  28867. if (diff == 0) {
  28868. throw getRuntime().newArgumentError("step cannot be 0");
  28869. }
  28870. if (diff > 0) {
  28871. for (long i = value; i <= end; i += diff) {
  28872. block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
  28873. }
  28874. } else {
  28875. for (long i = value; i >= end; i += diff) {
  28876. block.yield(context, RubyFixnum.newFixnum(getRuntime(), i));
  28877. }
  28878. }
  28879. } else if (this instanceof RubyFloat || to instanceof RubyFloat || step instanceof RubyFloat) {
  28880. double beg = num2dbl(this);
  28881. double end = num2dbl(to);
  28882. double unit = num2dbl(step);
  28883. if (unit == 0) {
  28884. throw getRuntime().newArgumentError("step cannot be 0");
  28885. }
  28886. double n = (end - beg)/unit;
  28887. double err = (Math.abs(beg) + Math.abs(end) + Math.abs(end - beg)) / Math.abs(unit) * DBL_EPSILON;
  28888. if (err>0.5) {
  28889. err=0.5;
  28890. }
  28891. n = Math.floor(n + err) + 1;
  28892. for(double i = 0; i < n; i++){
  28893. block.yield(context, RubyFloat.newFloat(getRuntime(), i * unit + beg));
  28894. }
  28895. } else {
  28896. RubyNumeric i = this;
  28897. int cmp;
  28898. String cmpString;
  28899. if (((RubyBoolean) step.callMethod(context, MethodIndex.OP_GT, ">", RubyFixnum.zero(getRuntime()))).isTrue()) {
  28900. cmp = MethodIndex.OP_GT;
  28901. } else {
  28902. cmp = MethodIndex.OP_LT;
  28903. }
  28904. cmpString = MethodIndex.NAMES.get(cmp);
  28905. while (true) {
  28906. if (i.callMethod(context, cmp, cmpString, to).isTrue()) {
  28907. break;
  28908. }
  28909. block.yield(context, i);
  28910. i = (RubyNumeric) i.callMethod(context, MethodIndex.OP_PLUS, "+", step);
  28911. }
  28912. }
  28913. return this;
  28914. }
  28915. /** num_equal, doesn't override RubyObject.op_equal
  28916. *
  28917. */
  28918. protected final IRubyObject op_num_equal(ThreadContext context, IRubyObject other) {
  28919. // it won't hurt fixnums
  28920. if (this == other) return getRuntime().getTrue();
  28921. return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this);
  28922. }
  28923. /** num_numerator
  28924. *
  28925. */
  28926. @JRubyMethod(name = "numerator", compat = CompatVersion.RUBY1_9)
  28927. public IRubyObject numerator(ThreadContext context) {
  28928. return RubyRational.newRationalConvert(context, this).callMethod(context, "numerator");
  28929. }
  28930. /** num_denominator
  28931. *
  28932. */
  28933. @JRubyMethod(name = "denominator", compat = CompatVersion.RUBY1_9)
  28934. public IRubyObject denominator(ThreadContext context) {
  28935. return RubyRational.newRationalConvert(context, this).callMethod(context, "denominator");
  28936. }
  28937. /** numeric_to_c
  28938. *
  28939. */
  28940. @JRubyMethod(name = "to_c", compat = CompatVersion.RUBY1_9)
  28941. public IRubyObject to_c(ThreadContext context) {
  28942. return RubyComplex.newComplexCanonicalize(context, this);
  28943. }
  28944. /** numeric_re
  28945. *
  28946. */
  28947. @JRubyMethod(name = "re", compat = CompatVersion.RUBY1_9)
  28948. public IRubyObject re(ThreadContext context) {
  28949. return RubyComplex.newComplexConvert(context, this);
  28950. }
  28951. /** numeric_im
  28952. *
  28953. */
  28954. @JRubyMethod(name = "im", compat = CompatVersion.RUBY1_9)
  28955. public IRubyObject im(ThreadContext context) {
  28956. return RubyComplex.newComplexConvert(context, RubyFixnum.zero(context.getRuntime()), this);
  28957. }
  28958. /** numeric_real
  28959. *
  28960. */
  28961. @JRubyMethod(name = "real", compat = CompatVersion.RUBY1_9)
  28962. public IRubyObject real(ThreadContext context) {
  28963. return this;
  28964. }
  28965. /** numeric_image
  28966. *
  28967. */
  28968. @JRubyMethod(name = {"image", "imag"}, compat = CompatVersion.RUBY1_9)
  28969. public IRubyObject image(ThreadContext context) {
  28970. return RubyFixnum.zero(context.getRuntime());
  28971. }
  28972. /** numeric_arg
  28973. *
  28974. */
  28975. @JRubyMethod(name = "arg", compat = CompatVersion.RUBY1_9)
  28976. public IRubyObject arg(ThreadContext context) {
  28977. if (!f_negative_p(context, this)) return RubyFixnum.zero(context.getRuntime());
  28978. return context.getRuntime().getMath().fastFetchConstant("PI");
  28979. }
  28980. /** numeric_polar
  28981. *
  28982. */
  28983. @JRubyMethod(name = "polar", compat = CompatVersion.RUBY1_9)
  28984. public IRubyObject polar(ThreadContext context) {
  28985. return context.getRuntime().newArray(f_abs(context, this), f_arg(context, this));
  28986. }
  28987. /** numeric_real
  28988. *
  28989. */
  28990. @JRubyMethod(name = "conjugate", compat = CompatVersion.RUBY1_9)
  28991. public IRubyObject conjugate(ThreadContext context) {
  28992. return this;
  28993. }
  28994. public static class InvalidIntegerException extends NumberFormatException {
  28995. private static final long serialVersionUID = 55019452543252148L;
  28996. public InvalidIntegerException() {
  28997. super();
  28998. }
  28999. public InvalidIntegerException(String message) {
  29000. super(message);
  29001. }
  29002. public Throwable fillInStackTrace() {
  29003. return this;
  29004. }
  29005. }
  29006. public static class NumberTooLargeException extends NumberFormatException {
  29007. private static final long serialVersionUID = -1835120694982699449L;
  29008. public NumberTooLargeException() {
  29009. super();
  29010. }
  29011. public NumberTooLargeException(String message) {
  29012. super(message);
  29013. }
  29014. public Throwable fillInStackTrace() {
  29015. return this;
  29016. }
  29017. }
  29018. }
  29019. /*
  29020. ***** BEGIN LICENSE BLOCK *****
  29021. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  29022. *
  29023. * The contents of this file are subject to the Common Public
  29024. * License Version 1.0 (the "License"); you may not use this file
  29025. * except in compliance with the License. You may obtain a copy of
  29026. * the License at http://www.eclipse.org/legal/cpl-v10.html
  29027. *
  29028. * Software distributed under the License is distributed on an "AS
  29029. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  29030. * implied. See the License for the specific language governing
  29031. * rights and limitations under the License.
  29032. *
  29033. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  29034. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  29035. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  29036. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  29037. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  29038. * Copyright (C) 2004-2006 Thomas E Enebo <enebo@acm.org>
  29039. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  29040. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  29041. * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
  29042. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  29043. * Copyright (C) 2007 MenTaLguY <mental@rydia.net>
  29044. * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
  29045. *
  29046. * Alternatively, the contents of this file may be used under the terms of
  29047. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29048. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29049. * in which case the provisions of the GPL or the LGPL are applicable instead
  29050. * of those above. If you wish to allow use of your version of this file only
  29051. * under the terms of either the GPL or the LGPL, and not to allow others to
  29052. * use your version of this file under the terms of the CPL, indicate your
  29053. * decision by deleting the provisions above and replace them with the notice
  29054. * and other provisions required by the GPL or the LGPL. If you do not delete
  29055. * the provisions above, a recipient may use your version of this file under
  29056. * the terms of any one of the CPL, the GPL or the LGPL.
  29057. ***** END LICENSE BLOCK *****/
  29058. package org.jruby;
  29059. import java.io.IOException;
  29060. import java.io.ObjectInputStream;
  29061. import java.io.ObjectOutputStream;
  29062. import java.io.Serializable;
  29063. import java.util.concurrent.atomic.AtomicBoolean;
  29064. import org.jruby.common.IRubyWarnings.ID;
  29065. import org.jruby.evaluator.ASTInterpreter;
  29066. import org.jruby.exceptions.JumpException;
  29067. import org.jruby.internal.runtime.methods.DynamicMethod;
  29068. import org.jruby.runtime.Block;
  29069. import org.jruby.runtime.CallType;
  29070. import org.jruby.runtime.ObjectAllocator;
  29071. import org.jruby.runtime.ThreadContext;
  29072. import org.jruby.runtime.Visibility;
  29073. import org.jruby.runtime.builtin.IRubyObject;
  29074. import org.jruby.runtime.builtin.Variable;
  29075. import org.jruby.runtime.component.VariableEntry;
  29076. import org.jruby.util.IdUtil;
  29077. import java.util.ArrayList;
  29078. import java.util.HashMap;
  29079. import java.util.List;
  29080. import java.util.Map;
  29081. import org.jruby.anno.JRubyClass;
  29082. import org.jruby.anno.JRubyMethod;
  29083. import org.jruby.javasupport.JavaObject;
  29084. import org.jruby.javasupport.util.RuntimeHelpers;
  29085. import org.jruby.runtime.ClassIndex;
  29086. import org.jruby.runtime.MethodIndex;
  29087. import org.jruby.runtime.builtin.InstanceVariables;
  29088. import org.jruby.runtime.builtin.InternalVariables;
  29089. import org.jruby.runtime.marshal.CoreObjectType;
  29090. import org.jruby.util.TypeConverter;
  29091. /**
  29092. * RubyObject is the only implementation of the
  29093. * {@link org.jruby.runtime.builtin.IRubyObject}. Every Ruby object in JRuby
  29094. * is represented by something that is an instance of RubyObject. In
  29095. * some of the core class implementations, this means doing a subclass
  29096. * that extends RubyObject, in other cases it means using a simple
  29097. * RubyObject instance and the data field to store specific
  29098. * information about the Ruby object.
  29099. *
  29100. * Some care has been taken to make the implementation be as
  29101. * monomorphic as possible, so that the Java Hotspot engine can
  29102. * improve performance of it. That is the reason for several patterns
  29103. * that might seem odd in this class.
  29104. *
  29105. * The IRubyObject interface used to have lots of methods for
  29106. * different things, but these have now mostly been refactored into
  29107. * several interfaces that gives access to that specific part of the
  29108. * object. This gives us the possibility to switch out that subsystem
  29109. * without changing interfaces again. For example, instance variable
  29110. * and internal variables are handled this way, but the implementation
  29111. * in RubyObject only returns "this" in {@link #getInstanceVariables()} and
  29112. * {@link #getInternalVariables()}.
  29113. *
  29114. * @author jpetersen
  29115. */
  29116. @JRubyClass(name="Object", include="Kernel")
  29117. public class RubyObject implements Cloneable, IRubyObject, Serializable, CoreObjectType, InstanceVariables, InternalVariables {
  29118. /**
  29119. * It's not valid to create a totally empty RubyObject. Since the
  29120. * RubyObject is always defined in relation to a runtime, that
  29121. * means that creating RubyObjects from outside the class might
  29122. * cause problems.
  29123. */
  29124. private RubyObject(){};
  29125. /**
  29126. * A value that is used as a null sentinel in among other places
  29127. * the RubyArray implementation. It will cause large problems to
  29128. * call any methods on this object.
  29129. */
  29130. public static final IRubyObject NEVER = new RubyObject();
  29131. /**
  29132. * A value that specifies an undefined value. This value is used
  29133. * as a sentinel for undefined constant values, and other places
  29134. * where neither null nor NEVER makes sense.
  29135. */
  29136. public static final IRubyObject UNDEF = new RubyObject();
  29137. // The class of this object
  29138. protected transient RubyClass metaClass;
  29139. /**
  29140. * The variableTable contains variables for an object, defined as:
  29141. * <ul>
  29142. * <li> instance variables
  29143. * <li> class variables (for classes/modules)
  29144. * <li> internal variables (such as those used when marshaling RubyRange and RubyException)
  29145. * </ul>
  29146. *
  29147. * Constants are stored separately, see {@link RubyModule}.
  29148. *
  29149. */
  29150. protected transient volatile VariableTableEntry[] variableTable;
  29151. protected transient int variableTableSize;
  29152. protected transient int variableTableThreshold;
  29153. // The dataStruct is a place where custom information can be
  29154. // contained for core implementations that doesn't necessarily
  29155. // want to go to the trouble of creating a subclass of
  29156. // RubyObject. The OpenSSL implementation uses this heavily to
  29157. // save holder objects containing Java cryptography objects.
  29158. // Java integration uses this to store the Java object ref.
  29159. protected transient Object dataStruct;
  29160. protected int flags; // zeroed by jvm
  29161. public static final int ALL_F = -1;
  29162. public static final int FALSE_F = 1 << 0;
  29163. /**
  29164. * This flag is a bit funny. It's used to denote that this value
  29165. * is nil. It's a bit counterintuitive for a Java programmer to
  29166. * not use subclassing to handle this case, since we have a
  29167. * RubyNil subclass anyway. Well, the reason for it being a flag
  29168. * is that the {@link #isNil()} method is called extremely often. So often
  29169. * that it gives a good speed boost to make it monomorphic and
  29170. * final. It turns out using a flag for this actually gives us
  29171. * better performance than having a polymorphic {@link #isNil()} method.
  29172. */
  29173. public static final int NIL_F = 1 << 1;
  29174. public static final int FROZEN_F = 1 << 2;
  29175. public static final int TAINTED_F = 1 << 3;
  29176. public static final int FL_USHIFT = 4;
  29177. public static final int USER0_F = (1<<(FL_USHIFT+0));
  29178. public static final int USER1_F = (1<<(FL_USHIFT+1));
  29179. public static final int USER2_F = (1<<(FL_USHIFT+2));
  29180. public static final int USER3_F = (1<<(FL_USHIFT+3));
  29181. public static final int USER4_F = (1<<(FL_USHIFT+4));
  29182. public static final int USER5_F = (1<<(FL_USHIFT+5));
  29183. public static final int USER6_F = (1<<(FL_USHIFT+6));
  29184. public static final int USER7_F = (1<<(FL_USHIFT+7));
  29185. /**
  29186. * Sets or unsets a flag on this object. The only flags that are
  29187. * guaranteed to be valid to use as the first argument is:
  29188. *
  29189. * <ul>
  29190. * <li>{@link #FALSE_F}</li>
  29191. * <li>{@link NIL_F}</li>
  29192. * <li>{@link FROZEN_F}</li>
  29193. * <li>{@link TAINTED_F}</li>
  29194. * <li>{@link USER0_F}</li>
  29195. * <li>{@link USER1_F}</li>
  29196. * <li>{@link USER2_F}</li>
  29197. * <li>{@link USER3_F}</li>
  29198. * <li>{@link USER4_F}</li>
  29199. * <li>{@link USER5_F}</li>
  29200. * <li>{@link USER6_F}</li>
  29201. * <li>{@link USER7_F}</li>
  29202. * </ul>
  29203. *
  29204. * @param flag the actual flag to set or unset.
  29205. * @param set if true, the flag will be set, if false, the flag will be unset.
  29206. */
  29207. public final void setFlag(int flag, boolean set) {
  29208. if (set) {
  29209. flags |= flag;
  29210. } else {
  29211. flags &= ~flag;
  29212. }
  29213. }
  29214. /**
  29215. * Get the value of a custom flag on this object. The only
  29216. * guaranteed flags that can be sent in to this method is:
  29217. *
  29218. * <ul>
  29219. * <li>{@link #FALSE_F}</li>
  29220. * <li>{@link NIL_F}</li>
  29221. * <li>{@link FROZEN_F}</li>
  29222. * <li>{@link TAINTED_F}</li>
  29223. * <li>{@link USER0_F}</li>
  29224. * <li>{@link USER1_F}</li>
  29225. * <li>{@link USER2_F}</li>
  29226. * <li>{@link USER3_F}</li>
  29227. * <li>{@link USER4_F}</li>
  29228. * <li>{@link USER5_F}</li>
  29229. * <li>{@link USER6_F}</li>
  29230. * <li>{@link USER7_F}</li>
  29231. * </ul>
  29232. *
  29233. * @param flag the flag to get
  29234. * @return true if the flag is set, false otherwise
  29235. */
  29236. public final boolean getFlag(int flag) {
  29237. return (flags & flag) != 0;
  29238. }
  29239. private transient Finalizer finalizer;
  29240. /**
  29241. * Class that keeps track of the finalizers for the object under
  29242. * operation.
  29243. */
  29244. public class Finalizer implements Finalizable {
  29245. private long id;
  29246. private List<IRubyObject> finalizers;
  29247. private AtomicBoolean finalized;
  29248. public Finalizer(long id) {
  29249. this.id = id;
  29250. this.finalized = new AtomicBoolean(false);
  29251. }
  29252. public void addFinalizer(IRubyObject finalizer) {
  29253. if (finalizers == null) {
  29254. finalizers = new ArrayList<IRubyObject>();
  29255. }
  29256. finalizers.add(finalizer);
  29257. }
  29258. public void removeFinalizers() {
  29259. finalizers = null;
  29260. }
  29261. @Override
  29262. public void finalize() {
  29263. if (finalized.compareAndSet(false, true)) {
  29264. if (finalizers != null) {
  29265. for (int i = 0; i < finalizers.size(); i++) {
  29266. IRubyObject finalizer = finalizers.get(i);
  29267. RuntimeHelpers.invoke(
  29268. finalizer.getRuntime().getCurrentContext(),
  29269. finalizer, "call", RubyObject.this.id());
  29270. }
  29271. }
  29272. }
  29273. }
  29274. }
  29275. /**
  29276. * Standard path for object creation. Objects are entered into ObjectSpace
  29277. * only if ObjectSpace is enabled.
  29278. */
  29279. public RubyObject(Ruby runtime, RubyClass metaClass) {
  29280. this.metaClass = metaClass;
  29281. if (runtime.isObjectSpaceEnabled()) addToObjectSpace(runtime);
  29282. if (runtime.getSafeLevel() >= 3) taint(runtime);
  29283. }
  29284. /**
  29285. * Path for objects who want to decide whether they don't want to be in
  29286. * ObjectSpace even when it is on. (notably used by objects being
  29287. * considered immediate, they'll always pass false here)
  29288. */
  29289. protected RubyObject(Ruby runtime, RubyClass metaClass, boolean useObjectSpace) {
  29290. this.metaClass = metaClass;
  29291. if (useObjectSpace) addToObjectSpace(runtime);
  29292. if (runtime.getSafeLevel() >= 3) taint(runtime);
  29293. }
  29294. private void addToObjectSpace(Ruby runtime) {
  29295. assert runtime.isObjectSpaceEnabled();
  29296. runtime.getObjectSpace().add(this);
  29297. }
  29298. /**
  29299. * Will create the Ruby class Object in the runtime
  29300. * specified. This method needs to take the actual class as an
  29301. * argument because of the Object class' central part in runtime
  29302. * initialization.
  29303. */
  29304. public static RubyClass createObjectClass(Ruby runtime, RubyClass objectClass) {
  29305. objectClass.index = ClassIndex.OBJECT;
  29306. objectClass.defineAnnotatedMethods(ObjectMethods.class);
  29307. return objectClass;
  29308. }
  29309. /**
  29310. * Interestingly, the Object class doesn't really have that many
  29311. * methods for itself. Instead almost all of the Object methods
  29312. * are really defined on the Kernel module. This class is a holder
  29313. * for all Object methods.
  29314. *
  29315. * @see RubyKernel
  29316. */
  29317. public static class ObjectMethods {
  29318. @JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
  29319. public static IRubyObject intialize(IRubyObject self) {
  29320. return self.getRuntime().getNil();
  29321. }
  29322. }
  29323. /**
  29324. * Default allocator instance for all Ruby objects. The only
  29325. * reason to not use this allocator is if you actually need to
  29326. * have all instances of something be a subclass of RubyObject.
  29327. *
  29328. * @see org.jruby.runtime.ObjectAllocator
  29329. */
  29330. public static final ObjectAllocator OBJECT_ALLOCATOR = new ObjectAllocator() {
  29331. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  29332. return new RubyObject(runtime, klass);
  29333. }
  29334. };
  29335. /**
  29336. * Will make sure that this object is added to the current object
  29337. * space.
  29338. *
  29339. * @see org.jruby.runtime.ObjectSpace
  29340. */
  29341. public void attachToObjectSpace() {
  29342. getRuntime().getObjectSpace().add(this);
  29343. }
  29344. /**
  29345. * This is overridden in the other concrete Java builtins to provide a fast way
  29346. * to determine what type they are.
  29347. *
  29348. * Will generally return a value from org.jruby.runtime.ClassIndex
  29349. *
  29350. * @see org.jruby.runtime.ClassInde
  29351. */
  29352. public int getNativeTypeIndex() {
  29353. return ClassIndex.OBJECT;
  29354. }
  29355. /**
  29356. * Specifically polymorphic method that are meant to be overridden
  29357. * by modules to specify that they are modules in an easy way.
  29358. */
  29359. public boolean isModule() {
  29360. return false;
  29361. }
  29362. /**
  29363. * Specifically polymorphic method that are meant to be overridden
  29364. * by classes to specify that they are classes in an easy way.
  29365. */
  29366. public boolean isClass() {
  29367. return false;
  29368. }
  29369. /**
  29370. * Is object immediate (def: Fixnum, Symbol, true, false, nil?).
  29371. */
  29372. public boolean isImmediate() {
  29373. return false;
  29374. }
  29375. /** rb_make_metaclass
  29376. *
  29377. * Will create a new meta class, insert this in the chain of
  29378. * classes for this specific object, and return the generated meta
  29379. * class.
  29380. */
  29381. public RubyClass makeMetaClass(RubyClass superClass) {
  29382. MetaClass klass = new MetaClass(getRuntime(), superClass); // rb_class_boot
  29383. setMetaClass(klass);
  29384. klass.setAttached(this);
  29385. klass.setMetaClass(superClass.getRealClass().getMetaClass());
  29386. return klass;
  29387. }
  29388. /**
  29389. * Will return the Java interface that most closely can represent
  29390. * this object, when working through JAva integration
  29391. * translations.
  29392. */
  29393. public Class getJavaClass() {
  29394. if (dataGetStruct() instanceof JavaObject) {
  29395. return ((JavaObject)dataGetStruct()).getValue().getClass();
  29396. }
  29397. return getClass();
  29398. }
  29399. /**
  29400. * Simple helper to print any objects.
  29401. */
  29402. public static void puts(Object obj) {
  29403. System.out.println(obj.toString());
  29404. }
  29405. /**
  29406. * This method is just a wrapper around the Ruby "==" method,
  29407. * provided so that RubyObjects can be used as keys in the Java
  29408. * HashMap object underlying RubyHash.
  29409. */
  29410. @Override
  29411. public boolean equals(Object other) {
  29412. return other == this ||
  29413. other instanceof IRubyObject &&
  29414. callMethod(getRuntime().getCurrentContext(), MethodIndex.EQUALEQUAL, "==", (IRubyObject) other).isTrue();
  29415. }
  29416. /**
  29417. * The default toString method is just a wrapper that calls the
  29418. * Ruby "to_s" method.
  29419. */
  29420. @Override
  29421. public String toString() {
  29422. return RuntimeHelpers.invoke(getRuntime().getCurrentContext(), this, "to_s").toString();
  29423. }
  29424. /**
  29425. * Will return the runtime that this object is associated with.
  29426. *
  29427. * @return current runtime
  29428. */
  29429. public final Ruby getRuntime() {
  29430. return getMetaClass().getClassRuntime();
  29431. }
  29432. /**
  29433. * if exist return the meta-class else return the type of the object.
  29434. *
  29435. */
  29436. public final RubyClass getMetaClass() {
  29437. return metaClass;
  29438. }
  29439. /**
  29440. * Makes it possible to change the metaclass of an object. In
  29441. * practice, this is a simple version of Smalltalks Become, except
  29442. * that it doesn't work when we're dealing with subclasses. In
  29443. * practice it's used to change the singleton/meta class used,
  29444. * without changing the "real" inheritance chain.
  29445. */
  29446. public void setMetaClass(RubyClass metaClass) {
  29447. this.metaClass = metaClass;
  29448. }
  29449. /**
  29450. * Is this value frozen or not? Shortcut for doing
  29451. * getFlag(FROZEN_F).
  29452. *
  29453. * @return true if this object is frozen, false otherwise
  29454. */
  29455. public boolean isFrozen() {
  29456. return (flags & FROZEN_F) != 0;
  29457. }
  29458. /**
  29459. * Sets whether this object is frozen or not. Shortcut for doing
  29460. * setFlag(FROZEN_F, frozen).
  29461. *
  29462. * @param frozen should this object be frozen?
  29463. */
  29464. public void setFrozen(boolean frozen) {
  29465. if (frozen) {
  29466. flags |= FROZEN_F;
  29467. } else {
  29468. flags &= ~FROZEN_F;
  29469. }
  29470. }
  29471. /** rb_frozen_class_p
  29472. *
  29473. * Helper to test whether this object is frozen, and if it is will
  29474. * throw an exception based on the message.
  29475. */
  29476. protected final void testFrozen(String message) {
  29477. if (isFrozen()) {
  29478. throw getRuntime().newFrozenError(message + " " + getMetaClass().getName());
  29479. }
  29480. }
  29481. /**
  29482. * The actual method that checks frozen with the default frozen message from MRI.
  29483. * If possible, call this instead of {@link #testFrozen}.
  29484. */
  29485. protected void checkFrozen() {
  29486. testFrozen("can't modify frozen ");
  29487. }
  29488. /**
  29489. * Gets the taint. Shortcut for getFlag(TAINTED_F).
  29490. *
  29491. * @return true if this object is tainted
  29492. */
  29493. public boolean isTaint() {
  29494. return (flags & TAINTED_F) != 0;
  29495. }
  29496. /**
  29497. * Sets the taint flag. Shortcut for setFlag(TAINTED_F, taint)
  29498. *
  29499. * @param taint should this object be tainted or not?
  29500. */
  29501. public void setTaint(boolean taint) {
  29502. if (taint) {
  29503. flags |= TAINTED_F;
  29504. } else {
  29505. flags &= ~TAINTED_F;
  29506. }
  29507. }
  29508. /**
  29509. * Does this object represent nil? See the docs for the {@link
  29510. * #NIL_F} flag for more information.
  29511. */
  29512. public final boolean isNil() {
  29513. return (flags & NIL_F) != 0;
  29514. }
  29515. /**
  29516. * Is this value a true value or not? Based on the {@link #FALSE_F} flag.
  29517. */
  29518. public final boolean isTrue() {
  29519. return (flags & FALSE_F) == 0;
  29520. }
  29521. /**
  29522. * Is this value a false value or not? Based on the {@link #FALSE_F} flag.
  29523. */
  29524. public final boolean isFalse() {
  29525. return (flags & FALSE_F) != 0;
  29526. }
  29527. /**
  29528. * Does this object respond to the specified message? Uses a
  29529. * shortcut if it can be proved that respond_to? haven't been
  29530. * overridden.
  29531. */
  29532. public final boolean respondsTo(String name) {
  29533. if(getMetaClass().searchMethod("respond_to?") == getRuntime().getRespondToMethod()) {
  29534. return getMetaClass().isMethodBound(name, false);
  29535. } else {
  29536. return callMethod(getRuntime().getCurrentContext(),"respond_to?",getRuntime().newSymbol(name)).isTrue();
  29537. }
  29538. }
  29539. /** rb_singleton_class
  29540. *
  29541. * Note: this method is specialized for RubyFixnum, RubySymbol,
  29542. * RubyNil and RubyBoolean
  29543. *
  29544. * Will either return the existing singleton class for this
  29545. * object, or create a new one and return that.
  29546. */
  29547. public RubyClass getSingletonClass() {
  29548. RubyClass klass;
  29549. if (getMetaClass().isSingleton() && ((MetaClass)getMetaClass()).getAttached() == this) {
  29550. klass = getMetaClass();
  29551. } else {
  29552. klass = makeMetaClass(getMetaClass());
  29553. }
  29554. klass.setTaint(isTaint());
  29555. if (isFrozen()) klass.setFrozen(true);
  29556. return klass;
  29557. }
  29558. /** rb_singleton_class_clone
  29559. *
  29560. * Will make sure that if the current objects class is a
  29561. * singleton, it will get cloned.
  29562. *
  29563. * @return either a real class, or a clone of the current singleton class
  29564. */
  29565. protected RubyClass getSingletonClassClone() {
  29566. RubyClass klass = getMetaClass();
  29567. if (!klass.isSingleton()) return klass;
  29568. MetaClass clone = new MetaClass(getRuntime());
  29569. clone.flags = flags;
  29570. if (this instanceof RubyClass) {
  29571. clone.setMetaClass(clone);
  29572. } else {
  29573. clone.setMetaClass(klass.getSingletonClassClone());
  29574. }
  29575. clone.setSuperClass(klass.getSuperClass());
  29576. if (klass.hasVariables()) {
  29577. clone.syncVariables(klass.getVariableList());
  29578. }
  29579. klass.cloneMethods(clone);
  29580. ((MetaClass)clone.getMetaClass()).setAttached(clone);
  29581. ((MetaClass)clone).setAttached(((MetaClass)klass).getAttached());
  29582. return clone;
  29583. }
  29584. /** init_copy
  29585. *
  29586. * Initializes a copy with variable and special instance variable
  29587. * information, and then call the initialize_copy Ruby method.
  29588. */
  29589. private static void initCopy(IRubyObject clone, RubyObject original) {
  29590. assert !clone.isFrozen() : "frozen object (" + clone.getMetaClass().getName() + ") allocated";
  29591. original.copySpecialInstanceVariables(clone);
  29592. if (original.hasVariables()) {
  29593. clone.syncVariables(original.getVariableList());
  29594. }
  29595. /* FIXME: finalizer should be dupped here */
  29596. clone.callMethod(clone.getRuntime().getCurrentContext(), "initialize_copy", original);
  29597. }
  29598. /** OBJ_INFECT
  29599. *
  29600. * Infects this object with traits from the argument obj. In real
  29601. * terms this currently means that if obj is tainted, this object
  29602. * will get tainted too. It's possible to hijack this method to do
  29603. * other infections if that would be interesting.
  29604. */
  29605. public IRubyObject infectBy(IRubyObject obj) {
  29606. if (obj.isTaint()) setTaint(true);
  29607. return this;
  29608. }
  29609. /**
  29610. * The protocol for super method invocation is a bit complicated
  29611. * in Ruby. In real terms it involves first finding the real
  29612. * implementation class (the super class), getting the name of the
  29613. * method to call from the frame, and then invoke that on the
  29614. * super class with the current self as the actual object
  29615. * invoking.
  29616. */
  29617. public IRubyObject callSuper(ThreadContext context, IRubyObject[] args, Block block) {
  29618. RubyModule klazz = context.getFrameKlazz();
  29619. RubyClass superClass = RuntimeHelpers.findImplementerIfNecessary(getMetaClass(), klazz).getSuperClass();
  29620. if (superClass == null) {
  29621. String name = context.getFrameName();
  29622. return RuntimeHelpers.callMethodMissing(context, this, klazz.searchMethod(name), name, args, this, CallType.SUPER, block);
  29623. }
  29624. return RuntimeHelpers.invokeAs(context, superClass, this, context.getFrameName(), args, CallType.SUPER, block);
  29625. }
  29626. /**
  29627. * Will invoke a named method with no arguments and no block.
  29628. */
  29629. public final IRubyObject callMethod(ThreadContext context, String name) {
  29630. return RuntimeHelpers.invoke(context, this, name);
  29631. }
  29632. /**
  29633. * Will invoke a named method with one argument and no block with
  29634. * functional invocation.
  29635. */
  29636. public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject arg) {
  29637. return RuntimeHelpers.invoke(context, this, name, arg);
  29638. }
  29639. /**
  29640. * Will invoke a named method with the supplied arguments and no
  29641. * block with functional invocation.
  29642. */
  29643. public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args) {
  29644. return RuntimeHelpers.invoke(context, this, name, args);
  29645. }
  29646. /**
  29647. * Will invoke a named method with the supplied arguments and
  29648. * supplied block with functional invocation.
  29649. */
  29650. public final IRubyObject callMethod(ThreadContext context, String name, IRubyObject[] args, Block block) {
  29651. return RuntimeHelpers.invoke(context, this, name, args, block);
  29652. }
  29653. /**
  29654. * Will invoke an indexed method with the no arguments and no
  29655. * block.
  29656. */
  29657. public final IRubyObject callMethod(ThreadContext context, int methodIndex, String name) {
  29658. return RuntimeHelpers.invoke(context, this, name);
  29659. }
  29660. /**
  29661. * Will invoke an indexed method with the one argument and no
  29662. * block with a functional invocation.
  29663. */
  29664. public final IRubyObject callMethod(ThreadContext context, int methodIndex, String name, IRubyObject arg) {
  29665. return RuntimeHelpers.invoke(context, this, name, arg, Block.NULL_BLOCK);
  29666. }
  29667. /**
  29668. * Call the Ruby initialize method with the supplied arguments and block.
  29669. */
  29670. public final void callInit(IRubyObject[] args, Block block) {
  29671. callMethod(getRuntime().getCurrentContext(), "initialize", args, block);
  29672. }
  29673. /** rb_to_id
  29674. *
  29675. * Will try to convert this object to a String using the Ruby
  29676. * "to_str" if the object isn't already a String. If this still
  29677. * doesn't work, will throw a Ruby TypeError.
  29678. *
  29679. */
  29680. public String asJavaString() {
  29681. IRubyObject asString = checkStringType();
  29682. if(!asString.isNil()) return ((RubyString)asString).asJavaString();
  29683. throw getRuntime().newTypeError(inspect().toString() + " is not a symbol");
  29684. }
  29685. /**
  29686. * Tries to convert this object to a Ruby Array using the "to_ary"
  29687. * method.
  29688. */
  29689. public RubyArray convertToArray() {
  29690. return (RubyArray) TypeConverter.convertToType(this, getRuntime().getArray(), MethodIndex.TO_ARY, "to_ary");
  29691. }
  29692. /**
  29693. * Tries to convert this object to a Ruby Hash using the "to_hash"
  29694. * method.
  29695. */
  29696. public RubyHash convertToHash() {
  29697. return (RubyHash)TypeConverter.convertToType(this, getRuntime().getHash(), MethodIndex.TO_HASH, "to_hash");
  29698. }
  29699. /**
  29700. * Tries to convert this object to a Ruby Float using the "to_f"
  29701. * method.
  29702. */
  29703. public RubyFloat convertToFloat() {
  29704. return (RubyFloat) TypeConverter.convertToType(this, getRuntime().getFloat(), MethodIndex.TO_F, "to_f");
  29705. }
  29706. /**
  29707. * Tries to convert this object to a Ruby Integer using the "to_int"
  29708. * method.
  29709. */
  29710. public RubyInteger convertToInteger() {
  29711. return convertToInteger(MethodIndex.TO_INT, "to_int");
  29712. }
  29713. /**
  29714. * Tries to convert this object to a Ruby Integer using the
  29715. * supplied conversion method.
  29716. */
  29717. public RubyInteger convertToInteger(int convertMethodIndex, String convertMethod) {
  29718. IRubyObject val = TypeConverter.convertToType(this, getRuntime().getInteger(), convertMethodIndex, convertMethod, true);
  29719. if (!(val instanceof RubyInteger)) throw getRuntime().newTypeError(getMetaClass().getName() + "#" + convertMethod + " should return Integer");
  29720. return (RubyInteger)val;
  29721. }
  29722. /**
  29723. * Tries to convert this object to a Ruby String using the
  29724. * "to_str" method.
  29725. */
  29726. public RubyString convertToString() {
  29727. return (RubyString) TypeConverter.convertToType(this, getRuntime().getString(), MethodIndex.TO_STR, "to_str");
  29728. }
  29729. /**
  29730. * Tries to convert this object to the specified Ruby type, using
  29731. * a specific conversion method.
  29732. */
  29733. public final IRubyObject convertToType(RubyClass target, int convertMethodIndex) {
  29734. return TypeConverter.convertToType(this, target, convertMethodIndex, (String)MethodIndex.NAMES.get(convertMethodIndex));
  29735. }
  29736. /** rb_obj_as_string
  29737. *
  29738. * First converts this object into a String using the "to_s"
  29739. * method, infects it with the current taint and returns it. If
  29740. * to_s doesn't return a Ruby String, {@link #anyToString} is used
  29741. * instead.
  29742. */
  29743. public RubyString asString() {
  29744. IRubyObject str = RuntimeHelpers.invoke(getRuntime().getCurrentContext(), this, "to_s");
  29745. if (!(str instanceof RubyString)) return (RubyString)anyToString();
  29746. if (isTaint()) str.setTaint(true);
  29747. return (RubyString) str;
  29748. }
  29749. /** rb_check_string_type
  29750. *
  29751. * Tries to return a coerced string representation of this object,
  29752. * using "to_str". If that returns something other than a String
  29753. * or nil, an empty String will be returned.
  29754. *
  29755. */
  29756. public IRubyObject checkStringType() {
  29757. IRubyObject str = TypeConverter.convertToTypeWithCheck(this, getRuntime().getString(), MethodIndex.TO_STR, "to_str");
  29758. if(!str.isNil() && !(str instanceof RubyString)) {
  29759. str = RubyString.newEmptyString(getRuntime());
  29760. }
  29761. return str;
  29762. }
  29763. /** rb_check_array_type
  29764. *
  29765. * Returns the result of trying to convert this object to an Array
  29766. * with "to_ary".
  29767. */
  29768. public IRubyObject checkArrayType() {
  29769. return TypeConverter.convertToTypeWithCheck(this, getRuntime().getArray(), MethodIndex.TO_ARY, "to_ary");
  29770. }
  29771. /** specific_eval
  29772. *
  29773. * Evaluates the block or string inside of the context of this
  29774. * object, using the supplied arguments. If a block is given, this
  29775. * will be yielded in the specific context of this object. If no
  29776. * block is given then a String-like object needs to be the first
  29777. * argument, and this string will be evaluated. Second and third
  29778. * arguments in the args-array is optional, but can contain the
  29779. * filename and line of the string under evaluation.
  29780. */
  29781. @Deprecated
  29782. public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject[] args, Block block) {
  29783. if (block.isGiven()) {
  29784. if (args.length > 0) throw getRuntime().newArgumentError(args.length, 0);
  29785. return yieldUnder(context, mod, block);
  29786. }
  29787. if (args.length == 0) {
  29788. throw getRuntime().newArgumentError("block not supplied");
  29789. } else if (args.length > 3) {
  29790. String lastFuncName = context.getFrameName();
  29791. throw getRuntime().newArgumentError(
  29792. "wrong # of arguments: " + lastFuncName + "(src) or " + lastFuncName + "{..}");
  29793. }
  29794. // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
  29795. RubyString evalStr;
  29796. if (args[0] instanceof RubyString) {
  29797. evalStr = (RubyString)args[0];
  29798. } else {
  29799. evalStr = args[0].convertToString();
  29800. }
  29801. String file;
  29802. int line;
  29803. if (args.length > 1) {
  29804. file = args[1].convertToString().asJavaString();
  29805. if (args.length > 2) {
  29806. line = (int)(args[2].convertToInteger().getLongValue() - 1);
  29807. } else {
  29808. line = 0;
  29809. }
  29810. } else {
  29811. file = "(eval)";
  29812. line = 0;
  29813. }
  29814. return evalUnder(context, mod, evalStr, file, line);
  29815. }
  29816. /** specific_eval
  29817. *
  29818. * Evaluates the block or string inside of the context of this
  29819. * object, using the supplied arguments. If a block is given, this
  29820. * will be yielded in the specific context of this object. If no
  29821. * block is given then a String-like object needs to be the first
  29822. * argument, and this string will be evaluated. Second and third
  29823. * arguments in the args-array is optional, but can contain the
  29824. * filename and line of the string under evaluation.
  29825. */
  29826. public IRubyObject specificEval(ThreadContext context, RubyModule mod, Block block) {
  29827. if (block.isGiven()) {
  29828. return yieldUnder(context, mod, block);
  29829. } else {
  29830. throw context.getRuntime().newArgumentError("block not supplied");
  29831. }
  29832. }
  29833. /** specific_eval
  29834. *
  29835. * Evaluates the block or string inside of the context of this
  29836. * object, using the supplied arguments. If a block is given, this
  29837. * will be yielded in the specific context of this object. If no
  29838. * block is given then a String-like object needs to be the first
  29839. * argument, and this string will be evaluated. Second and third
  29840. * arguments in the args-array is optional, but can contain the
  29841. * filename and line of the string under evaluation.
  29842. */
  29843. public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg, Block block) {
  29844. if (block.isGiven()) throw context.getRuntime().newArgumentError(1, 0);
  29845. // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
  29846. RubyString evalStr;
  29847. if (arg instanceof RubyString) {
  29848. evalStr = (RubyString)arg;
  29849. } else {
  29850. evalStr = arg.convertToString();
  29851. }
  29852. String file = "(eval)";
  29853. int line = 0;
  29854. return evalUnder(context, mod, evalStr, file, line);
  29855. }
  29856. /** specific_eval
  29857. *
  29858. * Evaluates the block or string inside of the context of this
  29859. * object, using the supplied arguments. If a block is given, this
  29860. * will be yielded in the specific context of this object. If no
  29861. * block is given then a String-like object needs to be the first
  29862. * argument, and this string will be evaluated. Second and third
  29863. * arguments in the args-array is optional, but can contain the
  29864. * filename and line of the string under evaluation.
  29865. */
  29866. public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg0, IRubyObject arg1, Block block) {
  29867. if (block.isGiven()) throw context.getRuntime().newArgumentError(2, 0);
  29868. // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
  29869. RubyString evalStr;
  29870. if (arg0 instanceof RubyString) {
  29871. evalStr = (RubyString)arg0;
  29872. } else {
  29873. evalStr = arg0.convertToString();
  29874. }
  29875. String file = arg1.convertToString().asJavaString();
  29876. int line = 0;
  29877. return evalUnder(context, mod, evalStr, file, line);
  29878. }
  29879. /** specific_eval
  29880. *
  29881. * Evaluates the block or string inside of the context of this
  29882. * object, using the supplied arguments. If a block is given, this
  29883. * will be yielded in the specific context of this object. If no
  29884. * block is given then a String-like object needs to be the first
  29885. * argument, and this string will be evaluated. Second and third
  29886. * arguments in the args-array is optional, but can contain the
  29887. * filename and line of the string under evaluation.
  29888. */
  29889. public IRubyObject specificEval(ThreadContext context, RubyModule mod, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  29890. if (block.isGiven()) throw context.getRuntime().newArgumentError(2, 0);
  29891. // We just want the TypeError if the argument doesn't convert to a String (JRUBY-386)
  29892. RubyString evalStr;
  29893. if (arg0 instanceof RubyString) {
  29894. evalStr = (RubyString)arg0;
  29895. } else {
  29896. evalStr = arg0.convertToString();
  29897. }
  29898. String file = arg1.convertToString().asJavaString();
  29899. int line = (int)(arg2.convertToInteger().getLongValue() - 1);
  29900. return evalUnder(context, mod, evalStr, file, line);
  29901. }
  29902. /**
  29903. * Evaluates the string src with self set to the current object,
  29904. * using the module under as the context.
  29905. * @deprecated Call with an int line number and String file
  29906. */
  29907. public IRubyObject evalUnder(final ThreadContext context, RubyModule under, IRubyObject src, IRubyObject file, IRubyObject line) {
  29908. return evalUnder(context, under, src.convertToString(), file.convertToString().toString(), (int) (line.convertToInteger().getLongValue() - 1));
  29909. }
  29910. /**
  29911. * Evaluates the string src with self set to the current object,
  29912. * using the module under as the context.
  29913. */
  29914. public IRubyObject evalUnder(final ThreadContext context, RubyModule under, RubyString src, String file, int line) {
  29915. Visibility savedVisibility = context.getCurrentVisibility();
  29916. context.setCurrentVisibility(Visibility.PUBLIC);
  29917. context.preExecuteUnder(under, Block.NULL_BLOCK);
  29918. try {
  29919. return ASTInterpreter.evalSimple(context, this, src,
  29920. file, line);
  29921. } finally {
  29922. context.postExecuteUnder();
  29923. context.setCurrentVisibility(savedVisibility);
  29924. }
  29925. }
  29926. /**
  29927. * Will yield to the specific block changing the self to be the
  29928. * current object instead of the self that is part of the frame
  29929. * saved in the block frame. This method is the basis for the Ruby
  29930. * instance_eval and module_eval methods. The arguments sent in to
  29931. * it in the args array will be yielded to the block. This makes
  29932. * it possible to emulate both instance_eval and instance_exec
  29933. * with this implementation.
  29934. */
  29935. private IRubyObject yieldUnder(final ThreadContext context, RubyModule under, IRubyObject[] args, Block block) {
  29936. context.preExecuteUnder(under, block);
  29937. Visibility savedVisibility = block.getBinding().getVisibility();
  29938. block.getBinding().setVisibility(Visibility.PUBLIC);
  29939. try {
  29940. IRubyObject valueInYield;
  29941. boolean aValue;
  29942. if (args.length == 1) {
  29943. valueInYield = args[0];
  29944. aValue = false;
  29945. } else {
  29946. valueInYield = RubyArray.newArrayNoCopy(context.getRuntime(), args);
  29947. aValue = true;
  29948. }
  29949. // FIXME: This is an ugly hack to resolve JRUBY-1381; I'm not proud of it
  29950. block = block.cloneBlock();
  29951. block.getBinding().setSelf(RubyObject.this);
  29952. block.getBinding().getFrame().setSelf(RubyObject.this);
  29953. // end hack
  29954. return block.yield(context, valueInYield, RubyObject.this, context.getRubyClass(), aValue);
  29955. //TODO: Should next and return also catch here?
  29956. } catch (JumpException.BreakJump bj) {
  29957. return (IRubyObject) bj.getValue();
  29958. } finally {
  29959. block.getBinding().setVisibility(savedVisibility);
  29960. context.postExecuteUnder();
  29961. }
  29962. }
  29963. /**
  29964. * Will yield to the specific block changing the self to be the
  29965. * current object instead of the self that is part of the frame
  29966. * saved in the block frame. This method is the basis for the Ruby
  29967. * instance_eval and module_eval methods. The arguments sent in to
  29968. * it in the args array will be yielded to the block. This makes
  29969. * it possible to emulate both instance_eval and instance_exec
  29970. * with this implementation.
  29971. */
  29972. private IRubyObject yieldUnder(final ThreadContext context, RubyModule under, Block block) {
  29973. context.preExecuteUnder(under, block);
  29974. Visibility savedVisibility = block.getBinding().getVisibility();
  29975. block.getBinding().setVisibility(Visibility.PUBLIC);
  29976. try {
  29977. // FIXME: This is an ugly hack to resolve JRUBY-1381; I'm not proud of it
  29978. block = block.cloneBlock();
  29979. block.getBinding().setSelf(RubyObject.this);
  29980. block.getBinding().getFrame().setSelf(RubyObject.this);
  29981. // end hack
  29982. return block.yield(context, this, this, context.getRubyClass(), false);
  29983. //TODO: Should next and return also catch here?
  29984. } catch (JumpException.BreakJump bj) {
  29985. return (IRubyObject) bj.getValue();
  29986. } finally {
  29987. block.getBinding().setVisibility(savedVisibility);
  29988. context.postExecuteUnder();
  29989. }
  29990. }
  29991. // Methods of the Object class (rb_obj_*):
  29992. /** rb_obj_equal
  29993. *
  29994. * Will by default use identity equality to compare objects. This
  29995. * follows the Ruby semantics.
  29996. */
  29997. @JRubyMethod(name = "==", required = 1)
  29998. public IRubyObject op_equal(ThreadContext context, IRubyObject obj) {
  29999. return this == obj ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  30000. }
  30001. /** rb_obj_equal
  30002. *
  30003. * Will use Java identity equality.
  30004. */
  30005. @JRubyMethod(name = "equal?", required = 1)
  30006. public IRubyObject equal_p(ThreadContext context, IRubyObject obj) {
  30007. return this == obj ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  30008. }
  30009. /** method used for Hash key comparison (specialized for String, Symbol and Fixnum)
  30010. *
  30011. * Will by default just call the Ruby method "eql?"
  30012. */
  30013. public boolean eql(IRubyObject other) {
  30014. return callMethod(getRuntime().getCurrentContext(), MethodIndex.EQL_P, "eql?", other).isTrue();
  30015. }
  30016. /** rb_obj_equal
  30017. *
  30018. * Just like "==" and "equal?", "eql?" will use identity equality for Object.
  30019. */
  30020. @JRubyMethod(name = "eql?", required = 1)
  30021. public IRubyObject eql_p(IRubyObject obj) {
  30022. return this == obj ? getRuntime().getTrue() : getRuntime().getFalse();
  30023. }
  30024. /** rb_equal
  30025. *
  30026. * The Ruby "===" method is used by default in case/when
  30027. * statements. The Object implementation first checks Java identity
  30028. * equality and then calls the "==" method too.
  30029. */
  30030. @JRubyMethod(name = "===", required = 1)
  30031. public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
  30032. return context.getRuntime().newBoolean(equalInternal(context, this, other));
  30033. }
  30034. /**
  30035. * Helper method for checking equality, first using Java identity
  30036. * equality, and then calling the "==" method.
  30037. */
  30038. protected static boolean equalInternal(final ThreadContext context, final IRubyObject that, final IRubyObject other){
  30039. return that == other || that.callMethod(context, MethodIndex.EQUALEQUAL, "==", other).isTrue();
  30040. }
  30041. /**
  30042. * Helper method for checking equality, first using Java identity
  30043. * equality, and then calling the "eql?" method.
  30044. */
  30045. protected static boolean eqlInternal(final ThreadContext context, final IRubyObject that, final IRubyObject other){
  30046. return that == other || that.callMethod(context, MethodIndex.EQL_P, "eql?", other).isTrue();
  30047. }
  30048. /** rb_obj_init_copy
  30049. *
  30050. * Initializes this object as a copy of the original, that is the
  30051. * parameter to this object. Will make sure that the argument
  30052. * actually has the same real class as this object. It shouldn't
  30053. * be possible to initialize an object with something totally
  30054. * different.
  30055. */
  30056. @JRubyMethod(name = "initialize_copy", required = 1, visibility = Visibility.PRIVATE)
  30057. public IRubyObject initialize_copy(IRubyObject original) {
  30058. if (this == original) return this;
  30059. checkFrozen();
  30060. if (getMetaClass().getRealClass() != original.getMetaClass().getRealClass()) {
  30061. throw getRuntime().newTypeError("initialize_copy should take same class object");
  30062. }
  30063. return this;
  30064. }
  30065. /** obj_respond_to
  30066. *
  30067. * respond_to?( aSymbol, includePriv=false ) -> true or false
  30068. *
  30069. * Returns true if this object responds to the given method. Private
  30070. * methods are included in the search only if the optional second
  30071. * parameter evaluates to true.
  30072. *
  30073. * @return true if this responds to the given method
  30074. *
  30075. * !!! 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
  30076. *
  30077. * Going back to splitting according to method arity. MRI is wrong
  30078. * about most of these anyway, and since we have arity splitting
  30079. * in both the compiler and the interpreter, the performance
  30080. * benefit is important for this method.
  30081. */
  30082. @JRubyMethod(name = "respond_to?")
  30083. public RubyBoolean respond_to_p(IRubyObject mname) {
  30084. String name = mname.asJavaString();
  30085. return getRuntime().newBoolean(getMetaClass().isMethodBound(name, true));
  30086. }
  30087. /** obj_respond_to
  30088. *
  30089. * respond_to?( aSymbol, includePriv=false ) -> true or false
  30090. *
  30091. * Returns true if this object responds to the given method. Private
  30092. * methods are included in the search only if the optional second
  30093. * parameter evaluates to true.
  30094. *
  30095. * @return true if this responds to the given method
  30096. *
  30097. * !!! 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
  30098. *
  30099. * Going back to splitting according to method arity. MRI is wrong
  30100. * about most of these anyway, and since we have arity splitting
  30101. * in both the compiler and the interpreter, the performance
  30102. * benefit is important for this method.
  30103. */
  30104. @JRubyMethod(name = "respond_to?")
  30105. public RubyBoolean respond_to_p(IRubyObject mname, IRubyObject includePrivate) {
  30106. String name = mname.asJavaString();
  30107. return getRuntime().newBoolean(getMetaClass().isMethodBound(name, !includePrivate.isTrue()));
  30108. }
  30109. /** rb_obj_id
  30110. *
  30111. * Return the internal id of an object.
  30112. *
  30113. * FIXME: Should this be renamed to match its ruby name?
  30114. */
  30115. @JRubyMethod(name = {"object_id", "__id__"})
  30116. public synchronized IRubyObject id() {
  30117. return getRuntime().newFixnum(getRuntime().getObjectSpace().idOf(this));
  30118. }
  30119. /** rb_obj_id_obsolete
  30120. *
  30121. * Old id version. This one is bound to the "id" name and will emit a deprecation warning.
  30122. */
  30123. @JRubyMethod(name = "id")
  30124. public synchronized IRubyObject id_deprecated() {
  30125. getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Object#id will be deprecated; use Object#object_id", "Object#id", "Object#object_id");
  30126. return id();
  30127. }
  30128. /** rb_obj_id
  30129. *
  30130. * Will return the hash code of this object. In comparison to MRI,
  30131. * this method will use the Java identity hash code instead of
  30132. * using rb_obj_id, since the usage of id in JRuby will incur the
  30133. * cost of some. ObjectSpace maintenance.
  30134. */
  30135. @JRubyMethod(name = "hash")
  30136. public RubyFixnum hash() {
  30137. return getRuntime().newFixnum(super.hashCode());
  30138. }
  30139. /**
  30140. * Override the Object#hashCode method to make sure that the Ruby
  30141. * hash is actually used as the hashcode for Ruby objects. If the
  30142. * Ruby "hash" method doesn't return a number, the Object#hashCode
  30143. * implementation will be used instead.
  30144. */
  30145. @Override
  30146. public int hashCode() {
  30147. IRubyObject hashValue = callMethod(getRuntime().getCurrentContext(), MethodIndex.HASH, "hash");
  30148. if (hashValue instanceof RubyFixnum) return (int) RubyNumeric.fix2long(hashValue);
  30149. return super.hashCode();
  30150. }
  30151. /** rb_obj_class
  30152. *
  30153. * Returns the real class of this object, excluding any
  30154. * singleton/meta class in the inheritance chain.
  30155. */
  30156. @JRubyMethod(name = "class")
  30157. public RubyClass type() {
  30158. return getMetaClass().getRealClass();
  30159. }
  30160. /** rb_obj_type
  30161. *
  30162. * The deprecated version of type, that emits a deprecation
  30163. * warning.
  30164. */
  30165. @JRubyMethod(name = "type")
  30166. public RubyClass type_deprecated() {
  30167. getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "Object#type is deprecated; use Object#class", "Object#type", "Object#class");
  30168. return type();
  30169. }
  30170. /** rb_obj_clone
  30171. *
  30172. * This method should be overridden only by: Proc, Method,
  30173. * UnboundedMethod, Binding. It will use the defined allocated of
  30174. * the object, then clone the singleton class, taint the object,
  30175. * call initCopy and then copy frozen state.
  30176. */
  30177. @JRubyMethod(name = "clone", frame = true)
  30178. public IRubyObject rbClone() {
  30179. if (isImmediate()) throw getRuntime().newTypeError("can't clone " + getMetaClass().getName());
  30180. // We're cloning ourselves, so we know the result should be a RubyObject
  30181. RubyObject clone = (RubyObject)getMetaClass().getRealClass().allocate();
  30182. clone.setMetaClass(getSingletonClassClone());
  30183. if (isTaint()) clone.setTaint(true);
  30184. initCopy(clone, this);
  30185. if (isFrozen()) clone.setFrozen(true);
  30186. return clone;
  30187. }
  30188. /** rb_obj_dup
  30189. *
  30190. * This method should be overridden only by: Proc
  30191. *
  30192. * Will allocate a new instance of the real class of this object,
  30193. * and then initialize that copy. It's different from {@link
  30194. * #rbClone} in that it doesn't copy the singleton class.
  30195. */
  30196. @JRubyMethod(name = "dup")
  30197. public IRubyObject dup() {
  30198. if (isImmediate()) throw getRuntime().newTypeError("can't dup " + getMetaClass().getName());
  30199. IRubyObject dup = getMetaClass().getRealClass().allocate();
  30200. if (isTaint()) dup.setTaint(true);
  30201. initCopy(dup, this);
  30202. return dup;
  30203. }
  30204. /**
  30205. * Lots of MRI objects keep their state in non-lookupable ivars
  30206. * (e:g. Range, Struct, etc). This method is responsible for
  30207. * dupping our java field equivalents
  30208. */
  30209. protected void copySpecialInstanceVariables(IRubyObject clone) {
  30210. }
  30211. /** rb_obj_display
  30212. *
  30213. * call-seq:
  30214. * obj.display(port=$>) => nil
  30215. *
  30216. * Prints <i>obj</i> on the given port (default <code>$></code>).
  30217. * Equivalent to:
  30218. *
  30219. * def display(port=$>)
  30220. * port.write self
  30221. * end
  30222. *
  30223. * For example:
  30224. *
  30225. * 1.display
  30226. * "cat".display
  30227. * [ 4, 5, 6 ].display
  30228. * puts
  30229. *
  30230. * <em>produces:</em>
  30231. *
  30232. * 1cat456
  30233. *
  30234. */
  30235. @JRubyMethod(name = "display", optional = 1)
  30236. public IRubyObject display(ThreadContext context, IRubyObject[] args) {
  30237. IRubyObject port = args.length == 0 ? context.getRuntime().getGlobalVariables().get("$>") : args[0];
  30238. port.callMethod(context, "write", this);
  30239. return context.getRuntime().getNil();
  30240. }
  30241. /** rb_obj_tainted
  30242. *
  30243. * call-seq:
  30244. * obj.tainted? => true or false
  30245. *
  30246. * Returns <code>true</code> if the object is tainted.
  30247. *
  30248. */
  30249. @JRubyMethod(name = "tainted?")
  30250. public RubyBoolean tainted_p(ThreadContext context) {
  30251. return context.getRuntime().newBoolean(isTaint());
  30252. }
  30253. /** rb_obj_taint
  30254. *
  30255. * call-seq:
  30256. * obj.taint -> obj
  30257. *
  30258. * Marks <i>obj</i> as tainted---if the <code>$SAFE</code> level is
  30259. * set appropriately, many method calls which might alter the running
  30260. * programs environment will refuse to accept tainted strings.
  30261. */
  30262. @JRubyMethod(name = "taint")
  30263. public IRubyObject taint(ThreadContext context) {
  30264. taint(context.getRuntime());
  30265. return this;
  30266. }
  30267. private void taint(Ruby runtime) {
  30268. runtime.secure(4);
  30269. if (!isTaint()) {
  30270. testFrozen("object");
  30271. setTaint(true);
  30272. }
  30273. }
  30274. /** rb_obj_untaint
  30275. *
  30276. * call-seq:
  30277. * obj.untaint => obj
  30278. *
  30279. * Removes the taint from <i>obj</i>.
  30280. *
  30281. * Only callable in if more secure than 3.
  30282. */
  30283. @JRubyMethod(name = "untaint")
  30284. public IRubyObject untaint(ThreadContext context) {
  30285. context.getRuntime().secure(3);
  30286. if (isTaint()) {
  30287. testFrozen("object");
  30288. setTaint(false);
  30289. }
  30290. return this;
  30291. }
  30292. /** rb_obj_freeze
  30293. *
  30294. * call-seq:
  30295. * obj.freeze => obj
  30296. *
  30297. * Prevents further modifications to <i>obj</i>. A
  30298. * <code>TypeError</code> will be raised if modification is attempted.
  30299. * There is no way to unfreeze a frozen object. See also
  30300. * <code>Object#frozen?</code>.
  30301. *
  30302. * a = [ "a", "b", "c" ]
  30303. * a.freeze
  30304. * a << "z"
  30305. *
  30306. * <em>produces:</em>
  30307. *
  30308. * prog.rb:3:in `<<': can't modify frozen array (TypeError)
  30309. * from prog.rb:3
  30310. */
  30311. @JRubyMethod(name = "freeze")
  30312. public IRubyObject freeze(ThreadContext context) {
  30313. if ((flags & FROZEN_F) == 0) {
  30314. if (context.getRuntime().getSafeLevel() >= 4 && isTaint()) {
  30315. throw context.getRuntime().newSecurityError("Insecure: can't freeze object");
  30316. }
  30317. flags |= FROZEN_F;
  30318. }
  30319. return this;
  30320. }
  30321. /** rb_obj_frozen_p
  30322. *
  30323. * call-seq:
  30324. * obj.frozen? => true or false
  30325. *
  30326. * Returns the freeze status of <i>obj</i>.
  30327. *
  30328. * a = [ "a", "b", "c" ]
  30329. * a.freeze #=> ["a", "b", "c"]
  30330. * a.frozen? #=> true
  30331. */
  30332. @JRubyMethod(name = "frozen?")
  30333. public RubyBoolean frozen_p(ThreadContext context) {
  30334. return context.getRuntime().newBoolean(isFrozen());
  30335. }
  30336. /** inspect_obj
  30337. *
  30338. * The internal helper method that takes care of the part of the
  30339. * inspection that inspects instance variables.
  30340. */
  30341. private StringBuilder inspectObj(StringBuilder part) {
  30342. ThreadContext context = getRuntime().getCurrentContext();
  30343. String sep = "";
  30344. for (Variable<IRubyObject> ivar : getInstanceVariableList()) {
  30345. part.append(sep).append(" ").append(ivar.getName()).append("=");
  30346. part.append(ivar.getValue().callMethod(context, "inspect"));
  30347. sep = ",";
  30348. }
  30349. part.append(">");
  30350. return part;
  30351. }
  30352. /** rb_inspect
  30353. *
  30354. * The internal helper that ensures a RubyString instance is returned
  30355. * so dangerous casting can be omitted
  30356. * Prefered over callMethod(context, "inspect")
  30357. */
  30358. static RubyString inspect(ThreadContext context, IRubyObject object) {
  30359. return RubyString.objAsString(context, object.callMethod(context, "inspect"));
  30360. }
  30361. /** rb_obj_inspect
  30362. *
  30363. * call-seq:
  30364. * obj.inspect => string
  30365. *
  30366. * Returns a string containing a human-readable representation of
  30367. * <i>obj</i>. If not overridden, uses the <code>to_s</code> method to
  30368. * generate the string.
  30369. *
  30370. * [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
  30371. * Time.new.inspect #=> "Wed Apr 09 08:54:39 CDT 2003"
  30372. */
  30373. @JRubyMethod(name = "inspect")
  30374. public IRubyObject inspect() {
  30375. Ruby runtime = getRuntime();
  30376. if ((!isImmediate()) &&
  30377. // TYPE(obj) == T_OBJECT
  30378. !(this instanceof RubyClass) &&
  30379. this != runtime.getObject() &&
  30380. this != runtime.getModule() &&
  30381. !(this instanceof RubyModule) &&
  30382. // TODO: should have #hasInstanceVariables method, though
  30383. // this will work here:
  30384. hasVariables()) {
  30385. StringBuilder part = new StringBuilder();
  30386. String cname = getMetaClass().getRealClass().getName();
  30387. part.append("#<").append(cname).append(":0x");
  30388. part.append(Integer.toHexString(System.identityHashCode(this)));
  30389. if (runtime.isInspecting(this)) {
  30390. /* 6:tags 16:addr 1:eos */
  30391. part.append(" ...>");
  30392. return runtime.newString(part.toString());
  30393. }
  30394. try {
  30395. runtime.registerInspecting(this);
  30396. return runtime.newString(inspectObj(part).toString());
  30397. } finally {
  30398. runtime.unregisterInspecting(this);
  30399. }
  30400. }
  30401. if (isNil()) return RubyNil.inspect(this);
  30402. return RuntimeHelpers.invoke(runtime.getCurrentContext(), this, "to_s");
  30403. }
  30404. /** rb_obj_is_instance_of
  30405. *
  30406. * call-seq:
  30407. * obj.instance_of?(class) => true or false
  30408. *
  30409. * Returns <code>true</code> if <i>obj</i> is an instance of the given
  30410. * class. See also <code>Object#kind_of?</code>.
  30411. */
  30412. @JRubyMethod(name = "instance_of?", required = 1)
  30413. public RubyBoolean instance_of_p(ThreadContext context, IRubyObject type) {
  30414. if (type() == type) {
  30415. return context.getRuntime().getTrue();
  30416. } else if (!(type instanceof RubyModule)) {
  30417. throw context.getRuntime().newTypeError("class or module required");
  30418. } else {
  30419. return context.getRuntime().getFalse();
  30420. }
  30421. }
  30422. /** rb_obj_is_kind_of
  30423. *
  30424. * call-seq:
  30425. * obj.is_a?(class) => true or false
  30426. * obj.kind_of?(class) => true or false
  30427. *
  30428. * Returns <code>true</code> if <i>class</i> is the class of
  30429. * <i>obj</i>, or if <i>class</i> is one of the superclasses of
  30430. * <i>obj</i> or modules included in <i>obj</i>.
  30431. *
  30432. * module M; end
  30433. * class A
  30434. * include M
  30435. * end
  30436. * class B < A; end
  30437. * class C < B; end
  30438. * b = B.new
  30439. * b.instance_of? A #=> false
  30440. * b.instance_of? B #=> true
  30441. * b.instance_of? C #=> false
  30442. * b.instance_of? M #=> false
  30443. * b.kind_of? A #=> true
  30444. * b.kind_of? B #=> true
  30445. * b.kind_of? C #=> false
  30446. * b.kind_of? M #=> true
  30447. */
  30448. @JRubyMethod(name = {"kind_of?", "is_a?"}, required = 1)
  30449. public RubyBoolean kind_of_p(ThreadContext context, IRubyObject type) {
  30450. // TODO: Generalize this type-checking code into IRubyObject helper.
  30451. if (!(type instanceof RubyModule)) {
  30452. // TODO: newTypeError does not offer enough for ruby error string...
  30453. throw context.getRuntime().newTypeError("class or module required");
  30454. }
  30455. return context.getRuntime().newBoolean(((RubyModule)type).isInstance(this));
  30456. }
  30457. /** rb_obj_methods
  30458. *
  30459. * call-seq:
  30460. * obj.methods => array
  30461. *
  30462. * Returns a list of the names of methods publicly accessible in
  30463. * <i>obj</i>. This will include all the methods accessible in
  30464. * <i>obj</i>'s ancestors.
  30465. *
  30466. * class Klass
  30467. * def kMethod()
  30468. * end
  30469. * end
  30470. * k = Klass.new
  30471. * k.methods[0..9] #=> ["kMethod", "freeze", "nil?", "is_a?",
  30472. * "class", "instance_variable_set",
  30473. * "methods", "extend", "__send__", "instance_eval"]
  30474. * k.methods.length #=> 42
  30475. */
  30476. @JRubyMethod(name = "methods", optional = 1)
  30477. public IRubyObject methods(ThreadContext context, IRubyObject[] args) {
  30478. boolean all = true;
  30479. if (args.length == 1) {
  30480. all = args[0].isTrue();
  30481. }
  30482. RubyArray singletonMethods = null;
  30483. if (getMetaClass().isSingleton()) {
  30484. singletonMethods =
  30485. getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getFalse()});
  30486. if (all) {
  30487. singletonMethods.concat(getMetaClass().getSuperClass().instance_methods(new IRubyObject[] {context.getRuntime().getTrue()}));
  30488. }
  30489. } else {
  30490. if (all) {
  30491. singletonMethods = getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getTrue()});
  30492. } else {
  30493. singletonMethods = context.getRuntime().newEmptyArray();
  30494. }
  30495. }
  30496. return singletonMethods;
  30497. }
  30498. /** rb_obj_public_methods
  30499. *
  30500. * call-seq:
  30501. * obj.public_methods(all=true) => array
  30502. *
  30503. * Returns the list of public methods accessible to <i>obj</i>. If
  30504. * the <i>all</i> parameter is set to <code>false</code>, only those methods
  30505. * in the receiver will be listed.
  30506. */
  30507. @JRubyMethod(name = "public_methods", optional = 1)
  30508. public IRubyObject public_methods(ThreadContext context, IRubyObject[] args) {
  30509. if (args.length == 0) {
  30510. args = new IRubyObject[] { context.getRuntime().getTrue() };
  30511. }
  30512. return getMetaClass().public_instance_methods(args);
  30513. }
  30514. /** rb_obj_protected_methods
  30515. *
  30516. * call-seq:
  30517. * obj.protected_methods(all=true) => array
  30518. *
  30519. * Returns the list of protected methods accessible to <i>obj</i>. If
  30520. * the <i>all</i> parameter is set to <code>false</code>, only those methods
  30521. * in the receiver will be listed.
  30522. *
  30523. * Internally this implementation uses the
  30524. * {@link RubyModule#protected_instance_methods} method.
  30525. */
  30526. @JRubyMethod(name = "protected_methods", optional = 1)
  30527. public IRubyObject protected_methods(ThreadContext context, IRubyObject[] args) {
  30528. if (args.length == 0) {
  30529. args = new IRubyObject[] { context.getRuntime().getTrue() };
  30530. }
  30531. return getMetaClass().protected_instance_methods(args);
  30532. }
  30533. /** rb_obj_private_methods
  30534. *
  30535. * call-seq:
  30536. * obj.private_methods(all=true) => array
  30537. *
  30538. * Returns the list of private methods accessible to <i>obj</i>. If
  30539. * the <i>all</i> parameter is set to <code>false</code>, only those methods
  30540. * in the receiver will be listed.
  30541. *
  30542. * Internally this implementation uses the
  30543. * {@link RubyModule#private_instance_methods} method.
  30544. */
  30545. @JRubyMethod(name = "private_methods", optional = 1)
  30546. public IRubyObject private_methods(ThreadContext context, IRubyObject[] args) {
  30547. if (args.length == 0) {
  30548. args = new IRubyObject[] { context.getRuntime().getTrue() };
  30549. }
  30550. return getMetaClass().private_instance_methods(args);
  30551. }
  30552. /** rb_obj_singleton_methods
  30553. *
  30554. * call-seq:
  30555. * obj.singleton_methods(all=true) => array
  30556. *
  30557. * Returns an array of the names of singleton methods for <i>obj</i>.
  30558. * If the optional <i>all</i> parameter is true, the list will include
  30559. * methods in modules included in <i>obj</i>.
  30560. *
  30561. * module Other
  30562. * def three() end
  30563. * end
  30564. *
  30565. * class Single
  30566. * def Single.four() end
  30567. * end
  30568. *
  30569. * a = Single.new
  30570. *
  30571. * def a.one()
  30572. * end
  30573. *
  30574. * class << a
  30575. * include Other
  30576. * def two()
  30577. * end
  30578. * end
  30579. *
  30580. * Single.singleton_methods #=> ["four"]
  30581. * a.singleton_methods(false) #=> ["two", "one"]
  30582. * a.singleton_methods #=> ["two", "one", "three"]
  30583. */
  30584. // TODO: This is almost RubyModule#instance_methods on the metaClass. Perhaps refactor.
  30585. @JRubyMethod(name = "singleton_methods", optional = 1)
  30586. public RubyArray singleton_methods(ThreadContext context, IRubyObject[] args) {
  30587. boolean all = true;
  30588. if(args.length == 1) {
  30589. all = args[0].isTrue();
  30590. }
  30591. RubyArray singletonMethods;
  30592. if (getMetaClass().isSingleton()) {
  30593. singletonMethods = getMetaClass().instance_methods(new IRubyObject[] {context.getRuntime().getFalse()});
  30594. if (all) {
  30595. RubyClass superClass = getMetaClass().getSuperClass();
  30596. while (superClass.isIncluded()) {
  30597. singletonMethods.concat(superClass.instance_methods(new IRubyObject[] {context.getRuntime().getFalse()}));
  30598. superClass = superClass.getSuperClass();
  30599. }
  30600. }
  30601. } else {
  30602. singletonMethods = context.getRuntime().newEmptyArray();
  30603. }
  30604. return singletonMethods;
  30605. }
  30606. /** rb_obj_method
  30607. *
  30608. * call-seq:
  30609. * obj.method(sym) => method
  30610. *
  30611. * Looks up the named method as a receiver in <i>obj</i>, returning a
  30612. * <code>Method</code> object (or raising <code>NameError</code>). The
  30613. * <code>Method</code> object acts as a closure in <i>obj</i>'s object
  30614. * instance, so instance variables and the value of <code>self</code>
  30615. * remain available.
  30616. *
  30617. * class Demo
  30618. * def initialize(n)
  30619. * @iv = n
  30620. * end
  30621. * def hello()
  30622. * "Hello, @iv = #{@iv}"
  30623. * end
  30624. * end
  30625. *
  30626. * k = Demo.new(99)
  30627. * m = k.method(:hello)
  30628. * m.call #=> "Hello, @iv = 99"
  30629. *
  30630. * l = Demo.new('Fred')
  30631. * m = l.method("hello")
  30632. * m.call #=> "Hello, @iv = Fred"
  30633. */
  30634. @JRubyMethod(name = "method", required = 1)
  30635. public IRubyObject method(IRubyObject symbol) {
  30636. return getMetaClass().newMethod(this, symbol.asJavaString(), true);
  30637. }
  30638. /**
  30639. * Internal method that helps to convert any object into the
  30640. * format of a class name and a hex string inside of #<>.
  30641. */
  30642. public IRubyObject anyToString() {
  30643. String cname = getMetaClass().getRealClass().getName();
  30644. /* 6:tags 16:addr 1:eos */
  30645. RubyString str = getRuntime().newString("#<" + cname + ":0x" + Integer.toHexString(System.identityHashCode(this)) + ">");
  30646. str.setTaint(isTaint());
  30647. return str;
  30648. }
  30649. /** rb_any_to_s
  30650. *
  30651. * call-seq:
  30652. * obj.to_s => string
  30653. *
  30654. * Returns a string representing <i>obj</i>. The default
  30655. * <code>to_s</code> prints the object's class and an encoding of the
  30656. * object id. As a special case, the top-level object that is the
  30657. * initial execution context of Ruby programs returns ``main.''
  30658. */
  30659. @JRubyMethod(name = "to_s")
  30660. public IRubyObject to_s() {
  30661. return anyToString();
  30662. }
  30663. /** rb_any_to_a
  30664. *
  30665. * call-seq:
  30666. * obj.to_a -> anArray
  30667. *
  30668. * Returns an array representation of <i>obj</i>. For objects of class
  30669. * <code>Object</code> and others that don't explicitly override the
  30670. * method, the return value is an array containing <code>self</code>.
  30671. * However, this latter behavior will soon be obsolete.
  30672. *
  30673. * self.to_a #=> -:1: warning: default `to_a' will be obsolete
  30674. * "hello".to_a #=> ["hello"]
  30675. * Time.new.to_a #=> [39, 54, 8, 9, 4, 2003, 3, 99, true, "CDT"]
  30676. *
  30677. * The default to_a method is deprecated.
  30678. */
  30679. @JRubyMethod(name = "to_a", visibility = Visibility.PUBLIC)
  30680. public RubyArray to_a() {
  30681. getRuntime().getWarnings().warn(ID.DEPRECATED_METHOD, "default 'to_a' will be obsolete", "to_a");
  30682. return getRuntime().newArray(this);
  30683. }
  30684. /** rb_obj_instance_eval
  30685. *
  30686. * call-seq:
  30687. * obj.instance_eval(string [, filename [, lineno]] ) => obj
  30688. * obj.instance_eval {| | block } => obj
  30689. *
  30690. * Evaluates a string containing Ruby source code, or the given block,
  30691. * within the context of the receiver (_obj_). In order to set the
  30692. * context, the variable +self+ is set to _obj_ while
  30693. * the code is executing, giving the code access to _obj_'s
  30694. * instance variables. In the version of <code>instance_eval</code>
  30695. * that takes a +String+, the optional second and third
  30696. * parameters supply a filename and starting line number that are used
  30697. * when reporting compilation errors.
  30698. *
  30699. * class Klass
  30700. * def initialize
  30701. * @secret = 99
  30702. * end
  30703. * end
  30704. * k = Klass.new
  30705. * k.instance_eval { @secret } #=> 99
  30706. */
  30707. @JRubyMethod(name = "instance_eval", frame = true)
  30708. public IRubyObject instance_eval(ThreadContext context, Block block) {
  30709. return specificEval(context, getInstanceEvalClass(), block);
  30710. }
  30711. @JRubyMethod(name = "instance_eval", frame = true)
  30712. public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, Block block) {
  30713. return specificEval(context, getInstanceEvalClass(), arg0, block);
  30714. }
  30715. @JRubyMethod(name = "instance_eval", frame = true)
  30716. public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  30717. return specificEval(context, getInstanceEvalClass(), arg0, arg1, block);
  30718. }
  30719. @JRubyMethod(name = "instance_eval", frame = true)
  30720. public IRubyObject instance_eval(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  30721. return specificEval(context, getInstanceEvalClass(), arg0, arg1, arg2, block);
  30722. }
  30723. @Deprecated
  30724. public IRubyObject instance_eval(ThreadContext context, IRubyObject[] args, Block block) {
  30725. RubyModule klazz;
  30726. if (isImmediate()) {
  30727. // Ruby uses Qnil here, we use "dummy" because we need a class
  30728. klazz = context.getRuntime().getDummy();
  30729. } else {
  30730. klazz = getSingletonClass();
  30731. }
  30732. return specificEval(context, klazz, args, block);
  30733. }
  30734. private RubyModule getInstanceEvalClass() {
  30735. if (isImmediate()) {
  30736. // Ruby uses Qnil here, we use "dummy" because we need a class
  30737. return getRuntime().getDummy();
  30738. } else {
  30739. return getSingletonClass();
  30740. }
  30741. }
  30742. /** rb_obj_instance_exec
  30743. *
  30744. * call-seq:
  30745. * obj.instance_exec(arg...) {|var...| block } => obj
  30746. *
  30747. * Executes the given block within the context of the receiver
  30748. * (_obj_). In order to set the context, the variable +self+ is set
  30749. * to _obj_ while the code is executing, giving the code access to
  30750. * _obj_'s instance variables. Arguments are passed as block parameters.
  30751. *
  30752. * class Klass
  30753. * def initialize
  30754. * @secret = 99
  30755. * end
  30756. * end
  30757. * k = Klass.new
  30758. * k.instance_exec(5) {|x| @secret+x } #=> 104
  30759. */
  30760. @JRubyMethod(name = "instance_exec", optional = 3, frame = true)
  30761. public IRubyObject instance_exec(ThreadContext context, IRubyObject[] args, Block block) {
  30762. if (!block.isGiven()) throw context.getRuntime().newArgumentError("block not supplied");
  30763. RubyModule klazz;
  30764. if (isImmediate()) {
  30765. // Ruby uses Qnil here, we use "dummy" because we need a class
  30766. klazz = context.getRuntime().getDummy();
  30767. } else {
  30768. klazz = getSingletonClass();
  30769. }
  30770. return yieldUnder(context, klazz, args, block);
  30771. }
  30772. /** rb_obj_extend
  30773. *
  30774. * call-seq:
  30775. * obj.extend(module, ...) => obj
  30776. *
  30777. * Adds to _obj_ the instance methods from each module given as a
  30778. * parameter.
  30779. *
  30780. * module Mod
  30781. * def hello
  30782. * "Hello from Mod.\n"
  30783. * end
  30784. * end
  30785. *
  30786. * class Klass
  30787. * def hello
  30788. * "Hello from Klass.\n"
  30789. * end
  30790. * end
  30791. *
  30792. * k = Klass.new
  30793. * k.hello #=> "Hello from Klass.\n"
  30794. * k.extend(Mod) #=> #<Klass:0x401b3bc8>
  30795. * k.hello #=> "Hello from Mod.\n"
  30796. */
  30797. @JRubyMethod(name = "extend", required = 1, rest = true)
  30798. public IRubyObject extend(IRubyObject[] args) {
  30799. Ruby runtime = getRuntime();
  30800. // Make sure all arguments are modules before calling the callbacks
  30801. for (int i = 0; i < args.length; i++) {
  30802. if (!args[i].isModule()) throw runtime.newTypeError(args[i], runtime.getModule());
  30803. }
  30804. ThreadContext context = runtime.getCurrentContext();
  30805. // MRI extends in order from last to first
  30806. for (int i = args.length - 1; i >= 0; i--) {
  30807. args[i].callMethod(context, "extend_object", this);
  30808. args[i].callMethod(context, "extended", this);
  30809. }
  30810. return this;
  30811. }
  30812. /** rb_obj_dummy
  30813. *
  30814. * Default initialize method. This one gets defined in some other
  30815. * place as a Ruby method.
  30816. */
  30817. public IRubyObject initialize() {
  30818. return getRuntime().getNil();
  30819. }
  30820. /** rb_f_send
  30821. *
  30822. * send( aSymbol [, args ]* ) -> anObject
  30823. *
  30824. * Invokes the method identified by aSymbol, passing it any arguments
  30825. * specified. You can use __send__ if the name send clashes with an
  30826. * existing method in this object.
  30827. *
  30828. * <pre>
  30829. * class Klass
  30830. * def hello(*args)
  30831. * "Hello " + args.join(' ')
  30832. * end
  30833. * end
  30834. *
  30835. * k = Klass.new
  30836. * k.send :hello, "gentle", "readers"
  30837. * </pre>
  30838. *
  30839. * @return the result of invoking the method identified by aSymbol.
  30840. */
  30841. @JRubyMethod(name = {"send", "__send__"})
  30842. public IRubyObject send(ThreadContext context, Block block) {
  30843. throw context.getRuntime().newArgumentError(0, 1);
  30844. }
  30845. @JRubyMethod(name = {"send", "__send__"})
  30846. public IRubyObject send(ThreadContext context, IRubyObject arg0, Block block) {
  30847. String name = arg0.asJavaString();
  30848. return getMetaClass().finvoke(context, this, name, block);
  30849. }
  30850. @JRubyMethod(name = {"send", "__send__"})
  30851. public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  30852. String name = arg0.asJavaString();
  30853. return getMetaClass().finvoke(context, this, name, arg1, block);
  30854. }
  30855. @JRubyMethod(name = {"send", "__send__"})
  30856. public IRubyObject send(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
  30857. String name = arg0.asJavaString();
  30858. return getMetaClass().finvoke(context, this, name, arg1, arg2, block);
  30859. }
  30860. @JRubyMethod(name = {"send", "__send__"}, rest = true)
  30861. public IRubyObject send(ThreadContext context, IRubyObject[] args, Block block) {
  30862. String name = args[0].asJavaString();
  30863. int newArgsLength = args.length - 1;
  30864. IRubyObject[] newArgs;
  30865. if (newArgsLength == 0) {
  30866. newArgs = IRubyObject.NULL_ARRAY;
  30867. } else {
  30868. newArgs = new IRubyObject[newArgsLength];
  30869. System.arraycopy(args, 1, newArgs, 0, newArgs.length);
  30870. }
  30871. return getMetaClass().finvoke(context, this, name, newArgs, block);
  30872. }
  30873. /** rb_false
  30874. *
  30875. * call_seq:
  30876. * nil.nil? => true
  30877. * <anything_else>.nil? => false
  30878. *
  30879. * Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
  30880. */
  30881. @JRubyMethod(name = "nil?")
  30882. public IRubyObject nil_p(ThreadContext context) {
  30883. return context.getRuntime().getFalse();
  30884. }
  30885. /** rb_obj_pattern_match
  30886. *
  30887. * call-seq:
  30888. * obj =~ other => false
  30889. *
  30890. * Pattern Match---Overridden by descendents (notably
  30891. * <code>Regexp</code> and <code>String</code>) to provide meaningful
  30892. * pattern-match semantics.
  30893. */
  30894. @JRubyMethod(name = "=~", required = 1)
  30895. public IRubyObject op_match(ThreadContext context, IRubyObject arg) {
  30896. return context.getRuntime().getFalse();
  30897. }
  30898. public IRubyObject to_java() {
  30899. throw getRuntime().newTypeError(getMetaClass().getBaseName() + " cannot coerce to a Java type.");
  30900. }
  30901. public IRubyObject as(Class javaClass) {
  30902. throw getRuntime().newTypeError(getMetaClass().getBaseName() + " cannot coerce to a Java type.");
  30903. }
  30904. /**
  30905. * @see org.jruby.runtime.builtin.IRubyObject#getType()
  30906. */
  30907. public RubyClass getType() {
  30908. return type();
  30909. }
  30910. /**
  30911. * @see org.jruby.runtime.builtin.IRubyObject#dataWrapStruct()
  30912. */
  30913. public synchronized void dataWrapStruct(Object obj) {
  30914. this.dataStruct = obj;
  30915. }
  30916. /**
  30917. * @see org.jruby.runtime.builtin.IRubyObject#dataGetStruct()
  30918. */
  30919. public synchronized Object dataGetStruct() {
  30920. return dataStruct;
  30921. }
  30922. /**
  30923. * Adds the specified object as a finalizer for this object.
  30924. */
  30925. public void addFinalizer(IRubyObject finalizer) {
  30926. if (this.finalizer == null) {
  30927. this.finalizer = new Finalizer(getRuntime().getObjectSpace().idOf(this));
  30928. getRuntime().addFinalizer(this.finalizer);
  30929. }
  30930. this.finalizer.addFinalizer(finalizer);
  30931. }
  30932. /**
  30933. * Remove all the finalizers for this object.
  30934. */
  30935. public void removeFinalizers() {
  30936. if (finalizer != null) {
  30937. finalizer.removeFinalizers();
  30938. finalizer = null;
  30939. getRuntime().removeFinalizer(this.finalizer);
  30940. }
  30941. }
  30942. //
  30943. // INSTANCE VARIABLE RUBY METHODS
  30944. //
  30945. /** rb_obj_ivar_defined
  30946. *
  30947. * call-seq:
  30948. * obj.instance_variable_defined?(symbol) => true or false
  30949. *
  30950. * Returns <code>true</code> if the given instance variable is
  30951. * defined in <i>obj</i>.
  30952. *
  30953. * class Fred
  30954. * def initialize(p1, p2)
  30955. * @a, @b = p1, p2
  30956. * end
  30957. * end
  30958. * fred = Fred.new('cat', 99)
  30959. * fred.instance_variable_defined?(:@a) #=> true
  30960. * fred.instance_variable_defined?("@b") #=> true
  30961. * fred.instance_variable_defined?("@c") #=> false
  30962. */
  30963. @JRubyMethod(name = "instance_variable_defined?", required = 1)
  30964. public IRubyObject instance_variable_defined_p(ThreadContext context, IRubyObject name) {
  30965. if (variableTableContains(validateInstanceVariable(name.asJavaString()))) {
  30966. return context.getRuntime().getTrue();
  30967. }
  30968. return context.getRuntime().getFalse();
  30969. }
  30970. /** rb_obj_ivar_get
  30971. *
  30972. * call-seq:
  30973. * obj.instance_variable_get(symbol) => obj
  30974. *
  30975. * Returns the value of the given instance variable, or nil if the
  30976. * instance variable is not set. The <code>@</code> part of the
  30977. * variable name should be included for regular instance
  30978. * variables. Throws a <code>NameError</code> exception if the
  30979. * supplied symbol is not valid as an instance variable name.
  30980. *
  30981. * class Fred
  30982. * def initialize(p1, p2)
  30983. * @a, @b = p1, p2
  30984. * end
  30985. * end
  30986. * fred = Fred.new('cat', 99)
  30987. * fred.instance_variable_get(:@a) #=> "cat"
  30988. * fred.instance_variable_get("@b") #=> 99
  30989. */
  30990. @JRubyMethod(name = "instance_variable_get", required = 1)
  30991. public IRubyObject instance_variable_get(ThreadContext context, IRubyObject name) {
  30992. IRubyObject value;
  30993. if ((value = variableTableFetch(validateInstanceVariable(name.asJavaString()))) != null) {
  30994. return value;
  30995. }
  30996. return context.getRuntime().getNil();
  30997. }
  30998. /** rb_obj_ivar_set
  30999. *
  31000. * call-seq:
  31001. * obj.instance_variable_set(symbol, obj) => obj
  31002. *
  31003. * Sets the instance variable names by <i>symbol</i> to
  31004. * <i>object</i>, thereby frustrating the efforts of the class's
  31005. * author to attempt to provide proper encapsulation. The variable
  31006. * did not have to exist prior to this call.
  31007. *
  31008. * class Fred
  31009. * def initialize(p1, p2)
  31010. * @a, @b = p1, p2
  31011. * end
  31012. * end
  31013. * fred = Fred.new('cat', 99)
  31014. * fred.instance_variable_set(:@a, 'dog') #=> "dog"
  31015. * fred.instance_variable_set(:@c, 'cat') #=> "cat"
  31016. * fred.inspect #=> "#<Fred:0x401b3da8 @a=\"dog\", @b=99, @c=\"cat\">"
  31017. */
  31018. @JRubyMethod(name = "instance_variable_set", required = 2)
  31019. public IRubyObject instance_variable_set(IRubyObject name, IRubyObject value) {
  31020. ensureInstanceVariablesSettable();
  31021. return variableTableStore(validateInstanceVariable(name.asJavaString()), value);
  31022. }
  31023. /** rb_obj_remove_instance_variable
  31024. *
  31025. * call-seq:
  31026. * obj.remove_instance_variable(symbol) => obj
  31027. *
  31028. * Removes the named instance variable from <i>obj</i>, returning that
  31029. * variable's value.
  31030. *
  31031. * class Dummy
  31032. * attr_reader :var
  31033. * def initialize
  31034. * @var = 99
  31035. * end
  31036. * def remove
  31037. * remove_instance_variable(:@var)
  31038. * end
  31039. * end
  31040. * d = Dummy.new
  31041. * d.var #=> 99
  31042. * d.remove #=> 99
  31043. * d.var #=> nil
  31044. */
  31045. @JRubyMethod(name = "remove_instance_variable", required = 1, frame = true, visibility = Visibility.PRIVATE)
  31046. public IRubyObject remove_instance_variable(ThreadContext context, IRubyObject name, Block block) {
  31047. ensureInstanceVariablesSettable();
  31048. IRubyObject value;
  31049. if ((value = variableTableRemove(validateInstanceVariable(name.asJavaString()))) != null) {
  31050. return value;
  31051. }
  31052. throw context.getRuntime().newNameError("instance variable " + name.asJavaString() + " not defined", name.asJavaString());
  31053. }
  31054. /** rb_obj_instance_variables
  31055. *
  31056. * call-seq:
  31057. * obj.instance_variables => array
  31058. *
  31059. * Returns an array of instance variable names for the receiver. Note
  31060. * that simply defining an accessor does not create the corresponding
  31061. * instance variable.
  31062. *
  31063. * class Fred
  31064. * attr_accessor :a1
  31065. * def initialize
  31066. * @iv = 3
  31067. * end
  31068. * end
  31069. * Fred.new.instance_variables #=> ["@iv"]
  31070. */
  31071. @JRubyMethod(name = "instance_variables")
  31072. public RubyArray instance_variables(ThreadContext context) {
  31073. Ruby runtime = context.getRuntime();
  31074. List<String> nameList = getInstanceVariableNameList();
  31075. RubyArray array = runtime.newArray(nameList.size());
  31076. for (String name : nameList) {
  31077. array.append(runtime.newString(name));
  31078. }
  31079. return array;
  31080. }
  31081. //
  31082. // INSTANCE VARIABLE API METHODS
  31083. //
  31084. /**
  31085. * Dummy method to avoid a cast, and to avoid polluting the
  31086. * IRubyObject interface with all the instance variable management
  31087. * methods.
  31088. */
  31089. public InstanceVariables getInstanceVariables() {
  31090. return this;
  31091. }
  31092. /**
  31093. * @see org.jruby.runtime.builtin.InstanceVariables#hasInstanceVariable
  31094. */
  31095. public boolean hasInstanceVariable(String name) {
  31096. assert IdUtil.isInstanceVariable(name);
  31097. return variableTableContains(name);
  31098. }
  31099. /**
  31100. * @see org.jruby.runtime.builtin.InstanceVariables#fastHasInstanceVariable
  31101. */
  31102. public boolean fastHasInstanceVariable(String internedName) {
  31103. assert IdUtil.isInstanceVariable(internedName);
  31104. return variableTableFastContains(internedName);
  31105. }
  31106. /**
  31107. * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariable
  31108. */
  31109. public IRubyObject getInstanceVariable(String name) {
  31110. assert IdUtil.isInstanceVariable(name);
  31111. return variableTableFetch(name);
  31112. }
  31113. /**
  31114. * @see org.jruby.runtime.builtin.InstanceVariables#fastGetInstanceVariable
  31115. */
  31116. public IRubyObject fastGetInstanceVariable(String internedName) {
  31117. assert IdUtil.isInstanceVariable(internedName);
  31118. return variableTableFastFetch(internedName);
  31119. }
  31120. /** rb_iv_set / rb_ivar_set
  31121. *
  31122. * @see org.jruby.runtime.builtin.InstanceVariables#setInstanceVariable
  31123. */
  31124. public IRubyObject setInstanceVariable(String name, IRubyObject value) {
  31125. assert IdUtil.isInstanceVariable(name) && value != null;
  31126. ensureInstanceVariablesSettable();
  31127. return variableTableStore(name, value);
  31128. }
  31129. /**
  31130. * @see org.jruby.runtime.builtin.InstanceVariables#fastSetInstanceVariable
  31131. */
  31132. public IRubyObject fastSetInstanceVariable(String internedName, IRubyObject value) {
  31133. assert IdUtil.isInstanceVariable(internedName) && value != null;
  31134. ensureInstanceVariablesSettable();
  31135. return variableTableFastStore(internedName, value);
  31136. }
  31137. /**
  31138. * @see org.jruby.runtime.builtin.InstanceVariables#removeInstanceVariable
  31139. */
  31140. public IRubyObject removeInstanceVariable(String name) {
  31141. assert IdUtil.isInstanceVariable(name);
  31142. ensureInstanceVariablesSettable();
  31143. return variableTableRemove(name);
  31144. }
  31145. /**
  31146. * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariableList
  31147. */
  31148. public List<Variable<IRubyObject>> getInstanceVariableList() {
  31149. VariableTableEntry[] table = variableTableGetTable();
  31150. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  31151. IRubyObject readValue;
  31152. for (int i = table.length; --i >= 0; ) {
  31153. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31154. if (IdUtil.isInstanceVariable(e.name)) {
  31155. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  31156. list.add(new VariableEntry<IRubyObject>(e.name, readValue));
  31157. }
  31158. }
  31159. }
  31160. return list;
  31161. }
  31162. /**
  31163. * @see org.jruby.runtime.builtin.InstanceVariables#getInstanceVariableNameList
  31164. */
  31165. public List<String> getInstanceVariableNameList() {
  31166. VariableTableEntry[] table = variableTableGetTable();
  31167. ArrayList<String> list = new ArrayList<String>();
  31168. for (int i = table.length; --i >= 0; ) {
  31169. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31170. if (IdUtil.isInstanceVariable(e.name)) {
  31171. list.add(e.name);
  31172. }
  31173. }
  31174. }
  31175. return list;
  31176. }
  31177. /**
  31178. * The error message used when some one tries to modify an
  31179. * instance variable in a high security setting.
  31180. */
  31181. protected static final String ERR_INSECURE_SET_INST_VAR = "Insecure: can't modify instance variable";
  31182. /**
  31183. * Checks if the name parameter represents a legal instance variable name, and otherwise throws a Ruby NameError
  31184. */
  31185. protected String validateInstanceVariable(String name) {
  31186. if (IdUtil.isValidInstanceVariableName(name)) return name;
  31187. throw getRuntime().newNameError("`" + name + "' is not allowable as an instance variable name", name);
  31188. }
  31189. /**
  31190. * Makes sure that instance variables can be set on this object,
  31191. * including information about whether this object is frozen, or
  31192. * tainted. Will throw a suitable exception in that case.
  31193. */
  31194. protected void ensureInstanceVariablesSettable() {
  31195. if (!isFrozen() && (getRuntime().getSafeLevel() < 4 || isTaint())) {
  31196. return;
  31197. }
  31198. if (getRuntime().getSafeLevel() >= 4 && !isTaint()) {
  31199. throw getRuntime().newSecurityError(ERR_INSECURE_SET_INST_VAR);
  31200. }
  31201. if (isFrozen()) {
  31202. if (this instanceof RubyModule) {
  31203. throw getRuntime().newFrozenError("class/module ");
  31204. } else {
  31205. throw getRuntime().newFrozenError("");
  31206. }
  31207. }
  31208. }
  31209. //
  31210. // INTERNAL VARIABLE METHODS
  31211. //
  31212. /**
  31213. * Dummy method to avoid a cast, and to avoid polluting the
  31214. * IRubyObject interface with all the instance variable management
  31215. * methods.
  31216. */
  31217. public InternalVariables getInternalVariables() {
  31218. return this;
  31219. }
  31220. /**
  31221. * @see org.jruby.runtime.builtin.InternalVariables#hasInternalVariable
  31222. */
  31223. public boolean hasInternalVariable(String name) {
  31224. assert !isRubyVariable(name);
  31225. return variableTableContains(name);
  31226. }
  31227. /**
  31228. * @see org.jruby.runtime.builtin.InternalVariables#fastHasInternalVariable
  31229. */
  31230. public boolean fastHasInternalVariable(String internedName) {
  31231. assert !isRubyVariable(internedName);
  31232. return variableTableFastContains(internedName);
  31233. }
  31234. /**
  31235. * @see org.jruby.runtime.builtin.InternalVariables#getInternalVariable
  31236. */
  31237. public IRubyObject getInternalVariable(String name) {
  31238. assert !isRubyVariable(name);
  31239. return variableTableFetch(name);
  31240. }
  31241. /**
  31242. * @see org.jruby.runtime.builtin.InternalVariables#fastGetInternalVariable
  31243. */
  31244. public IRubyObject fastGetInternalVariable(String internedName) {
  31245. assert !isRubyVariable(internedName);
  31246. return variableTableFastFetch(internedName);
  31247. }
  31248. /**
  31249. * @see org.jruby.runtime.builtin.InternalVariables#setInternalVariable
  31250. */
  31251. public void setInternalVariable(String name, IRubyObject value) {
  31252. assert !isRubyVariable(name);
  31253. variableTableStore(name, value);
  31254. }
  31255. /**
  31256. * @see org.jruby.runtime.builtin.InternalVariables#fastSetInternalVariable
  31257. */
  31258. public void fastSetInternalVariable(String internedName, IRubyObject value) {
  31259. assert !isRubyVariable(internedName);
  31260. variableTableFastStore(internedName, value);
  31261. }
  31262. /**
  31263. * @see org.jruby.runtime.builtin.InternalVariables#removeInternalVariable
  31264. */
  31265. public IRubyObject removeInternalVariable(String name) {
  31266. assert !isRubyVariable(name);
  31267. return variableTableRemove(name);
  31268. }
  31269. /**
  31270. * Sync one variable table with another - this is used to make
  31271. * rbClone work correctly.
  31272. */
  31273. public void syncVariables(List<Variable<IRubyObject>> variables) {
  31274. variableTableSync(variables);
  31275. }
  31276. /**
  31277. * @see org.jruby.runtime.builtin.InternalVariables#getInternalVariableList
  31278. */
  31279. public List<Variable<IRubyObject>> getInternalVariableList() {
  31280. VariableTableEntry[] table = variableTableGetTable();
  31281. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  31282. IRubyObject readValue;
  31283. for (int i = table.length; --i >= 0; ) {
  31284. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31285. if (!isRubyVariable(e.name)) {
  31286. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  31287. list.add(new VariableEntry<IRubyObject>(e.name, readValue));
  31288. }
  31289. }
  31290. }
  31291. return list;
  31292. }
  31293. //
  31294. // COMMON VARIABLE METHODS
  31295. //
  31296. /**
  31297. * Returns true if object has any variables, defined as:
  31298. * <ul>
  31299. * <li> instance variables
  31300. * <li> class variables
  31301. * <li> constants
  31302. * <li> internal variables, such as those used when marshaling Ranges and Exceptions
  31303. * </ul>
  31304. * @return true if object has any variables, else false
  31305. */
  31306. public boolean hasVariables() {
  31307. return variableTableGetSize() > 0;
  31308. }
  31309. /**
  31310. * Returns the amount of instance variables, class variables,
  31311. * constants and internal variables this object has.
  31312. */
  31313. public int getVariableCount() {
  31314. return variableTableGetSize();
  31315. }
  31316. /**
  31317. * Gets a list of all variables in this object.
  31318. */
  31319. // TODO: must override in RubyModule to pick up constants
  31320. public List<Variable<IRubyObject>> getVariableList() {
  31321. VariableTableEntry[] table = variableTableGetTable();
  31322. ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
  31323. IRubyObject readValue;
  31324. for (int i = table.length; --i >= 0; ) {
  31325. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31326. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  31327. list.add(new VariableEntry<IRubyObject>(e.name, readValue));
  31328. }
  31329. }
  31330. return list;
  31331. }
  31332. /**
  31333. * Gets a name list of all variables in this object.
  31334. */
  31335. // TODO: must override in RubyModule to pick up constants
  31336. public List<String> getVariableNameList() {
  31337. VariableTableEntry[] table = variableTableGetTable();
  31338. ArrayList<String> list = new ArrayList<String>();
  31339. for (int i = table.length; --i >= 0; ) {
  31340. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31341. list.add(e.name);
  31342. }
  31343. }
  31344. return list;
  31345. }
  31346. /**
  31347. * Gets internal access to the getmap for variables.
  31348. */
  31349. @SuppressWarnings("unchecked")
  31350. @Deprecated // born deprecated
  31351. public Map getVariableMap() {
  31352. return variableTableGetMap();
  31353. }
  31354. /**
  31355. * Check the syntax of a Ruby variable, including that it's longer
  31356. * than zero characters, and starts with either an @ or a capital
  31357. * letter.
  31358. */
  31359. // FIXME: this should go somewhere more generic -- maybe IdUtil
  31360. protected static final boolean isRubyVariable(String name) {
  31361. char c;
  31362. return name.length() > 0 && ((c = name.charAt(0)) == '@' || (c <= 'Z' && c >= 'A'));
  31363. }
  31364. //
  31365. // VARIABLE TABLE METHODS, ETC.
  31366. //
  31367. protected static final int VARIABLE_TABLE_DEFAULT_CAPACITY = 8; // MUST be power of 2!
  31368. protected static final int VARIABLE_TABLE_MAXIMUM_CAPACITY = 1 << 30;
  31369. protected static final float VARIABLE_TABLE_LOAD_FACTOR = 0.75f;
  31370. protected static final VariableTableEntry[] VARIABLE_TABLE_EMPTY_TABLE = new VariableTableEntry[0];
  31371. /**
  31372. * Every entry in the variable map is represented by an instance
  31373. * of this class.
  31374. */
  31375. protected static final class VariableTableEntry {
  31376. final int hash;
  31377. final String name;
  31378. volatile IRubyObject value;
  31379. final VariableTableEntry next;
  31380. VariableTableEntry(int hash, String name, IRubyObject value, VariableTableEntry next) {
  31381. assert name == name.intern() : name + " is not interned";
  31382. this.hash = hash;
  31383. this.name = name;
  31384. this.value = value;
  31385. this.next = next;
  31386. }
  31387. }
  31388. /**
  31389. * Reads the value of the specified entry, locked on the current
  31390. * object.
  31391. */
  31392. protected synchronized IRubyObject variableTableReadLocked(VariableTableEntry entry) {
  31393. return entry.value;
  31394. }
  31395. /**
  31396. * Checks if the variable table contains a variable of the
  31397. * specified name.
  31398. */
  31399. protected boolean variableTableContains(String name) {
  31400. VariableTableEntry[] table;
  31401. if ((table = variableTable) != null) {
  31402. int hash = name.hashCode();
  31403. for (VariableTableEntry e = table[hash & (table.length - 1)]; e != null; e = e.next) {
  31404. if (hash == e.hash && name.equals(e.name)) {
  31405. return true;
  31406. }
  31407. }
  31408. }
  31409. return false;
  31410. }
  31411. /**
  31412. * Checks if the variable table contains the the variable of the
  31413. * specified name, where the precondition is that the name must be
  31414. * an interned Java String.
  31415. */
  31416. protected boolean variableTableFastContains(String internedName) {
  31417. assert internedName == internedName.intern() : internedName + " not interned";
  31418. VariableTableEntry[] table;
  31419. if ((table = variableTable) != null) {
  31420. for (VariableTableEntry e = table[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
  31421. if (internedName == e.name) {
  31422. return true;
  31423. }
  31424. }
  31425. }
  31426. return false;
  31427. }
  31428. /**
  31429. * Fetch an object from the variable table based on the name.
  31430. *
  31431. * @return the object or null if not found
  31432. */
  31433. protected IRubyObject variableTableFetch(String name) {
  31434. VariableTableEntry[] table;
  31435. IRubyObject readValue;
  31436. if ((table = variableTable) != null) {
  31437. int hash = name.hashCode();
  31438. for (VariableTableEntry e = table[hash & (table.length - 1)]; e != null; e = e.next) {
  31439. if (hash == e.hash && name.equals(e.name)) {
  31440. if ((readValue = e.value) != null) return readValue;
  31441. return variableTableReadLocked(e);
  31442. }
  31443. }
  31444. }
  31445. return null;
  31446. }
  31447. /**
  31448. * Fetch an object from the variable table based on the name,
  31449. * where the name must be an interned Java String.
  31450. *
  31451. * @return the object or null if not found
  31452. */
  31453. protected IRubyObject variableTableFastFetch(String internedName) {
  31454. assert internedName == internedName.intern() : internedName + " not interned";
  31455. VariableTableEntry[] table;
  31456. IRubyObject readValue;
  31457. if ((table = variableTable) != null) {
  31458. for (VariableTableEntry e = table[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
  31459. if (internedName == e.name) {
  31460. if ((readValue = e.value) != null) return readValue;
  31461. return variableTableReadLocked(e);
  31462. }
  31463. }
  31464. }
  31465. return null;
  31466. }
  31467. /**
  31468. * Store a value in the variable store under the specific name.
  31469. */
  31470. protected IRubyObject variableTableStore(String name, IRubyObject value) {
  31471. int hash = name.hashCode();
  31472. synchronized(this) {
  31473. VariableTableEntry[] table;
  31474. VariableTableEntry e;
  31475. if ((table = variableTable) == null) {
  31476. table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  31477. e = new VariableTableEntry(hash, name.intern(), value, null);
  31478. table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
  31479. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  31480. variableTableSize = 1;
  31481. variableTable = table;
  31482. return value;
  31483. }
  31484. int potentialNewSize;
  31485. if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
  31486. table = variableTableRehash();
  31487. }
  31488. int index;
  31489. for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  31490. if (hash == e.hash && name.equals(e.name)) {
  31491. e.value = value;
  31492. return value;
  31493. }
  31494. }
  31495. e = new VariableTableEntry(hash, name.intern(), value, table[index]);
  31496. table[index] = e;
  31497. variableTableSize = potentialNewSize;
  31498. variableTable = table; // write-volatile
  31499. }
  31500. return value;
  31501. }
  31502. /**
  31503. * Will store the value under the specified name, where the name
  31504. * needs to be an interned Java String.
  31505. */
  31506. protected IRubyObject variableTableFastStore(String internedName, IRubyObject value) {
  31507. assert internedName == internedName.intern() : internedName + " not interned";
  31508. int hash = internedName.hashCode();
  31509. synchronized(this) {
  31510. VariableTableEntry[] table;
  31511. VariableTableEntry e;
  31512. if ((table = variableTable) == null) {
  31513. table = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  31514. e = new VariableTableEntry(hash, internedName, value, null);
  31515. table[hash & (VARIABLE_TABLE_DEFAULT_CAPACITY - 1)] = e;
  31516. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  31517. variableTableSize = 1;
  31518. variableTable = table;
  31519. return value;
  31520. }
  31521. int potentialNewSize;
  31522. if ((potentialNewSize = variableTableSize + 1) > variableTableThreshold) {
  31523. table = variableTableRehash();
  31524. }
  31525. int index;
  31526. for (e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  31527. if (internedName == e.name) {
  31528. e.value = value;
  31529. return value;
  31530. }
  31531. }
  31532. e = new VariableTableEntry(hash, internedName, value, table[index]);
  31533. table[index] = e;
  31534. variableTableSize = potentialNewSize;
  31535. variableTable = table; // write-volatile
  31536. }
  31537. return value;
  31538. }
  31539. /**
  31540. * Removes the entry with the specified name from the variable
  31541. * table, and returning the removed value.
  31542. */
  31543. protected IRubyObject variableTableRemove(String name) {
  31544. synchronized(this) {
  31545. VariableTableEntry[] table;
  31546. if ((table = variableTable) != null) {
  31547. int hash = name.hashCode();
  31548. int index = hash & (table.length - 1);
  31549. VariableTableEntry first = table[index];
  31550. VariableTableEntry e;
  31551. for (e = first; e != null; e = e.next) {
  31552. if (hash == e.hash && name.equals(e.name)) {
  31553. IRubyObject oldValue = e.value;
  31554. // All entries following removed node can stay
  31555. // in list, but all preceding ones need to be
  31556. // cloned.
  31557. VariableTableEntry newFirst = e.next;
  31558. for (VariableTableEntry p = first; p != e; p = p.next) {
  31559. newFirst = new VariableTableEntry(p.hash, p.name, p.value, newFirst);
  31560. }
  31561. table[index] = newFirst;
  31562. variableTableSize--;
  31563. variableTable = table; // write-volatile
  31564. return oldValue;
  31565. }
  31566. }
  31567. }
  31568. }
  31569. return null;
  31570. }
  31571. /**
  31572. * Get the actual table used to save variable entries.
  31573. */
  31574. protected VariableTableEntry[] variableTableGetTable() {
  31575. VariableTableEntry[] table;
  31576. if ((table = variableTable) != null) {
  31577. return table;
  31578. }
  31579. return VARIABLE_TABLE_EMPTY_TABLE;
  31580. }
  31581. /**
  31582. * Get the size of the variable table.
  31583. */
  31584. protected int variableTableGetSize() {
  31585. if (variableTable != null) {
  31586. return variableTableSize;
  31587. }
  31588. return 0;
  31589. }
  31590. /**
  31591. * Synchronize the variable table with the argument. In real terms
  31592. * this means copy all entries into a newly allocated table.
  31593. */
  31594. protected void variableTableSync(List<Variable<IRubyObject>> vars) {
  31595. synchronized(this) {
  31596. variableTableSize = 0;
  31597. variableTableThreshold = (int)(VARIABLE_TABLE_DEFAULT_CAPACITY * VARIABLE_TABLE_LOAD_FACTOR);
  31598. variableTable = new VariableTableEntry[VARIABLE_TABLE_DEFAULT_CAPACITY];
  31599. for (Variable<IRubyObject> var : vars) {
  31600. variableTableStore(var.getName(), var.getValue());
  31601. }
  31602. }
  31603. }
  31604. /**
  31605. * Rehashes the variable table. Must be called from a synchronized
  31606. * block.
  31607. */
  31608. // MUST be called from synchronized/locked block!
  31609. // should only be called by variableTableStore/variableTableFastStore
  31610. protected final VariableTableEntry[] variableTableRehash() {
  31611. VariableTableEntry[] oldTable = variableTable;
  31612. int oldCapacity;
  31613. if ((oldCapacity = oldTable.length) >= VARIABLE_TABLE_MAXIMUM_CAPACITY) {
  31614. return oldTable;
  31615. }
  31616. int newCapacity = oldCapacity << 1;
  31617. VariableTableEntry[] newTable = new VariableTableEntry[newCapacity];
  31618. variableTableThreshold = (int)(newCapacity * VARIABLE_TABLE_LOAD_FACTOR);
  31619. int sizeMask = newCapacity - 1;
  31620. VariableTableEntry e;
  31621. for (int i = oldCapacity; --i >= 0; ) {
  31622. // We need to guarantee that any existing reads of old Map can
  31623. // proceed. So we cannot yet null out each bin.
  31624. e = oldTable[i];
  31625. if (e != null) {
  31626. VariableTableEntry next = e.next;
  31627. int idx = e.hash & sizeMask;
  31628. // Single node on list
  31629. if (next == null)
  31630. newTable[idx] = e;
  31631. else {
  31632. // Reuse trailing consecutive sequence at same slot
  31633. VariableTableEntry lastRun = e;
  31634. int lastIdx = idx;
  31635. for (VariableTableEntry last = next;
  31636. last != null;
  31637. last = last.next) {
  31638. int k = last.hash & sizeMask;
  31639. if (k != lastIdx) {
  31640. lastIdx = k;
  31641. lastRun = last;
  31642. }
  31643. }
  31644. newTable[lastIdx] = lastRun;
  31645. // Clone all remaining nodes
  31646. for (VariableTableEntry p = e; p != lastRun; p = p.next) {
  31647. int k = p.hash & sizeMask;
  31648. VariableTableEntry m = new VariableTableEntry(p.hash, p.name, p.value, newTable[k]);
  31649. newTable[k] = m;
  31650. }
  31651. }
  31652. }
  31653. }
  31654. variableTable = newTable;
  31655. return newTable;
  31656. }
  31657. /**
  31658. * Method to help ease transition to new variables implementation.
  31659. * Will likely be deprecated in the near future.
  31660. */
  31661. @SuppressWarnings("unchecked")
  31662. protected Map variableTableGetMap() {
  31663. HashMap map = new HashMap();
  31664. VariableTableEntry[] table;
  31665. IRubyObject readValue;
  31666. if ((table = variableTable) != null) {
  31667. for (int i = table.length; --i >= 0; ) {
  31668. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31669. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  31670. map.put(e.name, readValue);
  31671. }
  31672. }
  31673. }
  31674. return map;
  31675. }
  31676. /**
  31677. * Method to help ease transition to new variables implementation.
  31678. * Will likely be deprecated in the near future.
  31679. */
  31680. @SuppressWarnings("unchecked")
  31681. protected Map variableTableGetMap(Map map) {
  31682. VariableTableEntry[] table;
  31683. IRubyObject readValue;
  31684. if ((table = variableTable) != null) {
  31685. for (int i = table.length; --i >= 0; ) {
  31686. for (VariableTableEntry e = table[i]; e != null; e = e.next) {
  31687. if ((readValue = e.value) == null) readValue = variableTableReadLocked(e);
  31688. map.put(e.name, readValue);
  31689. }
  31690. }
  31691. }
  31692. return map;
  31693. }
  31694. /**
  31695. * Tries to support Java serialization of Ruby objects. This is
  31696. * still experimental and might not work.
  31697. */
  31698. // NOTE: Serialization is primarily supported for testing purposes, and there is no general
  31699. // guarantee that serialization will work correctly. Specifically, instance variables pointing
  31700. // at symbols, threads, modules, classes, and other unserializable types are not detected.
  31701. private void writeObject(ObjectOutputStream out) throws IOException {
  31702. out.defaultWriteObject();
  31703. // write out ivar count followed by name/value pairs
  31704. List<String> names = getInstanceVariableNameList();
  31705. out.writeInt(names.size());
  31706. for (String name : names) {
  31707. out.writeObject(name);
  31708. out.writeObject(getInstanceVariables().getInstanceVariable(name));
  31709. }
  31710. }
  31711. /**
  31712. * Tries to support Java unserialization of Ruby objects. This is
  31713. * still experimental and might not work.
  31714. */
  31715. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  31716. in.defaultReadObject();
  31717. // rest in ivar count followed by name/value pairs
  31718. int ivarCount = in.readInt();
  31719. for (int i = 0; i < ivarCount; i++) {
  31720. setInstanceVariable((String)in.readObject(), (IRubyObject)in.readObject());
  31721. }
  31722. }
  31723. }
  31724. package org.jruby;
  31725. import org.jruby.runtime.Block;
  31726. import org.jruby.runtime.builtin.IRubyObject;
  31727. /**
  31728. *
  31729. * @author nicksieger
  31730. */
  31731. public interface RubyObjectAdapter {
  31732. boolean isKindOf(IRubyObject value, RubyModule rubyModule);
  31733. IRubyObject setInstanceVariable(IRubyObject obj, String variableName, IRubyObject value);
  31734. IRubyObject[] convertToJavaArray(IRubyObject array);
  31735. RubyInteger convertToRubyInteger(IRubyObject obj);
  31736. IRubyObject getInstanceVariable(IRubyObject obj, String variableName);
  31737. RubyString convertToRubyString(IRubyObject obj);
  31738. // These call* assume ThreadContext = receiver.getRuntime().getCurrentContext()
  31739. IRubyObject callMethod(IRubyObject receiver, String methodName);
  31740. IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject singleArg);
  31741. IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject[] args);
  31742. IRubyObject callMethod(IRubyObject receiver, String methodName, IRubyObject[] args, Block block);
  31743. IRubyObject callSuper(IRubyObject receiver, IRubyObject[] args);
  31744. IRubyObject callSuper(IRubyObject receiver, IRubyObject[] args, Block block);
  31745. }
  31746. /***** BEGIN LICENSE BLOCK *****
  31747. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  31748. *
  31749. * The contents of this file are subject to the Common Public
  31750. * License Version 1.0 (the "License"); you may not use this file
  31751. * except in compliance with the License. You may obtain a copy of
  31752. * the License at http://www.eclipse.org/legal/cpl-v10.html
  31753. *
  31754. * Software distributed under the License is distributed on an "AS
  31755. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  31756. * implied. See the License for the specific language governing
  31757. * rights and limitations under the License.
  31758. *
  31759. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  31760. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  31761. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  31762. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  31763. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  31764. *
  31765. * Alternatively, the contents of this file may be used under the terms of
  31766. * either of the GNU General Public License Version 2 or later (the "GPL"),
  31767. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31768. * in which case the provisions of the GPL or the LGPL are applicable instead
  31769. * of those above. If you wish to allow use of your version of this file only
  31770. * under the terms of either the GPL or the LGPL, and not to allow others to
  31771. * use your version of this file under the terms of the CPL, indicate your
  31772. * decision by deleting the provisions above and replace them with the notice
  31773. * and other provisions required by the GPL or the LGPL. If you do not delete
  31774. * the provisions above, a recipient may use your version of this file under
  31775. * the terms of any one of the CPL, the GPL or the LGPL.
  31776. ***** END LICENSE BLOCK *****/
  31777. package org.jruby;
  31778. import java.util.Iterator;
  31779. import org.jruby.anno.JRubyMethod;
  31780. import org.jruby.anno.JRubyModule;
  31781. import org.jruby.runtime.Block;
  31782. import org.jruby.runtime.ThreadContext;
  31783. import org.jruby.runtime.Visibility;
  31784. import org.jruby.runtime.builtin.IRubyObject;
  31785. @JRubyModule(name="ObjectSpace")
  31786. public class RubyObjectSpace {
  31787. /** Create the ObjectSpace module and add it to the Ruby runtime.
  31788. *
  31789. */
  31790. public static RubyModule createObjectSpaceModule(Ruby runtime) {
  31791. RubyModule objectSpaceModule = runtime.defineModule("ObjectSpace");
  31792. runtime.setObjectSpaceModule(objectSpaceModule);
  31793. objectSpaceModule.defineAnnotatedMethods(RubyObjectSpace.class);
  31794. return objectSpaceModule;
  31795. }
  31796. @JRubyMethod(name = "define_finalizer", required = 1, optional = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
  31797. public static IRubyObject define_finalizer(IRubyObject recv, IRubyObject[] args, Block block) {
  31798. Ruby runtime = recv.getRuntime();
  31799. IRubyObject finalizer = null;
  31800. if (args.length == 2) {
  31801. finalizer = args[1];
  31802. if (!finalizer.respondsTo("call")) {
  31803. throw runtime.newArgumentError("wrong type argument "
  31804. + finalizer.getType() + " (should be callable)");
  31805. }
  31806. } else {
  31807. finalizer = runtime.newProc(Block.Type.PROC, block);
  31808. }
  31809. IRubyObject obj = args[0];
  31810. runtime.getObjectSpace().addFinalizer(obj, finalizer);
  31811. return runtime.newArray(runtime.newFixnum(runtime.getSafeLevel()), finalizer);
  31812. }
  31813. @JRubyMethod(name = "undefine_finalizer", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
  31814. public static IRubyObject undefine_finalizer(IRubyObject recv, IRubyObject arg1, Block block) {
  31815. recv.getRuntime().getObjectSpace().removeFinalizers(RubyNumeric.fix2long(arg1.id()));
  31816. return recv;
  31817. }
  31818. @JRubyMethod(name = "_id2ref", required = 1, module = true, visibility = Visibility.PRIVATE)
  31819. public static IRubyObject id2ref(IRubyObject recv, IRubyObject id) {
  31820. Ruby runtime = id.getRuntime();
  31821. if (!(id instanceof RubyFixnum)) {
  31822. throw recv.getRuntime().newTypeError(id, recv.getRuntime().getFixnum());
  31823. }
  31824. RubyFixnum idFixnum = (RubyFixnum) id;
  31825. long longId = idFixnum.getLongValue();
  31826. if (longId == 0) {
  31827. return runtime.getFalse();
  31828. } else if (longId == 2) {
  31829. return runtime.getTrue();
  31830. } else if (longId == 4) {
  31831. return runtime.getNil();
  31832. } else if (longId % 2 != 0) {
  31833. // odd
  31834. return runtime.newFixnum((longId - 1) / 2);
  31835. } else {
  31836. IRubyObject object = runtime.getObjectSpace().id2ref(longId);
  31837. if (object == null) {
  31838. return runtime.getNil();
  31839. }
  31840. return object;
  31841. }
  31842. }
  31843. @JRubyMethod(name = "each_object", optional = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
  31844. public static IRubyObject each_object(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  31845. RubyModule rubyClass;
  31846. if (args.length == 0) {
  31847. rubyClass = recv.getRuntime().getObject();
  31848. } else {
  31849. if (!(args[0] instanceof RubyModule)) throw recv.getRuntime().newTypeError("class or module required");
  31850. rubyClass = (RubyModule) args[0];
  31851. }
  31852. Ruby runtime = recv.getRuntime();
  31853. int count = 0;
  31854. if (rubyClass != runtime.getClassClass()) {
  31855. if (!runtime.isObjectSpaceEnabled()) {
  31856. throw runtime.newRuntimeError("ObjectSpace is disabled; each_object will only work with Class, pass +O to enable");
  31857. }
  31858. Iterator iter = recv.getRuntime().getObjectSpace().iterator(rubyClass);
  31859. IRubyObject obj = null;
  31860. while ((obj = (IRubyObject)iter.next()) != null) {
  31861. count++;
  31862. block.yield(context, obj);
  31863. }
  31864. } else {
  31865. Iterator iter = runtime.getObject().subclasses(true).iterator();
  31866. while (iter.hasNext()) {
  31867. count++;
  31868. block.yield(context, (IRubyObject)iter.next());
  31869. }
  31870. }
  31871. return recv.getRuntime().newFixnum(count);
  31872. }
  31873. @JRubyMethod(name = "garbage_collect", module = true, visibility = Visibility.PRIVATE)
  31874. public static IRubyObject garbage_collect(IRubyObject recv) {
  31875. return RubyGC.start(recv);
  31876. }
  31877. }
  31878. /***** BEGIN LICENSE BLOCK *****
  31879. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  31880. *
  31881. * The contents of this file are subject to the Common Public
  31882. * License Version 1.0 (the "License"); you may not use this file
  31883. * except in compliance with the License. You may obtain a copy of
  31884. * the License at http://www.eclipse.org/legal/cpl-v10.html
  31885. *
  31886. * Software distributed under the License is distributed on an "AS
  31887. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  31888. * implied. See the License for the specific language governing
  31889. * rights and limitations under the License.
  31890. *
  31891. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  31892. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  31893. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  31894. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  31895. *
  31896. * Alternatively, the contents of this file may be used under the terms of
  31897. * either of the GNU General Public License Version 2 or later (the "GPL"),
  31898. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31899. * in which case the provisions of the GPL or the LGPL are applicable instead
  31900. * of those above. If you wish to allow use of your version of this file only
  31901. * under the terms of either the GPL or the LGPL, and not to allow others to
  31902. * use your version of this file under the terms of the CPL, indicate your
  31903. * decision by deleting the provisions above and replace them with the notice
  31904. * and other provisions required by the GPL or the LGPL. If you do not delete
  31905. * the provisions above, a recipient may use your version of this file under
  31906. * the terms of any one of the CPL, the GPL or the LGPL.
  31907. ***** END LICENSE BLOCK *****/
  31908. package org.jruby;
  31909. import org.jruby.anno.JRubyMethod;
  31910. import org.jruby.anno.JRubyModule;
  31911. import org.jruby.runtime.Arity;
  31912. import org.jruby.runtime.Block;
  31913. import org.jruby.runtime.ThreadContext;
  31914. import org.jruby.runtime.builtin.IRubyObject;
  31915. import org.jruby.runtime.callback.Callback;
  31916. /**
  31917. *
  31918. * @author jpetersen
  31919. */
  31920. @JRubyModule(name="Precision")
  31921. public class RubyPrecision {
  31922. public static RubyModule createPrecisionModule(Ruby runtime) {
  31923. RubyModule precisionModule = runtime.defineModule("Precision");
  31924. runtime.setPrecision(precisionModule);
  31925. precisionModule.defineAnnotatedMethods(RubyPrecision.class);
  31926. return precisionModule;
  31927. }
  31928. public static IRubyObject induced_from(IRubyObject receiver, IRubyObject source, Block block) {
  31929. throw receiver.getRuntime().newTypeError("Undefined conversion from " + source.getMetaClass().getName() + " into " + ((RubyClass)receiver).getName());
  31930. }
  31931. @JRubyMethod(name = "append_features", required = 1, frame = true, module = true)
  31932. public static IRubyObject append_features(IRubyObject receiver, IRubyObject include, Block block) {
  31933. if (include instanceof RubyModule) {
  31934. ((RubyModule) include).includeModule(receiver);
  31935. include.getSingletonClass().defineMethod("induced_from", new Callback() {
  31936. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
  31937. Arity.checkArgumentCount(recv.getRuntime(), args, 1, 1);
  31938. return RubyPrecision.induced_from(recv, args[0], block);
  31939. }
  31940. public Arity getArity() {
  31941. return Arity.ONE_ARGUMENT;
  31942. }
  31943. });
  31944. }
  31945. return receiver;
  31946. }
  31947. @JRubyMethod(name = "prec", required = 1, frame = true)
  31948. public static IRubyObject prec(ThreadContext context, IRubyObject receiver, IRubyObject type, Block block) {
  31949. return type.callMethod(context, "induced_from", receiver);
  31950. }
  31951. @JRubyMethod(name = "prec_i", frame = true)
  31952. public static IRubyObject prec_i(ThreadContext context, IRubyObject receiver, Block block) {
  31953. return receiver.getRuntime().getInteger().callMethod(context, "induced_from", receiver);
  31954. }
  31955. @JRubyMethod(name = "prec_f", frame = true)
  31956. public static IRubyObject prec_f(ThreadContext context, IRubyObject receiver, Block block) {
  31957. return receiver.getRuntime().getFloat().callMethod(context, "induced_from", receiver);
  31958. }
  31959. }
  31960. /***** BEGIN LICENSE BLOCK *****
  31961. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  31962. *
  31963. * The contents of this file are subject to the Common Public
  31964. * License Version 1.0 (the "License"); you may not use this file
  31965. * except in compliance with the License. You may obtain a copy of
  31966. * the License at http://www.eclipse.org/legal/cpl-v10.html
  31967. *
  31968. * Software distributed under the License is distributed on an "AS
  31969. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  31970. * implied. See the License for the specific language governing
  31971. * rights and limitations under the License.
  31972. *
  31973. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  31974. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  31975. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  31976. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  31977. * Copyright (C) 2002-2005 Thomas E Enebo <enebo@acm.org>
  31978. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  31979. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  31980. * Copyright (C) 2007 Miguel Covarrubias <mlcovarrubias@gmail.com>
  31981. *
  31982. * Alternatively, the contents of this file may be used under the terms of
  31983. * either of the GNU General Public License Version 2 or later (the "GPL"),
  31984. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  31985. * in which case the provisions of the GPL or the LGPL are applicable instead
  31986. * of those above. If you wish to allow use of your version of this file only
  31987. * under the terms of either the GPL or the LGPL, and not to allow others to
  31988. * use your version of this file under the terms of the CPL, indicate your
  31989. * decision by deleting the provisions above and replace them with the notice
  31990. * and other provisions required by the GPL or the LGPL. If you do not delete
  31991. * the provisions above, a recipient may use your version of this file under
  31992. * the terms of any one of the CPL, the GPL or the LGPL.
  31993. ***** END LICENSE BLOCK *****/
  31994. package org.jruby;
  31995. import java.lang.reflect.InvocationHandler;
  31996. import java.lang.reflect.Method;
  31997. import java.lang.reflect.Proxy;
  31998. import org.jruby.anno.JRubyMethod;
  31999. import org.jruby.anno.JRubyClass;
  32000. import org.jruby.exceptions.JumpException;
  32001. import org.jruby.internal.runtime.JumpTarget;
  32002. import org.jruby.java.MiniJava;
  32003. import org.jruby.runtime.Block;
  32004. import org.jruby.runtime.ObjectAllocator;
  32005. import org.jruby.runtime.ThreadContext;
  32006. import org.jruby.runtime.Visibility;
  32007. import org.jruby.runtime.builtin.IRubyObject;
  32008. /**
  32009. * @author jpetersen
  32010. */
  32011. @JRubyClass(name="Proc")
  32012. public class RubyProc extends RubyObject implements JumpTarget {
  32013. private Block block = Block.NULL_BLOCK;
  32014. private Block.Type type;
  32015. private String file;
  32016. private int line;
  32017. public RubyProc(Ruby runtime, RubyClass rubyClass, Block.Type type) {
  32018. super(runtime, rubyClass);
  32019. this.type = type;
  32020. }
  32021. private static ObjectAllocator PROC_ALLOCATOR = new ObjectAllocator() {
  32022. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  32023. RubyProc instance = RubyProc.newProc(runtime, Block.Type.PROC);
  32024. instance.setMetaClass(klass);
  32025. return instance;
  32026. }
  32027. };
  32028. public static RubyClass createProcClass(Ruby runtime) {
  32029. RubyClass procClass = runtime.defineClass("Proc", runtime.getObject(), PROC_ALLOCATOR);
  32030. runtime.setProc(procClass);
  32031. procClass.defineAnnotatedMethods(RubyProc.class);
  32032. return procClass;
  32033. }
  32034. public Block getBlock() {
  32035. return block;
  32036. }
  32037. // Proc class
  32038. public static RubyProc newProc(Ruby runtime, Block.Type type) {
  32039. return new RubyProc(runtime, runtime.getProc(), type);
  32040. }
  32041. public static RubyProc newProc(Ruby runtime, Block block, Block.Type type) {
  32042. RubyProc proc = new RubyProc(runtime, runtime.getProc(), type);
  32043. proc.callInit(NULL_ARRAY, block);
  32044. return proc;
  32045. }
  32046. /**
  32047. * Create a new instance of a Proc object. We override this method (from RubyClass)
  32048. * since we need to deal with special case of Proc.new with no arguments or block arg. In
  32049. * this case, we need to check previous frame for a block to consume.
  32050. */
  32051. @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
  32052. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  32053. // No passed in block, lets check next outer frame for one ('Proc.new')
  32054. if (!block.isGiven()) {
  32055. block = context.getPreviousFrame().getBlock();
  32056. }
  32057. if (block.isGiven() && block.getProcObject() != null) {
  32058. return block.getProcObject();
  32059. }
  32060. IRubyObject obj = ((RubyClass) recv).allocate();
  32061. obj.callMethod(context, "initialize", args, block);
  32062. return obj;
  32063. }
  32064. @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
  32065. public IRubyObject initialize(ThreadContext context, Block procBlock) {
  32066. if (!procBlock.isGiven()) {
  32067. throw getRuntime().newArgumentError("tried to create Proc object without a block");
  32068. }
  32069. if (type == Block.Type.LAMBDA && procBlock == null) {
  32070. // TODO: warn "tried to create Proc object without a block"
  32071. }
  32072. block = procBlock.cloneBlock();
  32073. block.type = type;
  32074. block.setProcObject(this);
  32075. file = context.getFile();
  32076. line = context.getLine();
  32077. return this;
  32078. }
  32079. @JRubyMethod(name = "clone")
  32080. public IRubyObject rbClone() {
  32081. RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
  32082. newProc.block = getBlock();
  32083. newProc.file = file;
  32084. newProc.line = line;
  32085. // TODO: CLONE_SETUP here
  32086. return newProc;
  32087. }
  32088. @JRubyMethod(name = "dup")
  32089. public IRubyObject dup() {
  32090. RubyProc newProc = new RubyProc(getRuntime(), getRuntime().getProc(), type);
  32091. newProc.block = getBlock();
  32092. newProc.file = file;
  32093. newProc.line = line;
  32094. return newProc;
  32095. }
  32096. @JRubyMethod(name = "==", required = 1)
  32097. public IRubyObject op_equal(IRubyObject other) {
  32098. if (!(other instanceof RubyProc)) return getRuntime().getFalse();
  32099. if (this == other || this.block == ((RubyProc)other).block) {
  32100. return getRuntime().getTrue();
  32101. }
  32102. return getRuntime().getFalse();
  32103. }
  32104. @JRubyMethod(name = "to_s")
  32105. public IRubyObject to_s() {
  32106. return RubyString.newString(getRuntime(),
  32107. "#<Proc:0x" + Integer.toString(block.hashCode(), 16) + "@" +
  32108. file + ":" + (line + 1) + ">");
  32109. }
  32110. @JRubyMethod(name = "binding")
  32111. public IRubyObject binding() {
  32112. return getRuntime().newBinding(block.getBinding());
  32113. }
  32114. @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
  32115. public IRubyObject call(ThreadContext context, IRubyObject[] args) {
  32116. return call(context, args, null);
  32117. }
  32118. public IRubyObject call(ThreadContext context, IRubyObject[] args, IRubyObject self) {
  32119. assert args != null;
  32120. Ruby runtime = getRuntime();
  32121. Block newBlock = block.cloneBlock();
  32122. JumpTarget jumpTarget = newBlock.getBinding().getFrame().getJumpTarget();
  32123. try {
  32124. if (self != null) newBlock.getBinding().setSelf(self);
  32125. return newBlock.call(context, args);
  32126. } catch (JumpException.BreakJump bj) {
  32127. switch(block.type) {
  32128. case LAMBDA: if (bj.getTarget() == jumpTarget) {
  32129. return (IRubyObject) bj.getValue();
  32130. } else {
  32131. throw runtime.newLocalJumpError("break", (IRubyObject)bj.getValue(), "unexpected break");
  32132. }
  32133. case PROC:
  32134. if (newBlock.isEscaped()) {
  32135. throw runtime.newLocalJumpError("break", (IRubyObject)bj.getValue(), "break from proc-closure");
  32136. } else {
  32137. throw bj;
  32138. }
  32139. default: throw bj;
  32140. }
  32141. } catch (JumpException.ReturnJump rj) {
  32142. Object target = rj.getTarget();
  32143. if (target == jumpTarget && block.type == Block.Type.LAMBDA) return (IRubyObject) rj.getValue();
  32144. if (type == Block.Type.THREAD) {
  32145. throw runtime.newThreadError("return can't jump across threads");
  32146. }
  32147. throw rj;
  32148. } catch (JumpException.RetryJump rj) {
  32149. throw runtime.newLocalJumpError("retry", (IRubyObject)rj.getValue(), "retry not supported outside rescue");
  32150. }
  32151. }
  32152. @JRubyMethod(name = "arity")
  32153. public RubyFixnum arity() {
  32154. return getRuntime().newFixnum(block.arity().getValue());
  32155. }
  32156. @JRubyMethod(name = "to_proc")
  32157. public RubyProc to_proc() {
  32158. return this;
  32159. }
  32160. public IRubyObject as(Class asClass) {
  32161. final Ruby ruby = getRuntime();
  32162. if (!asClass.isInterface()) {
  32163. throw ruby.newTypeError(asClass.getCanonicalName() + " is not an interface");
  32164. }
  32165. return MiniJava.javaToRuby(ruby, Proxy.newProxyInstance(Ruby.getClassLoader(), new Class[] {asClass}, new InvocationHandler() {
  32166. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  32167. IRubyObject[] rubyArgs = new IRubyObject[args.length + 1];
  32168. rubyArgs[0] = RubySymbol.newSymbol(ruby, method.getName());
  32169. for (int i = 1; i < rubyArgs.length; i++) {
  32170. rubyArgs[i] = MiniJava.javaToRuby(ruby, args[i - 1]);
  32171. }
  32172. return MiniJava.rubyToJava(call(ruby.getCurrentContext(), rubyArgs));
  32173. }
  32174. }));
  32175. }
  32176. }
  32177. /*
  32178. **** BEGIN LICENSE BLOCK *****
  32179. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  32180. *
  32181. * The contents of this file are subject to the Common Public
  32182. * License Version 1.0 (the "License"); you may not use this file
  32183. * except in compliance with the License. You may obtain a copy of
  32184. * the License at http://www.eclipse.org/legal/cpl-v10.html
  32185. *
  32186. * Software distributed under the License is distributed on an "AS
  32187. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  32188. * implied. See the License for the specific language governing
  32189. * rights and limitations under the License.
  32190. *
  32191. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  32192. * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  32193. *
  32194. * Alternatively, the contents of this file may be used under the terms of
  32195. * either of the GNU General Public License Version 2 or later (the "GPL"),
  32196. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32197. * in which case the provisions of the GPL or the LGPL are applicable instead
  32198. * of those above. If you wish to allow use of your version of this file only
  32199. * under the terms of either the GPL or the LGPL, and not to allow others to
  32200. * use your version of this file under the terms of the CPL, indicate your
  32201. * decision by deleting the provisions above and replace them with the notice
  32202. * and other provisions required by the GPL or the LGPL. If you do not delete
  32203. * the provisions above, a recipient may use your version of this file under
  32204. * the terms of any one of the CPL, the GPL or the LGPL.
  32205. ***** END LICENSE BLOCK *****/
  32206. package org.jruby;
  32207. import org.jruby.anno.JRubyClass;
  32208. import org.jruby.anno.JRubyMethod;
  32209. import org.jruby.anno.JRubyModule;
  32210. import org.jruby.ext.posix.POSIX;
  32211. import org.jruby.runtime.Arity;
  32212. import org.jruby.runtime.Block;
  32213. import org.jruby.runtime.BlockCallback;
  32214. import org.jruby.runtime.CallBlock;
  32215. import org.jruby.runtime.MethodIndex;
  32216. import org.jruby.runtime.ObjectAllocator;
  32217. import org.jruby.runtime.ThreadContext;
  32218. import org.jruby.runtime.Visibility;
  32219. import org.jruby.runtime.builtin.IRubyObject;
  32220. /**
  32221. */
  32222. @JRubyModule(name="Process")
  32223. public class RubyProcess {
  32224. public static RubyModule createProcessModule(Ruby runtime) {
  32225. RubyModule process = runtime.defineModule("Process");
  32226. runtime.setProcess(process);
  32227. // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
  32228. RubyClass process_status = process.defineClassUnder("Status", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  32229. runtime.setProcStatus(process_status);
  32230. RubyModule process_uid = process.defineModuleUnder("UID");
  32231. runtime.setProcUID(process_uid);
  32232. RubyModule process_gid = process.defineModuleUnder("GID");
  32233. runtime.setProcGID(process_gid);
  32234. RubyModule process_sys = process.defineModuleUnder("Sys");
  32235. runtime.setProcSys(process_sys);
  32236. process.defineAnnotatedMethods(RubyProcess.class);
  32237. process_status.defineAnnotatedMethods(RubyStatus.class);
  32238. process_uid.defineAnnotatedMethods(UserID.class);
  32239. process_gid.defineAnnotatedMethods(GroupID.class);
  32240. process_sys.defineAnnotatedMethods(Sys.class);
  32241. process.defineConstant("PRIO_PROCESS", runtime.newFixnum(0));
  32242. process.defineConstant("PRIO_PGRP", runtime.newFixnum(1));
  32243. process.defineConstant("PRIO_USER", runtime.newFixnum(2));
  32244. process.defineConstant("WNOHANG", runtime.newFixnum(1));
  32245. return process;
  32246. }
  32247. @JRubyClass(name="Process::Status")
  32248. public static class RubyStatus extends RubyObject {
  32249. private long status = 0L;
  32250. private static final long EXIT_SUCCESS = 0L;
  32251. public RubyStatus(Ruby runtime, RubyClass metaClass, long status) {
  32252. super(runtime, metaClass);
  32253. this.status = status;
  32254. }
  32255. public static RubyStatus newProcessStatus(Ruby runtime, long status) {
  32256. return new RubyStatus(runtime, runtime.getProcStatus(), status);
  32257. }
  32258. // Bunch of methods still not implemented
  32259. @JRubyMethod(name = {"to_int", "pid", "stopped?", "stopsig", "signaled?", "termsig?", "exited?", "coredump?"})
  32260. public IRubyObject not_implemented() {
  32261. String error = "Process::Status#" + getRuntime().getCurrentContext().getFrameName() + " not implemented";
  32262. throw getRuntime().newNotImplementedError(error);
  32263. }
  32264. @JRubyMethod(name = {"&"})
  32265. public IRubyObject not_implemented1(IRubyObject arg) {
  32266. String error = "Process::Status#" + getRuntime().getCurrentContext().getFrameName() + " not implemented";
  32267. throw getRuntime().newNotImplementedError(error);
  32268. }
  32269. @JRubyMethod
  32270. public IRubyObject exitstatus() {
  32271. return getRuntime().newFixnum(status);
  32272. }
  32273. @JRubyMethod(name = ">>")
  32274. public IRubyObject op_rshift(IRubyObject other) {
  32275. long shiftValue = other.convertToInteger().getLongValue();
  32276. return getRuntime().newFixnum(status >> shiftValue);
  32277. }
  32278. @JRubyMethod(name = "==")
  32279. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  32280. return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this.to_i());
  32281. }
  32282. @JRubyMethod
  32283. public IRubyObject to_i() {
  32284. return getRuntime().newFixnum(shiftedValue());
  32285. }
  32286. @JRubyMethod
  32287. public IRubyObject to_s() {
  32288. return getRuntime().newString(String.valueOf(shiftedValue()));
  32289. }
  32290. @JRubyMethod
  32291. public IRubyObject inspect() {
  32292. return getRuntime().newString("#<Process::Status: pid=????,exited(" + String.valueOf(status) + ")>");
  32293. }
  32294. @JRubyMethod(name = "success?")
  32295. public IRubyObject success_p() {
  32296. return getRuntime().newBoolean(status == EXIT_SUCCESS);
  32297. }
  32298. private long shiftedValue() {
  32299. return status << 8;
  32300. }
  32301. }
  32302. @JRubyModule(name="Process::UID")
  32303. public static class UserID {
  32304. @JRubyMethod(name = "change_privilege", module = true)
  32305. public static IRubyObject change_privilege(IRubyObject self, IRubyObject arg) {
  32306. throw self.getRuntime().newNotImplementedError("Process::UID::change_privilege not implemented yet");
  32307. }
  32308. @JRubyMethod(name = "eid", module = true)
  32309. public static IRubyObject eid(IRubyObject self) {
  32310. return euid(self);
  32311. }
  32312. @JRubyMethod(name = "eid=", module = true)
  32313. public static IRubyObject eid(IRubyObject self, IRubyObject arg) {
  32314. return euid_set(self, arg);
  32315. }
  32316. @JRubyMethod(name = "grant_privilege", module = true)
  32317. public static IRubyObject grant_privilege(IRubyObject self, IRubyObject arg) {
  32318. throw self.getRuntime().newNotImplementedError("Process::UID::grant_privilege not implemented yet");
  32319. }
  32320. @JRubyMethod(name = "re_exchange", module = true)
  32321. public static IRubyObject re_exchange(ThreadContext context, IRubyObject self) {
  32322. return switch_rb(context, self, Block.NULL_BLOCK);
  32323. }
  32324. @JRubyMethod(name = "re_exchangeable?", module = true)
  32325. public static IRubyObject re_exchangeable_p(IRubyObject self) {
  32326. throw self.getRuntime().newNotImplementedError("Process::UID::re_exchangeable? not implemented yet");
  32327. }
  32328. @JRubyMethod(name = "rid", module = true)
  32329. public static IRubyObject rid(IRubyObject self) {
  32330. return uid(self);
  32331. }
  32332. @JRubyMethod(name = "sid_available?", module = true)
  32333. public static IRubyObject sid_available_p(IRubyObject self) {
  32334. throw self.getRuntime().newNotImplementedError("Process::UID::sid_available not implemented yet");
  32335. }
  32336. @JRubyMethod(name = "switch", module = true, visibility = Visibility.PRIVATE)
  32337. public static IRubyObject switch_rb(ThreadContext context, IRubyObject self, Block block) {
  32338. Ruby runtime = self.getRuntime();
  32339. int uid = runtime.getPosix().getuid();
  32340. int euid = runtime.getPosix().geteuid();
  32341. if (block.isGiven()) {
  32342. try {
  32343. runtime.getPosix().seteuid(uid);
  32344. runtime.getPosix().setuid(euid);
  32345. return block.yield(context, runtime.getNil());
  32346. } finally {
  32347. runtime.getPosix().seteuid(euid);
  32348. runtime.getPosix().setuid(uid);
  32349. }
  32350. } else {
  32351. runtime.getPosix().seteuid(uid);
  32352. runtime.getPosix().setuid(euid);
  32353. return RubyFixnum.zero(runtime);
  32354. }
  32355. }
  32356. }
  32357. @JRubyModule(name="Process::GID")
  32358. public static class GroupID {
  32359. @JRubyMethod(name = "change_privilege", module = true)
  32360. public static IRubyObject change_privilege(IRubyObject self, IRubyObject arg) {
  32361. throw self.getRuntime().newNotImplementedError("Process::GID::change_privilege not implemented yet");
  32362. }
  32363. @JRubyMethod(name = "eid", module = true)
  32364. public static IRubyObject eid(IRubyObject self) {
  32365. return egid(self);
  32366. }
  32367. @JRubyMethod(name = "eid=", module = true)
  32368. public static IRubyObject eid(IRubyObject self, IRubyObject arg) {
  32369. return RubyProcess.egid_set(self, arg);
  32370. }
  32371. @JRubyMethod(name = "grant_privilege", module = true)
  32372. public static IRubyObject grant_privilege(IRubyObject self, IRubyObject arg) {
  32373. throw self.getRuntime().newNotImplementedError("Process::GID::grant_privilege not implemented yet");
  32374. }
  32375. @JRubyMethod(name = "re_exchange", module = true)
  32376. public static IRubyObject re_exchange(ThreadContext context, IRubyObject self) {
  32377. return switch_rb(context, self, Block.NULL_BLOCK);
  32378. }
  32379. @JRubyMethod(name = "re_exchangeable?", module = true)
  32380. public static IRubyObject re_exchangeable_p(IRubyObject self) {
  32381. throw self.getRuntime().newNotImplementedError("Process::GID::re_exchangeable? not implemented yet");
  32382. }
  32383. @JRubyMethod(name = "rid", module = true)
  32384. public static IRubyObject rid(IRubyObject self) {
  32385. return gid(self);
  32386. }
  32387. @JRubyMethod(name = "sid_available?", module = true)
  32388. public static IRubyObject sid_available_p(IRubyObject self) {
  32389. throw self.getRuntime().newNotImplementedError("Process::GID::sid_available not implemented yet");
  32390. }
  32391. @JRubyMethod(name = "switch", module = true, visibility = Visibility.PRIVATE)
  32392. public static IRubyObject switch_rb(ThreadContext context, IRubyObject self, Block block) {
  32393. Ruby runtime = self.getRuntime();
  32394. int gid = runtime.getPosix().getgid();
  32395. int egid = runtime.getPosix().getegid();
  32396. if (block.isGiven()) {
  32397. try {
  32398. runtime.getPosix().setegid(gid);
  32399. runtime.getPosix().setgid(egid);
  32400. return block.yield(context, runtime.getNil());
  32401. } finally {
  32402. runtime.getPosix().setegid(egid);
  32403. runtime.getPosix().setgid(gid);
  32404. }
  32405. } else {
  32406. runtime.getPosix().setegid(gid);
  32407. runtime.getPosix().setgid(egid);
  32408. return RubyFixnum.zero(runtime);
  32409. }
  32410. }
  32411. }
  32412. @JRubyModule(name="Process::Sys")
  32413. public static class Sys {
  32414. @JRubyMethod(name = "getegid", module = true, visibility = Visibility.PRIVATE)
  32415. public static IRubyObject getegid(IRubyObject self) {
  32416. return egid(self);
  32417. }
  32418. @JRubyMethod(name = "geteuid", module = true, visibility = Visibility.PRIVATE)
  32419. public static IRubyObject geteuid(IRubyObject self) {
  32420. return euid(self);
  32421. }
  32422. @JRubyMethod(name = "getgid", module = true, visibility = Visibility.PRIVATE)
  32423. public static IRubyObject getgid(IRubyObject self) {
  32424. return gid(self);
  32425. }
  32426. @JRubyMethod(name = "getuid", module = true, visibility = Visibility.PRIVATE)
  32427. public static IRubyObject getuid(IRubyObject self) {
  32428. return uid(self);
  32429. }
  32430. @JRubyMethod(name = "setegid", module = true, visibility = Visibility.PRIVATE)
  32431. public static IRubyObject setegid(IRubyObject recv, IRubyObject arg) {
  32432. return egid_set(recv, arg);
  32433. }
  32434. @JRubyMethod(name = "seteuid", module = true, visibility = Visibility.PRIVATE)
  32435. public static IRubyObject seteuid(IRubyObject recv, IRubyObject arg) {
  32436. return euid_set(recv, arg);
  32437. }
  32438. @JRubyMethod(name = "setgid", module = true, visibility = Visibility.PRIVATE)
  32439. public static IRubyObject setgid(IRubyObject recv, IRubyObject arg) {
  32440. return gid_set(recv, arg);
  32441. }
  32442. @JRubyMethod(name = "setuid", module = true, visibility = Visibility.PRIVATE)
  32443. public static IRubyObject setuid(IRubyObject recv, IRubyObject arg) {
  32444. return uid_set(recv, arg);
  32445. }
  32446. }
  32447. @JRubyMethod(name = "abort", optional = 1, module = true, visibility = Visibility.PRIVATE)
  32448. public static IRubyObject abort(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  32449. return RubyKernel.abort(context, recv, args);
  32450. }
  32451. @JRubyMethod(name = "exit!", optional = 1, module = true, visibility = Visibility.PRIVATE)
  32452. public static IRubyObject exit_bang(IRubyObject recv, IRubyObject[] args) {
  32453. return RubyKernel.exit_bang(recv, args);
  32454. }
  32455. @JRubyMethod(name = "groups", module = true, visibility = Visibility.PRIVATE)
  32456. public static IRubyObject groups(IRubyObject recv) {
  32457. throw recv.getRuntime().newNotImplementedError("Process#groups not yet implemented");
  32458. }
  32459. @JRubyMethod(name = "setrlimit", rest = true, module = true, visibility = Visibility.PRIVATE)
  32460. public static IRubyObject setrlimit(IRubyObject recv, IRubyObject[] args) {
  32461. throw recv.getRuntime().newNotImplementedError("Process#setrlimit not yet implemented");
  32462. }
  32463. @JRubyMethod(name = "getpgrp", module = true, visibility = Visibility.PRIVATE)
  32464. public static IRubyObject getpgrp(IRubyObject recv) {
  32465. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpgrp());
  32466. }
  32467. @JRubyMethod(name = "groups=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32468. public static IRubyObject groups_set(IRubyObject recv, IRubyObject arg) {
  32469. throw recv.getRuntime().newNotImplementedError("Process#groups not yet implemented");
  32470. }
  32471. @JRubyMethod(name = "waitpid", rest = true, module = true, visibility = Visibility.PRIVATE)
  32472. public static IRubyObject waitpid(IRubyObject recv, IRubyObject[] args) {
  32473. Ruby runtime = recv.getRuntime();
  32474. int pid = -1;
  32475. int flags = 0;
  32476. if (args.length > 0) {
  32477. pid = (int)args[0].convertToInteger().getLongValue();
  32478. }
  32479. if (args.length > 1) {
  32480. flags = (int)args[1].convertToInteger().getLongValue();
  32481. }
  32482. int[] status = new int[1];
  32483. pid = runtime.getPosix().waitpid(pid, status, flags);
  32484. if (pid == -1) {
  32485. throw runtime.newErrnoECHILDError();
  32486. }
  32487. runtime.getGlobalVariables().set(
  32488. "$?",
  32489. RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
  32490. return runtime.newFixnum(pid);
  32491. }
  32492. @JRubyMethod(name = "wait", rest = true, module = true, visibility = Visibility.PRIVATE)
  32493. public static IRubyObject wait(IRubyObject recv, IRubyObject[] args) {
  32494. Ruby runtime = recv.getRuntime();
  32495. if (args.length > 0) {
  32496. return waitpid(recv, args);
  32497. }
  32498. int[] status = new int[1];
  32499. int pid = runtime.getPosix().wait(status);
  32500. if (pid == -1) {
  32501. throw runtime.newErrnoECHILDError();
  32502. }
  32503. runtime.getGlobalVariables().set(
  32504. "$?",
  32505. RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
  32506. return runtime.newFixnum(pid);
  32507. }
  32508. @JRubyMethod(name = "waitall", module = true, visibility = Visibility.PRIVATE)
  32509. public static IRubyObject waitall(IRubyObject recv) {
  32510. Ruby runtime = recv.getRuntime();
  32511. POSIX posix = runtime.getPosix();
  32512. RubyArray results = recv.getRuntime().newArray();
  32513. int[] status = new int[1];
  32514. int result = posix.wait(status);
  32515. while (result != -1) {
  32516. results.append(runtime.newArray(runtime.newFixnum(result), RubyProcess.RubyStatus.newProcessStatus(runtime, status[0])));
  32517. result = posix.wait(status);
  32518. }
  32519. return results;
  32520. }
  32521. @JRubyMethod(name = "setsid", module = true, visibility = Visibility.PRIVATE)
  32522. public static IRubyObject setsid(IRubyObject recv) {
  32523. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setsid());
  32524. }
  32525. @JRubyMethod(name = "setpgrp", module = true, visibility = Visibility.PRIVATE)
  32526. public static IRubyObject setpgrp(IRubyObject recv) {
  32527. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setpgid(0, 0));
  32528. }
  32529. @JRubyMethod(name = "egid=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32530. public static IRubyObject egid_set(IRubyObject recv, IRubyObject arg) {
  32531. recv.getRuntime().getPosix().setegid((int)arg.convertToInteger().getLongValue());
  32532. return RubyFixnum.zero(recv.getRuntime());
  32533. }
  32534. @JRubyMethod(name = "euid", module = true, visibility = Visibility.PRIVATE)
  32535. public static IRubyObject euid(IRubyObject recv) {
  32536. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().geteuid());
  32537. }
  32538. @JRubyMethod(name = "uid=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32539. public static IRubyObject uid_set(IRubyObject recv, IRubyObject arg) {
  32540. recv.getRuntime().getPosix().setuid((int)arg.convertToInteger().getLongValue());
  32541. return RubyFixnum.zero(recv.getRuntime());
  32542. }
  32543. @JRubyMethod(name = "gid", module = true, visibility = Visibility.PRIVATE)
  32544. public static IRubyObject gid(IRubyObject recv) {
  32545. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getgid());
  32546. }
  32547. @JRubyMethod(name = "maxgroups", module = true, visibility = Visibility.PRIVATE)
  32548. public static IRubyObject maxgroups(IRubyObject recv) {
  32549. throw recv.getRuntime().newNotImplementedError("Process#maxgroups not yet implemented");
  32550. }
  32551. @JRubyMethod(name = "getpriority", required = 2, module = true, visibility = Visibility.PRIVATE)
  32552. public static IRubyObject getpriority(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
  32553. int which = (int)arg1.convertToInteger().getLongValue();
  32554. int who = (int)arg2.convertToInteger().getLongValue();
  32555. int result = recv.getRuntime().getPosix().getpriority(which, who);
  32556. return recv.getRuntime().newFixnum(result);
  32557. }
  32558. @JRubyMethod(name = "uid", module = true, visibility = Visibility.PRIVATE)
  32559. public static IRubyObject uid(IRubyObject recv) {
  32560. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getuid());
  32561. }
  32562. @JRubyMethod(name = "waitpid2", rest = true, module = true, visibility = Visibility.PRIVATE)
  32563. public static IRubyObject waitpid2(IRubyObject recv, IRubyObject[] args) {
  32564. Ruby runtime = recv.getRuntime();
  32565. int pid = -1;
  32566. int flags = 0;
  32567. if (args.length > 0) {
  32568. pid = (int)args[0].convertToInteger().getLongValue();
  32569. }
  32570. if (args.length > 1) {
  32571. flags = (int)args[1].convertToInteger().getLongValue();
  32572. }
  32573. int[] status = new int[1];
  32574. pid = runtime.getPosix().waitpid(pid, status, flags);
  32575. if (pid == -1) {
  32576. throw runtime.newErrnoECHILDError();
  32577. }
  32578. return runtime.newArray(runtime.newFixnum(pid), RubyProcess.RubyStatus.newProcessStatus(runtime, status[0]));
  32579. }
  32580. @JRubyMethod(name = "initgroups", required = 2, module = true, visibility = Visibility.PRIVATE)
  32581. public static IRubyObject initgroups(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
  32582. throw recv.getRuntime().newNotImplementedError("Process#initgroups not yet implemented");
  32583. }
  32584. @JRubyMethod(name = "maxgroups=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32585. public static IRubyObject maxgroups_set(IRubyObject recv, IRubyObject arg) {
  32586. throw recv.getRuntime().newNotImplementedError("Process#maxgroups_set not yet implemented");
  32587. }
  32588. @JRubyMethod(name = "ppid", module = true, visibility = Visibility.PRIVATE)
  32589. public static IRubyObject ppid(IRubyObject recv) {
  32590. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getppid());
  32591. }
  32592. @JRubyMethod(name = "gid=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32593. public static IRubyObject gid_set(IRubyObject recv, IRubyObject arg) {
  32594. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setgid((int)arg.convertToInteger().getLongValue()));
  32595. }
  32596. @JRubyMethod(name = "wait2", rest = true, module = true, visibility = Visibility.PRIVATE)
  32597. public static IRubyObject wait2(IRubyObject recv, IRubyObject[] args) {
  32598. return waitpid2(recv, args);
  32599. }
  32600. @JRubyMethod(name = "euid=", required = 1, module = true, visibility = Visibility.PRIVATE)
  32601. public static IRubyObject euid_set(IRubyObject recv, IRubyObject arg) {
  32602. recv.getRuntime().getPosix().seteuid((int)arg.convertToInteger().getLongValue());
  32603. return RubyFixnum.zero(recv.getRuntime());
  32604. }
  32605. @JRubyMethod(name = "setpriority", required = 3, module = true, visibility = Visibility.PRIVATE)
  32606. public static IRubyObject setpriority(IRubyObject recv, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
  32607. int which = (int)arg1.convertToInteger().getLongValue();
  32608. int who = (int)arg2.convertToInteger().getLongValue();
  32609. int prio = (int)arg3.convertToInteger().getLongValue();
  32610. int result = recv.getRuntime().getPosix().setpriority(which, who, prio);
  32611. return recv.getRuntime().newFixnum(result);
  32612. }
  32613. @JRubyMethod(name = "setpgid", required = 2, module = true, visibility = Visibility.PRIVATE)
  32614. public static IRubyObject setpgid(IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
  32615. int pid = (int)arg1.convertToInteger().getLongValue();
  32616. int gid = (int)arg2.convertToInteger().getLongValue();
  32617. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().setpgid(pid, gid));
  32618. }
  32619. @JRubyMethod(name = "getpgid", required = 1, module = true, visibility = Visibility.PRIVATE)
  32620. public static IRubyObject getpgid(IRubyObject recv, IRubyObject arg) {
  32621. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpgid((int)arg.convertToInteger().getLongValue()));
  32622. }
  32623. @JRubyMethod(name = "getrlimit", required = 1, module = true, visibility = Visibility.PRIVATE)
  32624. public static IRubyObject getrlimit(IRubyObject recv, IRubyObject arg) {
  32625. throw recv.getRuntime().newNotImplementedError("Process#getrlimit not yet implemented");
  32626. }
  32627. @JRubyMethod(name = "egid", module = true, visibility = Visibility.PRIVATE)
  32628. public static IRubyObject egid(IRubyObject recv) {
  32629. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getegid());
  32630. }
  32631. private static String[] signals = new String[] {"EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP",
  32632. "ABRT", "POLL", "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP",
  32633. "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "XCPU", "XFSZ", "VTALRM", "PROF", "USR1", "USR2"};
  32634. private static int parseSignalString(Ruby runtime, String value) {
  32635. int startIndex = 0;
  32636. boolean negative = value.startsWith("-");
  32637. if (negative) startIndex++;
  32638. boolean signalString = value.startsWith("SIG", startIndex);
  32639. if (signalString) startIndex += 3;
  32640. String signalName = value.substring(startIndex);
  32641. // FIXME: This table will get moved into POSIX library so we can get all actual supported
  32642. // signals. This is a quick fix to support basic signals until that happens.
  32643. for (int i = 0; i < signals.length; i++) {
  32644. if (signals[i].equals(signalName)) return negative ? -i : i;
  32645. }
  32646. throw runtime.newArgumentError("unsupported name `SIG" + signalName + "'");
  32647. }
  32648. @JRubyMethod(name = "kill", rest = true, module = true, visibility = Visibility.PRIVATE)
  32649. public static IRubyObject kill(IRubyObject recv, IRubyObject[] args) {
  32650. if (args.length < 2) {
  32651. throw recv.getRuntime().newArgumentError("wrong number of arguments -- kill(sig, pid...)");
  32652. }
  32653. Ruby runtime = recv.getRuntime();
  32654. int signal;
  32655. if (args[0] instanceof RubyFixnum) {
  32656. signal = (int) ((RubyFixnum) args[0]).getLongValue();
  32657. } else if (args[0] instanceof RubySymbol) {
  32658. signal = parseSignalString(runtime, args[0].toString());
  32659. } else if (args[0] instanceof RubyString) {
  32660. signal = parseSignalString(runtime, args[0].toString());
  32661. } else {
  32662. signal = parseSignalString(runtime, args[0].checkStringType().toString());
  32663. }
  32664. boolean processGroupKill = signal < 0;
  32665. if (processGroupKill) signal = -signal;
  32666. POSIX posix = runtime.getPosix();
  32667. for (int i = 1; i < args.length; i++) {
  32668. int pid = RubyNumeric.num2int(args[i]);
  32669. // FIXME: It may be possible to killpg on systems which support it. POSIX library
  32670. // needs to tell whether a particular method works or not
  32671. if (pid == 0) pid = runtime.getPosix().getpid();
  32672. posix.kill(processGroupKill ? -pid : pid, signal);
  32673. }
  32674. return runtime.newFixnum(args.length - 1);
  32675. }
  32676. @JRubyMethod(name = "detach", required = 1, module = true, visibility = Visibility.PRIVATE)
  32677. public static IRubyObject detach(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  32678. final int pid = (int)arg.convertToInteger().getLongValue();
  32679. Ruby runtime = recv.getRuntime();
  32680. BlockCallback callback = new BlockCallback() {
  32681. public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
  32682. int[] status = new int[1];
  32683. int result = context.getRuntime().getPosix().waitpid(pid, status, 0);
  32684. return context.getRuntime().newFixnum(result);
  32685. }
  32686. };
  32687. return RubyThread.newInstance(
  32688. runtime.getThread(),
  32689. IRubyObject.NULL_ARRAY,
  32690. CallBlock.newCallClosure(recv, (RubyModule)recv, Arity.NO_ARGUMENTS, callback, context));
  32691. }
  32692. @JRubyMethod(name = "times", frame = true, module = true, visibility = Visibility.PRIVATE)
  32693. public static IRubyObject times(IRubyObject recv, Block unusedBlock) {
  32694. Ruby runtime = recv.getRuntime();
  32695. double currentTime = System.currentTimeMillis() / 1000.0;
  32696. double startTime = runtime.getStartTime() / 1000.0;
  32697. RubyFloat zero = runtime.newFloat(0.0);
  32698. return RubyStruct.newStruct(runtime.getTmsStruct(),
  32699. new IRubyObject[] { runtime.newFloat(currentTime - startTime), zero, zero, zero },
  32700. Block.NULL_BLOCK);
  32701. }
  32702. @JRubyMethod(name = "pid", module = true, visibility = Visibility.PRIVATE)
  32703. public static IRubyObject pid(IRubyObject recv) {
  32704. return recv.getRuntime().newFixnum(recv.getRuntime().getPosix().getpid());
  32705. }
  32706. @JRubyMethod(name = "fork", module = true, visibility = Visibility.PRIVATE)
  32707. public static IRubyObject fork(ThreadContext context, IRubyObject recv, Block block) {
  32708. return RubyKernel.fork(context, recv, block);
  32709. }
  32710. @JRubyMethod(name = "exit", optional = 1, module = true, visibility = Visibility.PRIVATE)
  32711. public static IRubyObject exit(IRubyObject recv, IRubyObject[] args) {
  32712. return RubyKernel.exit(recv, args);
  32713. }
  32714. }
  32715. /***** BEGIN LICENSE BLOCK *****
  32716. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  32717. *
  32718. * The contents of this file are subject to the Common Public
  32719. * License Version 1.0 (the "License"); you may not use this file
  32720. * except in compliance with the License. You may obtain a copy of
  32721. * the License at http://www.eclipse.org/legal/cpl-v10.html
  32722. *
  32723. * Software distributed under the License is distributed on an "AS
  32724. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  32725. * implied. See the License for the specific language governing
  32726. * rights and limitations under the License.
  32727. *
  32728. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  32729. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  32730. * Copyright (C) 2001 Ed Sinjiashvili <slorcim@users.sourceforge.net>
  32731. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  32732. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  32733. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  32734. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  32735. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  32736. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  32737. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  32738. *
  32739. * Alternatively, the contents of this file may be used under the terms of
  32740. * either of the GNU General Public License Version 2 or later (the "GPL"),
  32741. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  32742. * in which case the provisions of the GPL or the LGPL are applicable instead
  32743. * of those above. If you wish to allow use of your version of this file only
  32744. * under the terms of either the GPL or the LGPL, and not to allow others to
  32745. * use your version of this file under the terms of the CPL, indicate your
  32746. * decision by deleting the provisions above and replace them with the notice
  32747. * and other provisions required by the GPL or the LGPL. If you do not delete
  32748. * the provisions above, a recipient may use your version of this file under
  32749. * the terms of any one of the CPL, the GPL or the LGPL.
  32750. ***** END LICENSE BLOCK *****/
  32751. package org.jruby;
  32752. import java.io.IOException;
  32753. import java.util.List;
  32754. import org.jruby.anno.JRubyClass;
  32755. import org.jruby.anno.JRubyMethod;
  32756. import org.jruby.exceptions.RaiseException;
  32757. import org.jruby.runtime.Arity;
  32758. import org.jruby.runtime.Block;
  32759. import org.jruby.runtime.BlockCallback;
  32760. import org.jruby.runtime.CallBlock;
  32761. import org.jruby.runtime.MethodIndex;
  32762. import org.jruby.runtime.ObjectAllocator;
  32763. import org.jruby.runtime.ObjectMarshal;
  32764. import org.jruby.runtime.ThreadContext;
  32765. import org.jruby.runtime.builtin.IRubyObject;
  32766. import org.jruby.runtime.builtin.Variable;
  32767. import org.jruby.runtime.component.VariableEntry;
  32768. import org.jruby.runtime.marshal.MarshalStream;
  32769. import org.jruby.runtime.marshal.UnmarshalStream;
  32770. /**
  32771. * @author jpetersen
  32772. */
  32773. @JRubyClass(name="Range", include="Enumerable")
  32774. public class RubyRange extends RubyObject {
  32775. private IRubyObject begin;
  32776. private IRubyObject end;
  32777. private boolean isExclusive;
  32778. public static RubyClass createRangeClass(Ruby runtime) {
  32779. RubyClass result = runtime.defineClass("Range", runtime.getObject(), RANGE_ALLOCATOR);
  32780. runtime.setRange(result);
  32781. result.kindOf = new RubyModule.KindOf() {
  32782. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  32783. return obj instanceof RubyRange;
  32784. }
  32785. };
  32786. result.setMarshal(RANGE_MARSHAL);
  32787. result.includeModule(runtime.getEnumerable());
  32788. // We override Enumerable#member? since ranges in 1.8.1 are continuous.
  32789. // result.defineMethod("member?", callbackFactory.getMethod("include_p", RubyKernel.IRUBY_OBJECT));
  32790. // result.defineMethod("===", callbackFactory.getMethod("include_p", RubyKernel.IRUBY_OBJECT));
  32791. result.defineAnnotatedMethods(RubyRange.class);
  32792. return result;
  32793. }
  32794. private static final ObjectAllocator RANGE_ALLOCATOR = new ObjectAllocator() {
  32795. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  32796. return new RubyRange(runtime, klass);
  32797. }
  32798. };
  32799. private RubyRange(Ruby runtime, RubyClass klass) {
  32800. super(runtime, klass);
  32801. begin = end = runtime.getNil();
  32802. }
  32803. public static RubyRange newRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end, boolean isExclusive) {
  32804. RubyRange range = new RubyRange(runtime, runtime.getRange());
  32805. range.init(context, begin, end, isExclusive);
  32806. return range;
  32807. }
  32808. public static RubyRange newExclusiveRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end) {
  32809. RubyRange range = new RubyRange(runtime, runtime.getRange());
  32810. range.init(context, begin, end, true);
  32811. return range;
  32812. }
  32813. public static RubyRange newInclusiveRange(Ruby runtime, ThreadContext context, IRubyObject begin, IRubyObject end) {
  32814. RubyRange range = new RubyRange(runtime, runtime.getRange());
  32815. range.init(context, begin, end, false);
  32816. return range;
  32817. }
  32818. protected void copySpecialInstanceVariables(IRubyObject clone) {
  32819. RubyRange range = (RubyRange)clone;
  32820. range.begin = begin;
  32821. range.end = end;
  32822. range.isExclusive = isExclusive;
  32823. }
  32824. final long[] begLen(long len, int err){
  32825. long beg = RubyNumeric.num2long(this.begin);
  32826. long end = RubyNumeric.num2long(this.end);
  32827. if (beg < 0) {
  32828. beg += len;
  32829. if (beg < 0) {
  32830. if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
  32831. return null;
  32832. }
  32833. }
  32834. if (err == 0 || err == 2) {
  32835. if (beg > len) {
  32836. if (err != 0) throw getRuntime().newRangeError(beg + ".." + (isExclusive ? "." : "") + end + " out of range");
  32837. return null;
  32838. }
  32839. if (end > len) end = len;
  32840. }
  32841. if (end < 0) end += len;
  32842. if (!isExclusive) end++;
  32843. len = end - beg;
  32844. if (len < 0) len = 0;
  32845. return new long[]{beg, len};
  32846. }
  32847. private void init(ThreadContext context, IRubyObject begin, IRubyObject end, boolean isExclusive) {
  32848. if (!(begin instanceof RubyFixnum && end instanceof RubyFixnum)) {
  32849. try {
  32850. IRubyObject result = begin.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", end);
  32851. if (result.isNil()) throw getRuntime().newArgumentError("bad value for range");
  32852. } catch (RaiseException re) {
  32853. throw getRuntime().newArgumentError("bad value for range");
  32854. }
  32855. }
  32856. this.begin = begin;
  32857. this.end = end;
  32858. this.isExclusive = isExclusive;
  32859. }
  32860. @JRubyMethod(name = "initialize", required = 2, optional = 1, frame = true)
  32861. public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block unusedBlock) {
  32862. init(context, args[0], args[1], args.length > 2 && args[2].isTrue());
  32863. return getRuntime().getNil();
  32864. }
  32865. @JRubyMethod(name = {"first", "begin"})
  32866. public IRubyObject first() {
  32867. return begin;
  32868. }
  32869. @JRubyMethod(name = {"last", "end"})
  32870. public IRubyObject last() {
  32871. return end;
  32872. }
  32873. @JRubyMethod(name = "hash")
  32874. public RubyFixnum hash(ThreadContext context) {
  32875. long hash = isExclusive ? 1 : 0;
  32876. long h = hash;
  32877. long v = begin.callMethod(context, MethodIndex.HASH, "hash").convertToInteger().getLongValue();
  32878. hash ^= v << 1;
  32879. v = end.callMethod(context, MethodIndex.HASH, "hash").convertToInteger().getLongValue();
  32880. hash ^= v << 9;
  32881. hash ^= h << 24;
  32882. return getRuntime().newFixnum(hash);
  32883. }
  32884. private static byte[] DOTDOTDOT = "...".getBytes();
  32885. private static byte[] DOTDOT = "..".getBytes();
  32886. @JRubyMethod(name = "inspect")
  32887. public IRubyObject inspect(ThreadContext context) {
  32888. RubyString str = inspect(context, begin).strDup(context.getRuntime());
  32889. RubyString str2 = inspect(context, end);
  32890. str.cat(isExclusive ? DOTDOTDOT : DOTDOT);
  32891. str.concat(str2);
  32892. str.infectBy(str2);
  32893. return str;
  32894. }
  32895. @JRubyMethod(name = "to_s")
  32896. public IRubyObject to_s(ThreadContext context) {
  32897. RubyString str = RubyString.objAsString(context, begin).strDup(context.getRuntime());
  32898. RubyString str2 = RubyString.objAsString(context, end);
  32899. str.cat(isExclusive ? DOTDOTDOT : DOTDOT);
  32900. str.concat(str2);
  32901. str.infectBy(str2);
  32902. return str;
  32903. }
  32904. @JRubyMethod(name = "exclude_end?")
  32905. public RubyBoolean exclude_end_p() {
  32906. return getRuntime().newBoolean(isExclusive);
  32907. }
  32908. @JRubyMethod(name = "==", required = 1)
  32909. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  32910. if (this == other) return getRuntime().getTrue();
  32911. if (!(other instanceof RubyRange)) return getRuntime().getFalse();
  32912. RubyRange otherRange = (RubyRange) other;
  32913. if (equalInternal(context, begin, otherRange.begin) &&
  32914. equalInternal(context, end, otherRange.end) &&
  32915. isExclusive == otherRange.isExclusive) return getRuntime().getTrue();
  32916. return getRuntime().getFalse();
  32917. }
  32918. @JRubyMethod(name = "eql?", required = 1)
  32919. public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
  32920. if (this == other) return getRuntime().getTrue();
  32921. if (!(other instanceof RubyRange)) return getRuntime().getFalse();
  32922. RubyRange otherRange = (RubyRange)other;
  32923. if (eqlInternal(context, begin, otherRange.begin) &&
  32924. eqlInternal(context, end, otherRange.end) &&
  32925. isExclusive == otherRange.isExclusive) return getRuntime().getTrue();
  32926. return getRuntime().getFalse();
  32927. }
  32928. private static abstract class RangeCallBack {
  32929. abstract void call(ThreadContext context, IRubyObject arg);
  32930. }
  32931. private static final class StepBlockCallBack extends RangeCallBack implements BlockCallback {
  32932. final Block block;
  32933. IRubyObject iter;
  32934. final IRubyObject step;
  32935. StepBlockCallBack(Block block, IRubyObject iter, IRubyObject step) {
  32936. this.block = block;
  32937. this.iter = iter;
  32938. this.step = step;
  32939. }
  32940. public IRubyObject call(ThreadContext context, IRubyObject[] args, Block originalBlock) {
  32941. call(context, args[0]);
  32942. return context.getRuntime().getNil();
  32943. }
  32944. void call(ThreadContext context, IRubyObject arg) {
  32945. if (iter instanceof RubyFixnum) {
  32946. iter = RubyFixnum.newFixnum(context.getRuntime(), ((RubyFixnum)iter).getLongValue() - 1);
  32947. } else {
  32948. iter = iter.callMethod(context, MethodIndex.OP_MINUS, "-", RubyFixnum.one(context.getRuntime()));
  32949. }
  32950. if (iter == RubyFixnum.zero(context.getRuntime())) {
  32951. block.yield(context, arg);
  32952. iter = step;
  32953. }
  32954. }
  32955. }
  32956. private IRubyObject rangeLt(ThreadContext context, IRubyObject a, IRubyObject b) {
  32957. IRubyObject result = a.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", b);
  32958. if (result.isNil()) return null;
  32959. return RubyComparable.cmpint(context, result, a, b) < 0 ? getRuntime().getTrue() : null;
  32960. }
  32961. private IRubyObject rangeLe(ThreadContext context, IRubyObject a, IRubyObject b) {
  32962. IRubyObject result = a.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", b);
  32963. if (result.isNil()) return null;
  32964. int c = RubyComparable.cmpint(context, result, a, b);
  32965. if (c == 0) return RubyFixnum.zero(getRuntime());
  32966. return c < 0 ? getRuntime().getTrue() : null;
  32967. }
  32968. private void rangeEach(ThreadContext context, RangeCallBack callback) {
  32969. IRubyObject v = begin;
  32970. if (isExclusive) {
  32971. while (rangeLt(context, v, end) != null) {
  32972. callback.call(context, v);
  32973. v = v.callMethod(context, "succ");
  32974. }
  32975. } else {
  32976. IRubyObject c;
  32977. while ((c = rangeLe(context, v, end)) != null && c.isTrue()) {
  32978. callback.call(context, v);
  32979. if (c == RubyFixnum.zero(getRuntime())) break;
  32980. v = v.callMethod(context, "succ");
  32981. }
  32982. }
  32983. }
  32984. @JRubyMethod(name = "each", frame = true)
  32985. public IRubyObject each(ThreadContext context, final Block block) {
  32986. final Ruby runtime = context.getRuntime();
  32987. if (begin instanceof RubyFixnum && end instanceof RubyFixnum) {
  32988. long lim = ((RubyFixnum) end).getLongValue();
  32989. if (!isExclusive) lim++;
  32990. for (long i = ((RubyFixnum) begin).getLongValue(); i < lim; i++) {
  32991. block.yield(context, RubyFixnum.newFixnum(runtime, i));
  32992. }
  32993. } else if (begin instanceof RubyString) {
  32994. ((RubyString) begin).upto(context, end, isExclusive, block);
  32995. } else {
  32996. if (!begin.respondsTo("succ")) throw getRuntime().newTypeError(
  32997. "can't iterate from " + begin.getMetaClass().getName());
  32998. rangeEach(context, new RangeCallBack() {
  32999. @Override
  33000. void call(ThreadContext context, IRubyObject arg) {
  33001. block.yield(context, arg);
  33002. }
  33003. });
  33004. }
  33005. return this;
  33006. }
  33007. @JRubyMethod(name = "step", optional = 1, frame = true)
  33008. public IRubyObject step(ThreadContext context, IRubyObject[] args, Block block) {
  33009. final Ruby runtime = context.getRuntime();
  33010. final IRubyObject step;
  33011. if (args.length == 0) {
  33012. step = RubyFixnum.one(runtime);
  33013. } else {
  33014. step = args[0];
  33015. }
  33016. long unit = RubyNumeric.num2long(step);
  33017. if (unit < 0) throw runtime.newArgumentError("step can't be negative");
  33018. if (begin instanceof RubyFixnum && end instanceof RubyFixnum) {
  33019. if (unit == 0) throw runtime.newArgumentError("step can't be 0");
  33020. long e = ((RubyFixnum)end).getLongValue();
  33021. if (!isExclusive) e++;
  33022. for (long i = ((RubyFixnum)begin).getLongValue(); i < e; i += unit) {
  33023. block.yield(context, RubyFixnum.newFixnum(runtime, i));
  33024. }
  33025. } else {
  33026. IRubyObject tmp = begin.checkStringType();
  33027. if (!tmp.isNil()) {
  33028. if (unit == 0) throw runtime.newArgumentError("step can't be 0");
  33029. // rb_iterate((VALUE(*)_((VALUE)))str_step, (VALUE)args, step_i, (VALUE)iter);
  33030. StepBlockCallBack callback = new StepBlockCallBack(block, RubyFixnum.one(runtime), step);
  33031. Block blockCallback = CallBlock.newCallClosure(this, runtime.getRange(), Arity.singleArgument(), callback, context);
  33032. ((RubyString)tmp).upto(context, end, isExclusive, blockCallback);
  33033. } else if (begin instanceof RubyNumeric) {
  33034. if (equalInternal(context, step, RubyFixnum.zero(runtime))) {
  33035. throw runtime.newArgumentError("step can't be 0");
  33036. }
  33037. final String method;
  33038. final int methodIndex;
  33039. if (isExclusive) {
  33040. method = "<";
  33041. methodIndex = MethodIndex.OP_LT;
  33042. } else {
  33043. method = "<=";
  33044. methodIndex = MethodIndex.OP_LE;
  33045. }
  33046. IRubyObject beg = begin;
  33047. while (beg.callMethod(context, methodIndex, method, end).isTrue()) {
  33048. block.yield(context, beg);
  33049. beg = beg.callMethod(context, MethodIndex.OP_PLUS, "+", step);
  33050. }
  33051. } else {
  33052. if (unit == 0) throw runtime.newArgumentError("step can't be 0");
  33053. if (!begin.respondsTo("succ")) throw runtime.newTypeError(
  33054. "can't iterate from " + begin.getMetaClass().getName());
  33055. // range_each_func(range, step_i, b, e, args);
  33056. rangeEach(context, new StepBlockCallBack(block, RubyFixnum.one(runtime), step));
  33057. }
  33058. }
  33059. return this;
  33060. }
  33061. @JRubyMethod(name = {"include?", "member?", "==="}, required = 1)
  33062. public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
  33063. if (rangeLe(context, begin, obj) != null) {
  33064. if (isExclusive) {
  33065. if (rangeLt(context, obj, end) != null) return context.getRuntime().getTrue();
  33066. } else {
  33067. if (rangeLe(context, obj, end) != null) return context.getRuntime().getTrue();
  33068. }
  33069. }
  33070. return context.getRuntime().getFalse();
  33071. }
  33072. private static final ObjectMarshal RANGE_MARSHAL = new ObjectMarshal() {
  33073. public void marshalTo(Ruby runtime, Object obj, RubyClass type,
  33074. MarshalStream marshalStream) throws IOException {
  33075. RubyRange range = (RubyRange)obj;
  33076. marshalStream.registerLinkTarget(range);
  33077. List<Variable<IRubyObject>> attrs = range.getVariableList();
  33078. attrs.add(new VariableEntry<IRubyObject>("begin", range.begin));
  33079. attrs.add(new VariableEntry<IRubyObject>("end", range.end));
  33080. attrs.add(new VariableEntry<IRubyObject>("excl", range.isExclusive ? runtime.getTrue() : runtime.getFalse()));
  33081. marshalStream.dumpVariables(attrs);
  33082. }
  33083. public Object unmarshalFrom(Ruby runtime, RubyClass type,
  33084. UnmarshalStream unmarshalStream) throws IOException {
  33085. RubyRange range = (RubyRange)type.allocate();
  33086. unmarshalStream.registerLinkTarget(range);
  33087. unmarshalStream.defaultVariablesUnmarshal(range);
  33088. range.begin = range.removeInternalVariable("begin");
  33089. range.end = range.removeInternalVariable("end");
  33090. range.isExclusive = range.removeInternalVariable("excl").isTrue();
  33091. return range;
  33092. }
  33093. };
  33094. }
  33095. /***** BEGIN LICENSE BLOCK *****
  33096. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  33097. *
  33098. * The contents of this file are subject to the Common Public
  33099. * License Version 1.0 (the "License"); you may not use this file
  33100. * except in compliance with the License. You may obtain a copy of
  33101. * the License at http://www.eclipse.org/legal/cpl-v10.html
  33102. *
  33103. * Software distributed under the License is distributed on an "AS
  33104. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  33105. * implied. See the License for the specific language governing
  33106. * rights and limitations under the License.
  33107. *
  33108. * Alternatively, the contents of this file may be used under the terms of
  33109. * either of the GNU General Public License Version 2 or later (the "GPL"),
  33110. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33111. * in which case the provisions of the GPL or the LGPL are applicable instead
  33112. * of those above. If you wish to allow use of your version of this file only
  33113. * under the terms of either the GPL or the LGPL, and not to allow others to
  33114. * use your version of this file under the terms of the CPL, indicate your
  33115. * decision by deleting the provisions above and replace them with the notice
  33116. * and other provisions required by the GPL or the LGPL. If you do not delete
  33117. * the provisions above, a recipient may use your version of this file under
  33118. * the terms of any one of the CPL, the GPL or the LGPL.
  33119. ***** END LICENSE BLOCK *****/
  33120. package org.jruby;
  33121. import static org.jruby.util.Numeric.f_add;
  33122. import static org.jruby.util.Numeric.f_cmp;
  33123. import static org.jruby.util.Numeric.f_div;
  33124. import static org.jruby.util.Numeric.f_equal_p;
  33125. import static org.jruby.util.Numeric.f_expt;
  33126. import static org.jruby.util.Numeric.f_floor;
  33127. import static org.jruby.util.Numeric.f_gcd;
  33128. import static org.jruby.util.Numeric.f_idiv;
  33129. import static org.jruby.util.Numeric.f_mul;
  33130. import static org.jruby.util.Numeric.f_negate;
  33131. import static org.jruby.util.Numeric.f_negative_p;
  33132. import static org.jruby.util.Numeric.f_one_p;
  33133. import static org.jruby.util.Numeric.f_rshift;
  33134. import static org.jruby.util.Numeric.f_sub;
  33135. import static org.jruby.util.Numeric.f_to_f;
  33136. import static org.jruby.util.Numeric.f_to_i;
  33137. import static org.jruby.util.Numeric.f_to_r;
  33138. import static org.jruby.util.Numeric.f_to_s;
  33139. import static org.jruby.util.Numeric.f_truncate;
  33140. import static org.jruby.util.Numeric.f_xor;
  33141. import static org.jruby.util.Numeric.f_zero_p;
  33142. import static org.jruby.util.Numeric.i_gcd;
  33143. import static org.jruby.util.Numeric.i_ilog2;
  33144. import static org.jruby.util.Numeric.ldexp;
  33145. import org.joni.encoding.specific.ASCIIEncoding;
  33146. import org.jruby.anno.JRubyClass;
  33147. import org.jruby.anno.JRubyMethod;
  33148. import org.jruby.common.IRubyWarnings;
  33149. import org.jruby.javasupport.util.RuntimeHelpers;
  33150. import org.jruby.runtime.Arity;
  33151. import org.jruby.runtime.ClassIndex;
  33152. import org.jruby.runtime.Frame;
  33153. import org.jruby.runtime.ObjectAllocator;
  33154. import org.jruby.runtime.ThreadContext;
  33155. import org.jruby.runtime.Visibility;
  33156. import org.jruby.runtime.builtin.IRubyObject;
  33157. import org.jruby.util.ByteList;
  33158. import org.jruby.util.Numeric;
  33159. /**
  33160. * 1.9 rational.c as of revision: 18876
  33161. */
  33162. @JRubyClass(name = "Rational", parent = "Numeric", include = "Precision")
  33163. public class RubyRational extends RubyNumeric {
  33164. public static RubyClass createRationalClass(Ruby runtime) {
  33165. RubyClass rationalc = runtime.defineClass("Rational", runtime.getNumeric(), RATIONAL_ALLOCATOR); // because one can Complex.send(:allocate)
  33166. runtime.setRational(rationalc);
  33167. rationalc.index = ClassIndex.RATIONAL;
  33168. rationalc.kindOf = new RubyModule.KindOf() {
  33169. @Override
  33170. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  33171. return obj instanceof RubyRational;
  33172. }
  33173. };
  33174. ThreadContext context = runtime.getCurrentContext();
  33175. rationalc.callMethod(context, "private_class_method", runtime.newSymbol("allocate"));
  33176. rationalc.defineAnnotatedMethods(RubyRational.class);
  33177. return rationalc;
  33178. }
  33179. private static ObjectAllocator RATIONAL_ALLOCATOR = new ObjectAllocator() {
  33180. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  33181. return new RubyRational(runtime, klass, RubyFixnum.zero(runtime), RubyFixnum.one(runtime));
  33182. }
  33183. };
  33184. /** internal
  33185. *
  33186. */
  33187. private RubyRational(Ruby runtime, IRubyObject clazz, IRubyObject num, IRubyObject den) {
  33188. super(runtime, (RubyClass)clazz);
  33189. this.num = num;
  33190. this.den = den;
  33191. }
  33192. /** rb_rational_raw
  33193. *
  33194. */
  33195. static RubyRational newRationalRaw(Ruby runtime, IRubyObject x, RubyObject y) {
  33196. return new RubyRational(runtime, runtime.getRational(), x, y);
  33197. }
  33198. /** rb_rational_raw1
  33199. *
  33200. */
  33201. static RubyRational newRationalRaw(Ruby runtime, IRubyObject x) {
  33202. return new RubyRational(runtime, runtime.getRational(), x, RubyFixnum.one(runtime));
  33203. }
  33204. /** rb_rational_new1
  33205. *
  33206. */
  33207. static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x) {
  33208. return newRationalCanonicalize(context, x, RubyFixnum.one(context.getRuntime()));
  33209. }
  33210. /** rb_rational_new
  33211. *
  33212. */
  33213. private static IRubyObject newRationalCanonicalize(ThreadContext context, IRubyObject x, IRubyObject y) {
  33214. return canonicalizeInternal(context, context.getRuntime().getRational(), x, y);
  33215. }
  33216. /** f_rational_new2
  33217. *
  33218. */
  33219. private static IRubyObject newRational(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  33220. assert x instanceof RubyRational && y instanceof RubyRational;
  33221. return canonicalizeInternal(context, clazz, x, y);
  33222. }
  33223. /** f_rational_new1
  33224. *
  33225. */
  33226. private static IRubyObject newRational(ThreadContext context, IRubyObject clazz, IRubyObject x) {
  33227. assert x instanceof RubyRational;
  33228. return canonicalizeInternal(context, clazz, x, RubyFixnum.one(context.getRuntime()));
  33229. }
  33230. /** f_rational_new_no_reduce2
  33231. *
  33232. */
  33233. private static IRubyObject newRationalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  33234. assert x instanceof RubyRational && y instanceof RubyRational;
  33235. return canonicalizeInternalNoReduce(context, clazz, x, y);
  33236. }
  33237. /** f_rational_new_no_reduce1
  33238. *
  33239. */
  33240. private static IRubyObject newRationalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject x) {
  33241. assert x instanceof RubyRational;
  33242. return canonicalizeInternalNoReduce(context, clazz, x, RubyFixnum.one(context.getRuntime()));
  33243. }
  33244. /** f_rational_new_bang2
  33245. *
  33246. */
  33247. private static RubyRational newRationalBang(ThreadContext context, IRubyObject clazz, IRubyObject x, IRubyObject y) {
  33248. assert !f_negative_p(context, y) && !(f_zero_p(context, y));
  33249. return new RubyRational(context.getRuntime(), clazz, x, y);
  33250. }
  33251. /** f_rational_new_bang1
  33252. *
  33253. */
  33254. private static RubyRational newRationalBang(ThreadContext context, IRubyObject clazz, IRubyObject x) {
  33255. return newRationalBang(context, clazz, x, RubyFixnum.one(context.getRuntime()));
  33256. }
  33257. private IRubyObject num;
  33258. private IRubyObject den;
  33259. /** nurat_s_new_bang
  33260. *
  33261. */
  33262. @Deprecated
  33263. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject[]args) {
  33264. switch (args.length) {
  33265. case 1: return newInstanceBang(context, recv, args[0]);
  33266. case 2: return newInstanceBang(context, recv, args[0], args[1]);
  33267. }
  33268. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
  33269. return null;
  33270. }
  33271. @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
  33272. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject num) {
  33273. return newInstanceBang(context, recv, num, RubyFixnum.one(context.getRuntime()));
  33274. }
  33275. @JRubyMethod(name = "new!", meta = true, visibility = Visibility.PRIVATE)
  33276. public static IRubyObject newInstanceBang(ThreadContext context, IRubyObject recv, IRubyObject num, IRubyObject den) {
  33277. if (!(num instanceof RubyInteger)) num = f_to_i(context, num);
  33278. if (!(den instanceof RubyInteger)) den = f_to_i(context, den);
  33279. Ruby runtime = context.getRuntime();
  33280. IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
  33281. if (res == RubyFixnum.minus_one(runtime)) {
  33282. num = f_negate(context, num);
  33283. den = f_negate(context, den);
  33284. } else if (res == RubyFixnum.zero(runtime)) {
  33285. throw runtime.newZeroDivisionError();
  33286. }
  33287. return new RubyRational(runtime, recv, num, den);
  33288. }
  33289. /** nurat_int_check
  33290. *
  33291. */
  33292. private static void intCheck(IRubyObject num) {
  33293. if (!(num instanceof RubyFixnum ) && !(num instanceof RubyBignum)) throw num.getRuntime().newArgumentError("not an integer");
  33294. }
  33295. /** nurat_s_canonicalize_internal
  33296. *
  33297. */
  33298. private static IRubyObject canonicalizeInternal(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
  33299. Ruby runtime = context.getRuntime();
  33300. IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
  33301. if (res == RubyFixnum.minus_one(runtime)) {
  33302. num = f_negate(context, num);
  33303. den = f_negate(context, den);
  33304. } else if (res == RubyFixnum.zero(runtime)) {
  33305. throw runtime.newZeroDivisionError();
  33306. }
  33307. IRubyObject gcd = f_gcd(context, num, den);
  33308. num = f_idiv(context, num, gcd);
  33309. den = f_idiv(context, den, gcd);
  33310. if (f_one_p(context, den) && ((RubyModule)clazz).fastHasConstant("Unify")) return num;
  33311. return new RubyRational(context.getRuntime(), clazz, num, den);
  33312. }
  33313. /** nurat_s_canonicalize_internal_no_reduce
  33314. *
  33315. */
  33316. private static IRubyObject canonicalizeInternalNoReduce(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
  33317. Ruby runtime = context.getRuntime();
  33318. IRubyObject res = f_cmp(context, den, RubyFixnum.zero(runtime));
  33319. if (res == RubyFixnum.minus_one(runtime)) {
  33320. num = f_negate(context, num);
  33321. den = f_negate(context, den);
  33322. } else if (res == RubyFixnum.zero(runtime)) {
  33323. throw runtime.newZeroDivisionError();
  33324. }
  33325. if (f_equal_p(context, den, RubyFixnum.one(runtime)) && ((RubyModule)clazz).fastHasConstant("Unify")) return num;
  33326. return new RubyRational(context.getRuntime(), clazz, num, den);
  33327. }
  33328. /** nurat_s_new
  33329. *
  33330. */
  33331. @Deprecated
  33332. public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject[]args) {
  33333. switch (args.length) {
  33334. case 1: return newInstance(context, clazz, args[0]);
  33335. case 2: return newInstance(context, clazz, args[0], args[1]);
  33336. }
  33337. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 1);
  33338. return null;
  33339. }
  33340. @JRubyMethod(name = "new", meta = true)
  33341. public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject num) {
  33342. intCheck(num);
  33343. return canonicalizeInternal(context, clazz, num, RubyFixnum.one(context.getRuntime()));
  33344. }
  33345. @JRubyMethod(name = "new", meta = true)
  33346. public static IRubyObject newInstance(ThreadContext context, IRubyObject clazz, IRubyObject num, IRubyObject den) {
  33347. intCheck(num);
  33348. intCheck(den);
  33349. return canonicalizeInternal(context, clazz, num, den);
  33350. }
  33351. /** rb_Rational1
  33352. *
  33353. */
  33354. public static IRubyObject newRationalConvert(ThreadContext context, IRubyObject x) {
  33355. return newRationalConvert(context, x, RubyFixnum.one(context.getRuntime()));
  33356. }
  33357. /** rb_Rational/rb_Rational2
  33358. *
  33359. */
  33360. public static IRubyObject newRationalConvert(ThreadContext context, IRubyObject x, IRubyObject y) {
  33361. return convert(context, context.getRuntime().getRational(), x, y);
  33362. }
  33363. @Deprecated
  33364. public static IRubyObject convert(ThreadContext context, IRubyObject clazz, IRubyObject[]args) {
  33365. switch (args.length) {
  33366. case 0: return convert(context, clazz);
  33367. case 1: return convert(context, clazz, args[0]);
  33368. case 2: return convert(context, clazz, args[0], args[1]);
  33369. }
  33370. Arity.raiseArgumentError(context.getRuntime(), args.length, 0, 2);
  33371. return null;
  33372. }
  33373. /** nurat_s_convert
  33374. *
  33375. */
  33376. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  33377. public static IRubyObject convert(ThreadContext context, IRubyObject recv) {
  33378. IRubyObject nil = context.getRuntime().getNil();
  33379. return convertCommon(context, recv, nil, nil);
  33380. }
  33381. /** nurat_s_convert
  33382. *
  33383. */
  33384. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  33385. public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1) {
  33386. return convertCommon(context, recv, a1, context.getRuntime().getNil());
  33387. }
  33388. /** nurat_s_convert
  33389. *
  33390. */
  33391. @JRubyMethod(name = "convert", meta = true, visibility = Visibility.PRIVATE)
  33392. public static IRubyObject convert(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
  33393. return convertCommon(context, recv, a1, a2);
  33394. }
  33395. private static IRubyObject convertCommon(ThreadContext context, IRubyObject recv, IRubyObject a1, IRubyObject a2) {
  33396. if (a1 instanceof RubyComplex) {
  33397. RubyComplex a1Complex = (RubyComplex)a1;
  33398. if (a1Complex.getImage() instanceof RubyFloat || !f_zero_p(context, a1Complex.getImage())) {
  33399. IRubyObject s = f_to_s(context, a1);
  33400. throw context.getRuntime().newArgumentError("can't accept " + s.convertToString());
  33401. }
  33402. a1 = a1Complex.getReal();
  33403. }
  33404. if (a2 instanceof RubyComplex) {
  33405. RubyComplex a2Complex = (RubyComplex)a2;
  33406. if (a2Complex.getImage() instanceof RubyFloat || !f_zero_p(context, a2Complex.getImage())) {
  33407. IRubyObject s = f_to_s(context, a2);
  33408. throw context.getRuntime().newArgumentError("can't accept " + s.convertToString());
  33409. }
  33410. a2 = a2Complex.getReal();
  33411. }
  33412. Frame frame = context.getCurrentFrame();
  33413. IRubyObject backref = frame.getBackRef();
  33414. if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
  33415. if (a1 instanceof RubyFloat) {
  33416. a1 = f_to_r(context, a1);
  33417. } else if (a1 instanceof RubyString) {
  33418. a1 = str_to_r_strict(context, a1);
  33419. }
  33420. if (a2 instanceof RubyFloat) {
  33421. a2 = f_to_r(context, a2);
  33422. } else if (a2 instanceof RubyString) {
  33423. a2 = str_to_r_strict(context, a2);
  33424. }
  33425. frame.setBackRef(backref);
  33426. if (a1 instanceof RubyRational) {
  33427. if (a2.isNil() || f_zero_p(context, a2)) return a1;
  33428. return f_div(context, a1, a2);
  33429. }
  33430. if (a2 instanceof RubyRational) {
  33431. return f_div(context, a1, a2);
  33432. }
  33433. return a2.isNil() ? newInstance(context, recv, a1) : newInstance(context, recv, a1, a2);
  33434. }
  33435. /** nurat_s_induced_from
  33436. *
  33437. */
  33438. @JRubyMethod(name = "induced_from", meta = true)
  33439. public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  33440. return f_to_r(context, arg);
  33441. }
  33442. /** nurat_numerator
  33443. *
  33444. */
  33445. @JRubyMethod(name = "numerator")
  33446. public IRubyObject numerator(ThreadContext context) {
  33447. return num;
  33448. }
  33449. /** nurat_denominator
  33450. *
  33451. */
  33452. @JRubyMethod(name = "denominator")
  33453. public IRubyObject denominator(ThreadContext context) {
  33454. return den;
  33455. }
  33456. /** f_imul
  33457. *
  33458. */
  33459. private static IRubyObject f_imul(ThreadContext context, long a, long b) {
  33460. Ruby runtime = context.getRuntime();
  33461. if (a == 0 || b == 0) {
  33462. return RubyFixnum.zero(runtime);
  33463. } else if (a == 1) {
  33464. return RubyFixnum.newFixnum(runtime, b);
  33465. } else if (b == 1) {
  33466. return RubyFixnum.newFixnum(runtime, a);
  33467. }
  33468. long c = a * b;
  33469. if(c / a != b) {
  33470. return RubyBignum.newBignum(runtime, a).op_mul(context, RubyBignum.newBignum(runtime, b));
  33471. }
  33472. return RubyFixnum.newFixnum(runtime, c);
  33473. }
  33474. /** f_addsub
  33475. *
  33476. */
  33477. private IRubyObject f_addsub(ThreadContext context, IRubyObject anum, IRubyObject aden, IRubyObject bnum, IRubyObject bden, boolean plus) {
  33478. Ruby runtime = context.getRuntime();
  33479. IRubyObject num, den, g, a, b;
  33480. if (anum instanceof RubyFixnum && aden instanceof RubyFixnum &&
  33481. bnum instanceof RubyFixnum && bden instanceof RubyFixnum) {
  33482. long an = ((RubyFixnum)anum).getLongValue();
  33483. long ad = ((RubyFixnum)aden).getLongValue();
  33484. long bn = ((RubyFixnum)bnum).getLongValue();
  33485. long bd = ((RubyFixnum)bden).getLongValue();
  33486. long ig = i_gcd(ad, bd);
  33487. g = RubyFixnum.newFixnum(runtime, ig);
  33488. a = f_imul(context, an, bd / ig);
  33489. b = f_imul(context, bn, ad / ig);
  33490. } else {
  33491. g = f_gcd(context, aden, bden);
  33492. a = f_mul(context, anum, f_idiv(context, bden, g));
  33493. b = f_mul(context, bnum, f_idiv(context, aden, g));
  33494. }
  33495. IRubyObject c = plus ? f_add(context, a, b) : f_sub(context, a, b);
  33496. b = f_idiv(context, aden, g);
  33497. g = f_gcd(context, c, g);
  33498. num = f_idiv(context, c, g);
  33499. a = f_idiv(context, bden, g);
  33500. den = f_mul(context, a, b);
  33501. return RubyRational.newRationalNoReduce(context, getMetaClass(), num, den);
  33502. }
  33503. /** nurat_add
  33504. *
  33505. */
  33506. @JRubyMethod(name = "+")
  33507. public IRubyObject op_add(ThreadContext context, IRubyObject other) {
  33508. switch (other.getMetaClass().index) {
  33509. case ClassIndex.FIXNUM:
  33510. case ClassIndex.BIGNUM:
  33511. return f_addsub(context, num, den, other, RubyFixnum.one(context.getRuntime()), true);
  33512. case ClassIndex.FLOAT:
  33513. return f_add(context, f_to_f(context, this), other);
  33514. case ClassIndex.RATIONAL:
  33515. RubyRational otherRational = (RubyRational)other;
  33516. return f_addsub(context, num, den, otherRational.num, otherRational.den, true);
  33517. }
  33518. return coerceBin(context, "+", other);
  33519. }
  33520. /** nurat_sub
  33521. *
  33522. */
  33523. @JRubyMethod(name = "-")
  33524. public IRubyObject op_sub(ThreadContext context, IRubyObject other) {
  33525. switch (other.getMetaClass().index) {
  33526. case ClassIndex.FIXNUM:
  33527. case ClassIndex.BIGNUM:
  33528. return f_addsub(context, num, den, other, RubyFixnum.one(context.getRuntime()), false);
  33529. case ClassIndex.FLOAT:
  33530. return f_sub(context, f_to_f(context, this), other);
  33531. case ClassIndex.RATIONAL:
  33532. RubyRational otherRational = (RubyRational)other;
  33533. return f_addsub(context, num, den, otherRational.num, otherRational.den, false);
  33534. }
  33535. return coerceBin(context, "-", other);
  33536. }
  33537. /** f_muldiv
  33538. *
  33539. */
  33540. private IRubyObject f_muldiv(ThreadContext context, IRubyObject anum, IRubyObject aden, IRubyObject bnum, IRubyObject bden, boolean mult) {
  33541. if (!mult) {
  33542. if (f_negative_p(context, bnum)) {
  33543. anum = f_negate(context, anum);
  33544. bnum = f_negate(context, bnum);
  33545. }
  33546. IRubyObject tmp = bnum;
  33547. bnum = bden;
  33548. bden = tmp;
  33549. }
  33550. final IRubyObject num, den;
  33551. if (anum instanceof RubyFixnum && aden instanceof RubyFixnum &&
  33552. bnum instanceof RubyFixnum && bden instanceof RubyFixnum) {
  33553. long an = ((RubyFixnum)anum).getLongValue();
  33554. long ad = ((RubyFixnum)aden).getLongValue();
  33555. long bn = ((RubyFixnum)bnum).getLongValue();
  33556. long bd = ((RubyFixnum)bden).getLongValue();
  33557. long g1 = i_gcd(an, bd);
  33558. long g2 = i_gcd(ad, bn);
  33559. num = f_imul(context, an / g1, bn / g2);
  33560. den = f_imul(context, ad / g2, bd / g1);
  33561. } else {
  33562. IRubyObject g1 = f_gcd(context, anum, bden);
  33563. IRubyObject g2 = f_gcd(context, aden, bnum);
  33564. num = f_mul(context, f_idiv(context, anum, g1), f_idiv(context, bnum, g2));
  33565. den = f_mul(context, f_idiv(context, aden, g2), f_idiv(context, bden, g1));
  33566. }
  33567. return RubyRational.newRationalNoReduce(context, getMetaClass(), num, den);
  33568. }
  33569. /** nurat_mul
  33570. *
  33571. */
  33572. @JRubyMethod(name = "*")
  33573. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  33574. switch (other.getMetaClass().index) {
  33575. case ClassIndex.FIXNUM:
  33576. case ClassIndex.BIGNUM:
  33577. return f_muldiv(context, num, den, other, RubyFixnum.one(context.getRuntime()), true);
  33578. case ClassIndex.FLOAT:
  33579. return f_mul(context, f_to_f(context, this), other);
  33580. case ClassIndex.RATIONAL:
  33581. RubyRational otherRational = (RubyRational)other;
  33582. return f_muldiv(context, num, den, otherRational.num, otherRational.den, true);
  33583. }
  33584. return coerceBin(context, "*", other);
  33585. }
  33586. /** nurat_div
  33587. *
  33588. */
  33589. @JRubyMethod(name = "/")
  33590. public IRubyObject op_div(ThreadContext context, IRubyObject other) {
  33591. switch (other.getMetaClass().index) {
  33592. case ClassIndex.FIXNUM:
  33593. case ClassIndex.BIGNUM:
  33594. if (f_zero_p(context, other)) throw context.getRuntime().newZeroDivisionError();
  33595. return f_muldiv(context, num, den, other, RubyFixnum.one(context.getRuntime()), false);
  33596. case ClassIndex.FLOAT:
  33597. return f_to_f(context, this).callMethod(context, "/", other);
  33598. case ClassIndex.RATIONAL:
  33599. if (f_zero_p(context, other)) throw context.getRuntime().newZeroDivisionError();
  33600. RubyRational otherRational = (RubyRational)other;
  33601. return f_muldiv(context, num, den, otherRational.num, otherRational.den, false);
  33602. }
  33603. return coerceBin(context, "/", other);
  33604. }
  33605. /** nurat_fdiv
  33606. *
  33607. */
  33608. @JRubyMethod(name = "fdiv")
  33609. public IRubyObject op_fdiv(ThreadContext context, IRubyObject other) {
  33610. return f_div(context, f_to_f(context, this), other);
  33611. }
  33612. /** nurat_expt
  33613. *
  33614. */
  33615. @JRubyMethod(name = "**")
  33616. public IRubyObject op_expt(ThreadContext context, IRubyObject other) {
  33617. Ruby runtime = context.getRuntime();
  33618. if (f_zero_p(context, other)) {
  33619. return RubyRational.newRationalBang(context, getMetaClass(), RubyFixnum.one(runtime));
  33620. }
  33621. if (other instanceof RubyRational) {
  33622. RubyRational otherRational = (RubyRational)other;
  33623. if (f_one_p(context, otherRational.den)) other = otherRational.num;
  33624. }
  33625. switch (other.getMetaClass().index) {
  33626. case ClassIndex.FIXNUM:
  33627. case ClassIndex.BIGNUM:
  33628. final IRubyObject tnum, tden;
  33629. IRubyObject res = f_cmp(context, other, RubyFixnum.zero(runtime));
  33630. if (res == RubyFixnum.one(runtime)) {
  33631. tnum = f_expt(context, num, other);
  33632. tden = f_expt(context, den, other);
  33633. } else if (res == RubyFixnum.minus_one(runtime)){
  33634. tnum = f_expt(context, den, f_negate(context, other));
  33635. tden = f_expt(context, num, f_negate(context, other));
  33636. } else {
  33637. tnum = tden = RubyFixnum.one(runtime);
  33638. }
  33639. return RubyRational.newRational(context, getMetaClass(), tnum, tden);
  33640. case ClassIndex.FLOAT:
  33641. case ClassIndex.RATIONAL:
  33642. return f_expt(context, f_to_f(context, this), other);
  33643. }
  33644. return coerceBin(context, "**", other);
  33645. }
  33646. /** nurat_cmp
  33647. *
  33648. */
  33649. @JRubyMethod(name = "<=>")
  33650. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  33651. switch (other.getMetaClass().index) {
  33652. case ClassIndex.FIXNUM:
  33653. case ClassIndex.BIGNUM:
  33654. if (den instanceof RubyFixnum && ((RubyFixnum)den).getLongValue() == 1) return f_cmp(context, num, other);
  33655. return f_cmp(context, this, RubyRational.newRationalBang(context, getMetaClass(), other));
  33656. case ClassIndex.FLOAT:
  33657. return f_cmp(context, f_to_f(context, this), other);
  33658. case ClassIndex.RATIONAL:
  33659. RubyRational otherRational = (RubyRational)other;
  33660. final IRubyObject num1, num2;
  33661. if (num instanceof RubyFixnum && den instanceof RubyFixnum &&
  33662. otherRational.num instanceof RubyFixnum && otherRational.den instanceof RubyFixnum) {
  33663. num1 = f_imul(context, ((RubyFixnum)num).getLongValue(), ((RubyFixnum)otherRational.den).getLongValue());
  33664. num2 = f_imul(context, ((RubyFixnum)otherRational.num).getLongValue(), ((RubyFixnum)den).getLongValue());
  33665. } else {
  33666. num1 = f_mul(context, num, otherRational.den);
  33667. num2 = f_mul(context, otherRational.num, den);
  33668. }
  33669. return f_cmp(context, f_sub(context, num1, num2), RubyFixnum.zero(context.getRuntime()));
  33670. }
  33671. return coerceBin(context, "<=>", other);
  33672. }
  33673. /** nurat_equal_p
  33674. *
  33675. */
  33676. @JRubyMethod(name = "==")
  33677. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  33678. Ruby runtime = context.getRuntime();
  33679. switch (other.getMetaClass().index) {
  33680. case ClassIndex.FIXNUM:
  33681. case ClassIndex.BIGNUM:
  33682. if (f_zero_p(context, num) && f_zero_p(context, den)) return runtime.getTrue();
  33683. if (!(den instanceof RubyFixnum) || ((RubyFixnum)den).getLongValue() != 1) return runtime.getFalse();
  33684. if (f_equal_p(context, num, other)) return runtime.getTrue();
  33685. return runtime.getFalse();
  33686. case ClassIndex.FLOAT:
  33687. return f_equal_p(context, f_to_f(context, this), other) ? runtime.getTrue() : runtime.getFalse();
  33688. case ClassIndex.RATIONAL:
  33689. RubyRational otherRational = (RubyRational)other;
  33690. if (f_zero_p(context, num) && f_zero_p(context, otherRational.num)) return runtime.getTrue();
  33691. if (f_equal_p(context, num, otherRational.num) && f_equal_p(context, den, otherRational.den)) return runtime.getTrue();
  33692. return runtime.getFalse();
  33693. }
  33694. return f_equal_p(context, other, this) ? runtime.getTrue() : runtime.getFalse();
  33695. }
  33696. /** nurat_coerce
  33697. *
  33698. */
  33699. @JRubyMethod(name = "coerce")
  33700. public IRubyObject op_coerce(ThreadContext context, IRubyObject other) {
  33701. Ruby runtime = context.getRuntime();
  33702. switch (other.getMetaClass().index) {
  33703. case ClassIndex.FIXNUM:
  33704. case ClassIndex.BIGNUM:
  33705. return runtime.newArray(RubyRational.newRationalBang(context, getMetaClass(), other), this);
  33706. case ClassIndex.FLOAT:
  33707. return runtime.newArray(other, f_to_f(context, this));
  33708. }
  33709. throw runtime.newTypeError(other.getMetaClass() + " can't be coerced into " + getMetaClass());
  33710. }
  33711. /** nurat_idiv
  33712. *
  33713. */
  33714. @JRubyMethod(name = "div")
  33715. public IRubyObject op_idiv(ThreadContext context, IRubyObject other) {
  33716. return f_floor(context, f_div(context, this, other));
  33717. }
  33718. /** nurat_mod
  33719. *
  33720. */
  33721. @JRubyMethod(name = {"modulo", "%"})
  33722. public IRubyObject op_mod(ThreadContext context, IRubyObject other) {
  33723. IRubyObject val = f_floor(context, f_div(context, this, other));
  33724. return f_sub(context, this, f_mul(context, other, val));
  33725. }
  33726. /** nurat_divmod
  33727. *
  33728. */
  33729. @JRubyMethod(name = "divmod")
  33730. public IRubyObject op_divmod(ThreadContext context, IRubyObject other) {
  33731. IRubyObject val = f_floor(context, f_div(context, this, other));
  33732. return context.getRuntime().newArray(val, f_sub(context, this, f_mul(context, other, val)));
  33733. }
  33734. /** nurat_rem
  33735. *
  33736. */
  33737. @JRubyMethod(name = "remainder")
  33738. public IRubyObject op_rem(ThreadContext context, IRubyObject other) {
  33739. IRubyObject val = f_truncate(context, f_div(context, this, other));
  33740. return f_sub(context, this, f_mul(context, other, val));
  33741. }
  33742. /** nurat_abs
  33743. *
  33744. */
  33745. @JRubyMethod(name = "abs")
  33746. public IRubyObject op_abs(ThreadContext context) {
  33747. if (!f_negative_p(context, this)) return this;
  33748. return f_negate(context, this);
  33749. }
  33750. /** nurat_floor
  33751. *
  33752. */
  33753. @JRubyMethod(name = "floor")
  33754. public IRubyObject op_floor(ThreadContext context) {
  33755. return f_idiv(context, num, den);
  33756. }
  33757. /** nurat_ceil
  33758. *
  33759. */
  33760. @JRubyMethod(name = "ceil")
  33761. public IRubyObject op_ceil(ThreadContext context) {
  33762. return f_negate(context, f_idiv(context, f_negate(context, num), den));
  33763. }
  33764. /** nurat_truncate
  33765. *
  33766. */
  33767. @JRubyMethod(name = {"truncate", "to_i"})
  33768. public IRubyObject op_truncate(ThreadContext context) {
  33769. if (f_negative_p(context, num)) {
  33770. return f_negate(context, f_idiv(context, f_negate(context, num), den));
  33771. }
  33772. return f_idiv(context, num, den);
  33773. }
  33774. /** nurat_round
  33775. *
  33776. */
  33777. @JRubyMethod(name = "round")
  33778. public IRubyObject op_round(ThreadContext context) {
  33779. IRubyObject two = RubyFixnum.two(context.getRuntime());
  33780. if (f_negative_p(context, num)) {
  33781. IRubyObject tnum = f_negate(context, num);
  33782. tnum = f_add(context, f_mul(context, tnum, two), den);
  33783. IRubyObject tden = f_mul(context, den, two);
  33784. return f_negate(context, f_idiv(context, tnum, tden));
  33785. } else {
  33786. IRubyObject tnum = f_add(context, f_mul(context, num, two), den);
  33787. IRubyObject tden = f_mul(context, den, two);
  33788. return f_idiv(context, tnum, tden);
  33789. }
  33790. }
  33791. /** nurat_to_f
  33792. *
  33793. */
  33794. private static long ML = (long)(Math.log(Double.MAX_VALUE) / Math.log(2.0) - 1);
  33795. @JRubyMethod(name = "to_f")
  33796. public IRubyObject to_f(ThreadContext context) {
  33797. Ruby runtime = context.getRuntime();
  33798. if (f_zero_p(context, num)) return runtime.newFloat(0);
  33799. IRubyObject num = this.num;
  33800. IRubyObject den = this.den;
  33801. boolean minus = false;
  33802. if (f_negative_p(context, num)) {
  33803. num = f_negate(context, num);
  33804. minus = true;
  33805. }
  33806. long nl = i_ilog2(context, num);
  33807. long dl = i_ilog2(context, den);
  33808. long ne = 0;
  33809. if (nl > ML) {
  33810. ne = nl - ML;
  33811. num = f_rshift(context, num, RubyFixnum.newFixnum(runtime, ne));
  33812. }
  33813. long de = 0;
  33814. if (dl > ML) {
  33815. de = dl - ML;
  33816. den = f_rshift(context, den, RubyFixnum.newFixnum(runtime, de));
  33817. }
  33818. long e = ne - de;
  33819. if (e > 1023 || e < -1022) {
  33820. runtime.getWarnings().warn(IRubyWarnings.ID.FLOAT_OUT_OF_RANGE, "out of Float range", getMetaClass());
  33821. return runtime.newFloat(e > 0 ? Double.MAX_VALUE : 0);
  33822. }
  33823. double f = RubyNumeric.num2dbl(num) / RubyNumeric.num2dbl(den);
  33824. if (minus) {
  33825. f = -f;
  33826. f = ldexp(f, e);
  33827. }
  33828. if (Double.isInfinite(f) || Double.isNaN(f)) {
  33829. runtime.getWarnings().warn(IRubyWarnings.ID.FLOAT_OUT_OF_RANGE, "out of Float range", getMetaClass());
  33830. }
  33831. return runtime.newFloat(f);
  33832. }
  33833. /** nurat_to_r
  33834. *
  33835. */
  33836. @JRubyMethod(name = "to_r")
  33837. public IRubyObject to_r(ThreadContext context) {
  33838. return this;
  33839. }
  33840. /** nurat_to_r
  33841. *
  33842. */
  33843. @JRubyMethod(name = "hash")
  33844. public IRubyObject hash(ThreadContext context) {
  33845. return f_xor(context, num, den);
  33846. }
  33847. /** nurat_to_s
  33848. *
  33849. */
  33850. @JRubyMethod(name = "to_s")
  33851. public IRubyObject to_s(ThreadContext context) {
  33852. return RuntimeHelpers.invoke(context, context.getRuntime().getKernel(), "format", context.getRuntime().newString("%d/%d"), num, den);
  33853. }
  33854. /** nurat_inspect
  33855. *
  33856. */
  33857. @JRubyMethod(name = "inspect")
  33858. public IRubyObject inspect(ThreadContext context) {
  33859. return RuntimeHelpers.invoke(context, context.getRuntime().getKernel(), "format", context.getRuntime().newString("(%d/%d)"), num, den);
  33860. }
  33861. /** nurat_marshal_dump
  33862. *
  33863. */
  33864. @JRubyMethod(name = "marshal_dump")
  33865. public IRubyObject marshal_dump(ThreadContext context) {
  33866. return context.getRuntime().newArray(num, den);
  33867. }
  33868. /** nurat_marshal_load
  33869. *
  33870. */
  33871. @JRubyMethod(name = "marshal_load")
  33872. public IRubyObject marshal_load(ThreadContext context, IRubyObject arg) {
  33873. RubyArray a = arg.convertToArray();
  33874. num = a.size() > 0 ? a.eltInternal(0) : context.getRuntime().getNil();
  33875. den = a.size() > 1 ? a.eltInternal(1) : context.getRuntime().getNil();
  33876. if (f_zero_p(context, den)) throw context.getRuntime().newZeroDivisionError();
  33877. return this;
  33878. }
  33879. /** rb_gcd
  33880. *
  33881. */
  33882. @JRubyMethod(name = "gcd")
  33883. public IRubyObject gcd(ThreadContext context, IRubyObject other) {
  33884. intCheck(other);
  33885. return f_gcd(context, this, other);
  33886. }
  33887. /** rb_lcm
  33888. *
  33889. */
  33890. @JRubyMethod(name = "lcm")
  33891. public IRubyObject lcm(ThreadContext context, IRubyObject other) {
  33892. intCheck(other);
  33893. return Numeric.f_lcm(context, this, other);
  33894. }
  33895. /** rb_gcdlcm
  33896. *
  33897. */
  33898. @JRubyMethod(name = "gcdlcm")
  33899. public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
  33900. intCheck(other);
  33901. return context.getRuntime().newArray(f_gcd(context, this, other), Numeric.f_lcm(context, this, other));
  33902. }
  33903. static RubyArray str_to_r_internal(ThreadContext context, IRubyObject recv) {
  33904. RubyString s = recv.callMethod(context, "strip").convertToString();
  33905. ByteList bytes = s.getByteList();
  33906. Ruby runtime = context.getRuntime();
  33907. if (bytes.realSize == 0) return runtime.newArray(runtime.getNil(), recv);
  33908. IRubyObject m = RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.rat_pat).callMethod(context, "match", s);
  33909. if (!m.isNil()) {
  33910. IRubyObject si = m.callMethod(context, "[]", RubyFixnum.one(runtime));
  33911. IRubyObject nu = m.callMethod(context, "[]", RubyFixnum.two(runtime));
  33912. IRubyObject de = m.callMethod(context, "[]", RubyFixnum.three(runtime));
  33913. IRubyObject re = m.callMethod(context, "post_match");
  33914. RubyArray a = nu.callMethod(context, "split", RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.an_e_pat)).convertToArray();
  33915. IRubyObject ifp = a.eltInternal(0);
  33916. IRubyObject exp = a.size() != 2 ? runtime.getNil() : a.eltInternal(1);
  33917. a = ifp.callMethod(context, "split", RubyRegexp.newRegexp(runtime, Numeric.RationalPatterns.a_dot_pat)).convertToArray();
  33918. IRubyObject ip = a.eltInternal(0);
  33919. IRubyObject fp = a.size() != 2 ? runtime.getNil() : a.eltInternal(1);
  33920. IRubyObject v = RubyRational.newRationalCanonicalize(context, f_to_i(context, ip));
  33921. if (!fp.isNil()) {
  33922. bytes = fp.convertToString().getByteList();
  33923. int count = 0;
  33924. byte[]buf = bytes.bytes;
  33925. int i = bytes.begin;
  33926. int end = i + bytes.realSize;
  33927. while (i < end) if (ASCIIEncoding.INSTANCE.isDigit(buf[i++])) count++;
  33928. IRubyObject l = f_expt(context, RubyFixnum.newFixnum(runtime, 10), RubyFixnum.newFixnum(runtime, count));
  33929. v = f_mul(context, v, l);
  33930. v = f_add(context, v, f_to_i(context, fp));
  33931. v = f_div(context, v, l);
  33932. }
  33933. if (!exp.isNil()) {
  33934. v = f_mul(context, v, f_expt(context, RubyFixnum.newFixnum(runtime, 10), f_to_i(context, exp)));
  33935. }
  33936. if (!si.isNil()) {
  33937. bytes = si.convertToString().getByteList();
  33938. if (bytes.length() > 0 && bytes.get(0) == '-') v = f_negate(context, v);
  33939. }
  33940. if (!de.isNil()) {
  33941. v = f_div(context, v, f_to_i(context, de));
  33942. }
  33943. return runtime.newArray(v, re);
  33944. }
  33945. return runtime.newArray(runtime.getNil(), recv);
  33946. }
  33947. private static IRubyObject str_to_r_strict(ThreadContext context, IRubyObject recv) {
  33948. RubyArray a = str_to_r_internal(context, recv);
  33949. if (a.eltInternal(0).isNil() || a.eltInternal(1).convertToString().getByteList().length() > 0) {
  33950. IRubyObject s = recv.callMethod(context, "inspect");
  33951. throw context.getRuntime().newArgumentError("invalid value for Rational: " + s.convertToString());
  33952. }
  33953. return a.eltInternal(0);
  33954. }
  33955. }
  33956. /***** BEGIN LICENSE BLOCK *****
  33957. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  33958. *
  33959. * The contents of this file are subject to the Common Public
  33960. * License Version 1.0 (the "License"); you may not use this file
  33961. * except in compliance with the License. You may obtain a copy of
  33962. * the License at http://www.eclipse.org/legal/cpl-v10.html
  33963. *
  33964. * Software distributed under the License is distributed on an "AS
  33965. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  33966. * implied. See the License for the specific language governing
  33967. * rights and limitations under the License.
  33968. *
  33969. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  33970. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  33971. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  33972. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  33973. * Copyright (C) 2004-2005 Thomas E Enebo <enebo@acm.org>
  33974. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  33975. * Copyright (C) 2005 David Corbin <dcorbin@users.sourceforge.net>
  33976. * Copyright (C) 2006 Nick Sieger <nicksieger@gmail.com>
  33977. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  33978. *
  33979. * Alternatively, the contents of this file may be used under the terms of
  33980. * either of the GNU General Public License Version 2 or later (the "GPL"),
  33981. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  33982. * in which case the provisions of the GPL or the LGPL are applicable instead
  33983. * of those above. If you wish to allow use of your version of this file only
  33984. * under the terms of either the GPL or the LGPL, and not to allow others to
  33985. * use your version of this file under the terms of the CPL, indicate your
  33986. * decision by deleting the provisions above and replace them with the notice
  33987. * and other provisions required by the GPL or the LGPL. If you do not delete
  33988. * the provisions above, a recipient may use your version of this file under
  33989. * the terms of any one of the CPL, the GPL or the LGPL.
  33990. ***** END LICENSE BLOCK *****/
  33991. package org.jruby;
  33992. import java.lang.ref.SoftReference;
  33993. import java.util.concurrent.ConcurrentHashMap;
  33994. import java.util.Iterator;
  33995. import java.util.Map;
  33996. import org.joni.Matcher;
  33997. import org.joni.NameEntry;
  33998. import org.joni.Option;
  33999. import org.joni.Regex;
  34000. import org.joni.Region;
  34001. import org.joni.Syntax;
  34002. import org.joni.WarnCallback;
  34003. import org.joni.encoding.Encoding;
  34004. import org.joni.exception.JOniException;
  34005. import static org.jruby.anno.FrameField.*;
  34006. import org.jruby.anno.JRubyMethod;
  34007. import org.jruby.anno.JRubyClass;
  34008. import org.jruby.common.IRubyWarnings.ID;
  34009. import org.jruby.parser.ReOptions;
  34010. import org.jruby.runtime.Arity;
  34011. import org.jruby.runtime.Block;
  34012. import org.jruby.runtime.ClassIndex;
  34013. import org.jruby.runtime.Frame;
  34014. import org.jruby.runtime.ObjectAllocator;
  34015. import org.jruby.runtime.ThreadContext;
  34016. import org.jruby.runtime.Visibility;
  34017. import org.jruby.runtime.builtin.IRubyObject;
  34018. import org.jruby.runtime.marshal.MarshalStream;
  34019. import org.jruby.runtime.marshal.UnmarshalStream;
  34020. import org.jruby.util.ByteList;
  34021. import org.jruby.util.KCode;
  34022. import org.jruby.util.TypeConverter;
  34023. /**
  34024. *
  34025. */
  34026. @JRubyClass(name="Regexp")
  34027. public class RubyRegexp extends RubyObject implements ReOptions, WarnCallback {
  34028. private KCode kcode;
  34029. private Regex pattern;
  34030. private ByteList str;
  34031. private static final int REGEXP_LITERAL_F = 1 << 11;
  34032. private static final int REGEXP_KCODE_DEFAULT = 1 << 12;
  34033. public void setLiteral() {
  34034. flags |= REGEXP_LITERAL_F;
  34035. }
  34036. public void clearLiteral() {
  34037. flags &= ~REGEXP_LITERAL_F;
  34038. }
  34039. public boolean isLiteral() {
  34040. return (flags & REGEXP_LITERAL_F) != 0;
  34041. }
  34042. public void setKCodeDefault() {
  34043. flags |= REGEXP_KCODE_DEFAULT;
  34044. }
  34045. public void clearKCodeDefault() {
  34046. flags &= ~REGEXP_KCODE_DEFAULT;
  34047. }
  34048. public boolean isKCodeDefault() {
  34049. return (flags & REGEXP_KCODE_DEFAULT) != 0;
  34050. }
  34051. public KCode getKCode() {
  34052. return kcode;
  34053. }
  34054. private static Map<ByteList, Regex> getPatternCache() {
  34055. Map<ByteList, Regex> cache = patternCache.get();
  34056. if (cache == null) {
  34057. cache = new ConcurrentHashMap<ByteList, Regex>(5);
  34058. patternCache = new SoftReference<Map<ByteList, Regex>>(cache);
  34059. }
  34060. return cache;
  34061. }
  34062. static volatile SoftReference<Map<ByteList, Regex>> patternCache = new SoftReference<Map<ByteList, Regex>>(null);
  34063. public static RubyClass createRegexpClass(Ruby runtime) {
  34064. RubyClass regexpClass = runtime.defineClass("Regexp", runtime.getObject(), REGEXP_ALLOCATOR);
  34065. runtime.setRegexp(regexpClass);
  34066. regexpClass.index = ClassIndex.REGEXP;
  34067. regexpClass.kindOf = new RubyModule.KindOf() {
  34068. @Override
  34069. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  34070. return obj instanceof RubyRegexp;
  34071. }
  34072. };
  34073. regexpClass.defineConstant("IGNORECASE", runtime.newFixnum(RE_OPTION_IGNORECASE));
  34074. regexpClass.defineConstant("EXTENDED", runtime.newFixnum(RE_OPTION_EXTENDED));
  34075. regexpClass.defineConstant("MULTILINE", runtime.newFixnum(RE_OPTION_MULTILINE));
  34076. regexpClass.defineAnnotatedMethods(RubyRegexp.class);
  34077. return regexpClass;
  34078. }
  34079. private static ObjectAllocator REGEXP_ALLOCATOR = new ObjectAllocator() {
  34080. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  34081. RubyRegexp instance = new RubyRegexp(runtime, klass);
  34082. return instance;
  34083. }
  34084. };
  34085. /** used by allocator
  34086. *
  34087. */
  34088. private RubyRegexp(Ruby runtime, RubyClass klass) {
  34089. super(runtime, klass);
  34090. }
  34091. /** default constructor
  34092. *
  34093. */
  34094. private RubyRegexp(Ruby runtime) {
  34095. super(runtime, runtime.getRegexp());
  34096. }
  34097. // used only by the compiler/interpreter (will set the literal flag)
  34098. public static RubyRegexp newRegexp(Ruby runtime, String pattern, int options) {
  34099. return newRegexp(runtime, ByteList.create(pattern), options);
  34100. }
  34101. // used only by the compiler/interpreter (will set the literal flag)
  34102. public static RubyRegexp newRegexp(Ruby runtime, ByteList pattern, int options) {
  34103. RubyRegexp regexp = newRegexp(runtime, pattern, options, false);
  34104. regexp.setLiteral();
  34105. return regexp;
  34106. }
  34107. public static RubyRegexp newRegexp(Ruby runtime, ByteList pattern, int options, boolean quote) {
  34108. RubyRegexp regexp = new RubyRegexp(runtime);
  34109. regexp.initialize(pattern, options, quote);
  34110. return regexp;
  34111. }
  34112. // internal usage
  34113. static RubyRegexp newRegexp(Ruby runtime, Regex regex) {
  34114. RubyRegexp regexp = new RubyRegexp(runtime);
  34115. regexp.pattern = regex;
  34116. regexp.str = ByteList.EMPTY_BYTELIST;
  34117. return regexp;
  34118. }
  34119. public void warn(String message) {
  34120. getRuntime().getWarnings().warn(ID.MISCELLANEOUS, message);
  34121. }
  34122. @JRubyMethod(name = "kcode")
  34123. public IRubyObject kcode(ThreadContext context) {
  34124. return (!isKCodeDefault() && kcode != null) ?
  34125. context.getRuntime().newString(kcode.name()) : context.getRuntime().getNil();
  34126. }
  34127. @Override
  34128. public int getNativeTypeIndex() {
  34129. return ClassIndex.REGEXP;
  34130. }
  34131. public Regex getPattern() {
  34132. return pattern;
  34133. }
  34134. private void check() {
  34135. if (pattern == null || str == null) throw getRuntime().newTypeError("uninitialized Regexp");
  34136. }
  34137. @JRubyMethod(name = "hash")
  34138. @Override
  34139. public RubyFixnum hash() {
  34140. check();
  34141. int hashval = (int)pattern.getOptions();
  34142. int len = this.str.realSize;
  34143. int p = this.str.begin;
  34144. while (len-->0) {
  34145. hashval = hashval * 33 + str.bytes[p++];
  34146. }
  34147. hashval = hashval + (hashval>>5);
  34148. return getRuntime().newFixnum(hashval);
  34149. }
  34150. @JRubyMethod(name = {"==", "eql?"}, required = 1)
  34151. @Override
  34152. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  34153. if(this == other) return context.getRuntime().getTrue();
  34154. if(!(other instanceof RubyRegexp)) return context.getRuntime().getFalse();
  34155. RubyRegexp otherRegex = (RubyRegexp)other;
  34156. check();
  34157. otherRegex.check();
  34158. return context.getRuntime().newBoolean(str.equal(otherRegex.str) &&
  34159. kcode == otherRegex.kcode && pattern.getOptions() == otherRegex.pattern.getOptions());
  34160. }
  34161. @JRubyMethod(name = "~", reads = {LASTLINE, BACKREF}, writes = BACKREF)
  34162. public IRubyObject op_match2(ThreadContext context) {
  34163. Ruby runtime = context.getRuntime();
  34164. IRubyObject line = context.getCurrentFrame().getLastLine();
  34165. if(!(line instanceof RubyString)) {
  34166. context.getCurrentFrame().setBackRef(runtime.getNil());
  34167. return runtime.getNil();
  34168. }
  34169. int start = search(context, (RubyString)line, 0, false);
  34170. if(start < 0) {
  34171. return runtime.getNil();
  34172. } else {
  34173. return runtime.newFixnum(start);
  34174. }
  34175. }
  34176. /** rb_reg_eqq
  34177. *
  34178. */
  34179. @JRubyMethod(name = "===", required = 1, writes = BACKREF)
  34180. public IRubyObject eqq(ThreadContext context, IRubyObject str) {
  34181. Ruby runtime = context.getRuntime();
  34182. if(!(str instanceof RubyString)) str = str.checkStringType();
  34183. if (str.isNil()) {
  34184. context.getCurrentFrame().setBackRef(runtime.getNil());
  34185. return runtime.getFalse();
  34186. }
  34187. int start = search(context, (RubyString)str, 0, false);
  34188. return (start < 0) ? runtime.getFalse() : runtime.getTrue();
  34189. }
  34190. private static final int REGEX_QUOTED = 1;
  34191. private void initialize(ByteList bytes, int options, boolean quote) {
  34192. if (!isTaint() && getRuntime().getSafeLevel() >= 4) throw getRuntime().newSecurityError("Insecure: can't modify regexp");
  34193. checkFrozen();
  34194. if (isLiteral()) throw getRuntime().newSecurityError("can't modify literal regexp");
  34195. setKCode(options);
  34196. Map<ByteList, Regex> cache = getPatternCache();
  34197. Regex pat = cache.get(bytes);
  34198. if (pat != null &&
  34199. pat.getEncoding() == kcode.getEncoding() &&
  34200. pat.getOptions() == (options & 0xf) &&
  34201. ((pat.getUserOptions() & REGEX_QUOTED) != 0) == quote) { // cache hit
  34202. pattern = pat;
  34203. } else {
  34204. if (quote) bytes = quote(bytes, getRuntime().getKCode());
  34205. makeRegexp(bytes, bytes.begin, bytes.realSize, options & 0xf, kcode.getEncoding());
  34206. if (quote) pattern.setUserOptions(REGEX_QUOTED);
  34207. cache.put(bytes, pattern);
  34208. }
  34209. str = bytes;
  34210. }
  34211. private void makeRegexp(ByteList bytes, int start, int len, int flags, Encoding enc) {
  34212. try {
  34213. pattern = new Regex(bytes.bytes, start, start + len, flags, enc, Syntax.DEFAULT, this);
  34214. } catch(Exception e) {
  34215. rb_reg_raise(bytes.bytes, start, len, e.getMessage(), flags);
  34216. }
  34217. }
  34218. private final void rb_reg_raise(byte[] s, int start, int len, String err,int flags) {
  34219. throw getRuntime().newRegexpError(err + ": " + rb_reg_desc(s,start, len,flags));
  34220. }
  34221. private final StringBuilder rb_reg_desc(byte[] s, int start, int len, int flags) {
  34222. StringBuilder sb = new StringBuilder("/");
  34223. rb_reg_expr_str(sb, s, start, len);
  34224. sb.append("/");
  34225. if((flags & ReOptions.RE_OPTION_MULTILINE) != 0) sb.append("m");
  34226. if((flags & ReOptions.RE_OPTION_IGNORECASE) != 0) sb.append("i");
  34227. if((flags & ReOptions.RE_OPTION_EXTENDED) != 0) sb.append("x");
  34228. if (kcode != null && !isKCodeDefault()) {
  34229. sb.append(kcode.name().charAt(0));
  34230. }
  34231. return sb;
  34232. }
  34233. private final void rb_reg_expr_str(StringBuilder sb, byte[] s, int start, int len) {
  34234. int p,pend;
  34235. boolean need_escape = false;
  34236. p = start;
  34237. pend = start+len;
  34238. Encoding enc = kcode.getEncoding();
  34239. while(p<pend) {
  34240. if(s[p] == '/' || (!(' ' == s[p] || (!Character.isWhitespace(s[p]) &&
  34241. !Character.isISOControl(s[p]))) &&
  34242. enc.length(s[p])==1)) {
  34243. need_escape = true;
  34244. break;
  34245. }
  34246. p += enc.length(s[p]);
  34247. }
  34248. if(!need_escape) {
  34249. sb.append(new ByteList(s,start,len,false).toString());
  34250. } else {
  34251. p = 0;
  34252. while(p < pend) {
  34253. if(s[p] == '\\') {
  34254. int n = enc.length(s[p+1]) + 1;
  34255. sb.append(new ByteList(s,p,n,false).toString());
  34256. p += n;
  34257. continue;
  34258. } else if(s[p] == '/') {
  34259. sb.append("\\/");
  34260. } else if(enc.length(s[p])!=1) {
  34261. sb.append(new ByteList(s,p,enc.length(s[p]),false).toString());
  34262. p += enc.length(s[p]);
  34263. continue;
  34264. } else if((' ' == s[p] || (!Character.isWhitespace(s[p]) &&
  34265. !Character.isISOControl(s[p])))) {
  34266. sb.append((char)(s[p]&0xFF));
  34267. } else if(!Character.isWhitespace((char)(s[p]&0xFF))) {
  34268. sb.append('\\');
  34269. sb.append(Integer.toString((int)(s[p]&0377),8));
  34270. } else {
  34271. sb.append((char)(s[p]&0xFF));
  34272. }
  34273. p++;
  34274. }
  34275. }
  34276. }
  34277. /** rb_reg_init_copy
  34278. */
  34279. @JRubyMethod(name = "initialize_copy", required = 1)
  34280. @Override
  34281. public IRubyObject initialize_copy(IRubyObject re) {
  34282. if(this == re) return this;
  34283. checkFrozen();
  34284. if (getMetaClass().getRealClass() != re.getMetaClass().getRealClass()) {
  34285. throw getRuntime().newTypeError("wrong argument type");
  34286. }
  34287. RubyRegexp regexp = (RubyRegexp)re;
  34288. regexp.check();
  34289. initialize(regexp.str, regexp.getOptions(), false);
  34290. return this;
  34291. }
  34292. /** rb_set_kcode
  34293. */
  34294. private int getKcode() {
  34295. if(kcode == KCode.NONE) {
  34296. return 16;
  34297. } else if(kcode == KCode.EUC) {
  34298. return 32;
  34299. } else if(kcode == KCode.SJIS) {
  34300. return 48;
  34301. } else if(kcode == KCode.UTF8) {
  34302. return 64;
  34303. }
  34304. return 0;
  34305. }
  34306. /**
  34307. */
  34308. private void setKCode(int options) {
  34309. clearKCodeDefault();
  34310. switch(options & ~0xf) {
  34311. case 0:
  34312. default:
  34313. setKCodeDefault();
  34314. kcode = getRuntime().getKCode();
  34315. break;
  34316. case 16:
  34317. kcode = KCode.NONE;
  34318. break;
  34319. case 32:
  34320. kcode = KCode.EUC;
  34321. break;
  34322. case 48:
  34323. kcode = KCode.SJIS;
  34324. break;
  34325. case 64:
  34326. kcode = KCode.UTF8;
  34327. break;
  34328. }
  34329. }
  34330. /** rb_reg_options
  34331. */
  34332. private int getOptions() {
  34333. check();
  34334. int options = (int)(pattern.getOptions() & (RE_OPTION_IGNORECASE|RE_OPTION_MULTILINE|RE_OPTION_EXTENDED));
  34335. if(!isKCodeDefault()) {
  34336. options |= getKcode();
  34337. }
  34338. return options;
  34339. }
  34340. /** rb_reg_initialize_m
  34341. */
  34342. @JRubyMethod(name = "initialize", optional = 3, visibility = Visibility.PRIVATE)
  34343. public IRubyObject initialize_m(IRubyObject[] args) {
  34344. ByteList bytes;
  34345. int regexFlags = 0;
  34346. if(args[0] instanceof RubyRegexp) {
  34347. if(args.length > 1) {
  34348. getRuntime().getWarnings().warn(ID.REGEXP_IGNORED_FLAGS, "flags" + (args.length == 3 ? " and encoding" : "") + " ignored");
  34349. }
  34350. RubyRegexp regexp = (RubyRegexp)args[0];
  34351. regexp.check();
  34352. regexFlags = (int)regexp.pattern.getOptions() & 0xF;
  34353. if (!regexp.isKCodeDefault() && regexp.kcode != null && regexp.kcode != KCode.NIL) {
  34354. if (regexp.kcode == KCode.NONE) {
  34355. regexFlags |= 16;
  34356. } else if (regexp.kcode == KCode.EUC) {
  34357. regexFlags |= 32;
  34358. } else if (regexp.kcode == KCode.SJIS) {
  34359. regexFlags |= 48;
  34360. } else if (regexp.kcode == KCode.UTF8) {
  34361. regexFlags |= 64;
  34362. }
  34363. }
  34364. bytes = regexp.str;
  34365. } else {
  34366. if (args.length >= 2) {
  34367. if (args[1] instanceof RubyFixnum) {
  34368. regexFlags = RubyNumeric.fix2int(args[1]);
  34369. } else if (args[1].isTrue()) {
  34370. regexFlags = RE_OPTION_IGNORECASE;
  34371. }
  34372. }
  34373. if (args.length == 3 && !args[2].isNil()) {
  34374. ByteList kcodeBytes = args[2].convertToString().getByteList();
  34375. char first = kcodeBytes.length() > 0 ? kcodeBytes.charAt(0) : 0;
  34376. regexFlags &= ~0x70;
  34377. switch (first) {
  34378. case 'n': case 'N':
  34379. regexFlags |= 16;
  34380. break;
  34381. case 'e': case 'E':
  34382. regexFlags |= 32;
  34383. break;
  34384. case 's': case 'S':
  34385. regexFlags |= 48;
  34386. break;
  34387. case 'u': case 'U':
  34388. regexFlags |= 64;
  34389. break;
  34390. default:
  34391. break;
  34392. }
  34393. }
  34394. bytes = args[0].convertToString().getByteList();
  34395. }
  34396. initialize(bytes, regexFlags, false);
  34397. return this;
  34398. }
  34399. @JRubyMethod(name = {"new", "compile"}, required = 1, optional = 2, meta = true)
  34400. public static RubyRegexp newInstance(IRubyObject recv, IRubyObject[] args) {
  34401. RubyClass klass = (RubyClass)recv;
  34402. RubyRegexp re = (RubyRegexp) klass.allocate();
  34403. re.callInit(args, Block.NULL_BLOCK);
  34404. return re;
  34405. }
  34406. @JRubyMethod(name = "options")
  34407. public IRubyObject options() {
  34408. return getRuntime().newFixnum(getOptions());
  34409. }
  34410. /** rb_reg_search
  34411. */
  34412. public int search(ThreadContext context, RubyString str, int pos, boolean reverse) {
  34413. Ruby runtime = context.getRuntime();
  34414. Frame frame = context.getCurrentRubyFrame();
  34415. ByteList value = str.getByteList();
  34416. if (pos > value.realSize || pos < 0) {
  34417. frame.setBackRef(runtime.getNil());
  34418. return -1;
  34419. }
  34420. return performSearch(reverse, pos, value, frame, runtime, context, str);
  34421. }
  34422. private int performSearch(boolean reverse, int pos, ByteList value, Frame frame, Ruby runtime, ThreadContext context, RubyString str) {
  34423. check();
  34424. int realSize = value.realSize;
  34425. int begin = value.begin;
  34426. int range = reverse ? -pos : realSize - pos;
  34427. Matcher matcher = pattern.matcher(value.bytes, begin, begin + realSize);
  34428. int result = matcher.search(begin + pos, begin + pos + range, Option.NONE);
  34429. if (result < 0) {
  34430. frame.setBackRef(runtime.getNil());
  34431. return result;
  34432. }
  34433. updateBackRef(context, str, frame, matcher);
  34434. return result;
  34435. }
  34436. final RubyMatchData updateBackRef(ThreadContext context, RubyString str, Frame frame, Matcher matcher) {
  34437. Ruby runtime = context.getRuntime();
  34438. IRubyObject backref = frame.getBackRef();
  34439. final RubyMatchData match;
  34440. if (backref == null || backref.isNil() || ((RubyMatchData)backref).used()) {
  34441. match = new RubyMatchData(runtime);
  34442. } else {
  34443. match = (RubyMatchData)backref;
  34444. match.setTaint(runtime.getSafeLevel() >= 3);
  34445. }
  34446. match.regs = matcher.getRegion(); // lazy, null when no groups defined
  34447. match.begin = matcher.getBegin();
  34448. match.end = matcher.getEnd();
  34449. match.str = (RubyString)str.strDup(runtime).freeze(context);
  34450. match.pattern = pattern;
  34451. frame.setBackRef(match);
  34452. match.infectBy(this);
  34453. match.infectBy(str);
  34454. return match;
  34455. }
  34456. /** rb_reg_match
  34457. *
  34458. */
  34459. @JRubyMethod(name = "=~", required = 1, reads = BACKREF, writes = BACKREF)
  34460. @Override
  34461. public IRubyObject op_match(ThreadContext context, IRubyObject str) {
  34462. int start;
  34463. if(str.isNil()) {
  34464. context.getCurrentFrame().setBackRef(context.getRuntime().getNil());
  34465. return str;
  34466. }
  34467. start = search(context, str.convertToString(), 0, false);
  34468. if (start < 0) return context.getRuntime().getNil();
  34469. return RubyFixnum.newFixnum(context.getRuntime(), start);
  34470. }
  34471. /** rb_reg_match_m
  34472. *
  34473. */
  34474. @JRubyMethod(name = "match", required = 1, reads = BACKREF)
  34475. public IRubyObject match_m(ThreadContext context, IRubyObject str) {
  34476. if (op_match(context, str).isNil()) return context.getRuntime().getNil();
  34477. IRubyObject result = context.getCurrentFrame().getBackRef();
  34478. if (result instanceof RubyMatchData) {
  34479. ((RubyMatchData)result).use();
  34480. }
  34481. return result;
  34482. }
  34483. public RubyString regsub(RubyString str, RubyString src, Matcher matcher) {
  34484. Region regs = matcher.getRegion();
  34485. int mbeg = matcher.getBegin();
  34486. int mend = matcher.getEnd();
  34487. int p,s,e;
  34488. p = s = 0;
  34489. int no = -1;
  34490. ByteList bs = str.getByteList();
  34491. ByteList srcbs = src.getByteList();
  34492. e = bs.length();
  34493. RubyString val = null;
  34494. Encoding enc = kcode.getEncoding();
  34495. int beg, end;
  34496. while(s < e) {
  34497. int ss = s;
  34498. char c = bs.charAt(s++);
  34499. if(enc.length((byte)c) != 1) {
  34500. s += enc.length((byte)c) - 1;
  34501. continue;
  34502. }
  34503. if (c != '\\' || s == e) continue;
  34504. if (val == null) val = RubyString.newString(getRuntime(), new ByteList(ss - p));
  34505. val.cat(bs.bytes, bs.begin + p, ss - p);
  34506. c = bs.charAt(s++);
  34507. p = s;
  34508. switch(c) {
  34509. case '0': case '1': case '2': case '3': case '4':
  34510. case '5': case '6': case '7': case '8': case '9':
  34511. no = c - '0';
  34512. break;
  34513. case '&':
  34514. no = 0;
  34515. break;
  34516. case '`':
  34517. beg = regs == null ? mbeg : regs.beg[0];
  34518. val.cat(srcbs.bytes, srcbs.begin, beg);
  34519. continue;
  34520. case '\'':
  34521. end = regs == null ? mend : regs.end[0];
  34522. val.cat(srcbs.bytes, srcbs.begin + end, src.getByteList().realSize - end);
  34523. continue;
  34524. case '+':
  34525. if (regs == null) {
  34526. if (mbeg == -1) {
  34527. no = 0;
  34528. continue;
  34529. }
  34530. } else {
  34531. no = regs.numRegs-1;
  34532. while(regs.beg[no] == -1 && no > 0) no--;
  34533. if (no == 0) continue;
  34534. }
  34535. break;
  34536. case '\\':
  34537. val.cat(bs.bytes, s - 1, 1);
  34538. continue;
  34539. default:
  34540. val.cat(bs.bytes, s - 2, 2);
  34541. continue;
  34542. }
  34543. if (regs != null) {
  34544. if (no >= 0) {
  34545. if (no >= regs.numRegs || regs.beg[no] == -1) continue;
  34546. val.cat(srcbs.bytes, srcbs.begin + regs.beg[no], regs.end[no] - regs.beg[no]);
  34547. }
  34548. } else {
  34549. if (no != 0 || mbeg == -1) continue;
  34550. val.cat(srcbs.bytes, srcbs.begin + mbeg, mend - mbeg);
  34551. }
  34552. }
  34553. if(p < e) {
  34554. if(val == null) {
  34555. val = RubyString.newString(getRuntime(), bs.makeShared(p, e-p));
  34556. } else {
  34557. val.cat(bs.bytes, bs.begin + p, e - p);
  34558. }
  34559. }
  34560. if (val == null) return str;
  34561. return val;
  34562. }
  34563. final int adjustStartPos(RubyString str, int pos, boolean reverse) {
  34564. check();
  34565. ByteList value = str.getByteList();
  34566. return pattern.adjustStartPosition(value.bytes, value.begin, value.realSize, pos, reverse);
  34567. }
  34568. @JRubyMethod(name = "casefold?")
  34569. public IRubyObject casefold_p(ThreadContext context) {
  34570. check();
  34571. return context.getRuntime().newBoolean((pattern.getOptions() & RE_OPTION_IGNORECASE) != 0);
  34572. }
  34573. /** rb_reg_source
  34574. *
  34575. */
  34576. @JRubyMethod(name = "source")
  34577. public IRubyObject source() {
  34578. Ruby runtime = getRuntime();
  34579. check();
  34580. RubyString str = RubyString.newStringShared(runtime, this.str);
  34581. if(isTaint()) {
  34582. str.taint(runtime.getCurrentContext());
  34583. }
  34584. return str;
  34585. }
  34586. final int length() {
  34587. return str.realSize;
  34588. }
  34589. /** rb_reg_inspect
  34590. *
  34591. */
  34592. @JRubyMethod(name = "inspect")
  34593. @Override
  34594. public IRubyObject inspect() {
  34595. check();
  34596. return getRuntime().newString(ByteList.create(rb_reg_desc(str.bytes, str.begin, str.realSize, pattern.getOptions()).toString()));
  34597. }
  34598. private final static int EMBEDDABLE = RE_OPTION_MULTILINE|RE_OPTION_IGNORECASE|RE_OPTION_EXTENDED;
  34599. @JRubyMethod(name = "to_s")
  34600. @Override
  34601. public IRubyObject to_s() {
  34602. RubyString ss = getRuntime().newString("(?");
  34603. check();
  34604. int options = pattern.getOptions();
  34605. int ptr = str.begin;
  34606. int len = str.realSize;
  34607. byte[] bytes = str.bytes;
  34608. again: do {
  34609. if (len >= 4 && bytes[ptr] == '(' && bytes[ptr + 1] == '?') {
  34610. boolean err = true;
  34611. ptr += 2;
  34612. if ((len -= 2) > 0) {
  34613. do {
  34614. if (bytes[ptr] == 'm') {
  34615. options |= RE_OPTION_MULTILINE;
  34616. } else if (bytes[ptr] == 'i') {
  34617. options |= RE_OPTION_IGNORECASE;
  34618. } else if (bytes[ptr] == 'x') {
  34619. options |= RE_OPTION_EXTENDED;
  34620. } else {
  34621. break;
  34622. }
  34623. ptr++;
  34624. } while(--len > 0);
  34625. }
  34626. if (len > 1 && bytes[ptr] == '-') {
  34627. ++ptr;
  34628. --len;
  34629. do {
  34630. if (bytes[ptr] == 'm') {
  34631. options &= ~RE_OPTION_MULTILINE;
  34632. } else if (bytes[ptr] == 'i') {
  34633. options &= ~RE_OPTION_IGNORECASE;
  34634. } else if (bytes[ptr] == 'x') {
  34635. options &= ~RE_OPTION_EXTENDED;
  34636. } else {
  34637. break;
  34638. }
  34639. ptr++;
  34640. } while(--len > 0);
  34641. }
  34642. if (bytes[ptr] == ')') {
  34643. --len;
  34644. ++ptr;
  34645. continue again;
  34646. }
  34647. if (bytes[ptr] == ':' && bytes[ptr + len - 1] == ')') {
  34648. try {
  34649. new Regex(bytes, ++ptr, ptr + (len-=2) ,Option.DEFAULT, kcode.getEncoding(), Syntax.DEFAULT);
  34650. err = false;
  34651. } catch (JOniException e) {
  34652. err = true;
  34653. }
  34654. }
  34655. if (err) {
  34656. options = (int)pattern.getOptions();
  34657. ptr = str.begin;
  34658. len = str.realSize;
  34659. }
  34660. }
  34661. if ((options & RE_OPTION_MULTILINE) !=0 ) ss.cat((byte)'m');
  34662. if ((options & RE_OPTION_IGNORECASE) !=0 ) ss.cat((byte)'i');
  34663. if ((options & RE_OPTION_EXTENDED) !=0 ) ss.cat((byte)'x');
  34664. if ((options & EMBEDDABLE) != EMBEDDABLE) {
  34665. ss.cat((byte)'-');
  34666. if ((options & RE_OPTION_MULTILINE) == 0) ss.cat((byte)'m');
  34667. if ((options & RE_OPTION_IGNORECASE) == 0) ss.cat((byte)'i');
  34668. if ((options & RE_OPTION_EXTENDED) == 0) ss.cat((byte)'x');
  34669. }
  34670. ss.cat((byte)':');
  34671. rb_reg_expr_str(ss, ptr, len);
  34672. ss.cat((byte)')');
  34673. ss.infectBy(this);
  34674. return ss;
  34675. } while(true);
  34676. }
  34677. private final void rb_reg_expr_str(RubyString ss, int s, int len) {
  34678. int p = s;
  34679. int pend = p + len;
  34680. boolean need_escape = false;
  34681. Encoding enc = kcode.getEncoding();
  34682. while (p < pend) {
  34683. if (str.bytes[p] == '/' || (!enc.isPrint(str.bytes[p] & 0xff) && enc.length(str.bytes[p]) == 1)) {
  34684. need_escape = true;
  34685. break;
  34686. }
  34687. p += enc.length(str.bytes[p]);
  34688. }
  34689. if (!need_escape) {
  34690. ss.cat(str.bytes, s, len);
  34691. } else {
  34692. p = s;
  34693. while (p<pend) {
  34694. if (str.bytes[p] == '\\') {
  34695. int n = enc.length(str.bytes[p+1]) + 1;
  34696. ss.cat(str.bytes, p, n);
  34697. p += n;
  34698. continue;
  34699. } else if (str.bytes[p] == '/') {
  34700. ss.cat((byte)'\\');
  34701. ss.cat(str.bytes, p, 1);
  34702. } else if (enc.length(str.bytes[p]) != 1) {
  34703. ss.cat(str.bytes, p, enc.length(str.bytes[p]));
  34704. p += enc.length(str.bytes[p]);
  34705. continue;
  34706. } else if (enc.isPrint(str.bytes[p] & 0xff)) {
  34707. ss.cat(str.bytes,p,1);
  34708. } else if (!enc.isSpace(str.bytes[p] & 0xff)) {
  34709. ss.cat(ByteList.create(Integer.toString(str.bytes[p] & 0377, 8)));
  34710. } else {
  34711. ss.cat(str.bytes, p, 1);
  34712. }
  34713. p++;
  34714. }
  34715. }
  34716. }
  34717. /** rb_reg_s_quote
  34718. *
  34719. */
  34720. @JRubyMethod(name = {"quote", "escape"}, required = 1, optional = 1, meta = true)
  34721. public static RubyString quote(IRubyObject recv, IRubyObject[] args) {
  34722. IRubyObject kcode = args.length == 2 ? args[1] : null;
  34723. IRubyObject str = args[0];
  34724. KCode code = recv.getRuntime().getKCode();
  34725. if (kcode != null && !kcode.isNil()) {
  34726. code = KCode.create(recv.getRuntime(), kcode.toString());
  34727. }
  34728. RubyString src = str.convertToString();
  34729. RubyString dst = RubyString.newString(recv.getRuntime(), quote(src.getByteList(), code));
  34730. dst.infectBy(src);
  34731. return dst;
  34732. }
  34733. /** rb_reg_quote
  34734. *
  34735. */
  34736. public static ByteList quote(ByteList str, KCode kcode) {
  34737. ByteList bs = str;
  34738. int tix = 0;
  34739. int s = bs.begin;
  34740. char c;
  34741. int send = s+bs.length();
  34742. Encoding enc = kcode.getEncoding();
  34743. meta_found: do {
  34744. for(; s<send; s++) {
  34745. c = (char)(bs.bytes[s]&0xFF);
  34746. if(enc.length((byte)c) != 1) {
  34747. int n = enc.length((byte)c);
  34748. while(n-- > 0 && s < send) {
  34749. s++;
  34750. }
  34751. s--;
  34752. continue;
  34753. }
  34754. switch (c) {
  34755. case '[': case ']': case '{': case '}':
  34756. case '(': case ')': case '|': case '-':
  34757. case '*': case '.': case '\\':
  34758. case '?': case '+': case '^': case '$':
  34759. case ' ': case '#':
  34760. case '\t': case '\f': case '\n': case '\r':
  34761. break meta_found;
  34762. }
  34763. }
  34764. return bs;
  34765. } while(false);
  34766. ByteList b1 = new ByteList(send*2);
  34767. System.arraycopy(bs.bytes,bs.begin,b1.bytes,b1.begin,s-bs.begin);
  34768. tix += (s-bs.begin);
  34769. for(; s<send; s++) {
  34770. c = (char)(bs.bytes[s]&0xFF);
  34771. if(enc.length((byte)c) != 1) {
  34772. int n = enc.length((byte)c);
  34773. while(n-- > 0 && s < send) {
  34774. b1.bytes[tix++] = bs.bytes[s++];
  34775. }
  34776. s--;
  34777. continue;
  34778. }
  34779. switch(c) {
  34780. case '[': case ']': case '{': case '}':
  34781. case '(': case ')': case '|': case '-':
  34782. case '*': case '.': case '\\':
  34783. case '?': case '+': case '^': case '$':
  34784. case '#':
  34785. b1.bytes[tix++] = '\\';
  34786. break;
  34787. case ' ':
  34788. b1.bytes[tix++] = '\\';
  34789. b1.bytes[tix++] = ' ';
  34790. continue;
  34791. case '\t':
  34792. b1.bytes[tix++] = '\\';
  34793. b1.bytes[tix++] = 't';
  34794. continue;
  34795. case '\n':
  34796. b1.bytes[tix++] = '\\';
  34797. b1.bytes[tix++] = 'n';
  34798. continue;
  34799. case '\r':
  34800. b1.bytes[tix++] = '\\';
  34801. b1.bytes[tix++] = 'r';
  34802. continue;
  34803. case '\f':
  34804. b1.bytes[tix++] = '\\';
  34805. b1.bytes[tix++] = 'f';
  34806. continue;
  34807. }
  34808. b1.bytes[tix++] = (byte)c;
  34809. }
  34810. b1.realSize = tix;
  34811. return b1;
  34812. }
  34813. /** rb_reg_nth_match
  34814. *
  34815. */
  34816. public static IRubyObject nth_match(int nth, IRubyObject match) {
  34817. if (match.isNil()) return match;
  34818. RubyMatchData m = (RubyMatchData)match;
  34819. int start, end;
  34820. if (m.regs == null) {
  34821. if (nth >= 1) return match.getRuntime().getNil();
  34822. if (nth < 0 && ++nth <= 0) return match.getRuntime().getNil();
  34823. start = m.begin;
  34824. end = m.end;
  34825. } else {
  34826. if (nth >= m.regs.numRegs) return match.getRuntime().getNil();
  34827. if (nth < 0 && (nth+=m.regs.numRegs) <= 0) return match.getRuntime().getNil();
  34828. start = m.regs.beg[nth];
  34829. end = m.regs.end[nth];
  34830. }
  34831. if (start == -1) return match.getRuntime().getNil();
  34832. RubyString str = m.str.makeShared(match.getRuntime(), start, end - start);
  34833. str.infectBy(match);
  34834. return str;
  34835. }
  34836. /** rb_reg_last_match
  34837. *
  34838. */
  34839. public static IRubyObject last_match(IRubyObject match) {
  34840. return nth_match(0, match);
  34841. }
  34842. /**
  34843. * Variable arity version for compatibility. Not bound to a Ruby method.
  34844. * @deprecated Use the versions with zero, one, or two args.
  34845. */
  34846. public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  34847. switch (args.length) {
  34848. case 0:
  34849. return last_match_s(context, recv);
  34850. case 1:
  34851. return last_match_s(context, recv, args[0]);
  34852. default:
  34853. Arity.raiseArgumentError(recv.getRuntime(), args.length, 0, 1);
  34854. return null; // not reached
  34855. }
  34856. }
  34857. /** rb_reg_s_last_match / match_getter
  34858. *
  34859. */
  34860. @JRubyMethod(name = "last_match", meta = true, reads = BACKREF)
  34861. public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv) {
  34862. IRubyObject match = context.getCurrentFrame().getBackRef();
  34863. if (match instanceof RubyMatchData) ((RubyMatchData)match).use();
  34864. return match;
  34865. }
  34866. /** rb_reg_s_last_match
  34867. *
  34868. */
  34869. @JRubyMethod(name = "last_match", meta = true, reads = BACKREF)
  34870. public static IRubyObject last_match_s(ThreadContext context, IRubyObject recv, IRubyObject nth) {
  34871. IRubyObject match = context.getCurrentFrame().getBackRef();
  34872. if (match.isNil()) return match;
  34873. return nth_match(((RubyMatchData)match).backrefNumber(nth), match);
  34874. }
  34875. /** rb_reg_match_pre
  34876. *
  34877. */
  34878. public static IRubyObject match_pre(IRubyObject match) {
  34879. if (match.isNil()) return match;
  34880. RubyMatchData m = (RubyMatchData)match;
  34881. int beg = m.regs == null ? m.begin : m.regs.beg[0];
  34882. if(beg == -1) match.getRuntime().getNil();
  34883. RubyString str = m.str.makeShared(match.getRuntime(), 0, beg);
  34884. str.infectBy(match);
  34885. return str;
  34886. }
  34887. /** rb_reg_match_post
  34888. *
  34889. */
  34890. public static IRubyObject match_post(IRubyObject match) {
  34891. if (match.isNil()) return match;
  34892. RubyMatchData m = (RubyMatchData)match;
  34893. int end;
  34894. if (m.regs == null) {
  34895. if (m.begin == -1) return match.getRuntime().getNil();
  34896. end = m.end;
  34897. } else {
  34898. if (m.regs.beg[0] == -1) return match.getRuntime().getNil();
  34899. end = m.regs.end[0];
  34900. }
  34901. RubyString str = m.str.makeShared(match.getRuntime(), end, m.str.getByteList().realSize - end);
  34902. str.infectBy(match);
  34903. return str;
  34904. }
  34905. /** rb_reg_match_last
  34906. *
  34907. */
  34908. public static IRubyObject match_last(IRubyObject match) {
  34909. if (match.isNil()) return match;
  34910. RubyMatchData m = (RubyMatchData)match;
  34911. if (m.regs == null || m.regs.beg[0] == -1) return match.getRuntime().getNil();
  34912. int i;
  34913. for (i=m.regs.numRegs-1; m.regs.beg[i]==-1 && i>0; i--);
  34914. if (i == 0) return match.getRuntime().getNil();
  34915. return nth_match(i,match);
  34916. }
  34917. /** rb_reg_s_union
  34918. *
  34919. */
  34920. @JRubyMethod(name = "union", rest = true, meta = true)
  34921. public static IRubyObject union(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
  34922. if (args.length == 0) {
  34923. return newRegexp(recv.getRuntime(), ByteList.create("(?!)"), 0, false);
  34924. } else if (args.length == 1) {
  34925. IRubyObject v = TypeConverter.convertToTypeWithCheck(args[0], recv.getRuntime().getRegexp(), 0, "to_regexp");
  34926. if(!v.isNil()) {
  34927. return v;
  34928. } else {
  34929. // newInstance here
  34930. return newRegexp(recv.getRuntime(), quote(recv,args).getByteList(), 0, false);
  34931. }
  34932. } else {
  34933. KCode kcode = null;
  34934. IRubyObject kcode_re = recv.getRuntime().getNil();
  34935. RubyString source = recv.getRuntime().newString();
  34936. IRubyObject[] _args = new IRubyObject[3];
  34937. for (int i = 0; i < args.length; i++) {
  34938. if (0 < i) {
  34939. source.cat((byte)'|');
  34940. }
  34941. IRubyObject v = TypeConverter.convertToTypeWithCheck(args[i], recv.getRuntime().getRegexp(), 0, "to_regexp");
  34942. if (!v.isNil()) {
  34943. if (!((RubyRegexp)v).isKCodeDefault()) {
  34944. if (kcode == null) {
  34945. kcode_re = v;
  34946. kcode = ((RubyRegexp)v).kcode;
  34947. } else if (((RubyRegexp)v).kcode != kcode) {
  34948. IRubyObject str1 = kcode_re.inspect();
  34949. IRubyObject str2 = v.inspect();
  34950. throw recv.getRuntime().newArgumentError("mixed kcode " + str1 + " and " + str2);
  34951. }
  34952. }
  34953. v = ((RubyRegexp)v).to_s();
  34954. } else {
  34955. v = quote(recv, new IRubyObject[]{args[i]});
  34956. }
  34957. source.append(v);
  34958. }
  34959. _args[0] = source;
  34960. _args[1] = recv.getRuntime().getNil();
  34961. if (kcode == null) {
  34962. _args[2] = recv.getRuntime().getNil();
  34963. } else if (kcode == KCode.NONE) {
  34964. _args[2] = recv.getRuntime().newString("n");
  34965. } else if (kcode == KCode.EUC) {
  34966. _args[2] = recv.getRuntime().newString("e");
  34967. } else if (kcode == KCode.SJIS) {
  34968. _args[2] = recv.getRuntime().newString("s");
  34969. } else if (kcode == KCode.UTF8) {
  34970. _args[2] = recv.getRuntime().newString("u");
  34971. }
  34972. return recv.callMethod(context, "new", _args);
  34973. }
  34974. }
  34975. /** rb_reg_names
  34976. *
  34977. */
  34978. @JRubyMethod(name = "names", compat = CompatVersion.RUBY1_9)
  34979. public IRubyObject names() {
  34980. if (pattern.numberOfNames() == 0) return getRuntime().newEmptyArray();
  34981. RubyArray ary = getRuntime().newArray(pattern.numberOfNames());
  34982. for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
  34983. NameEntry e = i.next();
  34984. ary.append(RubyString.newStringShared(getRuntime(), e.name, e.nameP, e.nameEnd - e.nameP));
  34985. }
  34986. return ary;
  34987. }
  34988. /** rb_reg_named_captures
  34989. *
  34990. */
  34991. @JRubyMethod(name = "named_captures", compat = CompatVersion.RUBY1_9)
  34992. public IRubyObject named_captures(ThreadContext context) {
  34993. RubyHash hash = RubyHash.newHash(getRuntime());
  34994. if (pattern.numberOfNames() == 0) return hash;
  34995. for (Iterator<NameEntry> i = pattern.namedBackrefIterator(); i.hasNext();) {
  34996. NameEntry e = i.next();
  34997. int[]backrefs = e.getBackRefs();
  34998. RubyArray ary = getRuntime().newArray(backrefs.length);
  34999. for (int backref : backrefs) ary.append(RubyFixnum.newFixnum(getRuntime(), backref));
  35000. hash.fastASet(RubyString.newStringShared(getRuntime(), e.name, e.nameP, e.nameEnd - e.nameP).freeze(context), ary);
  35001. }
  35002. return hash;
  35003. }
  35004. public static RubyRegexp unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  35005. RubyRegexp result = newRegexp(input.getRuntime(), input.unmarshalString(), input.unmarshalInt(), false);
  35006. input.registerLinkTarget(result);
  35007. return result;
  35008. }
  35009. public static void marshalTo(RubyRegexp regexp, MarshalStream output) throws java.io.IOException {
  35010. output.registerLinkTarget(regexp);
  35011. output.writeString(new String(regexp.str.bytes,regexp.str.begin,regexp.str.realSize));
  35012. output.writeInt(regexp.pattern.getOptions() & EMBEDDABLE);
  35013. }
  35014. }
  35015. package org.jruby;
  35016. import org.jruby.runtime.builtin.IRubyObject;
  35017. /**
  35018. *
  35019. * @author nicksieger
  35020. */
  35021. public interface RubyRuntimeAdapter {
  35022. IRubyObject eval(Ruby runtime, String script);
  35023. }
  35024. /***** BEGIN LICENSE BLOCK *****
  35025. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  35026. *
  35027. * The contents of this file are subject to the Common Public
  35028. * License Version 1.0 (the "License"); you may not use this file
  35029. * except in compliance with the License. You may obtain a copy of
  35030. * the License at http://www.eclipse.org/legal/cpl-v10.html
  35031. *
  35032. * Software distributed under the License is distributed on an "AS
  35033. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  35034. * implied. See the License for the specific language governing
  35035. * rights and limitations under the License.
  35036. *
  35037. * Copyright (C) 2007 Ola Bini <ola@ologix.com>
  35038. *
  35039. * Alternatively, the contents of this file may be used under the terms of
  35040. * either of the GNU General Public License Version 2 or later (the "GPL"),
  35041. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  35042. * in which case the provisions of the GPL or the LGPL are applicable instead
  35043. * of those above. If you wish to allow use of your version of this file only
  35044. * under the terms of either the GPL or the LGPL, and not to allow others to
  35045. * use your version of this file under the terms of the CPL, indicate your
  35046. * decision by deleting the provisions above and replace them with the notice
  35047. * and other provisions required by the GPL or the LGPL. If you do not delete
  35048. * the provisions above, a recipient may use your version of this file under
  35049. * the terms of any one of the CPL, the GPL or the LGPL.
  35050. ***** END LICENSE BLOCK *****/
  35051. package org.jruby;
  35052. import org.jruby.anno.JRubyMethod;
  35053. import org.jruby.anno.JRubyModule;
  35054. import org.jruby.javasupport.util.RuntimeHelpers;
  35055. import org.jruby.runtime.Block;
  35056. import org.jruby.runtime.CallType;
  35057. import org.jruby.runtime.ThreadContext;
  35058. import org.jruby.runtime.builtin.IRubyObject;
  35059. import org.jruby.util.SignalFacade;
  35060. import org.jruby.util.NoFunctionalitySignalFacade;
  35061. @JRubyModule(name="Signal")
  35062. public class RubySignal {
  35063. private final static SignalFacade SIGNALS = getSignalFacade();
  35064. private final static SignalFacade getSignalFacade() {
  35065. try {
  35066. Class realFacadeClass = Class.forName("org.jruby.util.SunSignalFacade");
  35067. return (SignalFacade)realFacadeClass.newInstance();
  35068. } catch(Throwable e) {
  35069. return new NoFunctionalitySignalFacade();
  35070. }
  35071. }
  35072. // NOTE: The indicies here match exactly the signal values; do not reorder
  35073. public static final String[] NAMES = {
  35074. "EXIT", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT",
  35075. "FPE", "KILL", "BUS", "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG",
  35076. "STOP", "TSTP", "CONT", "CHLD", "TTIN", "TTOU", "IO", "XCPU",
  35077. "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", "USR1", "USR2"};
  35078. public static void createSignal(Ruby runtime) {
  35079. RubyModule mSignal = runtime.defineModule("Signal");
  35080. mSignal.defineAnnotatedMethods(RubySignal.class);
  35081. }
  35082. @JRubyMethod(name = "trap", required = 1, optional = 1, frame = true, meta = true)
  35083. public static IRubyObject trap(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  35084. Ruby runtime = recv.getRuntime();
  35085. runtime.getLoadService().require("jsignal");
  35086. return RuntimeHelpers.invoke(context, runtime.getKernel(), "__jtrap", args, block);
  35087. }
  35088. @JRubyMethod(name = "list", meta = true)
  35089. public static IRubyObject list(ThreadContext context, IRubyObject recv) {
  35090. Ruby runtime = recv.getRuntime();
  35091. RubyHash names = RubyHash.newHash(runtime);
  35092. for (int i = 0; i < NAMES.length; i++) {
  35093. names.op_aset(context, runtime.newString(NAMES[i]), runtime.newFixnum(i));
  35094. }
  35095. // IOT is also 6
  35096. names.op_aset(context, runtime.newString("IOT"), runtime.newFixnum(6));
  35097. // CLD is also 20
  35098. names.op_aset(context, runtime.newString("CLD"), runtime.newFixnum(20));
  35099. return names;
  35100. }
  35101. @JRubyMethod(name = "__jtrap_kernel", required = 3,meta = true)
  35102. public static IRubyObject __jtrap_kernel(final IRubyObject recv, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
  35103. return SIGNALS.trap(recv, arg1, arg2, arg3);
  35104. }
  35105. }// RubySignal
  35106. /*
  35107. **** BEGIN LICENSE BLOCK *****
  35108. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  35109. *
  35110. * The contents of this file are subject to the Common Public
  35111. * License Version 1.0 (the "License"); you may not use this file
  35112. * except in compliance with the License. You may obtain a copy of
  35113. * the License at http://www.eclipse.org/legal/cpl-v10.html
  35114. *
  35115. * Software distributed under the License is distributed on an "AS
  35116. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  35117. * implied. See the License for the specific language governing
  35118. * rights and limitations under the License.
  35119. *
  35120. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  35121. * Copyright (C) 2001-2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  35122. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  35123. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  35124. * Copyright (C) 2002-2006 Thomas E Enebo <enebo@acm.org>
  35125. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  35126. * Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
  35127. * Copyright (C) 2005 Tim Azzopardi <tim@tigerfive.com>
  35128. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  35129. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  35130. * Copyright (C) 2007 Nick Sieger <nicksieger@gmail.com>
  35131. *
  35132. * Alternatively, the contents of this file may be used under the terms of
  35133. * either of the GNU General Public License Version 2 or later (the "GPL"),
  35134. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  35135. * in which case the provisions of the GPL or the LGPL are applicable instead
  35136. * of those above. If you wish to allow use of your version of this file only
  35137. * under the terms of either the GPL or the LGPL, and not to allow others to
  35138. * use your version of this file under the terms of the CPL, indicate your
  35139. * decision by deleting the provisions above and replace them with the notice
  35140. * and other provisions required by the GPL or the LGPL. If you do not delete
  35141. * the provisions above, a recipient may use your version of this file under
  35142. * the terms of any one of the CPL, the GPL or the LGPL.
  35143. ***** END LICENSE BLOCK *****/
  35144. package org.jruby;
  35145. import static org.jruby.anno.FrameField.BACKREF;
  35146. import static org.jruby.anno.FrameField.LASTLINE;
  35147. import java.io.UnsupportedEncodingException;
  35148. import java.util.Locale;
  35149. import org.joni.Matcher;
  35150. import org.joni.Option;
  35151. import org.joni.Regex;
  35152. import org.joni.Region;
  35153. import org.joni.encoding.Encoding;
  35154. import org.joni.encoding.specific.ASCIIEncoding;
  35155. import org.jruby.anno.JRubyClass;
  35156. import org.jruby.anno.JRubyMethod;
  35157. import org.jruby.exceptions.RaiseException;
  35158. import org.jruby.java.MiniJava;
  35159. import org.jruby.javasupport.util.RuntimeHelpers;
  35160. import org.jruby.runtime.Arity;
  35161. import org.jruby.runtime.Block;
  35162. import org.jruby.runtime.ClassIndex;
  35163. import org.jruby.runtime.Frame;
  35164. import org.jruby.runtime.MethodIndex;
  35165. import org.jruby.runtime.ObjectAllocator;
  35166. import org.jruby.runtime.ThreadContext;
  35167. import org.jruby.runtime.Visibility;
  35168. import org.jruby.runtime.builtin.IRubyObject;
  35169. import org.jruby.runtime.marshal.UnmarshalStream;
  35170. import org.jruby.util.ByteList;
  35171. import org.jruby.util.Numeric;
  35172. import org.jruby.util.Pack;
  35173. import org.jruby.util.Sprintf;
  35174. import org.jruby.util.string.JavaCrypt;
  35175. /**
  35176. * Implementation of Ruby String class
  35177. *
  35178. * Concurrency: no synchronization is required among readers, but
  35179. * all users must synchronize externally with writers.
  35180. *
  35181. */
  35182. @JRubyClass(name="String", include={"Enumerable", "Comparable"})
  35183. public class RubyString extends RubyObject {
  35184. private static final ASCIIEncoding ASCII = ASCIIEncoding.INSTANCE;
  35185. // string doesn't share any resources
  35186. private static final int SHARE_LEVEL_NONE = 0;
  35187. // string has it's own ByteList, but it's pointing to a shared buffer (byte[])
  35188. private static final int SHARE_LEVEL_BUFFER = 1;
  35189. // string doesn't have it's own ByteList (values)
  35190. private static final int SHARE_LEVEL_BYTELIST = 2;
  35191. private volatile int shareLevel = SHARE_LEVEL_NONE;
  35192. private ByteList value;
  35193. private static ObjectAllocator STRING_ALLOCATOR = new ObjectAllocator() {
  35194. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  35195. return RubyString.newEmptyString(runtime, klass);
  35196. }
  35197. };
  35198. public static RubyClass createStringClass(Ruby runtime) {
  35199. RubyClass stringClass = runtime.defineClass("String", runtime.getObject(), STRING_ALLOCATOR);
  35200. runtime.setString(stringClass);
  35201. stringClass.index = ClassIndex.STRING;
  35202. stringClass.kindOf = new RubyModule.KindOf() {
  35203. @Override
  35204. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  35205. return obj instanceof RubyString;
  35206. }
  35207. };
  35208. stringClass.includeModule(runtime.getComparable());
  35209. stringClass.includeModule(runtime.getEnumerable());
  35210. stringClass.defineAnnotatedMethods(RubyString.class);
  35211. return stringClass;
  35212. }
  35213. /** short circuit for String key comparison
  35214. *
  35215. */
  35216. @Override
  35217. public final boolean eql(IRubyObject other) {
  35218. if (other.getMetaClass() == getRuntime().getString()) return value.equal(((RubyString)other).value);
  35219. return super.eql(other);
  35220. }
  35221. private RubyString(Ruby runtime, RubyClass rubyClass, CharSequence value) {
  35222. super(runtime, rubyClass);
  35223. assert value != null;
  35224. this.value = new ByteList(ByteList.plain(value), false);
  35225. }
  35226. private RubyString(Ruby runtime, RubyClass rubyClass, byte[] value) {
  35227. super(runtime, rubyClass);
  35228. assert value != null;
  35229. this.value = new ByteList(value);
  35230. }
  35231. private RubyString(Ruby runtime, RubyClass rubyClass, ByteList value) {
  35232. super(runtime, rubyClass);
  35233. assert value != null;
  35234. this.value = value;
  35235. }
  35236. private RubyString(Ruby runtime, RubyClass rubyClass, ByteList value, boolean objectSpace) {
  35237. super(runtime, rubyClass, objectSpace);
  35238. assert value != null;
  35239. this.value = value;
  35240. }
  35241. /** Create a new String which uses the same Ruby runtime and the same
  35242. * class like this String.
  35243. *
  35244. * This method should be used to satisfy RCR #38.
  35245. * @deprecated
  35246. */
  35247. public RubyString newString(CharSequence s) {
  35248. return new RubyString(getRuntime(), getType(), s);
  35249. }
  35250. /** Create a new String which uses the same Ruby runtime and the same
  35251. * class like this String.
  35252. *
  35253. * This method should be used to satisfy RCR #38.
  35254. * @deprecated
  35255. */
  35256. public RubyString newString(ByteList s) {
  35257. return new RubyString(getRuntime(), getMetaClass(), s);
  35258. }
  35259. // Methods of the String class (rb_str_*):
  35260. /** rb_str_new2
  35261. *
  35262. */
  35263. public static RubyString newString(Ruby runtime, CharSequence str) {
  35264. return new RubyString(runtime, runtime.getString(), str);
  35265. }
  35266. public static RubyString newEmptyString(Ruby runtime) {
  35267. return newEmptyString(runtime, runtime.getString());
  35268. }
  35269. public static RubyString newEmptyString(Ruby runtime, RubyClass metaClass) {
  35270. RubyString empty = new RubyString(runtime, metaClass, ByteList.EMPTY_BYTELIST);
  35271. empty.shareLevel = SHARE_LEVEL_BYTELIST;
  35272. return empty;
  35273. }
  35274. public static RubyString newUnicodeString(Ruby runtime, String str) {
  35275. try {
  35276. return new RubyString(runtime, runtime.getString(), new ByteList(str.getBytes("UTF8"), false));
  35277. } catch (UnsupportedEncodingException uee) {
  35278. return new RubyString(runtime, runtime.getString(), str);
  35279. }
  35280. }
  35281. @Deprecated
  35282. public static RubyString newString(Ruby runtime, RubyClass clazz, CharSequence str) {
  35283. return new RubyString(runtime, clazz, str);
  35284. }
  35285. public static RubyString newString(Ruby runtime, byte[] bytes) {
  35286. return new RubyString(runtime, runtime.getString(), bytes);
  35287. }
  35288. public static RubyString newString(Ruby runtime, byte[] bytes, int start, int length) {
  35289. byte[] copy = new byte[length];
  35290. System.arraycopy(bytes, start, copy, 0, length);
  35291. return new RubyString(runtime, runtime.getString(), new ByteList(copy, false));
  35292. }
  35293. public static RubyString newString(Ruby runtime, ByteList bytes) {
  35294. return new RubyString(runtime, runtime.getString(), bytes);
  35295. }
  35296. public static RubyString newStringLight(Ruby runtime, ByteList bytes) {
  35297. return new RubyString(runtime, runtime.getString(), bytes, false);
  35298. }
  35299. public static RubyString newStringShared(Ruby runtime, RubyString orig) {
  35300. orig.shareLevel = SHARE_LEVEL_BYTELIST;
  35301. RubyString str = new RubyString(runtime, runtime.getString(), orig.value);
  35302. str.shareLevel = SHARE_LEVEL_BYTELIST;
  35303. return str;
  35304. }
  35305. public static RubyString newStringShared(Ruby runtime, ByteList bytes) {
  35306. return newStringShared(runtime, runtime.getString(), bytes);
  35307. }
  35308. public static RubyString newStringShared(Ruby runtime, RubyClass clazz, ByteList bytes) {
  35309. RubyString str = new RubyString(runtime, clazz, bytes);
  35310. str.shareLevel = SHARE_LEVEL_BYTELIST;
  35311. return str;
  35312. }
  35313. public static RubyString newStringShared(Ruby runtime, byte[] bytes, int start, int length) {
  35314. RubyString str = new RubyString(runtime, runtime.getString(), new ByteList(bytes, start, length, false));
  35315. str.shareLevel = SHARE_LEVEL_BUFFER;
  35316. return str;
  35317. }
  35318. @Override
  35319. public int getNativeTypeIndex() {
  35320. return ClassIndex.STRING;
  35321. }
  35322. @Override
  35323. public Class getJavaClass() {
  35324. return String.class;
  35325. }
  35326. @Override
  35327. public RubyString convertToString() {
  35328. return this;
  35329. }
  35330. @Override
  35331. public String toString() {
  35332. return value.toString();
  35333. }
  35334. /** rb_str_dup
  35335. *
  35336. */
  35337. @Deprecated
  35338. public final RubyString strDup() {
  35339. return strDup(getRuntime(), getMetaClass());
  35340. }
  35341. public final RubyString strDup(Ruby runtime) {
  35342. return strDup(runtime, getMetaClass());
  35343. }
  35344. @Deprecated
  35345. final RubyString strDup(RubyClass clazz) {
  35346. return strDup(getRuntime(), getMetaClass());
  35347. }
  35348. final RubyString strDup(Ruby runtime, RubyClass clazz) {
  35349. shareLevel = SHARE_LEVEL_BYTELIST;
  35350. RubyString dup = new RubyString(runtime, clazz, value);
  35351. dup.shareLevel = SHARE_LEVEL_BYTELIST;
  35352. dup.infectBy(this);
  35353. return dup;
  35354. }
  35355. public final RubyString makeShared(Ruby runtime, int index, int len) {
  35356. if (len == 0) {
  35357. RubyString s = newEmptyString(runtime, getMetaClass());
  35358. s.infectBy(this);
  35359. return s;
  35360. }
  35361. if (shareLevel == SHARE_LEVEL_NONE) shareLevel = SHARE_LEVEL_BUFFER;
  35362. RubyString shared = new RubyString(runtime, getMetaClass(), value.makeShared(index, len));
  35363. shared.shareLevel = SHARE_LEVEL_BUFFER;
  35364. shared.infectBy(this);
  35365. return shared;
  35366. }
  35367. final void modifyCheck() {
  35368. if ((flags & FROZEN_F) != 0) throw getRuntime().newFrozenError("string");
  35369. if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
  35370. throw getRuntime().newSecurityError("Insecure: can't modify string");
  35371. }
  35372. }
  35373. private final void modifyCheck(byte[] b, int len) {
  35374. if (value.bytes != b || value.realSize != len) throw getRuntime().newRuntimeError("string modified");
  35375. }
  35376. private final void frozenCheck() {
  35377. if (isFrozen()) throw getRuntime().newRuntimeError("string frozen");
  35378. }
  35379. /** rb_str_modify
  35380. *
  35381. */
  35382. public final void modify() {
  35383. modifyCheck();
  35384. if (shareLevel != SHARE_LEVEL_NONE) {
  35385. if (shareLevel == SHARE_LEVEL_BYTELIST) {
  35386. value = value.dup();
  35387. } else {
  35388. value.unshare();
  35389. }
  35390. shareLevel = SHARE_LEVEL_NONE;
  35391. }
  35392. value.invalidate();
  35393. }
  35394. /** rb_str_modify (with length bytes ensured)
  35395. *
  35396. */
  35397. public final void modify(int length) {
  35398. modifyCheck();
  35399. if (shareLevel != SHARE_LEVEL_NONE) {
  35400. if (shareLevel == SHARE_LEVEL_BYTELIST) {
  35401. value = value.dup(length);
  35402. } else {
  35403. value.unshare(length);
  35404. }
  35405. shareLevel = SHARE_LEVEL_NONE;
  35406. } else {
  35407. value.ensure(length);
  35408. }
  35409. value.invalidate();
  35410. }
  35411. private final void view(ByteList bytes) {
  35412. modifyCheck();
  35413. value = bytes;
  35414. shareLevel = SHARE_LEVEL_NONE;
  35415. }
  35416. private final void view(byte[]bytes) {
  35417. modifyCheck();
  35418. value.replace(bytes);
  35419. shareLevel = SHARE_LEVEL_NONE;
  35420. value.invalidate();
  35421. }
  35422. private final void view(int index, int len) {
  35423. modifyCheck();
  35424. if (shareLevel != SHARE_LEVEL_NONE) {
  35425. if (shareLevel == SHARE_LEVEL_BYTELIST) {
  35426. // if len == 0 then shared empty
  35427. value = value.makeShared(index, len);
  35428. shareLevel = SHARE_LEVEL_BUFFER;
  35429. } else {
  35430. value.view(index, len);
  35431. }
  35432. } else {
  35433. value.view(index, len);
  35434. // FIXME this below is temporary, but its much safer for COW (it prevents not shared Strings with begin != 0)
  35435. // this allows now e.g.: ByteList#set not to be begin aware
  35436. shareLevel = SHARE_LEVEL_BUFFER;
  35437. }
  35438. value.invalidate();
  35439. }
  35440. public static String bytesToString(byte[] bytes, int beg, int len) {
  35441. return new String(ByteList.plain(bytes, beg, len));
  35442. }
  35443. public static String byteListToString(ByteList bytes) {
  35444. return bytesToString(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  35445. }
  35446. public static String bytesToString(byte[] bytes) {
  35447. return bytesToString(bytes, 0, bytes.length);
  35448. }
  35449. public static byte[] stringToBytes(String string) {
  35450. return ByteList.plain(string);
  35451. }
  35452. public static boolean isDigit(int c) {
  35453. return c >= '0' && c <= '9';
  35454. }
  35455. public static boolean isUpper(int c) {
  35456. return c >= 'A' && c <= 'Z';
  35457. }
  35458. public static boolean isLower(int c) {
  35459. return c >= 'a' && c <= 'z';
  35460. }
  35461. public static boolean isLetter(int c) {
  35462. return isUpper(c) || isLower(c);
  35463. }
  35464. public static boolean isAlnum(int c) {
  35465. return isUpper(c) || isLower(c) || isDigit(c);
  35466. }
  35467. public static boolean isPrint(int c) {
  35468. return c >= 0x20 && c <= 0x7E;
  35469. }
  35470. @Override
  35471. public RubyString asString() {
  35472. return this;
  35473. }
  35474. @Override
  35475. public IRubyObject checkStringType() {
  35476. return this;
  35477. }
  35478. @JRubyMethod(name = {"to_s", "to_str"})
  35479. @Override
  35480. public IRubyObject to_s() {
  35481. Ruby runtime = getRuntime();
  35482. if (getMetaClass().getRealClass() != runtime.getString()) {
  35483. return strDup(runtime, runtime.getString());
  35484. }
  35485. return this;
  35486. }
  35487. /* rb_str_cmp_m */
  35488. @JRubyMethod(name = "<=>", required = 1)
  35489. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  35490. if (other instanceof RubyString) {
  35491. return context.getRuntime().newFixnum(op_cmp((RubyString)other));
  35492. }
  35493. // deal with case when "other" is not a string
  35494. if (other.respondsTo("to_str") && other.respondsTo("<=>")) {
  35495. IRubyObject result = other.callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", this);
  35496. if (result instanceof RubyNumeric) {
  35497. return ((RubyNumeric) result).op_uminus(context);
  35498. }
  35499. }
  35500. return context.getRuntime().getNil();
  35501. }
  35502. /**
  35503. *
  35504. */
  35505. @JRubyMethod(name = "==", required = 1)
  35506. @Override
  35507. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  35508. Ruby runtime = context.getRuntime();
  35509. if (this == other) return runtime.getTrue();
  35510. if (!(other instanceof RubyString)) {
  35511. if (!other.respondsTo("to_str")) return runtime.getFalse();
  35512. return other.callMethod(context, MethodIndex.EQUALEQUAL, "==", this).isTrue() ? runtime.getTrue() : runtime.getFalse();
  35513. }
  35514. return value.equal(((RubyString)other).value) ? runtime.getTrue() : runtime.getFalse();
  35515. }
  35516. @JRubyMethod(name = "+", required = 1)
  35517. public IRubyObject op_plus(ThreadContext context, IRubyObject other) {
  35518. RubyString str = other.convertToString();
  35519. ByteList result = new ByteList(value.realSize + str.value.realSize);
  35520. result.realSize = value.realSize + str.value.realSize;
  35521. System.arraycopy(value.bytes, value.begin, result.bytes, 0, value.realSize);
  35522. System.arraycopy(str.value.bytes, str.value.begin, result.bytes, value.realSize, str.value.realSize);
  35523. RubyString resultStr = newString(context.getRuntime(), result);
  35524. if (isTaint() || str.isTaint()) resultStr.setTaint(true);
  35525. return resultStr;
  35526. }
  35527. @JRubyMethod(name = "*", required = 1)
  35528. public IRubyObject op_mul(ThreadContext context, IRubyObject other) {
  35529. RubyInteger otherInteger = (RubyInteger) other.convertToInteger();
  35530. long len = otherInteger.getLongValue();
  35531. if (len < 0) throw context.getRuntime().newArgumentError("negative argument");
  35532. // we limit to int because ByteBuffer can only allocate int sizes
  35533. if (len > 0 && Integer.MAX_VALUE / len < value.length()) {
  35534. throw context.getRuntime().newArgumentError("argument too big");
  35535. }
  35536. ByteList newBytes = new ByteList(value.length() * (int)len);
  35537. for (int i = 0; i < len; i++) {
  35538. newBytes.append(value);
  35539. }
  35540. RubyString newString = new RubyString(context.getRuntime(), getMetaClass(), newBytes);
  35541. newString.setTaint(isTaint());
  35542. return newString;
  35543. }
  35544. @JRubyMethod(name = "%", required = 1)
  35545. public IRubyObject op_format(ThreadContext context, IRubyObject arg) {
  35546. final RubyString s;
  35547. IRubyObject tmp = arg.checkArrayType();
  35548. if (tmp.isNil()) {
  35549. tmp = arg;
  35550. }
  35551. // FIXME: Should we make this work with platform's locale,
  35552. // or continue hardcoding US?
  35553. s = Sprintf.sprintf(context.getRuntime(), Locale.US, value, tmp);
  35554. s.infectBy(this);
  35555. return s;
  35556. }
  35557. @JRubyMethod(name = "hash")
  35558. @Override
  35559. public RubyFixnum hash() {
  35560. return getRuntime().newFixnum(value.hashCode());
  35561. }
  35562. @Override
  35563. public int hashCode() {
  35564. return value.hashCode();
  35565. }
  35566. @Override
  35567. public boolean equals(Object other) {
  35568. if (this == other) return true;
  35569. if (other instanceof RubyString) {
  35570. RubyString string = (RubyString) other;
  35571. if (string.value.equal(value)) return true;
  35572. }
  35573. return false;
  35574. }
  35575. /** rb_obj_as_string
  35576. *
  35577. */
  35578. public static RubyString objAsString(ThreadContext context, IRubyObject obj) {
  35579. if (obj instanceof RubyString) return (RubyString) obj;
  35580. IRubyObject str = obj.callMethod(context, MethodIndex.TO_S, "to_s");
  35581. if (!(str instanceof RubyString)) return (RubyString) obj.anyToString();
  35582. if (obj.isTaint()) str.setTaint(true);
  35583. return (RubyString) str;
  35584. }
  35585. /** rb_str_cmp
  35586. *
  35587. */
  35588. public int op_cmp(RubyString other) {
  35589. return value.cmp(other.value);
  35590. }
  35591. /** rb_to_id
  35592. *
  35593. */
  35594. @Override
  35595. public String asJavaString() {
  35596. // TODO: This used to intern; but it didn't appear to change anything
  35597. // turning that off, and it's unclear if it was needed. Plus, we intern
  35598. //
  35599. return toString();
  35600. }
  35601. public IRubyObject doClone(){
  35602. return newString(getRuntime(), value.dup());
  35603. }
  35604. public RubyString cat(byte[] str) {
  35605. modify(value.realSize + str.length);
  35606. System.arraycopy(str, 0, value.bytes, value.begin + value.realSize, str.length);
  35607. value.realSize += str.length;
  35608. return this;
  35609. }
  35610. public RubyString cat(byte[] str, int beg, int len) {
  35611. modify(value.realSize + len);
  35612. System.arraycopy(str, beg, value.bytes, value.begin + value.realSize, len);
  35613. value.realSize += len;
  35614. return this;
  35615. }
  35616. public RubyString cat(ByteList str) {
  35617. modify(value.realSize + str.realSize);
  35618. System.arraycopy(str.bytes, str.begin, value.bytes, value.begin + value.realSize, str.realSize);
  35619. value.realSize += str.realSize;
  35620. return this;
  35621. }
  35622. public RubyString cat(byte ch) {
  35623. modify(value.realSize + 1);
  35624. value.bytes[value.begin + value.realSize] = ch;
  35625. value.realSize++;
  35626. return this;
  35627. }
  35628. /** rb_str_replace_m
  35629. *
  35630. */
  35631. @JRubyMethod(name = {"replace", "initialize_copy"}, required = 1)
  35632. public RubyString replace(IRubyObject other) {
  35633. if (this == other) return this;
  35634. modifyCheck();
  35635. RubyString otherStr = stringValue(other);
  35636. otherStr.shareLevel = shareLevel = SHARE_LEVEL_BYTELIST;
  35637. value = otherStr.value;
  35638. infectBy(other);
  35639. return this;
  35640. }
  35641. @JRubyMethod(name = "reverse")
  35642. public RubyString reverse(ThreadContext context) {
  35643. if (value.length() <= 1) return strDup(context.getRuntime());
  35644. ByteList buf = new ByteList(value.length()+2);
  35645. buf.realSize = value.length();
  35646. int src = value.length() - 1;
  35647. int dst = 0;
  35648. while (src >= 0) buf.set(dst++, value.get(src--));
  35649. RubyString rev = new RubyString(context.getRuntime(), getMetaClass(), buf);
  35650. rev.infectBy(this);
  35651. return rev;
  35652. }
  35653. @JRubyMethod(name = "reverse!")
  35654. public RubyString reverse_bang() {
  35655. if (value.length() > 1) {
  35656. modify();
  35657. for (int i = 0; i < (value.length() / 2); i++) {
  35658. byte b = (byte) value.get(i);
  35659. value.set(i, value.get(value.length() - i - 1));
  35660. value.set(value.length() - i - 1, b);
  35661. }
  35662. }
  35663. return this;
  35664. }
  35665. /** rb_str_s_new
  35666. *
  35667. */
  35668. public static RubyString newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
  35669. RubyString newString = newStringShared(recv.getRuntime(), ByteList.EMPTY_BYTELIST);
  35670. newString.setMetaClass((RubyClass) recv);
  35671. newString.callInit(args, block);
  35672. return newString;
  35673. }
  35674. /**
  35675. * Variable-arity version for compatibility. Not bound to Ruby.
  35676. * @deprecated Use the versions with zero or one arguments
  35677. */
  35678. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  35679. switch (args.length) {
  35680. case 0:
  35681. return this;
  35682. case 1:
  35683. return initialize(args[0]);
  35684. default:
  35685. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  35686. return null; // not reached
  35687. }
  35688. }
  35689. @JRubyMethod(frame = true, visibility = Visibility.PRIVATE)
  35690. @Override
  35691. public IRubyObject initialize() {
  35692. return this;
  35693. }
  35694. @JRubyMethod(frame = true, visibility = Visibility.PRIVATE)
  35695. public IRubyObject initialize(IRubyObject arg0) {
  35696. replace(arg0);
  35697. return this;
  35698. }
  35699. @JRubyMethod
  35700. public IRubyObject casecmp(IRubyObject other) {
  35701. int compare = value.caseInsensitiveCmp(stringValue(other).value);
  35702. return RubyFixnum.newFixnum(getRuntime(), compare);
  35703. }
  35704. /** rb_str_match
  35705. *
  35706. */
  35707. @JRubyMethod(name = "=~")
  35708. @Override
  35709. public IRubyObject op_match(ThreadContext context, IRubyObject other) {
  35710. if (other instanceof RubyRegexp) return ((RubyRegexp) other).op_match(context, this);
  35711. if (other instanceof RubyString) {
  35712. throw context.getRuntime().newTypeError("type mismatch: String given");
  35713. }
  35714. return other.callMethod(context, "=~", this);
  35715. }
  35716. /** rb_str_match2
  35717. *
  35718. */
  35719. @JRubyMethod(name = "~", reads = {LASTLINE, BACKREF}, writes = BACKREF)
  35720. public IRubyObject op_match2(ThreadContext context) {
  35721. return RubyRegexp.newRegexp(context.getRuntime(), value, 0, false).op_match2(context);
  35722. }
  35723. /**
  35724. * String#match(pattern)
  35725. *
  35726. * rb_str_match_m
  35727. *
  35728. * @param pattern Regexp or String
  35729. */
  35730. @JRubyMethod
  35731. public IRubyObject match(ThreadContext context, IRubyObject pattern) {
  35732. return getPattern(pattern, false).callMethod(context, "match", this);
  35733. }
  35734. /** rb_str_capitalize
  35735. *
  35736. */
  35737. @JRubyMethod
  35738. public IRubyObject capitalize(ThreadContext context) {
  35739. RubyString str = strDup(context.getRuntime());
  35740. str.capitalize_bang(context);
  35741. return str;
  35742. }
  35743. /** rb_str_capitalize_bang
  35744. *
  35745. */
  35746. @JRubyMethod(name = "capitalize!")
  35747. public IRubyObject capitalize_bang(ThreadContext context) {
  35748. if (value.realSize == 0) {
  35749. modifyCheck();
  35750. return context.getRuntime().getNil();
  35751. }
  35752. modify();
  35753. int s = value.begin;
  35754. int send = s + value.realSize;
  35755. byte[]buf = value.bytes;
  35756. boolean modify = false;
  35757. int c = buf[s] & 0xff;
  35758. if (ASCII.isLower(c)) {
  35759. buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
  35760. modify = true;
  35761. }
  35762. while (++s < send) {
  35763. c = (char)(buf[s] & 0xff);
  35764. if (ASCII.isUpper(c)) {
  35765. buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
  35766. modify = true;
  35767. }
  35768. }
  35769. if (modify) return this;
  35770. return context.getRuntime().getNil();
  35771. }
  35772. @JRubyMethod(name = ">=")
  35773. public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
  35774. if (other instanceof RubyString) {
  35775. return context.getRuntime().newBoolean(op_cmp((RubyString) other) >= 0);
  35776. }
  35777. return RubyComparable.op_ge(context, this, other);
  35778. }
  35779. @JRubyMethod(name = ">")
  35780. public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
  35781. if (other instanceof RubyString) {
  35782. return context.getRuntime().newBoolean(op_cmp((RubyString) other) > 0);
  35783. }
  35784. return RubyComparable.op_gt(context, this, other);
  35785. }
  35786. @JRubyMethod(name = "<=")
  35787. public IRubyObject op_le(ThreadContext context, IRubyObject other) {
  35788. if (other instanceof RubyString) {
  35789. return context.getRuntime().newBoolean(op_cmp((RubyString) other) <= 0);
  35790. }
  35791. return RubyComparable.op_le(context, this, other);
  35792. }
  35793. @JRubyMethod(name = "<")
  35794. public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
  35795. if (other instanceof RubyString) {
  35796. return context.getRuntime().newBoolean(op_cmp((RubyString) other) < 0);
  35797. }
  35798. return RubyComparable.op_lt(context, this, other);
  35799. }
  35800. @JRubyMethod(name = "eql?")
  35801. public IRubyObject str_eql_p(ThreadContext context, IRubyObject other) {
  35802. if (!(other instanceof RubyString)) return context.getRuntime().getFalse();
  35803. RubyString otherString = (RubyString)other;
  35804. return value.equal(otherString.value) ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  35805. }
  35806. /** rb_str_upcase
  35807. *
  35808. */
  35809. @JRubyMethod
  35810. public RubyString upcase(ThreadContext context) {
  35811. RubyString str = strDup(context.getRuntime());
  35812. str.upcase_bang(context);
  35813. return str;
  35814. }
  35815. /** rb_str_upcase_bang
  35816. *
  35817. */
  35818. @JRubyMethod(name = "upcase!")
  35819. public IRubyObject upcase_bang(ThreadContext context) {
  35820. if (value.realSize == 0) {
  35821. modifyCheck();
  35822. return context.getRuntime().getNil();
  35823. }
  35824. modify();
  35825. int s = value.begin;
  35826. int send = s + value.realSize;
  35827. byte []buf = value.bytes;
  35828. boolean modify = false;
  35829. while (s < send) {
  35830. int c = buf[s] & 0xff;
  35831. if (ASCII.isLower(c)) {
  35832. buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
  35833. modify = true;
  35834. }
  35835. s++;
  35836. }
  35837. if (modify) return this;
  35838. return context.getRuntime().getNil();
  35839. }
  35840. /** rb_str_downcase
  35841. *
  35842. */
  35843. @JRubyMethod
  35844. public RubyString downcase(ThreadContext context) {
  35845. RubyString str = strDup(context.getRuntime());
  35846. str.downcase_bang(context);
  35847. return str;
  35848. }
  35849. /** rb_str_downcase_bang
  35850. *
  35851. */
  35852. @JRubyMethod(name = "downcase!")
  35853. public IRubyObject downcase_bang(ThreadContext context) {
  35854. if (value.realSize == 0) {
  35855. modifyCheck();
  35856. return context.getRuntime().getNil();
  35857. }
  35858. modify();
  35859. int s = value.begin;
  35860. int send = s + value.realSize;
  35861. byte []buf = value.bytes;
  35862. boolean modify = false;
  35863. while (s < send) {
  35864. int c = buf[s] & 0xff;
  35865. if (ASCII.isUpper(c)) {
  35866. buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
  35867. modify = true;
  35868. }
  35869. s++;
  35870. }
  35871. if (modify) return this;
  35872. return context.getRuntime().getNil();
  35873. }
  35874. /** rb_str_swapcase
  35875. *
  35876. */
  35877. @JRubyMethod
  35878. public RubyString swapcase(ThreadContext context) {
  35879. RubyString str = strDup(context.getRuntime());
  35880. str.swapcase_bang(context);
  35881. return str;
  35882. }
  35883. /** rb_str_swapcase_bang
  35884. *
  35885. */
  35886. @JRubyMethod(name = "swapcase!")
  35887. public IRubyObject swapcase_bang(ThreadContext context) {
  35888. if (value.realSize == 0) {
  35889. modifyCheck();
  35890. return context.getRuntime().getNil();
  35891. }
  35892. modify();
  35893. int s = value.begin;
  35894. int send = s + value.realSize;
  35895. byte[]buf = value.bytes;
  35896. boolean modify = false;
  35897. while (s < send) {
  35898. int c = buf[s] & 0xff;
  35899. if (ASCII.isUpper(c)) {
  35900. buf[s] = (byte)ASCIIEncoding.asciiToLower(c);
  35901. modify = true;
  35902. } else if (ASCII.isLower(c)) {
  35903. buf[s] = (byte)ASCIIEncoding.asciiToUpper(c);
  35904. modify = true;
  35905. }
  35906. s++;
  35907. }
  35908. if (modify) return this;
  35909. return context.getRuntime().getNil();
  35910. }
  35911. /** rb_str_dump
  35912. *
  35913. */
  35914. @JRubyMethod
  35915. public IRubyObject dump() {
  35916. RubyString s = new RubyString(getRuntime(), getMetaClass(), inspectIntoByteList(true));
  35917. s.infectBy(this);
  35918. return s;
  35919. }
  35920. @JRubyMethod
  35921. public IRubyObject insert(ThreadContext context, IRubyObject indexArg, IRubyObject stringArg) {
  35922. // MRI behavior: first check for ability to convert to String...
  35923. RubyString s = (RubyString)stringArg.convertToString();
  35924. ByteList insert = s.value;
  35925. // ... and then the index
  35926. int index = (int) indexArg.convertToInteger().getLongValue();
  35927. if (index < 0) index += value.length() + 1;
  35928. if (index < 0 || index > value.length()) {
  35929. throw context.getRuntime().newIndexError("index " + index + " out of range");
  35930. }
  35931. modify();
  35932. value.unsafeReplace(index, 0, insert);
  35933. this.infectBy(s);
  35934. return this;
  35935. }
  35936. /** rb_str_inspect
  35937. *
  35938. */
  35939. @JRubyMethod
  35940. @Override
  35941. public IRubyObject inspect() {
  35942. RubyString s = getRuntime().newString(inspectIntoByteList(false));
  35943. s.infectBy(this);
  35944. return s;
  35945. }
  35946. private ByteList inspectIntoByteList(boolean ignoreKCode) {
  35947. Ruby runtime = getRuntime();
  35948. Encoding enc = runtime.getKCode().getEncoding();
  35949. final int length = value.length();
  35950. ByteList sb = new ByteList(length + 2 + length / 100);
  35951. sb.append('\"');
  35952. for (int i = 0; i < length; i++) {
  35953. int c = value.get(i) & 0xFF;
  35954. if (!ignoreKCode) {
  35955. int seqLength = enc.length((byte)c);
  35956. if (seqLength > 1 && (i + seqLength -1 < length)) {
  35957. // don't escape multi-byte characters, leave them as bytes
  35958. sb.append(value, i, seqLength);
  35959. i += seqLength - 1;
  35960. continue;
  35961. }
  35962. }
  35963. if (isAlnum(c)) {
  35964. sb.append((char)c);
  35965. } else if (c == '\"' || c == '\\') {
  35966. sb.append('\\').append((char)c);
  35967. } else if (c == '#' && isEVStr(i, length)) {
  35968. sb.append('\\').append((char)c);
  35969. } else if (isPrint(c)) {
  35970. sb.append((char)c);
  35971. } else if (c == '\n') {
  35972. sb.append('\\').append('n');
  35973. } else if (c == '\r') {
  35974. sb.append('\\').append('r');
  35975. } else if (c == '\t') {
  35976. sb.append('\\').append('t');
  35977. } else if (c == '\f') {
  35978. sb.append('\\').append('f');
  35979. } else if (c == '\u000B') {
  35980. sb.append('\\').append('v');
  35981. } else if (c == '\u0007') {
  35982. sb.append('\\').append('a');
  35983. } else if (c == '\u0008') {
  35984. sb.append('\\').append('b');
  35985. } else if (c == '\u001B') {
  35986. sb.append('\\').append('e');
  35987. } else {
  35988. sb.append(ByteList.plain(Sprintf.sprintf(runtime,"\\%03o",c)));
  35989. }
  35990. }
  35991. sb.append('\"');
  35992. return sb;
  35993. }
  35994. private boolean isEVStr(int i, int length) {
  35995. if (i+1 >= length) return false;
  35996. int c = value.get(i+1) & 0xFF;
  35997. return c == '$' || c == '@' || c == '{';
  35998. }
  35999. /** rb_str_length
  36000. *
  36001. */
  36002. @JRubyMethod(name = {"length", "size"})
  36003. public RubyFixnum length() {
  36004. return getRuntime().newFixnum(value.length());
  36005. }
  36006. /** rb_str_empty
  36007. *
  36008. */
  36009. @JRubyMethod(name = "empty?")
  36010. public RubyBoolean empty_p(ThreadContext context) {
  36011. return isEmpty() ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  36012. }
  36013. public boolean isEmpty() {
  36014. return value.length() == 0;
  36015. }
  36016. /** rb_str_append
  36017. *
  36018. */
  36019. public RubyString append(IRubyObject other) {
  36020. infectBy(other);
  36021. return cat(stringValue(other).value);
  36022. }
  36023. /** rb_str_concat
  36024. *
  36025. */
  36026. @JRubyMethod(name = {"concat", "<<"})
  36027. public RubyString concat(IRubyObject other) {
  36028. if (other instanceof RubyFixnum) {
  36029. long value = ((RubyFixnum) other).getLongValue();
  36030. if (value >= 0 && value < 256) return cat((byte) value);
  36031. }
  36032. return append(other);
  36033. }
  36034. /** rb_str_crypt
  36035. *
  36036. */
  36037. @JRubyMethod(name = "crypt")
  36038. public RubyString crypt(ThreadContext context, IRubyObject other) {
  36039. ByteList salt = stringValue(other).getByteList();
  36040. if (salt.realSize < 2) {
  36041. throw context.getRuntime().newArgumentError("salt too short(need >=2 bytes)");
  36042. }
  36043. salt = salt.makeShared(0, 2);
  36044. RubyString s = RubyString.newStringShared(context.getRuntime(), JavaCrypt.crypt(salt, this.getByteList()));
  36045. s.infectBy(this);
  36046. s.infectBy(other);
  36047. return s;
  36048. }
  36049. /* RubyString aka rb_string_value */
  36050. public static RubyString stringValue(IRubyObject object) {
  36051. return (RubyString) (object instanceof RubyString ? object :
  36052. object.convertToString());
  36053. }
  36054. /**
  36055. * Variable-arity version for compatibility. Not bound to Ruby.
  36056. * @deprecated Use the versions with one or two args.
  36057. */
  36058. public IRubyObject sub(ThreadContext context, IRubyObject[] args, Block block) {
  36059. RubyString str = strDup(context.getRuntime());
  36060. str.sub_bang(context, args, block);
  36061. return str;
  36062. }
  36063. /** rb_str_sub
  36064. *
  36065. */
  36066. @JRubyMethod(name = "sub", frame = true)
  36067. public IRubyObject sub(ThreadContext context, IRubyObject arg0, Block block) {
  36068. RubyString str = strDup(context.getRuntime());
  36069. str.sub_bang(context, arg0, block);
  36070. return str;
  36071. }
  36072. /** rb_str_sub
  36073. *
  36074. */
  36075. @JRubyMethod(name = "sub", frame = true)
  36076. public IRubyObject sub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  36077. RubyString str = strDup(context.getRuntime());
  36078. str.sub_bang(context, arg0, arg1, block);
  36079. return str;
  36080. }
  36081. /**
  36082. * Variable-arity version for compatibility. Not bound to Ruby.
  36083. * @deprecated Use the versions with one or two arguments.
  36084. */
  36085. public IRubyObject sub_bang(ThreadContext context, IRubyObject[] args, Block block) {
  36086. switch (args.length) {
  36087. case 1:
  36088. return sub_bang(context, args[0], block);
  36089. case 2:
  36090. return sub_bang(context, args[0], args[1], block);
  36091. default:
  36092. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36093. return null; // not reached
  36094. }
  36095. }
  36096. /** rb_str_sub_bang
  36097. *
  36098. */
  36099. @JRubyMethod(name = "sub!", frame = true, reads = BACKREF, writes = BACKREF)
  36100. public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, Block block) {
  36101. if (block.isGiven()) {
  36102. RubyRegexp rubyRegex = getPattern(arg0, true);
  36103. Regex regex = rubyRegex.getPattern();
  36104. return subBangCommon(regex, context, true, rubyRegex, block, null, false);
  36105. } else {
  36106. throw context.getRuntime().newArgumentError("wrong number of arguments (1 for 2)");
  36107. }
  36108. }
  36109. /** rb_str_sub_bang
  36110. *
  36111. */
  36112. @JRubyMethod(name = "sub!", frame = true, reads = BACKREF, writes = BACKREF)
  36113. public IRubyObject sub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  36114. RubyString repl = arg1.convertToString();
  36115. RubyRegexp rubyRegex = getPattern(arg0, true);
  36116. Regex regex = rubyRegex.getPattern();
  36117. return subBangCommon(regex, context, false, rubyRegex, block, repl, repl.isTaint());
  36118. }
  36119. private IRubyObject subBangCommon(Regex regex, ThreadContext context, final boolean iter, RubyRegexp rubyRegex, Block block, RubyString repl, boolean tainted) {
  36120. int range = value.begin + value.realSize;
  36121. Matcher matcher = regex.matcher(value.bytes, value.begin, range);
  36122. Frame frame = context.getPreviousFrame();
  36123. if (matcher.search(value.begin, range, Option.NONE) >= 0) {
  36124. if (iter) {
  36125. byte[] bytes = value.bytes;
  36126. int size = value.realSize;
  36127. RubyMatchData match = rubyRegex.updateBackRef(context, this, frame, matcher);
  36128. match.use();
  36129. if (regex.numberOfCaptures() == 0) {
  36130. repl = objAsString(context, block.yield(context, substr(matcher.getBegin(), matcher.getEnd() - matcher.getBegin())));
  36131. } else {
  36132. Region region = matcher.getRegion();
  36133. repl = objAsString(context, block.yield(context, substr(region.beg[0], region.end[0] - region.beg[0])));
  36134. }
  36135. modifyCheck(bytes, size);
  36136. frozenCheck();
  36137. frame.setBackRef(match);
  36138. } else {
  36139. repl = rubyRegex.regsub(repl, this, matcher);
  36140. rubyRegex.updateBackRef(context, this, frame, matcher);
  36141. }
  36142. final int beg;
  36143. final int plen;
  36144. if (regex.numberOfCaptures() == 0) {
  36145. beg = matcher.getBegin();
  36146. plen = matcher.getEnd() - beg;
  36147. } else {
  36148. Region region = matcher.getRegion();
  36149. beg = region.beg[0];
  36150. plen = region.end[0] - beg;
  36151. }
  36152. ByteList replValue = repl.value;
  36153. if (replValue.realSize > plen) {
  36154. modify(value.realSize + replValue.realSize - plen);
  36155. } else {
  36156. modify();
  36157. }
  36158. if (repl.isTaint()) {
  36159. tainted = true;
  36160. }
  36161. if (replValue.realSize != plen) {
  36162. int src = value.begin + beg + plen;
  36163. int dst = value.begin + beg + replValue.realSize;
  36164. int length = value.realSize - beg - plen;
  36165. System.arraycopy(value.bytes, src, value.bytes, dst, length);
  36166. }
  36167. System.arraycopy(replValue.bytes, replValue.begin, value.bytes, value.begin + beg, replValue.realSize);
  36168. value.realSize += replValue.realSize - plen;
  36169. if (tainted) {
  36170. setTaint(true);
  36171. }
  36172. return this;
  36173. } else {
  36174. frame.setBackRef(context.getRuntime().getNil());
  36175. return context.getRuntime().getNil();
  36176. }
  36177. }
  36178. /**
  36179. * Variable-arity version for compatibility. Not bound to Ruby.
  36180. * @deprecated Use the versions with one or two arguments.
  36181. */
  36182. public IRubyObject gsub(ThreadContext context, IRubyObject[] args, Block block) {
  36183. switch (args.length) {
  36184. case 1:
  36185. return gsub(context, args[0], block);
  36186. case 2:
  36187. return gsub(context, args[0], args[1], block);
  36188. default:
  36189. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36190. return null; // not reached
  36191. }
  36192. }
  36193. /** rb_str_gsub
  36194. *
  36195. */
  36196. @JRubyMethod(name = "gsub", frame = true, reads = BACKREF, writes = BACKREF)
  36197. public IRubyObject gsub(ThreadContext context, IRubyObject arg0, Block block) {
  36198. return gsub(context, arg0, block, false);
  36199. }
  36200. /** rb_str_gsub
  36201. *
  36202. */
  36203. @JRubyMethod(name = "gsub", frame = true, reads = BACKREF, writes = BACKREF)
  36204. public IRubyObject gsub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  36205. return gsub(context, arg0, arg1, block, false);
  36206. }
  36207. /**
  36208. * Variable-arity version for compatibility. Not bound to Ruby.
  36209. * @deprecated Use the versions with one or two arguments.
  36210. */
  36211. public IRubyObject gsub_bang(ThreadContext context, IRubyObject[] args, Block block) {
  36212. switch (args.length) {
  36213. case 1:
  36214. return gsub_bang(context, args[0], block);
  36215. case 2:
  36216. return gsub_bang(context, args[0], args[1], block);
  36217. default:
  36218. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36219. return null; // not reached
  36220. }
  36221. }
  36222. /** rb_str_gsub_bang
  36223. *
  36224. */
  36225. @JRubyMethod(name = "gsub!", frame = true, reads = BACKREF, writes = BACKREF)
  36226. public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, Block block) {
  36227. return gsub(context, arg0, block, true);
  36228. }
  36229. /** rb_str_gsub_bang
  36230. *
  36231. */
  36232. @JRubyMethod(name = "gsub!", frame = true, reads = BACKREF, writes = BACKREF)
  36233. public IRubyObject gsub_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block) {
  36234. return gsub(context, arg0, arg1, block, true);
  36235. }
  36236. private final IRubyObject gsub(ThreadContext context, IRubyObject arg0, Block block, final boolean bang) {
  36237. if (block.isGiven()) {
  36238. RubyRegexp rubyRegex = getPattern(arg0, true);
  36239. Regex regex = rubyRegex.getPattern();
  36240. return gsubCommon(regex, context, bang, true, rubyRegex, block, null, false);
  36241. } else {
  36242. throw context.getRuntime().newArgumentError("wrong number of arguments (1 for 2)");
  36243. }
  36244. }
  36245. private final IRubyObject gsub(ThreadContext context, IRubyObject arg0, IRubyObject arg1, Block block, final boolean bang) {
  36246. IRubyObject repl = arg1.convertToString();
  36247. RubyRegexp rubyRegex = getPattern(arg0, true);
  36248. Regex regex = rubyRegex.getPattern();
  36249. return gsubCommon(regex, context, bang, false, rubyRegex, block, repl, repl.isTaint());
  36250. }
  36251. private IRubyObject gsubCommon(Regex regex, ThreadContext context, final boolean bang, final boolean iter, RubyRegexp rubyRegex, Block block, IRubyObject repl, boolean tainted) {
  36252. int begin = value.begin;
  36253. int range = begin + value.realSize;
  36254. Matcher matcher = regex.matcher(value.bytes, begin, range);
  36255. int beg = matcher.search(begin, range, Option.NONE);
  36256. Frame frame = context.getPreviousFrame();
  36257. if (beg < 0) {
  36258. frame.setBackRef(context.getRuntime().getNil());
  36259. return bang ? context.getRuntime().getNil() : strDup(context.getRuntime()); /* bang: true, no match, no substitution */
  36260. }
  36261. int blen = value.realSize + 30; /* len + margin */
  36262. ByteList dest = new ByteList(blen);
  36263. dest.realSize = blen;
  36264. int buf = 0;
  36265. int bp = 0;
  36266. int cp = value.begin;
  36267. int offset = 0;
  36268. RubyString val;
  36269. RubyMatchData match = null;
  36270. while (beg >= 0) {
  36271. final int begz;
  36272. final int endz;
  36273. if (iter) {
  36274. byte[] bytes = value.bytes;
  36275. int size = value.realSize;
  36276. match = rubyRegex.updateBackRef(context, this, frame, matcher);
  36277. match.use();
  36278. if (regex.numberOfCaptures() == 0) {
  36279. begz = matcher.getBegin();
  36280. endz = matcher.getEnd();
  36281. val = objAsString(context, block.yield(context, substr(context.getRuntime(), begz, endz - begz)));
  36282. } else {
  36283. Region region = matcher.getRegion();
  36284. begz = region.beg[0];
  36285. endz = region.end[0];
  36286. val = objAsString(context, block.yield(context, substr(context.getRuntime(), begz, endz - begz)));
  36287. }
  36288. modifyCheck(bytes, size);
  36289. if (bang) {
  36290. frozenCheck();
  36291. }
  36292. } else {
  36293. val = rubyRegex.regsub((RubyString) repl, this, matcher);
  36294. if (regex.numberOfCaptures() == 0) {
  36295. begz = matcher.getBegin();
  36296. endz = matcher.getEnd();
  36297. } else {
  36298. Region region = matcher.getRegion();
  36299. begz = region.beg[0];
  36300. endz = region.end[0];
  36301. }
  36302. }
  36303. if (val.isTaint()) {
  36304. tainted = true;
  36305. }
  36306. ByteList vbuf = val.value;
  36307. int len = (bp - buf) + (beg - offset) + vbuf.realSize + 3;
  36308. if (blen < len) {
  36309. while (blen < len) {
  36310. blen <<= 1;
  36311. }
  36312. len = bp - buf;
  36313. dest.realloc(blen);
  36314. dest.realSize = blen;
  36315. bp = buf + len;
  36316. }
  36317. len = beg - offset; /* copy pre-match substr */
  36318. System.arraycopy(value.bytes, cp, dest.bytes, bp, len);
  36319. bp += len;
  36320. System.arraycopy(vbuf.bytes, vbuf.begin, dest.bytes, bp, vbuf.realSize);
  36321. bp += vbuf.realSize;
  36322. offset = endz;
  36323. if (begz == endz) {
  36324. if (value.realSize <= endz) {
  36325. break;
  36326. }
  36327. len = regex.getEncoding().length(value.bytes[begin + endz]);
  36328. System.arraycopy(value.bytes, begin + endz, dest.bytes, bp, len);
  36329. bp += len;
  36330. offset = endz + len;
  36331. }
  36332. cp = begin + offset;
  36333. if (offset > value.realSize) {
  36334. break;
  36335. }
  36336. beg = matcher.search(cp, range, Option.NONE);
  36337. }
  36338. if (value.realSize > offset) {
  36339. int len = bp - buf;
  36340. if (blen - len < value.realSize - offset) {
  36341. blen = len + value.realSize - offset;
  36342. dest.realloc(blen);
  36343. bp = buf + len;
  36344. }
  36345. System.arraycopy(value.bytes, cp, dest.bytes, bp, value.realSize - offset);
  36346. bp += value.realSize - offset;
  36347. }
  36348. if (match != null) {
  36349. frame.setBackRef(match);
  36350. } else {
  36351. rubyRegex.updateBackRef(context, this, frame, matcher);
  36352. }
  36353. dest.realSize = bp - buf;
  36354. if (bang) {
  36355. view(dest);
  36356. if (tainted) {
  36357. setTaint(true);
  36358. }
  36359. return this;
  36360. } else {
  36361. RubyString destStr = new RubyString(context.getRuntime(), getMetaClass(), dest);
  36362. destStr.infectBy(this);
  36363. if (tainted) {
  36364. destStr.setTaint(true);
  36365. }
  36366. return destStr;
  36367. }
  36368. }
  36369. /**
  36370. * Variable-arity version for compatibility. Not bound to Ruby.
  36371. * @deprecated Use the versions with one or two args.
  36372. */
  36373. public IRubyObject index(ThreadContext context, IRubyObject[] args) {
  36374. switch (args.length) {
  36375. case 1:
  36376. return index(context, args[0]);
  36377. case 2:
  36378. return index(context, args[0], args[1]);
  36379. default:
  36380. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36381. return null; // not reached
  36382. }
  36383. }
  36384. /** rb_str_index_m
  36385. *
  36386. */
  36387. @JRubyMethod(reads = BACKREF, writes = BACKREF)
  36388. public IRubyObject index(ThreadContext context, IRubyObject arg0) {
  36389. return indexCommon(0, arg0, context);
  36390. }
  36391. /** rb_str_index_m
  36392. *
  36393. */
  36394. @JRubyMethod(reads = BACKREF, writes = BACKREF)
  36395. public IRubyObject index(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  36396. int pos = RubyNumeric.num2int(arg1);
  36397. if (pos < 0) {
  36398. pos += value.realSize;
  36399. if (pos < 0) {
  36400. if (arg0 instanceof RubyRegexp) {
  36401. context.getPreviousFrame().setBackRef(context.getRuntime().getNil());
  36402. }
  36403. return context.getRuntime().getNil();
  36404. }
  36405. }
  36406. return indexCommon(pos, arg0, context);
  36407. }
  36408. private IRubyObject indexCommon(int pos, IRubyObject sub, ThreadContext context) throws RaiseException {
  36409. if (sub instanceof RubyRegexp) {
  36410. RubyRegexp regSub = (RubyRegexp) sub;
  36411. pos = regSub.adjustStartPos(this, pos, false);
  36412. pos = regSub.search(context, this, pos, false);
  36413. } else if (sub instanceof RubyFixnum) {
  36414. int c_int = RubyNumeric.fix2int(sub);
  36415. if (c_int < 0x00 || c_int > 0xFF) {
  36416. // out of byte range
  36417. // there will be no match for sure
  36418. return context.getRuntime().getNil();
  36419. }
  36420. byte c = (byte) c_int;
  36421. byte[] bytes = value.bytes;
  36422. int end = value.begin + value.realSize;
  36423. pos += value.begin;
  36424. for (; pos < end; pos++) {
  36425. if (bytes[pos] == c) {
  36426. return RubyFixnum.newFixnum(context.getRuntime(), pos - value.begin);
  36427. }
  36428. }
  36429. return context.getRuntime().getNil();
  36430. } else if (sub instanceof RubyString) {
  36431. pos = strIndex((RubyString) sub, pos);
  36432. } else {
  36433. IRubyObject tmp = sub.checkStringType();
  36434. if (tmp.isNil()) {
  36435. throw context.getRuntime().newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
  36436. }
  36437. pos = strIndex((RubyString) tmp, pos);
  36438. }
  36439. return pos == -1 ? context.getRuntime().getNil() : RubyFixnum.newFixnum(context.getRuntime(), pos);
  36440. }
  36441. private int strIndex(RubyString sub, int offset) {
  36442. if (offset < 0) {
  36443. offset += value.realSize;
  36444. if (offset < 0) return -1;
  36445. }
  36446. if (value.realSize - offset < sub.value.realSize) return -1;
  36447. if (sub.value.realSize == 0) return offset;
  36448. return value.indexOf(sub.value, offset);
  36449. }
  36450. /**
  36451. * Variable-arity version for compatibility. Not bound to Ruby.
  36452. * @deprecated Use the versions with one or two arguments.
  36453. */
  36454. public IRubyObject rindex(ThreadContext context, IRubyObject[] args) {
  36455. switch (args.length) {
  36456. case 1:
  36457. return rindex(context, args[0]);
  36458. case 2:
  36459. return rindex(context, args[0], args[1]);
  36460. default:
  36461. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36462. return null; // not reached
  36463. }
  36464. }
  36465. /** rb_str_rindex_m
  36466. *
  36467. */
  36468. @JRubyMethod(reads = BACKREF, writes = BACKREF)
  36469. public IRubyObject rindex(ThreadContext context, IRubyObject arg0) {
  36470. return rindexCommon(arg0, value.realSize, context);
  36471. }
  36472. /** rb_str_rindex_m
  36473. *
  36474. */
  36475. @JRubyMethod(reads = BACKREF, writes = BACKREF)
  36476. public IRubyObject rindex(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  36477. int pos = RubyNumeric.num2int(arg1);
  36478. if (pos < 0) {
  36479. pos += value.realSize;
  36480. if (pos < 0) {
  36481. if (arg0 instanceof RubyRegexp) {
  36482. context.getPreviousFrame().setBackRef(context.getRuntime().getNil());
  36483. }
  36484. return context.getRuntime().getNil();
  36485. }
  36486. }
  36487. if (pos > value.realSize) pos = value.realSize;
  36488. return rindexCommon(arg0, pos, context);
  36489. }
  36490. private IRubyObject rindexCommon(final IRubyObject sub, int pos, ThreadContext context) throws RaiseException {
  36491. if (sub instanceof RubyRegexp) {
  36492. RubyRegexp regSub = (RubyRegexp) sub;
  36493. if (regSub.length() > 0) {
  36494. pos = regSub.adjustStartPos(this, pos, true);
  36495. pos = regSub.search(context, this, pos, true);
  36496. }
  36497. if (pos >= 0) {
  36498. return RubyFixnum.newFixnum(context.getRuntime(), pos);
  36499. }
  36500. } else if (sub instanceof RubyString) {
  36501. pos = strRindex((RubyString) sub, pos);
  36502. if (pos >= 0) return RubyFixnum.newFixnum(context.getRuntime(), pos);
  36503. } else if (sub instanceof RubyFixnum) {
  36504. int c_int = RubyNumeric.fix2int(sub);
  36505. if (c_int < 0x00 || c_int > 0xFF) {
  36506. // out of byte range
  36507. // there will be no match for sure
  36508. return context.getRuntime().getNil();
  36509. }
  36510. byte c = (byte) c_int;
  36511. byte[] bytes = value.bytes;
  36512. int pbeg = value.begin;
  36513. int p = pbeg + pos;
  36514. if (pos == value.realSize) {
  36515. if (pos == 0) {
  36516. return context.getRuntime().getNil();
  36517. }
  36518. --p;
  36519. }
  36520. while (pbeg <= p) {
  36521. if (bytes[p] == c) {
  36522. return RubyFixnum.newFixnum(context.getRuntime(), p - value.begin);
  36523. }
  36524. p--;
  36525. }
  36526. return context.getRuntime().getNil();
  36527. } else {
  36528. IRubyObject tmp = sub.checkStringType();
  36529. if (tmp.isNil()) throw context.getRuntime().newTypeError("type mismatch: " + sub.getMetaClass().getName() + " given");
  36530. pos = strRindex((RubyString) tmp, pos);
  36531. if (pos >= 0) return RubyFixnum.newFixnum(context.getRuntime(), pos);
  36532. }
  36533. return context.getRuntime().getNil();
  36534. }
  36535. private int strRindex(RubyString sub, int pos) {
  36536. int subLength = sub.value.realSize;
  36537. /* substring longer than string */
  36538. if (value.realSize < subLength) return -1;
  36539. if (value.realSize - pos < subLength) pos = value.realSize - subLength;
  36540. return value.lastIndexOf(sub.value, pos);
  36541. }
  36542. /* rb_str_substr */
  36543. public IRubyObject substr(int beg, int len) {
  36544. return substr(getRuntime(), beg, len);
  36545. }
  36546. public IRubyObject substr(Ruby runtime, int beg, int len) {
  36547. int length = value.length();
  36548. if (len < 0 || beg > length) return getRuntime().getNil();
  36549. if (beg < 0) {
  36550. beg += length;
  36551. if (beg < 0) return getRuntime().getNil();
  36552. }
  36553. int end = Math.min(length, beg + len);
  36554. return makeShared(getRuntime(), beg, end - beg);
  36555. }
  36556. /* rb_str_replace */
  36557. public IRubyObject replace(int beg, int len, RubyString replaceWith) {
  36558. if (beg + len >= value.length()) len = value.length() - beg;
  36559. modify();
  36560. value.unsafeReplace(beg,len,replaceWith.value);
  36561. return infectBy(replaceWith);
  36562. }
  36563. /**
  36564. * Variable-arity version for compatibility. Not bound to Ruby.
  36565. * @deprecated Use the versions with one or two args
  36566. */
  36567. public IRubyObject op_aref(ThreadContext context, IRubyObject[] args) {
  36568. switch (args.length) {
  36569. case 1:
  36570. return op_aref(context, args[0]);
  36571. case 2:
  36572. return op_aref(context, args[0], args[1]);
  36573. default:
  36574. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36575. return null; // not reached
  36576. }
  36577. }
  36578. /** rb_str_aref, rb_str_aref_m
  36579. *
  36580. */
  36581. @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
  36582. public IRubyObject op_aref(ThreadContext context, IRubyObject arg1, IRubyObject arg2) {
  36583. if (arg1 instanceof RubyRegexp) {
  36584. if(((RubyRegexp)arg1).search(context, this, 0, false) >= 0) {
  36585. return RubyRegexp.nth_match(RubyNumeric.fix2int(arg2), context.getCurrentFrame().getBackRef());
  36586. }
  36587. return context.getRuntime().getNil();
  36588. }
  36589. return substr(context.getRuntime(), RubyNumeric.fix2int(arg1), RubyNumeric.fix2int(arg2));
  36590. }
  36591. /** rb_str_aref, rb_str_aref_m
  36592. *
  36593. */
  36594. @JRubyMethod(name = {"[]", "slice"}, reads = BACKREF, writes = BACKREF)
  36595. public IRubyObject op_aref(ThreadContext context, IRubyObject arg) {
  36596. if (arg instanceof RubyRegexp) {
  36597. if(((RubyRegexp)arg).search(context, this, 0, false) >= 0) {
  36598. return RubyRegexp.nth_match(0, context.getCurrentFrame().getBackRef());
  36599. }
  36600. return context.getRuntime().getNil();
  36601. } else if (arg instanceof RubyString) {
  36602. return value.indexOf(stringValue(arg).value) != -1 ?
  36603. arg : context.getRuntime().getNil();
  36604. } else if (arg instanceof RubyRange) {
  36605. long[] begLen = ((RubyRange) arg).begLen(value.length(), 0);
  36606. return begLen == null ? context.getRuntime().getNil() :
  36607. substr(context.getRuntime(), (int) begLen[0], (int) begLen[1]);
  36608. }
  36609. int idx = (int) arg.convertToInteger().getLongValue();
  36610. if (idx < 0) idx += value.length();
  36611. if (idx < 0 || idx >= value.length()) return context.getRuntime().getNil();
  36612. return context.getRuntime().newFixnum(value.get(idx) & 0xFF);
  36613. }
  36614. /**
  36615. * rb_str_subpat_set
  36616. *
  36617. */
  36618. private void subpatSet(ThreadContext context, RubyRegexp regexp, int nth, IRubyObject repl) {
  36619. RubyMatchData match;
  36620. int start, end, len;
  36621. if (regexp.search(context, this, 0, false) < 0) throw context.getRuntime().newIndexError("regexp not matched");
  36622. match = (RubyMatchData)context.getCurrentFrame().getBackRef();
  36623. if (match.regs == null) {
  36624. if (nth >= 1) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
  36625. if (nth < 0) {
  36626. if(-nth >= 1) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
  36627. nth += 1;
  36628. }
  36629. start = match.begin;
  36630. if(start == -1) throw context.getRuntime().newIndexError("regexp group " + nth + " not matched");
  36631. end = match.end;
  36632. } else {
  36633. if(nth >= match.regs.numRegs) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
  36634. if(nth < 0) {
  36635. if(-nth >= match.regs.numRegs) throw context.getRuntime().newIndexError("index " + nth + " out of regexp");
  36636. nth += match.regs.numRegs;
  36637. }
  36638. start = match.regs.beg[nth];
  36639. if(start == -1) throw context.getRuntime().newIndexError("regexp group " + nth + " not matched");
  36640. end = match.regs.end[nth];
  36641. }
  36642. len = end - start;
  36643. replace(start, len, stringValue(repl));
  36644. }
  36645. /**
  36646. * Variable arity version for compatibility. Not bound to a Ruby method.
  36647. * @deprecated Use the versions with two or three args.
  36648. */
  36649. public IRubyObject op_aset(ThreadContext context, IRubyObject[] args) {
  36650. switch (args.length) {
  36651. case 2:
  36652. return op_aset(context, args[0], args[1]);
  36653. case 3:
  36654. return op_aset(context, args[0], args[1], args[2]);
  36655. default:
  36656. Arity.raiseArgumentError(context.getRuntime(), args.length, 2, 3);
  36657. return null; // not reached
  36658. }
  36659. }
  36660. /** rb_str_aset, rb_str_aset_m
  36661. *
  36662. */
  36663. @JRubyMethod(name = "[]=", reads = BACKREF)
  36664. public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  36665. if (arg0 instanceof RubyFixnum || arg0.respondsTo("to_int")) { // FIXME: RubyNumeric or RubyInteger instead?
  36666. int idx = RubyNumeric.fix2int(arg0);
  36667. if (idx < 0) idx += value.length();
  36668. if (idx < 0 || idx >= value.length()) {
  36669. throw context.getRuntime().newIndexError("string index out of bounds");
  36670. }
  36671. if (arg1 instanceof RubyFixnum) {
  36672. modify();
  36673. value.set(idx, (byte) RubyNumeric.fix2int(arg1));
  36674. } else {
  36675. replace(idx, 1, stringValue(arg1));
  36676. }
  36677. return arg1;
  36678. }
  36679. if (arg0 instanceof RubyRegexp) {
  36680. RubyString repl = stringValue(arg1);
  36681. subpatSet(context, (RubyRegexp) arg0, 0, repl);
  36682. return repl;
  36683. }
  36684. if (arg0 instanceof RubyString) {
  36685. RubyString orig = (RubyString)arg0;
  36686. int beg = value.indexOf(orig.value);
  36687. if (beg < 0) throw context.getRuntime().newIndexError("string not matched");
  36688. replace(beg, orig.value.length(), stringValue(arg1));
  36689. return arg1;
  36690. }
  36691. if (arg0 instanceof RubyRange) {
  36692. long[] begLen = ((RubyRange) arg0).begLen(value.realSize, 2);
  36693. replace((int) begLen[0], (int) begLen[1], stringValue(arg1));
  36694. return arg1;
  36695. }
  36696. throw context.getRuntime().newTypeError("wrong argument type");
  36697. }
  36698. /** rb_str_aset, rb_str_aset_m
  36699. *
  36700. */
  36701. @JRubyMethod(name = "[]=", reads = BACKREF)
  36702. public IRubyObject op_aset(ThreadContext context, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
  36703. if (arg0 instanceof RubyRegexp) {
  36704. RubyString repl = stringValue(arg2);
  36705. int nth = RubyNumeric.fix2int(arg1);
  36706. subpatSet(context, (RubyRegexp) arg0, nth, repl);
  36707. return repl;
  36708. }
  36709. RubyString repl = stringValue(arg2);
  36710. int beg = RubyNumeric.fix2int(arg0);
  36711. int len = RubyNumeric.fix2int(arg1);
  36712. if (len < 0) throw context.getRuntime().newIndexError("negative length");
  36713. int strLen = value.length();
  36714. if (beg < 0) beg += strLen;
  36715. if (beg < 0 || (beg > 0 && beg > strLen)) {
  36716. throw context.getRuntime().newIndexError("string index out of bounds");
  36717. }
  36718. if (beg + len > strLen) len = strLen - beg;
  36719. replace(beg, len, repl);
  36720. return repl;
  36721. }
  36722. /**
  36723. * Variable arity version for compatibility. Not bound as a Ruby method.
  36724. * @deprecated Use the versions with one or two args.
  36725. */
  36726. public IRubyObject slice_bang(ThreadContext context, IRubyObject[] args) {
  36727. switch (args.length) {
  36728. case 1:
  36729. return slice_bang(context, args[0]);
  36730. case 2:
  36731. return slice_bang(context, args[0], args[1]);
  36732. default:
  36733. Arity.raiseArgumentError(context.getRuntime(), args.length, 1, 2);
  36734. return null; // not reached
  36735. }
  36736. }
  36737. /** rb_str_slice_bang
  36738. *
  36739. */
  36740. @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
  36741. public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0) {
  36742. IRubyObject result = op_aref(context, arg0);
  36743. if (result.isNil()) return result;
  36744. op_aset(context, arg0, RubyString.newEmptyString(context.getRuntime()));
  36745. return result;
  36746. }
  36747. /** rb_str_slice_bang
  36748. *
  36749. */
  36750. @JRubyMethod(name = "slice!", reads = BACKREF, writes = BACKREF)
  36751. public IRubyObject slice_bang(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  36752. IRubyObject result = op_aref(context, arg0, arg1);
  36753. if (result.isNil()) return result;
  36754. op_aset(context, arg0, arg1, RubyString.newEmptyString(context.getRuntime()));
  36755. return result;
  36756. }
  36757. @JRubyMethod(name = {"succ", "next"})
  36758. public IRubyObject succ(ThreadContext context) {
  36759. RubyString str = strDup(context.getRuntime());
  36760. str.succ_bang();
  36761. return str;
  36762. }
  36763. @JRubyMethod(name = {"succ!", "next!"})
  36764. public IRubyObject succ_bang() {
  36765. if (value.length() == 0) {
  36766. modifyCheck();
  36767. return this;
  36768. }
  36769. modify();
  36770. boolean alnumSeen = false;
  36771. int pos = -1;
  36772. int c = 0;
  36773. int n = 0;
  36774. for (int i = value.length() - 1; i >= 0; i--) {
  36775. c = value.get(i) & 0xFF;
  36776. if (isAlnum(c)) {
  36777. alnumSeen = true;
  36778. if ((isDigit(c) && c < '9') || (isLower(c) && c < 'z') || (isUpper(c) && c < 'Z')) {
  36779. value.set(i, (byte)(c + 1));
  36780. pos = -1;
  36781. break;
  36782. }
  36783. pos = i;
  36784. n = isDigit(c) ? '1' : (isLower(c) ? 'a' : 'A');
  36785. value.set(i, (byte)(isDigit(c) ? '0' : (isLower(c) ? 'a' : 'A')));
  36786. }
  36787. }
  36788. if (!alnumSeen) {
  36789. for (int i = value.length() - 1; i >= 0; i--) {
  36790. c = value.get(i) & 0xFF;
  36791. if (c < 0xff) {
  36792. value.set(i, (byte)(c + 1));
  36793. pos = -1;
  36794. break;
  36795. }
  36796. pos = i;
  36797. n = '\u0001';
  36798. value.set(i, 0);
  36799. }
  36800. }
  36801. if (pos > -1) {
  36802. // This represents left most digit in a set of incremented
  36803. // values? Therefore leftmost numeric must be '1' and not '0'
  36804. // 999 -> 1000, not 999 -> 0000. whereas chars should be
  36805. // zzz -> aaaa and non-alnum byte values should be "\377" -> "\001\000"
  36806. value.insert(pos, (byte) n);
  36807. }
  36808. return this;
  36809. }
  36810. /** rb_str_upto_m
  36811. *
  36812. */
  36813. @JRubyMethod(name = "upto", required = 1, frame = true)
  36814. public IRubyObject upto(ThreadContext context, IRubyObject str, Block block) {
  36815. return upto(context, str, false, block);
  36816. }
  36817. /* rb_str_upto */
  36818. public IRubyObject upto(ThreadContext context, IRubyObject str, boolean excl, Block block) {
  36819. RubyString end = str.convertToString();
  36820. int n = value.cmp(end.value);
  36821. if (n > 0 || (excl && n == 0)) return this;
  36822. IRubyObject afterEnd = end.callMethod(context, "succ");
  36823. RubyString current = this;
  36824. while (!current.op_equal(context, afterEnd).isTrue()) {
  36825. block.yield(context, current);
  36826. if (!excl && current.op_equal(context, end).isTrue()) break;
  36827. current = current.callMethod(context, "succ").convertToString();
  36828. if (excl && current.op_equal(context, end).isTrue()) break;
  36829. if (current.value.realSize > end.value.realSize || current.value.realSize == 0) break;
  36830. }
  36831. return this;
  36832. }
  36833. /** rb_str_include
  36834. *
  36835. */
  36836. @JRubyMethod(name = "include?", required = 1)
  36837. public RubyBoolean include_p(ThreadContext context, IRubyObject obj) {
  36838. if (obj instanceof RubyFixnum) {
  36839. int c = RubyNumeric.fix2int(obj);
  36840. for (int i = 0; i < value.length(); i++) {
  36841. if (value.get(i) == (byte)c) {
  36842. return context.getRuntime().getTrue();
  36843. }
  36844. }
  36845. return context.getRuntime().getFalse();
  36846. }
  36847. ByteList str = stringValue(obj).value;
  36848. return context.getRuntime().newBoolean(value.indexOf(str) != -1);
  36849. }
  36850. /**
  36851. * Variable-arity version for compatibility. Not bound as a Ruby method.
  36852. * @deprecated Use the versions with zero or one args.
  36853. */
  36854. public IRubyObject to_i(IRubyObject[] args) {
  36855. switch (args.length) {
  36856. case 0:
  36857. return to_i();
  36858. case 1:
  36859. return to_i(args[0]);
  36860. default:
  36861. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  36862. return null; // not reached
  36863. }
  36864. }
  36865. /** rb_str_to_i
  36866. *
  36867. */
  36868. @JRubyMethod(name = "to_i")
  36869. public IRubyObject to_i() {
  36870. return RubyNumeric.str2inum(getRuntime(), this, 10);
  36871. }
  36872. /** rb_str_to_i
  36873. *
  36874. */
  36875. @JRubyMethod(name = "to_i")
  36876. public IRubyObject to_i(IRubyObject arg0) {
  36877. long base = arg0.convertToInteger().getLongValue();
  36878. return RubyNumeric.str2inum(getRuntime(), this, (int) base);
  36879. }
  36880. /** rb_str_oct
  36881. *
  36882. */
  36883. @JRubyMethod(name = "oct")
  36884. public IRubyObject oct(ThreadContext context) {
  36885. if (isEmpty()) return context.getRuntime().newFixnum(0);
  36886. int base = 8;
  36887. int ix = value.begin;
  36888. while(ix < value.begin+value.realSize && ASCII.isSpace(value.bytes[ix] & 0xff)) {
  36889. ix++;
  36890. }
  36891. int pos = (value.bytes[ix] == '-' || value.bytes[ix] == '+') ? ix+1 : ix;
  36892. if((pos+1) < value.begin+value.realSize && value.bytes[pos] == '0') {
  36893. if(value.bytes[pos+1] == 'x' || value.bytes[pos+1] == 'X') {
  36894. base = 16;
  36895. } else if(value.bytes[pos+1] == 'b' || value.bytes[pos+1] == 'B') {
  36896. base = 2;
  36897. } else if(value.bytes[pos+1] == 'd' || value.bytes[pos+1] == 'D') {
  36898. base = 10;
  36899. }
  36900. }
  36901. return RubyNumeric.str2inum(context.getRuntime(), this, base);
  36902. }
  36903. /** rb_str_hex
  36904. *
  36905. */
  36906. @JRubyMethod(name = "hex")
  36907. public IRubyObject hex(ThreadContext context) {
  36908. return RubyNumeric.str2inum(context.getRuntime(), this, 16);
  36909. }
  36910. /** rb_str_to_f
  36911. *
  36912. */
  36913. @JRubyMethod(name = "to_f")
  36914. public IRubyObject to_f() {
  36915. return RubyNumeric.str2fnum(getRuntime(), this);
  36916. }
  36917. /**
  36918. * Variable arity version for compatibility. Not bound to a Ruby method.
  36919. * @deprecated Use the versions with zero, one, or two args.
  36920. */
  36921. public RubyArray split(ThreadContext context, IRubyObject[] args) {
  36922. switch (args.length) {
  36923. case 0:
  36924. return split(context);
  36925. case 1:
  36926. return split(context, args[0]);
  36927. case 2:
  36928. return split(context, args[0], args[1]);
  36929. default:
  36930. Arity.raiseArgumentError(context.getRuntime(), args.length, 0, 2);
  36931. return null; // not reached
  36932. }
  36933. }
  36934. /** rb_str_split_m
  36935. *
  36936. */
  36937. @JRubyMethod(writes = BACKREF)
  36938. public RubyArray split(ThreadContext context) {
  36939. return split(context, context.getRuntime().getNil());
  36940. }
  36941. /** rb_str_split_m
  36942. *
  36943. */
  36944. @JRubyMethod(writes = BACKREF)
  36945. public RubyArray split(ThreadContext context, IRubyObject arg0) {
  36946. return splitCommon(arg0, false, 0, 0, context);
  36947. }
  36948. /** rb_str_split_m
  36949. *
  36950. */
  36951. @JRubyMethod(writes = BACKREF)
  36952. public RubyArray split(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
  36953. final int lim = RubyNumeric.fix2int(arg1);
  36954. if (lim <= 0) {
  36955. return splitCommon(arg0, false, lim, 1, context);
  36956. } else {
  36957. if (lim == 1) return value.realSize == 0 ? context.getRuntime().newArray() : context.getRuntime().newArray(this);
  36958. return splitCommon(arg0, true, lim, 1, context);
  36959. }
  36960. }
  36961. private RubyArray splitCommon(IRubyObject spat, final boolean limit, final int lim, final int i, ThreadContext context) {
  36962. final RubyArray result;
  36963. if (spat.isNil() && (spat = context.getRuntime().getGlobalVariables().get("$;")).isNil()) {
  36964. result = awkSplit(limit, lim, i);
  36965. } else {
  36966. if (spat instanceof RubyString && ((RubyString) spat).value.realSize == 1) {
  36967. RubyString strSpat = (RubyString) spat;
  36968. if (strSpat.value.bytes[strSpat.value.begin] == (byte) ' ') {
  36969. result = awkSplit(limit, lim, i);
  36970. } else {
  36971. result = split(context, spat, limit, lim, i);
  36972. }
  36973. } else {
  36974. result = split(context, spat, limit, lim, i);
  36975. }
  36976. }
  36977. if (!limit && lim == 0) {
  36978. while (result.size() > 0 && ((RubyString) result.eltInternal(result.size() - 1)).value.realSize == 0) {
  36979. result.pop();
  36980. }
  36981. }
  36982. return result;
  36983. }
  36984. private RubyArray split(ThreadContext context, IRubyObject pat, boolean limit, int lim, int i) {
  36985. Ruby runtime = context.getRuntime();
  36986. final Regex regex = getPattern(pat, true).getPattern();
  36987. int beg, end, start;
  36988. int begin = value.begin;
  36989. start = begin;
  36990. beg = 0;
  36991. int range = value.begin + value.realSize;
  36992. final Matcher matcher = regex.matcher(value.bytes, value.begin, range);
  36993. boolean lastNull = false;
  36994. RubyArray result = runtime.newArray();
  36995. if (regex.numberOfCaptures() == 0) { // shorter path, no captures defined, no region will be returned
  36996. while ((end = matcher.search(start, range, Option.NONE)) >= 0) {
  36997. if (start == end + begin && matcher.getBegin() == matcher.getEnd()) {
  36998. if (value.realSize == 0) {
  36999. result.append(newEmptyString(runtime, getMetaClass()));
  37000. break;
  37001. } else if (lastNull) {
  37002. result.append(substr(runtime, beg, regex.getEncoding().length(value.bytes[begin + beg])));
  37003. beg = start - begin;
  37004. } else {
  37005. if (start == range) {
  37006. start++;
  37007. } else {
  37008. start += regex.getEncoding().length(value.bytes[start]);
  37009. }
  37010. lastNull = true;
  37011. continue;
  37012. }
  37013. } else {
  37014. result.append(substr(beg, end - beg));
  37015. beg = matcher.getEnd();
  37016. start = begin + matcher.getEnd();
  37017. }
  37018. lastNull = false;
  37019. if (limit && lim <= ++i) break;
  37020. }
  37021. } else {
  37022. while ((end = matcher.search(start, range, Option.NONE)) >= 0) {
  37023. final Region region = matcher.getRegion();
  37024. if (start == end + begin && region.beg[0] == region.end[0]) {
  37025. if (value.realSize == 0) {
  37026. result.append(newEmptyString(runtime, getMetaClass()));
  37027. break;
  37028. } else if (lastNull) {
  37029. result.append(substr(beg, regex.getEncoding().length(value.bytes[begin + beg])));
  37030. beg = start - begin;
  37031. } else {
  37032. if (start == range) {
  37033. start++;
  37034. } else {
  37035. start += regex.getEncoding().length(value.bytes[start]);
  37036. }
  37037. lastNull = true;
  37038. continue;
  37039. }
  37040. } else {
  37041. result.append(substr(beg, end - beg));
  37042. beg = start = region.end[0];
  37043. start += begin;
  37044. }
  37045. lastNull = false;
  37046. for (int idx=1; idx<region.numRegs; idx++) {
  37047. if (region.beg[idx] == -1) continue;
  37048. if (region.beg[idx] == region.end[idx]) {
  37049. result.append(newEmptyString(runtime, getMetaClass()));
  37050. } else {
  37051. result.append(substr(region.beg[idx], region.end[idx] - region.beg[idx]));
  37052. }
  37053. }
  37054. if (limit && lim <= ++i) break;
  37055. }
  37056. }
  37057. // only this case affects backrefs
  37058. context.getCurrentFrame().setBackRef(runtime.getNil());
  37059. if (value.realSize > 0 && (limit || value.realSize > beg || lim < 0)) {
  37060. if (value.realSize == beg) {
  37061. result.append(newEmptyString(runtime, getMetaClass()));
  37062. } else {
  37063. result.append(substr(beg, value.realSize - beg));
  37064. }
  37065. }
  37066. return result;
  37067. }
  37068. private RubyArray awkSplit(boolean limit, int lim, int i) {
  37069. Ruby runtime = getRuntime();
  37070. RubyArray result = runtime.newArray();
  37071. byte[]bytes = value.bytes;
  37072. int p = value.begin;
  37073. int endp = p + value.realSize;
  37074. boolean skip = true;
  37075. int end, beg = 0;
  37076. for (end = beg = 0; p < endp; p++) {
  37077. if (skip) {
  37078. if (ASCII.isSpace(bytes[p] & 0xff)) {
  37079. beg++;
  37080. } else {
  37081. end = beg + 1;
  37082. skip = false;
  37083. if (limit && lim <= i) break;
  37084. }
  37085. } else {
  37086. if (ASCII.isSpace(bytes[p] & 0xff)) {
  37087. result.append(makeShared(runtime, beg, end - beg));
  37088. skip = true;
  37089. beg = end + 1;
  37090. if (limit) i++;
  37091. } else {
  37092. end++;
  37093. }
  37094. }
  37095. }
  37096. if (value.realSize > 0 && (limit || value.realSize > beg || lim < 0)) {
  37097. if (value.realSize == beg) {
  37098. result.append(newEmptyString(runtime, getMetaClass()));
  37099. } else {
  37100. result.append(makeShared(runtime, beg, value.realSize - beg));
  37101. }
  37102. }
  37103. return result;
  37104. }
  37105. /** get_pat
  37106. *
  37107. */
  37108. private final RubyRegexp getPattern(IRubyObject obj, boolean quote) {
  37109. if (obj instanceof RubyRegexp) {
  37110. return (RubyRegexp)obj;
  37111. } else if (!(obj instanceof RubyString)) {
  37112. IRubyObject val = obj.checkStringType();
  37113. if (val.isNil()) throw getRuntime().newTypeError("wrong argument type " + obj.getMetaClass() + " (expected Regexp)");
  37114. obj = val;
  37115. }
  37116. return RubyRegexp.newRegexp(getRuntime(), ((RubyString)obj).value, 0, quote);
  37117. }
  37118. /** rb_str_scan
  37119. *
  37120. */
  37121. @JRubyMethod(name = "scan", required = 1, frame = true, reads = BACKREF, writes = BACKREF)
  37122. public IRubyObject scan(ThreadContext context, IRubyObject arg, Block block) {
  37123. Ruby runtime = context.getRuntime();
  37124. Frame frame = context.getPreviousFrame();
  37125. final RubyRegexp rubyRegex = getPattern(arg, true);
  37126. final Regex regex = rubyRegex.getPattern();
  37127. int range = value.begin + value.realSize;
  37128. final Matcher matcher = regex.matcher(value.bytes, value.begin, range);
  37129. matcher.value = 0; // implicit start argument to scanOnce(NG)
  37130. IRubyObject result;
  37131. if (!block.isGiven()) {
  37132. RubyArray ary = runtime.newArray();
  37133. if (regex.numberOfCaptures() == 0) {
  37134. while ((result = scanOnceNG(rubyRegex, matcher, range)) != null) ary.append(result);
  37135. } else {
  37136. while ((result = scanOnce(rubyRegex, matcher, range)) != null) ary.append(result);
  37137. }
  37138. if (ary.size() > 0) {
  37139. rubyRegex.updateBackRef(context, this, frame, matcher);
  37140. } else {
  37141. frame.setBackRef(runtime.getNil());
  37142. }
  37143. return ary;
  37144. } else {
  37145. byte[]bytes = value.bytes;
  37146. int size = value.realSize;
  37147. RubyMatchData match = null;
  37148. if (regex.numberOfCaptures() == 0) {
  37149. while ((result = scanOnceNG(rubyRegex, matcher, range)) != null) {
  37150. match = rubyRegex.updateBackRef(context, this, frame, matcher);
  37151. match.use();
  37152. block.yield(context, result);
  37153. modifyCheck(bytes, size);
  37154. }
  37155. } else {
  37156. while ((result = scanOnce(rubyRegex, matcher, range)) != null) {
  37157. match = rubyRegex.updateBackRef(context, this, frame, matcher);
  37158. match.use();
  37159. block.yield(context, result);
  37160. modifyCheck(bytes, size);
  37161. }
  37162. }
  37163. frame.setBackRef(match == null ? runtime.getNil() : match);
  37164. return this;
  37165. }
  37166. }
  37167. /**
  37168. * rb_enc_check
  37169. */
  37170. @SuppressWarnings("unused")
  37171. private Encoding encodingCheck(RubyRegexp pattern) {
  37172. // For 1.9 compatibility, should check encoding compat between string and pattern
  37173. return pattern.getKCode().getEncoding();
  37174. }
  37175. // no group version
  37176. private IRubyObject scanOnceNG(RubyRegexp regex, Matcher matcher, int range) {
  37177. if (matcher.search(matcher.value + value.begin, range, Option.NONE) >= 0) {
  37178. int end = matcher.getEnd();
  37179. if (matcher.getBegin() == end) {
  37180. if (value.realSize > end) {
  37181. matcher.value = end + regex.getPattern().getEncoding().length(value.bytes[value.begin + end]);
  37182. } else {
  37183. matcher.value = end + 1;
  37184. }
  37185. } else {
  37186. matcher.value = end;
  37187. }
  37188. return substr(matcher.getBegin(), end - matcher.getBegin()).infectBy(regex);
  37189. }
  37190. return null;
  37191. }
  37192. // group version
  37193. private IRubyObject scanOnce(RubyRegexp regex, Matcher matcher, int range) {
  37194. if (matcher.search(matcher.value + value.begin, range, Option.NONE) >= 0) {
  37195. Region region = matcher.getRegion();
  37196. int end = region.end[0];
  37197. if (region.beg[0] == end) {
  37198. if (value.realSize > end) {
  37199. matcher.value = end + regex.getPattern().getEncoding().length(value.bytes[value.begin + end]);
  37200. } else {
  37201. matcher.value = end + 1;
  37202. }
  37203. } else {
  37204. matcher.value = end;
  37205. }
  37206. RubyArray result = getRuntime().newArray(region.numRegs);
  37207. for (int i=1; i<region.numRegs; i++) {
  37208. int beg = region.beg[i];
  37209. if (beg == -1) {
  37210. result.append(getRuntime().getNil());
  37211. } else {
  37212. result.append(substr(beg, region.end[i] - beg).infectBy(regex));
  37213. }
  37214. }
  37215. return result;
  37216. }
  37217. return null;
  37218. }
  37219. private static final ByteList SPACE_BYTELIST = new ByteList(ByteList.plain(" "));
  37220. private final IRubyObject justify(IRubyObject arg0, char jflag) {
  37221. Ruby runtime = getRuntime();
  37222. int width = RubyFixnum.num2int(arg0);
  37223. int f, flen = 0;
  37224. byte[]fbuf;
  37225. IRubyObject pad;
  37226. f = SPACE_BYTELIST.begin;
  37227. flen = SPACE_BYTELIST.realSize;
  37228. fbuf = SPACE_BYTELIST.bytes;
  37229. pad = runtime.getNil();
  37230. return justifyCommon(width, jflag, flen, fbuf, f, runtime, pad);
  37231. }
  37232. private final IRubyObject justify(IRubyObject arg0, IRubyObject arg1, char jflag) {
  37233. Ruby runtime = getRuntime();
  37234. int width = RubyFixnum.num2int(arg0);
  37235. int f, flen = 0;
  37236. byte[]fbuf;
  37237. IRubyObject pad;
  37238. pad = arg1.convertToString();
  37239. ByteList fList = ((RubyString)pad).value;
  37240. f = fList.begin;
  37241. flen = fList.realSize;
  37242. if (flen == 0) throw runtime.newArgumentError("zero width padding");
  37243. fbuf = fList.bytes;
  37244. return justifyCommon(width, jflag, flen, fbuf, f, runtime, pad);
  37245. }
  37246. private IRubyObject justifyCommon(int width, char jflag, int flen, byte[] fbuf, int f, Ruby runtime, IRubyObject pad) {
  37247. if (width < 0 || value.realSize >= width) return strDup(runtime);
  37248. ByteList res = new ByteList(width);
  37249. res.realSize = width;
  37250. int p = res.begin;
  37251. int pend;
  37252. byte[] pbuf = res.bytes;
  37253. if (jflag != 'l') {
  37254. int n = width - value.realSize;
  37255. pend = p + ((jflag == 'r') ? n : n / 2);
  37256. if (flen <= 1) {
  37257. while (p < pend) {
  37258. pbuf[p++] = fbuf[f];
  37259. }
  37260. } else {
  37261. int q = f;
  37262. while (p + flen <= pend) {
  37263. System.arraycopy(fbuf, f, pbuf, p, flen);
  37264. p += flen;
  37265. }
  37266. while (p < pend) {
  37267. pbuf[p++] = fbuf[q++];
  37268. }
  37269. }
  37270. }
  37271. System.arraycopy(value.bytes, value.begin, pbuf, p, value.realSize);
  37272. if (jflag != 'r') {
  37273. p += value.realSize;
  37274. pend = res.begin + width;
  37275. if (flen <= 1) {
  37276. while (p < pend) {
  37277. pbuf[p++] = fbuf[f];
  37278. }
  37279. } else {
  37280. while (p + flen <= pend) {
  37281. System.arraycopy(fbuf, f, pbuf, p, flen);
  37282. p += flen;
  37283. }
  37284. while (p < pend) {
  37285. pbuf[p++] = fbuf[f++];
  37286. }
  37287. }
  37288. }
  37289. RubyString resStr = new RubyString(runtime, getMetaClass(), res);
  37290. resStr.infectBy(this);
  37291. if (flen > 0) {
  37292. resStr.infectBy(pad);
  37293. }
  37294. return resStr;
  37295. }
  37296. /**
  37297. * Variable-arity version for compatibility. Not bound to Ruby.
  37298. * @deprecated use the one or two argument versions.
  37299. */
  37300. public IRubyObject ljust(IRubyObject [] args) {
  37301. switch (args.length) {
  37302. case 1:
  37303. return ljust(args[0]);
  37304. case 2:
  37305. return ljust(args[0], args[1]);
  37306. default:
  37307. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  37308. return null; // not reached
  37309. }
  37310. }
  37311. /** rb_str_ljust
  37312. *
  37313. */
  37314. @JRubyMethod
  37315. public IRubyObject ljust(IRubyObject arg0) {
  37316. return justify(arg0, 'l');
  37317. }
  37318. /** rb_str_ljust
  37319. *
  37320. */
  37321. @JRubyMethod
  37322. public IRubyObject ljust(IRubyObject arg0, IRubyObject arg1) {
  37323. return justify(arg0, arg1, 'l');
  37324. }
  37325. /**
  37326. * Variable-arity version for compatibility. Not bound to Ruby.
  37327. * @deprecated use the one or two argument versions.
  37328. */
  37329. public IRubyObject rjust(IRubyObject [] args) {
  37330. switch (args.length) {
  37331. case 1:
  37332. return rjust(args[0]);
  37333. case 2:
  37334. return rjust(args[0], args[1]);
  37335. default:
  37336. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  37337. return null; // not reached
  37338. }
  37339. }
  37340. /** rb_str_rjust
  37341. *
  37342. */
  37343. @JRubyMethod
  37344. public IRubyObject rjust(IRubyObject arg0) {
  37345. return justify(arg0, 'r');
  37346. }
  37347. /** rb_str_rjust
  37348. *
  37349. */
  37350. @JRubyMethod
  37351. public IRubyObject rjust(IRubyObject arg0, IRubyObject arg1) {
  37352. return justify(arg0, arg1, 'r');
  37353. }
  37354. /**
  37355. * Variable-arity version for compatibility. Not bound to Ruby.
  37356. * @deprecated use the one or two argument versions.
  37357. */
  37358. public IRubyObject center(IRubyObject [] args) {
  37359. switch (args.length) {
  37360. case 1:
  37361. return center(args[0]);
  37362. case 2:
  37363. return center(args[0], args[1]);
  37364. default:
  37365. Arity.raiseArgumentError(getRuntime(), args.length, 1, 2);
  37366. return null; // not reached
  37367. }
  37368. }
  37369. /** rb_str_center
  37370. *
  37371. */
  37372. @JRubyMethod
  37373. public IRubyObject center(IRubyObject arg0) {
  37374. return justify(arg0, 'c');
  37375. }
  37376. /** rb_str_center
  37377. *
  37378. */
  37379. @JRubyMethod
  37380. public IRubyObject center(IRubyObject arg0, IRubyObject arg1) {
  37381. return justify(arg0, arg1, 'c');
  37382. }
  37383. @JRubyMethod(name = "chop")
  37384. public IRubyObject chop(ThreadContext context) {
  37385. RubyString str = strDup(context.getRuntime());
  37386. str.chop_bang();
  37387. return str;
  37388. }
  37389. /** rb_str_chop_bang
  37390. *
  37391. */
  37392. @JRubyMethod(name = "chop!")
  37393. public IRubyObject chop_bang() {
  37394. int end = value.realSize - 1;
  37395. if (end < 0) return getRuntime().getNil();
  37396. if ((value.bytes[value.begin + end]) == '\n') {
  37397. if (end > 0 && (value.bytes[value.begin + end - 1]) == '\r') end--;
  37398. }
  37399. view(0, end);
  37400. return this;
  37401. }
  37402. /**
  37403. * Variable-arity version for compatibility. Not bound to Ruby
  37404. *
  37405. * @param args
  37406. * @return
  37407. * @deprecated Use the zero or one argument versions.
  37408. */
  37409. public RubyString chomp(IRubyObject[] args) {
  37410. switch (args.length) {
  37411. case 0:
  37412. return chomp();
  37413. case 1:
  37414. return chomp(args[0]);
  37415. default:
  37416. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  37417. return null; // not reached
  37418. }
  37419. }
  37420. /** rb_str_chop
  37421. *
  37422. */
  37423. @JRubyMethod
  37424. public RubyString chomp() {
  37425. RubyString str = strDup(getRuntime());
  37426. str.chomp_bang();
  37427. return str;
  37428. }
  37429. /** rb_str_chop
  37430. *
  37431. */
  37432. @JRubyMethod
  37433. public RubyString chomp(IRubyObject arg0) {
  37434. RubyString str = strDup(getRuntime());
  37435. str.chomp_bang(arg0);
  37436. return str;
  37437. }
  37438. /**
  37439. * Variable-arity version for compatibility. Not bound to Ruby.
  37440. * @deprecated Use the zero or one argument versions.
  37441. */
  37442. public IRubyObject chomp_bang(IRubyObject[] args) {
  37443. switch (args.length) {
  37444. case 0:
  37445. return chomp_bang();
  37446. case 1:
  37447. return chomp_bang(args[0]);
  37448. default:
  37449. Arity.raiseArgumentError(getRuntime(), args.length, 0, 1);
  37450. return null; // not reached
  37451. }
  37452. }
  37453. /**
  37454. * rb_str_chomp_bang
  37455. *
  37456. * In the common case, removes CR and LF characters in various ways depending on the value of
  37457. * the optional args[0].
  37458. * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
  37459. * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
  37460. * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
  37461. * all(!)).
  37462. * @param args See method description.
  37463. */
  37464. @JRubyMethod(name = "chomp!")
  37465. public IRubyObject chomp_bang() {
  37466. IRubyObject rsObj;
  37467. int len = value.length();
  37468. if (len == 0) return getRuntime().getNil();
  37469. byte[]buff = value.bytes;
  37470. rsObj = getRuntime().getGlobalVariables().get("$/");
  37471. if (rsObj == getRuntime().getGlobalVariables().getDefaultSeparator()) {
  37472. int realSize = value.realSize;
  37473. int begin = value.begin;
  37474. if (buff[begin + len - 1] == (byte)'\n') {
  37475. realSize--;
  37476. if (realSize > 0 && buff[begin + realSize - 1] == (byte)'\r') realSize--;
  37477. view(0, realSize);
  37478. } else if (buff[begin + len - 1] == (byte)'\r') {
  37479. realSize--;
  37480. view(0, realSize);
  37481. } else {
  37482. modifyCheck();
  37483. return getRuntime().getNil();
  37484. }
  37485. return this;
  37486. }
  37487. return chompBangCommon(rsObj);
  37488. }
  37489. /**
  37490. * rb_str_chomp_bang
  37491. *
  37492. * In the common case, removes CR and LF characters in various ways depending on the value of
  37493. * the optional args[0].
  37494. * If args.length==0 removes one instance of CR, CRLF or LF from the end of the string.
  37495. * If args.length>0 and args[0] is "\n" then same behaviour as args.length==0 .
  37496. * If args.length>0 and args[0] is "" then removes trailing multiple LF or CRLF (but no CRs at
  37497. * all(!)).
  37498. * @param args See method description.
  37499. */
  37500. @JRubyMethod(name = "chomp!")
  37501. public IRubyObject chomp_bang(IRubyObject arg0) {
  37502. return chompBangCommon(arg0);
  37503. }
  37504. private IRubyObject chompBangCommon(IRubyObject rsObj) {
  37505. if (rsObj.isNil()) {
  37506. return getRuntime().getNil();
  37507. }
  37508. RubyString rs = rsObj.convertToString();
  37509. int len = value.realSize;
  37510. int begin = value.begin;
  37511. if (len == 0) {
  37512. return getRuntime().getNil();
  37513. }
  37514. byte[] buff = value.bytes;
  37515. int rslen = rs.value.realSize;
  37516. if (rslen == 0) {
  37517. while (len > 0 && buff[begin + len - 1] == (byte) '\n') {
  37518. len--;
  37519. if (len > 0 && buff[begin + len - 1] == (byte) '\r') {
  37520. len--;
  37521. }
  37522. }
  37523. if (len < value.realSize) {
  37524. view(0, len);
  37525. return this;
  37526. }
  37527. return getRuntime().getNil();
  37528. }
  37529. if (rslen > len) {
  37530. return getRuntime().getNil();
  37531. }
  37532. byte newline = rs.value.bytes[rslen - 1];
  37533. if (rslen == 1 && newline == (byte) '\n') {
  37534. buff = value.bytes;
  37535. int realSize = value.realSize;
  37536. if (buff[begin + len - 1] == (byte) '\n') {
  37537. realSize--;
  37538. if (realSize > 0 && buff[begin + realSize - 1] == (byte) '\r') {
  37539. realSize--;
  37540. }
  37541. view(0, realSize);
  37542. } else if (buff[begin + len - 1] == (byte) '\r') {
  37543. realSize--;
  37544. view(0, realSize);
  37545. } else {
  37546. modifyCheck();
  37547. return getRuntime().getNil();
  37548. }
  37549. return this;
  37550. }
  37551. if (buff[begin + len - 1] == newline && rslen <= 1 || value.endsWith(rs.value)) {
  37552. view(0, value.realSize - rslen);
  37553. return this;
  37554. }
  37555. return getRuntime().getNil();
  37556. }
  37557. /** rb_str_lstrip
  37558. *
  37559. */
  37560. @JRubyMethod
  37561. public IRubyObject lstrip(ThreadContext context) {
  37562. RubyString str = strDup(context.getRuntime());
  37563. str.lstrip_bang();
  37564. return str;
  37565. }
  37566. /** rb_str_lstrip_bang
  37567. */
  37568. @JRubyMethod(name = "lstrip!")
  37569. public IRubyObject lstrip_bang() {
  37570. if (value.realSize == 0) return getRuntime().getNil();
  37571. int i=0;
  37572. while (i < value.realSize && ASCII.isSpace(value.bytes[value.begin + i] & 0xff)) i++;
  37573. if (i > 0) {
  37574. view(i, value.realSize - i);
  37575. return this;
  37576. }
  37577. return getRuntime().getNil();
  37578. }
  37579. /** rb_str_rstrip
  37580. *
  37581. */
  37582. @JRubyMethod
  37583. public IRubyObject rstrip(ThreadContext context) {
  37584. RubyString str = strDup(context.getRuntime());
  37585. str.rstrip_bang();
  37586. return str;
  37587. }
  37588. /** rb_str_rstrip_bang
  37589. */
  37590. @JRubyMethod(name = "rstrip!")
  37591. public IRubyObject rstrip_bang() {
  37592. if (value.realSize == 0) return getRuntime().getNil();
  37593. int i=value.realSize - 1;
  37594. while (i >= 0 && value.bytes[value.begin+i] == 0) i--;
  37595. while (i >= 0 && ASCII.isSpace(value.bytes[value.begin + i] & 0xff)) i--;
  37596. if (i < value.realSize - 1) {
  37597. view(0, i + 1);
  37598. return this;
  37599. }
  37600. return getRuntime().getNil();
  37601. }
  37602. /** rb_str_strip
  37603. *
  37604. */
  37605. @JRubyMethod
  37606. public IRubyObject strip(ThreadContext context) {
  37607. RubyString str = strDup(context.getRuntime());
  37608. str.strip_bang();
  37609. return str;
  37610. }
  37611. /** rb_str_strip_bang
  37612. */
  37613. @JRubyMethod(name = "strip!")
  37614. public IRubyObject strip_bang() {
  37615. IRubyObject l = lstrip_bang();
  37616. IRubyObject r = rstrip_bang();
  37617. if(l.isNil() && r.isNil()) {
  37618. return l;
  37619. }
  37620. return this;
  37621. }
  37622. /** rb_str_count
  37623. *
  37624. */
  37625. @JRubyMethod(name = "count", required = 1, rest = true)
  37626. public IRubyObject count(IRubyObject[] args) {
  37627. if (args.length < 1) throw getRuntime().newArgumentError("wrong number of arguments");
  37628. if (value.realSize == 0) return getRuntime().newFixnum(0);
  37629. boolean[]table = new boolean[TRANS_SIZE];
  37630. boolean init = true;
  37631. for (int i=0; i<args.length; i++) {
  37632. RubyString s = args[i].convertToString();
  37633. s.setup_table(table, init);
  37634. init = false;
  37635. }
  37636. int s = value.begin;
  37637. int send = s + value.realSize;
  37638. byte[]buf = value.bytes;
  37639. int i = 0;
  37640. while (s < send) if (table[buf[s++] & 0xff]) i++;
  37641. return getRuntime().newFixnum(i);
  37642. }
  37643. /** rb_str_delete
  37644. *
  37645. */
  37646. @JRubyMethod(name = "delete", required = 1, rest = true)
  37647. public IRubyObject delete(ThreadContext context, IRubyObject[] args) {
  37648. RubyString str = strDup(context.getRuntime());
  37649. str.delete_bang(args);
  37650. return str;
  37651. }
  37652. /** rb_str_delete_bang
  37653. *
  37654. */
  37655. @JRubyMethod(name = "delete!", required = 1, rest = true)
  37656. public IRubyObject delete_bang(IRubyObject[] args) {
  37657. if (args.length < 1) throw getRuntime().newArgumentError("wrong number of arguments");
  37658. boolean[]squeeze = new boolean[TRANS_SIZE];
  37659. boolean init = true;
  37660. for (int i=0; i<args.length; i++) {
  37661. RubyString s = args[i].convertToString();
  37662. s.setup_table(squeeze, init);
  37663. init = false;
  37664. }
  37665. modify();
  37666. if (value.realSize == 0) return getRuntime().getNil();
  37667. int s = value.begin;
  37668. int t = s;
  37669. int send = s + value.realSize;
  37670. byte[]buf = value.bytes;
  37671. boolean modify = false;
  37672. while (s < send) {
  37673. if (squeeze[buf[s] & 0xff]) {
  37674. modify = true;
  37675. } else {
  37676. buf[t++] = buf[s];
  37677. }
  37678. s++;
  37679. }
  37680. value.realSize = t - value.begin;
  37681. if (modify) return this;
  37682. return getRuntime().getNil();
  37683. }
  37684. /** rb_str_squeeze
  37685. *
  37686. */
  37687. @JRubyMethod(name = "squeeze", rest = true)
  37688. public IRubyObject squeeze(ThreadContext context, IRubyObject[] args) {
  37689. RubyString str = strDup(context.getRuntime());
  37690. str.squeeze_bang(args);
  37691. return str;
  37692. }
  37693. /** rb_str_squeeze_bang
  37694. *
  37695. */
  37696. @JRubyMethod(name = "squeeze!", rest = true)
  37697. public IRubyObject squeeze_bang(IRubyObject[] args) {
  37698. if (value.realSize == 0) {
  37699. modifyCheck();
  37700. return getRuntime().getNil();
  37701. }
  37702. final boolean squeeze[] = new boolean[TRANS_SIZE];
  37703. if (args.length == 0) {
  37704. for (int i=0; i<TRANS_SIZE; i++) squeeze[i] = true;
  37705. } else {
  37706. boolean init = true;
  37707. for (int i=0; i<args.length; i++) {
  37708. RubyString s = args[i].convertToString();
  37709. s.setup_table(squeeze, init);
  37710. init = false;
  37711. }
  37712. }
  37713. modify();
  37714. int s = value.begin;
  37715. int t = s;
  37716. int send = s + value.realSize;
  37717. byte[]buf = value.bytes;
  37718. int save = -1;
  37719. while (s < send) {
  37720. int c = buf[s++] & 0xff;
  37721. if (c != save || !squeeze[c]) buf[t++] = (byte)(save = c);
  37722. }
  37723. if (t - value.begin != value.realSize) { // modified
  37724. value.realSize = t - value.begin;
  37725. return this;
  37726. }
  37727. return getRuntime().getNil();
  37728. }
  37729. /** rb_str_tr
  37730. *
  37731. */
  37732. @JRubyMethod
  37733. public IRubyObject tr(ThreadContext context, IRubyObject src, IRubyObject repl) {
  37734. RubyString str = strDup(context.getRuntime());
  37735. str.tr_trans(src, repl, false);
  37736. return str;
  37737. }
  37738. /** rb_str_tr_bang
  37739. *
  37740. */
  37741. @JRubyMethod(name = "tr!")
  37742. public IRubyObject tr_bang(IRubyObject src, IRubyObject repl) {
  37743. return tr_trans(src, repl, false);
  37744. }
  37745. private static final class TR {
  37746. int gen, now, max;
  37747. int p, pend;
  37748. byte[]buf;
  37749. }
  37750. private static final int TRANS_SIZE = 256;
  37751. /** tr_setup_table
  37752. *
  37753. */
  37754. private final void setup_table(boolean[]table, boolean init) {
  37755. final boolean[]buf = new boolean[TRANS_SIZE];
  37756. final TR tr = new TR();
  37757. int c;
  37758. boolean cflag = false;
  37759. tr.p = value.begin;
  37760. tr.pend = value.begin + value.realSize;
  37761. tr.buf = value.bytes;
  37762. tr.gen = tr.now = tr.max = 0;
  37763. if (value.realSize > 1 && value.bytes[value.begin] == '^') {
  37764. cflag = true;
  37765. tr.p++;
  37766. }
  37767. if (init) for (int i=0; i<TRANS_SIZE; i++) table[i] = true;
  37768. for (int i=0; i<TRANS_SIZE; i++) buf[i] = cflag;
  37769. while ((c = trnext(tr)) >= 0) buf[c & 0xff] = !cflag;
  37770. for (int i=0; i<TRANS_SIZE; i++) table[i] = table[i] && buf[i];
  37771. }
  37772. /** tr_trans
  37773. *
  37774. */
  37775. private final IRubyObject tr_trans(IRubyObject src, IRubyObject repl, boolean sflag) {
  37776. if (value.realSize == 0) return getRuntime().getNil();
  37777. ByteList replList = repl.convertToString().value;
  37778. if (replList.realSize == 0) return delete_bang(new IRubyObject[]{src});
  37779. ByteList srcList = src.convertToString().value;
  37780. final TR trsrc = new TR();
  37781. final TR trrepl = new TR();
  37782. boolean cflag = false;
  37783. boolean modify = false;
  37784. trsrc.p = srcList.begin;
  37785. trsrc.pend = srcList.begin + srcList.realSize;
  37786. trsrc.buf = srcList.bytes;
  37787. if (srcList.realSize >= 2 && srcList.bytes[srcList.begin] == '^') {
  37788. cflag = true;
  37789. trsrc.p++;
  37790. }
  37791. trrepl.p = replList.begin;
  37792. trrepl.pend = replList.begin + replList.realSize;
  37793. trrepl.buf = replList.bytes;
  37794. trsrc.gen = trrepl.gen = 0;
  37795. trsrc.now = trrepl.now = 0;
  37796. trsrc.max = trrepl.max = 0;
  37797. int c;
  37798. final int[]trans = new int[TRANS_SIZE];
  37799. if (cflag) {
  37800. for (int i=0; i<TRANS_SIZE; i++) trans[i] = 1;
  37801. while ((c = trnext(trsrc)) >= 0) trans[c & 0xff] = -1;
  37802. while ((c = trnext(trrepl)) >= 0);
  37803. for (int i=0; i<TRANS_SIZE; i++) {
  37804. if (trans[i] >= 0) trans[i] = trrepl.now;
  37805. }
  37806. } else {
  37807. for (int i=0; i<TRANS_SIZE; i++) trans[i] = -1;
  37808. while ((c = trnext(trsrc)) >= 0) {
  37809. int r = trnext(trrepl);
  37810. if (r == -1) r = trrepl.now;
  37811. trans[c & 0xff] = r;
  37812. }
  37813. }
  37814. modify();
  37815. int s = value.begin;
  37816. int send = s + value.realSize;
  37817. byte sbuf[] = value.bytes;
  37818. if (sflag) {
  37819. int t = s;
  37820. int c0, last = -1;
  37821. while (s < send) {
  37822. c0 = sbuf[s++];
  37823. if ((c = trans[c0 & 0xff]) >= 0) {
  37824. if (last == c) continue;
  37825. last = c;
  37826. sbuf[t++] = (byte)(c & 0xff);
  37827. modify = true;
  37828. } else {
  37829. last = -1;
  37830. sbuf[t++] = (byte)c0;
  37831. }
  37832. }
  37833. if (value.realSize > (t - value.begin)) {
  37834. value.realSize = t - value.begin;
  37835. modify = true;
  37836. }
  37837. } else {
  37838. while (s < send) {
  37839. if ((c = trans[sbuf[s] & 0xff]) >= 0) {
  37840. sbuf[s] = (byte)(c & 0xff);
  37841. modify = true;
  37842. }
  37843. s++;
  37844. }
  37845. }
  37846. if (modify) return this;
  37847. return getRuntime().getNil();
  37848. }
  37849. /** trnext
  37850. *
  37851. */
  37852. private final int trnext(TR t) {
  37853. byte [] buf = t.buf;
  37854. for (;;) {
  37855. if (t.gen == 0) {
  37856. if (t.p == t.pend) return -1;
  37857. if (t.p < t.pend -1 && buf[t.p] == '\\') t.p++;
  37858. t.now = buf[t.p++];
  37859. if (t.p < t.pend - 1 && buf[t.p] == '-') {
  37860. t.p++;
  37861. if (t.p < t.pend) {
  37862. if (t.now > ((int)buf[t.p] & 0xFF)) {
  37863. t.p++;
  37864. continue;
  37865. }
  37866. t.gen = 1;
  37867. t.max = (int)buf[t.p++] & 0xFF;
  37868. }
  37869. }
  37870. return t.now & 0xff;
  37871. } else if (++t.now < t.max) {
  37872. return t.now & 0xff;
  37873. } else {
  37874. t.gen = 0;
  37875. return t.max & 0xff;
  37876. }
  37877. }
  37878. }
  37879. /** rb_str_tr_s
  37880. *
  37881. */
  37882. @JRubyMethod
  37883. public IRubyObject tr_s(ThreadContext context, IRubyObject src, IRubyObject repl) {
  37884. RubyString str = strDup(context.getRuntime());
  37885. str.tr_trans(src, repl, true);
  37886. return str;
  37887. }
  37888. /** rb_str_tr_s_bang
  37889. *
  37890. */
  37891. @JRubyMethod(name = "tr_s!")
  37892. public IRubyObject tr_s_bang(IRubyObject src, IRubyObject repl) {
  37893. return tr_trans(src, repl, true);
  37894. }
  37895. /** rb_str_each_line
  37896. *
  37897. */
  37898. @JRubyMethod(name = {"each_line", "each"}, required = 0, optional = 1, frame = true)
  37899. public IRubyObject each_line(ThreadContext context, IRubyObject[] args, Block block) {
  37900. byte newline;
  37901. int p = value.begin;
  37902. int pend = p + value.realSize;
  37903. int s;
  37904. int ptr = p;
  37905. int len = value.realSize;
  37906. int rslen;
  37907. IRubyObject line;
  37908. IRubyObject _rsep;
  37909. if (args.length == 0) {
  37910. _rsep = getRuntime().getGlobalVariables().get("$/");
  37911. } else {
  37912. _rsep = args[0];
  37913. }
  37914. if(_rsep.isNil()) {
  37915. block.yield(context, this);
  37916. return this;
  37917. }
  37918. RubyString rsep = stringValue(_rsep);
  37919. ByteList rsepValue = rsep.value;
  37920. byte[] strBytes = value.bytes;
  37921. rslen = rsepValue.realSize;
  37922. if(rslen == 0) {
  37923. newline = '\n';
  37924. } else {
  37925. newline = rsepValue.bytes[rsepValue.begin + rslen-1];
  37926. }
  37927. s = p;
  37928. p+=rslen;
  37929. for(; p < pend; p++) {
  37930. if(rslen == 0 && strBytes[p] == '\n') {
  37931. if(strBytes[++p] != '\n') {
  37932. continue;
  37933. }
  37934. while(p < pend && strBytes[p] == '\n') {
  37935. p++;
  37936. }
  37937. }
  37938. if(ptr<p && strBytes[p-1] == newline &&
  37939. (rslen <= 1 ||
  37940. ByteList.memcmp(rsepValue.bytes, rsepValue.begin, rslen, strBytes, p-rslen, rslen) == 0)) {
  37941. line = RubyString.newStringShared(getRuntime(), getMetaClass(), this.value.makeShared(s-ptr, p-s));
  37942. line.infectBy(this);
  37943. block.yield(context, line);
  37944. modifyCheck(strBytes,len);
  37945. s = p;
  37946. }
  37947. }
  37948. if(s != pend) {
  37949. if(p > pend) {
  37950. p = pend;
  37951. }
  37952. line = RubyString.newStringShared(getRuntime(), getMetaClass(), this.value.makeShared(s-ptr, p-s));
  37953. line.infectBy(this);
  37954. block.yield(context, line);
  37955. }
  37956. return this;
  37957. }
  37958. /**
  37959. * rb_str_each_byte
  37960. */
  37961. @JRubyMethod(name = "each_byte", frame = true)
  37962. public RubyString each_byte(ThreadContext context, Block block) {
  37963. Ruby runtime = getRuntime();
  37964. // Check the length every iteration, since
  37965. // the block can modify this string.
  37966. for (int i = 0; i < value.length(); i++) {
  37967. block.yield(context, runtime.newFixnum(value.get(i) & 0xFF));
  37968. }
  37969. return this;
  37970. }
  37971. /** rb_str_intern
  37972. *
  37973. */
  37974. public RubySymbol intern() {
  37975. String s = toString();
  37976. if (s.length() == 0) {
  37977. throw getRuntime().newArgumentError("interning empty string");
  37978. }
  37979. if (s.indexOf('\0') >= 0) {
  37980. throw getRuntime().newArgumentError("symbol string may not contain '\\0'");
  37981. }
  37982. return getRuntime().newSymbol(s);
  37983. }
  37984. @JRubyMethod(name = {"to_sym", "intern"})
  37985. public RubySymbol to_sym() {
  37986. return intern();
  37987. }
  37988. @JRubyMethod(name = "sum", optional = 1)
  37989. public RubyInteger sum(IRubyObject[] args) {
  37990. if (args.length > 1) {
  37991. throw getRuntime().newArgumentError("wrong number of arguments (" + args.length + " for 1)");
  37992. }
  37993. long bitSize = 16;
  37994. if (args.length == 1) {
  37995. long bitSizeArg = ((RubyInteger) args[0].convertToInteger()).getLongValue();
  37996. if (bitSizeArg > 0) {
  37997. bitSize = bitSizeArg;
  37998. }
  37999. }
  38000. long result = 0;
  38001. for (int i = 0; i < value.length(); i++) {
  38002. result += value.get(i) & 0xFF;
  38003. }
  38004. return getRuntime().newFixnum(bitSize == 0 ? result : result % (long) Math.pow(2, bitSize));
  38005. }
  38006. /** string_to_c
  38007. *
  38008. */
  38009. @JRubyMethod(name = "to_c", reads = BACKREF, writes = BACKREF, compat = CompatVersion.RUBY1_9)
  38010. public IRubyObject to_c(ThreadContext context) {
  38011. Ruby runtime = context.getRuntime();
  38012. Frame frame = context.getCurrentFrame();
  38013. IRubyObject backref = frame.getBackRef();
  38014. if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
  38015. IRubyObject s = RuntimeHelpers.invoke(
  38016. context, this, "gsub",
  38017. RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.underscores_pat),
  38018. runtime.newString(new ByteList(new byte[]{'_'})));
  38019. RubyArray a = RubyComplex.str_to_c_internal(context, s);
  38020. frame.setBackRef(backref);
  38021. if (!a.eltInternal(0).isNil()) {
  38022. return a.eltInternal(0);
  38023. } else {
  38024. return RubyComplex.newComplexCanonicalize(context, RubyFixnum.zero(runtime));
  38025. }
  38026. }
  38027. /** string_to_r
  38028. *
  38029. */
  38030. @JRubyMethod(name = "to_r", reads = BACKREF, writes = BACKREF, compat = CompatVersion.RUBY1_9)
  38031. public IRubyObject to_r(ThreadContext context) {
  38032. Ruby runtime = context.getRuntime();
  38033. Frame frame = context.getCurrentFrame();
  38034. IRubyObject backref = frame.getBackRef();
  38035. if (backref != null && backref instanceof RubyMatchData) ((RubyMatchData)backref).use();
  38036. IRubyObject s = RuntimeHelpers.invoke(
  38037. context, this, "gsub",
  38038. RubyRegexp.newRegexp(runtime, Numeric.ComplexPatterns.underscores_pat),
  38039. runtime.newString(new ByteList(new byte[]{'_'})));
  38040. RubyArray a = RubyRational.str_to_r_internal(context, s);
  38041. frame.setBackRef(backref);
  38042. if (!a.eltInternal(0).isNil()) {
  38043. return a.eltInternal(0);
  38044. } else {
  38045. return RubyRational.newRationalCanonicalize(context, RubyFixnum.zero(runtime));
  38046. }
  38047. }
  38048. public static RubyString unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  38049. RubyString result = newString(input.getRuntime(), input.unmarshalString());
  38050. input.registerLinkTarget(result);
  38051. return result;
  38052. }
  38053. /**
  38054. * @see org.jruby.util.Pack#unpack
  38055. */
  38056. @JRubyMethod
  38057. public RubyArray unpack(IRubyObject obj) {
  38058. return Pack.unpack(getRuntime(), this.value, stringValue(obj).value);
  38059. }
  38060. public void empty() {
  38061. value = ByteList.EMPTY_BYTELIST;
  38062. shareLevel = SHARE_LEVEL_BYTELIST;
  38063. }
  38064. /**
  38065. * Mutator for internal string representation.
  38066. *
  38067. * @param value The new java.lang.String this RubyString should encapsulate
  38068. * @deprecated
  38069. */
  38070. public void setValue(CharSequence value) {
  38071. view(ByteList.plain(value));
  38072. }
  38073. public void setValue(ByteList value) {
  38074. view(value);
  38075. }
  38076. public CharSequence getValue() {
  38077. return toString();
  38078. }
  38079. public byte[] getBytes() {
  38080. return value.bytes();
  38081. }
  38082. public ByteList getByteList() {
  38083. return value;
  38084. }
  38085. /** used by ar-jdbc
  38086. *
  38087. */
  38088. public String getUnicodeValue() {
  38089. try {
  38090. return new String(value.bytes,value.begin,value.realSize, "UTF8");
  38091. } catch (Exception e) {
  38092. throw new RuntimeException("Something's seriously broken with encodings", e);
  38093. }
  38094. }
  38095. @Override
  38096. public IRubyObject to_java() {
  38097. return MiniJava.javaToRuby(getRuntime(), new String(getBytes()));
  38098. }
  38099. }
  38100. /***** BEGIN LICENSE BLOCK *****
  38101. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  38102. *
  38103. * The contents of this file are subject to the Common Public
  38104. * License Version 1.0 (the "License"); you may not use this file
  38105. * except in compliance with the License. You may obtain a copy of
  38106. * the License at http://www.eclipse.org/legal/cpl-v10.html
  38107. *
  38108. * Software distributed under the License is distributed on an "AS
  38109. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  38110. * implied. See the License for the specific language governing
  38111. * rights and limitations under the License.
  38112. *
  38113. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  38114. * Copyright (C) 2006 Ryan Bell <ryan.l.bell@gmail.com>
  38115. * Copyright (C) 2007 Thomas E Enebo <enebo@acm.org>
  38116. * Copyright (C) 2008 Vladimir Sizikov <vsizikov@gmail.com>
  38117. *
  38118. * Alternatively, the contents of this file may be used under the terms of
  38119. * either of the GNU General Public License Version 2 or later (the "GPL"),
  38120. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  38121. * in which case the provisions of the GPL or the LGPL are applicable instead
  38122. * of those above. If you wish to allow use of your version of this file only
  38123. * under the terms of either the GPL or the LGPL, and not to allow others to
  38124. * use your version of this file under the terms of the CPL, indicate your
  38125. * decision by deleting the provisions above and replace them with the notice
  38126. * and other provisions required by the GPL or the LGPL. If you do not delete
  38127. * the provisions above, a recipient may use your version of this file under
  38128. * the terms of any one of the CPL, the GPL or the LGPL.
  38129. ***** END LICENSE BLOCK *****/
  38130. package org.jruby;
  38131. import java.util.ArrayList;
  38132. import java.util.List;
  38133. import org.jruby.anno.FrameField;
  38134. import org.jruby.anno.JRubyClass;
  38135. import org.jruby.anno.JRubyMethod;
  38136. import org.jruby.runtime.Block;
  38137. import org.jruby.runtime.MethodIndex;
  38138. import org.jruby.runtime.ObjectAllocator;
  38139. import org.jruby.runtime.ThreadContext;
  38140. import org.jruby.runtime.Visibility;
  38141. import org.jruby.runtime.builtin.IRubyObject;
  38142. import org.jruby.util.ByteList;
  38143. import org.jruby.util.TypeConverter;
  38144. import org.jruby.util.io.InvalidValueException;
  38145. import org.jruby.util.io.ModeFlags;
  38146. import org.jruby.util.io.Stream;
  38147. @JRubyClass(name="StringIO")
  38148. public class RubyStringIO extends RubyObject {
  38149. private static ObjectAllocator STRINGIO_ALLOCATOR = new ObjectAllocator() {
  38150. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  38151. return new RubyStringIO(runtime, klass);
  38152. }
  38153. };
  38154. public static RubyClass createStringIOClass(final Ruby runtime) {
  38155. RubyClass stringIOClass = runtime.defineClass(
  38156. "StringIO", runtime.fastGetClass("Data"), STRINGIO_ALLOCATOR);
  38157. stringIOClass.defineAnnotatedMethods(RubyStringIO.class);
  38158. stringIOClass.includeModule(runtime.getEnumerable());
  38159. return stringIOClass;
  38160. }
  38161. @JRubyMethod(name = "open", optional = 2, frame = true, meta = true)
  38162. public static IRubyObject open(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  38163. RubyStringIO strio = (RubyStringIO)((RubyClass)recv).newInstance(context, args, Block.NULL_BLOCK);
  38164. IRubyObject val = strio;
  38165. if (block.isGiven()) {
  38166. try {
  38167. val = block.yield(context, strio);
  38168. } finally {
  38169. strio.doFinalize();
  38170. }
  38171. }
  38172. return val;
  38173. }
  38174. protected RubyStringIO(Ruby runtime, RubyClass klass) {
  38175. super(runtime, klass);
  38176. }
  38177. private long pos = 0L;
  38178. private int lineno = 0;
  38179. private boolean eof = false;
  38180. /**
  38181. * ATTN: the value of internal might be reset to null
  38182. * (during StringIO.open with block), so watch out for that.
  38183. */
  38184. private RubyString internal;
  38185. // Has read/write been closed or is it still open for business
  38186. private boolean closedRead = false;
  38187. private boolean closedWrite = false;
  38188. // Support IO modes that this object was opened with
  38189. ModeFlags modes;
  38190. private void initializeModes(Object modeArgument) {
  38191. try {
  38192. if (modeArgument == null) {
  38193. modes = new ModeFlags(RubyIO.getIOModesIntFromString(getRuntime(), "r+"));
  38194. } else if (modeArgument instanceof Long) {
  38195. modes = new ModeFlags(((Long)modeArgument).longValue());
  38196. } else {
  38197. modes = new ModeFlags(RubyIO.getIOModesIntFromString(getRuntime(), (String) modeArgument));
  38198. }
  38199. } catch (InvalidValueException e) {
  38200. throw getRuntime().newErrnoEINVALError();
  38201. }
  38202. setupModes();
  38203. }
  38204. @JRubyMethod(name = "initialize", optional = 2, frame = true, visibility = Visibility.PRIVATE)
  38205. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  38206. Object modeArgument = null;
  38207. switch (args.length) {
  38208. case 0:
  38209. internal = RubyString.newEmptyString(getRuntime());
  38210. modeArgument = "r+";
  38211. break;
  38212. case 1:
  38213. internal = args[0].convertToString();
  38214. modeArgument = internal.isFrozen() ? "r" : "r+";
  38215. break;
  38216. case 2:
  38217. internal = args[0].convertToString();
  38218. if (args[1] instanceof RubyFixnum) {
  38219. modeArgument = RubyFixnum.fix2long(args[1]);
  38220. } else {
  38221. modeArgument = args[1].convertToString().toString();
  38222. }
  38223. break;
  38224. }
  38225. initializeModes(modeArgument);
  38226. if (modes.isWritable() && internal.isFrozen()) {
  38227. throw getRuntime().newErrnoEACCESError("Permission denied");
  38228. }
  38229. if (modes.isTruncate()) {
  38230. internal.modifyCheck();
  38231. internal.empty();
  38232. }
  38233. return this;
  38234. }
  38235. @JRubyMethod(visibility = Visibility.PRIVATE)
  38236. public IRubyObject initialize_copy(IRubyObject other) {
  38237. RubyStringIO otherIO = (RubyStringIO) TypeConverter.convertToType(
  38238. other, getRuntime().fastGetClass("StringIO"),
  38239. MethodIndex.getIndex("to_strio"), "to_strio");
  38240. if (this == otherIO) {
  38241. return this;
  38242. }
  38243. pos = otherIO.pos;
  38244. lineno = otherIO.lineno;
  38245. eof = otherIO.eof;
  38246. closedRead = otherIO.closedRead;
  38247. closedWrite = otherIO.closedWrite;
  38248. internal = otherIO.internal;
  38249. modes = otherIO.modes;
  38250. if (otherIO.isTaint()) {
  38251. setTaint(true);
  38252. }
  38253. return this;
  38254. }
  38255. @JRubyMethod(name = "<<", required = 1)
  38256. public IRubyObject append(ThreadContext context, IRubyObject arg) {
  38257. writeInternal(context, arg);
  38258. return this;
  38259. }
  38260. @JRubyMethod(name = "binmode")
  38261. public IRubyObject binmode() {
  38262. return this;
  38263. }
  38264. @JRubyMethod(name = "close", frame=true)
  38265. public IRubyObject close() {
  38266. checkInitialized();
  38267. checkOpen();
  38268. closedRead = true;
  38269. closedWrite = true;
  38270. return getRuntime().getNil();
  38271. }
  38272. private void doFinalize() {
  38273. closedRead = true;
  38274. closedWrite = true;
  38275. internal = null;
  38276. }
  38277. @JRubyMethod(name = "closed?")
  38278. public IRubyObject closed_p() {
  38279. checkInitialized();
  38280. return getRuntime().newBoolean(closedRead && closedWrite);
  38281. }
  38282. @JRubyMethod(name = "close_read")
  38283. public IRubyObject close_read() {
  38284. checkReadable();
  38285. closedRead = true;
  38286. return getRuntime().getNil();
  38287. }
  38288. @JRubyMethod(name = "closed_read?")
  38289. public IRubyObject closed_read_p() {
  38290. checkInitialized();
  38291. return getRuntime().newBoolean(closedRead);
  38292. }
  38293. @JRubyMethod(name = "close_write")
  38294. public IRubyObject close_write() {
  38295. checkWritable();
  38296. closedWrite = true;
  38297. return getRuntime().getNil();
  38298. }
  38299. @JRubyMethod(name = "closed_write?")
  38300. public IRubyObject closed_write_p() {
  38301. checkInitialized();
  38302. return getRuntime().newBoolean(closedWrite);
  38303. }
  38304. @JRubyMethod(name = "each", optional = 1, frame = true, writes = FrameField.LASTLINE)
  38305. public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) {
  38306. IRubyObject line = gets(context, args);
  38307. while (!line.isNil()) {
  38308. block.yield(context, line);
  38309. line = gets(context, args);
  38310. }
  38311. return this;
  38312. }
  38313. @JRubyMethod(name = "each_byte", frame = true)
  38314. public IRubyObject each_byte(ThreadContext context, Block block) {
  38315. checkReadable();
  38316. Ruby runtime = context.getRuntime();
  38317. ByteList bytes = internal.getByteList();
  38318. // Check the length every iteration, since
  38319. // the block can modify this string.
  38320. while (pos < bytes.length()) {
  38321. block.yield(context, runtime.newFixnum(bytes.get((int) pos++) & 0xFF));
  38322. }
  38323. return runtime.getNil();
  38324. }
  38325. @JRubyMethod(name = "each_line", optional = 1, frame = true)
  38326. public IRubyObject each_line(ThreadContext context, IRubyObject[] args, Block block) {
  38327. return each(context, args, block);
  38328. }
  38329. @JRubyMethod(name = {"eof", "eof?"})
  38330. public IRubyObject eof() {
  38331. return getRuntime().newBoolean(isEOF());
  38332. }
  38333. private boolean isEOF() {
  38334. return (pos >= internal.getByteList().length()) || eof;
  38335. }
  38336. @JRubyMethod(name = "fcntl")
  38337. public IRubyObject fcntl() {
  38338. throw getRuntime().newNotImplementedError("fcntl not implemented");
  38339. }
  38340. @JRubyMethod(name = "fileno")
  38341. public IRubyObject fileno() {
  38342. return getRuntime().getNil();
  38343. }
  38344. @JRubyMethod(name = "flush")
  38345. public IRubyObject flush() {
  38346. return this;
  38347. }
  38348. @JRubyMethod(name = "fsync")
  38349. public IRubyObject fsync() {
  38350. return RubyFixnum.zero(getRuntime());
  38351. }
  38352. @JRubyMethod(name = "getc")
  38353. public IRubyObject getc() {
  38354. checkReadable();
  38355. if (pos >= internal.getByteList().length()) {
  38356. return getRuntime().getNil();
  38357. }
  38358. return getRuntime().newFixnum(internal.getByteList().get((int)pos++) & 0xFF);
  38359. }
  38360. private IRubyObject internalGets(ThreadContext context, IRubyObject[] args) {
  38361. Ruby runtime = context.getRuntime();
  38362. if (pos < internal.getByteList().realSize && !eof) {
  38363. boolean isParagraph = false;
  38364. ByteList sep;
  38365. if (args.length > 0) {
  38366. if (args[0].isNil()) {
  38367. ByteList buf = internal.getByteList().makeShared(
  38368. (int)pos, internal.getByteList().realSize - (int)pos);
  38369. pos += buf.realSize;
  38370. return RubyString.newString(runtime, buf);
  38371. }
  38372. sep = args[0].convertToString().getByteList();
  38373. if (sep.realSize == 0) {
  38374. isParagraph = true;
  38375. sep = Stream.PARAGRAPH_SEPARATOR;
  38376. }
  38377. } else {
  38378. sep = ((RubyString)runtime.getGlobalVariables().get("$/")).getByteList();
  38379. }
  38380. ByteList ss = internal.getByteList();
  38381. if (isParagraph) {
  38382. swallowLF(ss);
  38383. if (pos == ss.realSize) {
  38384. return runtime.getNil();
  38385. }
  38386. }
  38387. int ix = ss.indexOf(sep, (int)pos);
  38388. ByteList add;
  38389. if (-1 == ix) {
  38390. ix = internal.getByteList().realSize;
  38391. add = new ByteList(new byte[0], false);
  38392. } else {
  38393. add = isParagraph? NEWLINE : sep;
  38394. }
  38395. ByteList line = internal.getByteList().makeShared((int)pos, ix - (int)pos);
  38396. line.unshare();
  38397. line.append(add);
  38398. line.invalidate();
  38399. pos = ix + add.realSize;
  38400. lineno++;
  38401. return RubyString.newString(runtime,line);
  38402. }
  38403. return runtime.getNil();
  38404. }
  38405. private void swallowLF(ByteList list) {
  38406. while (pos < list.realSize) {
  38407. if (list.get((int)pos) == '\n') {
  38408. pos++;
  38409. } else {
  38410. break;
  38411. }
  38412. }
  38413. }
  38414. @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
  38415. public IRubyObject gets(ThreadContext context, IRubyObject[] args) {
  38416. checkReadable();
  38417. IRubyObject result = internalGets(context, args);
  38418. context.getCurrentFrame().setLastLine(result);
  38419. return result;
  38420. }
  38421. @JRubyMethod(name = {"tty?", "isatty"})
  38422. public IRubyObject isatty() {
  38423. return getRuntime().getFalse();
  38424. }
  38425. @JRubyMethod(name = {"length", "size"})
  38426. public IRubyObject length() {
  38427. checkFinalized();
  38428. return getRuntime().newFixnum(internal.getByteList().length());
  38429. }
  38430. @JRubyMethod(name = "lineno")
  38431. public IRubyObject lineno() {
  38432. return getRuntime().newFixnum(lineno);
  38433. }
  38434. @JRubyMethod(name = "lineno=", required = 1)
  38435. public IRubyObject set_lineno(IRubyObject arg) {
  38436. lineno = RubyNumeric.fix2int(arg);
  38437. return getRuntime().getNil();
  38438. }
  38439. @JRubyMethod(name = "path")
  38440. public IRubyObject path() {
  38441. return getRuntime().getNil();
  38442. }
  38443. @JRubyMethod(name = "pid")
  38444. public IRubyObject pid() {
  38445. return getRuntime().getNil();
  38446. }
  38447. @JRubyMethod(name = {"pos", "tell"})
  38448. public IRubyObject pos() {
  38449. return getRuntime().newFixnum(pos);
  38450. }
  38451. @JRubyMethod(name = "pos=", required = 1)
  38452. public IRubyObject set_pos(IRubyObject arg) {
  38453. pos = RubyNumeric.fix2int(arg);
  38454. if (pos < 0) {
  38455. throw getRuntime().newErrnoEINVALError("Invalid argument");
  38456. }
  38457. return getRuntime().getNil();
  38458. }
  38459. @JRubyMethod(name = "print", rest = true)
  38460. public IRubyObject print(ThreadContext context, IRubyObject[] args) {
  38461. Ruby runtime = context.getRuntime();
  38462. if (args.length != 0) {
  38463. for (int i=0,j=args.length;i<j;i++) {
  38464. append(context, args[i]);
  38465. }
  38466. } else {
  38467. IRubyObject arg = runtime.getGlobalVariables().get("$_");
  38468. append(context, arg.isNil() ? runtime.newString("nil") : arg);
  38469. }
  38470. IRubyObject sep = runtime.getGlobalVariables().get("$\\");
  38471. if (!sep.isNil()) {
  38472. append(context, sep);
  38473. }
  38474. return getRuntime().getNil();
  38475. }
  38476. @JRubyMethod(name = "printf", required = 1, rest = true)
  38477. public IRubyObject printf(ThreadContext context, IRubyObject[] args) {
  38478. append(context, RubyKernel.sprintf(context, this, args));
  38479. return getRuntime().getNil();
  38480. }
  38481. @JRubyMethod(name = "putc", required = 1)
  38482. public IRubyObject putc(IRubyObject obj) {
  38483. checkWritable();
  38484. byte c = RubyNumeric.num2chr(obj);
  38485. checkFrozen();
  38486. internal.modify();
  38487. ByteList bytes = internal.getByteList();
  38488. if (modes.isAppendable()) {
  38489. pos = bytes.length();
  38490. bytes.append(c);
  38491. } else {
  38492. if (pos >= bytes.length()) {
  38493. bytes.length((int)pos + 1);
  38494. }
  38495. bytes.set((int) pos, c);
  38496. pos++;
  38497. }
  38498. return obj;
  38499. }
  38500. public static final ByteList NEWLINE = ByteList.create("\n");
  38501. @JRubyMethod(name = "puts", rest = true)
  38502. public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
  38503. checkWritable();
  38504. // FIXME: the code below is a copy of RubyIO.puts,
  38505. // and we should avoid copy-paste.
  38506. if (args.length == 0) {
  38507. callMethod(context, "write", RubyString.newStringShared(getRuntime(), NEWLINE));
  38508. return getRuntime().getNil();
  38509. }
  38510. for (int i = 0; i < args.length; i++) {
  38511. String line;
  38512. if (args[i].isNil()) {
  38513. line = "nil";
  38514. } else {
  38515. IRubyObject tmp = args[i].checkArrayType();
  38516. if (!tmp.isNil()) {
  38517. RubyArray arr = (RubyArray) tmp;
  38518. if (getRuntime().isInspecting(arr)) {
  38519. line = "[...]";
  38520. } else {
  38521. inspectPuts(context, arr);
  38522. continue;
  38523. }
  38524. } else {
  38525. line = args[i].toString();
  38526. }
  38527. }
  38528. callMethod(context, "write", getRuntime().newString(line));
  38529. if (!line.endsWith("\n")) {
  38530. callMethod(context, "write", RubyString.newStringShared(getRuntime(), NEWLINE));
  38531. }
  38532. }
  38533. return getRuntime().getNil();
  38534. }
  38535. private IRubyObject inspectPuts(ThreadContext context, RubyArray array) {
  38536. try {
  38537. getRuntime().registerInspecting(array);
  38538. return puts(context, array.toJavaArray());
  38539. } finally {
  38540. getRuntime().unregisterInspecting(array);
  38541. }
  38542. }
  38543. @SuppressWarnings("fallthrough")
  38544. @JRubyMethod(name = "read", optional = 2)
  38545. public IRubyObject read(IRubyObject[] args) {
  38546. checkReadable();
  38547. ByteList buf = null;
  38548. int length = 0;
  38549. int oldLength = 0;
  38550. RubyString originalString = null;
  38551. switch (args.length) {
  38552. case 2:
  38553. originalString = args[1].convertToString();
  38554. // must let original string know we're modifying, so shared buffers aren't damaged
  38555. originalString.modify();
  38556. buf = originalString.getByteList();
  38557. case 1:
  38558. if (!args[0].isNil()) {
  38559. length = RubyNumeric.fix2int(args[0]);
  38560. oldLength = length;
  38561. if (length < 0) {
  38562. throw getRuntime().newArgumentError("negative length " + length + " given");
  38563. }
  38564. if (length > 0 && pos >= internal.getByteList().length()) {
  38565. eof = true;
  38566. if (buf != null) buf.realSize = 0;
  38567. return getRuntime().getNil();
  38568. } else if (eof) {
  38569. if (buf != null) buf.realSize = 0;
  38570. return getRuntime().getNil();
  38571. }
  38572. break;
  38573. }
  38574. case 0:
  38575. oldLength = -1;
  38576. length = internal.getByteList().length();
  38577. if (length <= pos) {
  38578. eof = true;
  38579. if (buf == null) {
  38580. buf = new ByteList();
  38581. } else {
  38582. buf.realSize = 0;
  38583. }
  38584. return getRuntime().newString(buf);
  38585. } else {
  38586. length -= pos;
  38587. }
  38588. break;
  38589. default:
  38590. getRuntime().newArgumentError(args.length, 0);
  38591. }
  38592. if (buf == null) {
  38593. int internalLength = internal.getByteList().length();
  38594. if (internalLength > 0) {
  38595. if (internalLength >= pos + length) {
  38596. buf = new ByteList(internal.getByteList(), (int) pos, length);
  38597. } else {
  38598. int rest = (int) (internal.getByteList().length() - pos);
  38599. if (length > rest) length = rest;
  38600. buf = new ByteList(internal.getByteList(), (int) pos, length);
  38601. }
  38602. }
  38603. } else {
  38604. int rest = (int) (internal.getByteList().length() - pos);
  38605. if (length > rest) length = rest;
  38606. // Yow...this is ugly
  38607. buf.realSize = length;
  38608. buf.replace(0, length, internal.getByteList().bytes, (int) pos, length);
  38609. }
  38610. if (buf == null) {
  38611. if (!eof) buf = new ByteList();
  38612. length = 0;
  38613. } else {
  38614. length = buf.length();
  38615. pos += length;
  38616. }
  38617. if (oldLength < 0 || oldLength > length) eof = true;
  38618. return originalString != null ? originalString : getRuntime().newString(buf);
  38619. }
  38620. @JRubyMethod(name = "readchar")
  38621. public IRubyObject readchar() {
  38622. IRubyObject c = getc();
  38623. if (c.isNil()) throw getRuntime().newEOFError();
  38624. return c;
  38625. }
  38626. @JRubyMethod(name = "readline", optional = 1, writes = FrameField.LASTLINE)
  38627. public IRubyObject readline(ThreadContext context, IRubyObject[] args) {
  38628. IRubyObject line = gets(context, args);
  38629. if (line.isNil()) throw getRuntime().newEOFError();
  38630. return line;
  38631. }
  38632. @JRubyMethod(name = "readlines", optional = 1)
  38633. public IRubyObject readlines(ThreadContext context, IRubyObject[] arg) {
  38634. checkReadable();
  38635. List<IRubyObject> lns = new ArrayList<IRubyObject>();
  38636. while (!(isEOF())) {
  38637. IRubyObject line = internalGets(context, arg);
  38638. if (line.isNil()) {
  38639. break;
  38640. }
  38641. lns.add(line);
  38642. }
  38643. return getRuntime().newArray(lns);
  38644. }
  38645. @JRubyMethod(name = "reopen", required = 0, optional = 2)
  38646. public IRubyObject reopen(IRubyObject[] args) {
  38647. if (args.length == 1 && !(args[0] instanceof RubyString)) {
  38648. return initialize_copy(args[0]);
  38649. }
  38650. // reset the state
  38651. doRewind();
  38652. closedRead = false;
  38653. closedWrite = false;
  38654. return initialize(args, Block.NULL_BLOCK);
  38655. }
  38656. @JRubyMethod(name = "rewind")
  38657. public IRubyObject rewind() {
  38658. doRewind();
  38659. return RubyFixnum.zero(getRuntime());
  38660. }
  38661. private void doRewind() {
  38662. this.pos = 0L;
  38663. this.eof = false;
  38664. this.lineno = 0;
  38665. }
  38666. @JRubyMethod(name = "seek", required = 1, optional = 1, frame=true)
  38667. public IRubyObject seek(IRubyObject[] args) {
  38668. // MRI 1.8.7 behavior:
  38669. // checkOpen();
  38670. checkFinalized();
  38671. long amount = RubyNumeric.num2long(args[0]);
  38672. int whence = Stream.SEEK_SET;
  38673. long newPosition = pos;
  38674. if (args.length > 1 && !args[0].isNil()) whence = RubyNumeric.fix2int(args[1]);
  38675. if (whence == Stream.SEEK_CUR) {
  38676. newPosition += amount;
  38677. } else if (whence == Stream.SEEK_END) {
  38678. newPosition = internal.getByteList().length() + amount;
  38679. } else {
  38680. newPosition = amount;
  38681. }
  38682. if (newPosition < 0) throw getRuntime().newErrnoEINVALError();
  38683. pos = newPosition;
  38684. eof = false;
  38685. return RubyFixnum.zero(getRuntime());
  38686. }
  38687. @JRubyMethod(name = "string=", required = 1)
  38688. public IRubyObject set_string(IRubyObject arg) {
  38689. return reopen(new IRubyObject[] { arg.convertToString() });
  38690. }
  38691. @JRubyMethod(name = "sync=", required = 1)
  38692. public IRubyObject set_sync(IRubyObject args) {
  38693. return args;
  38694. }
  38695. @JRubyMethod(name = "string")
  38696. public IRubyObject string() {
  38697. if (internal == null) {
  38698. return getRuntime().getNil();
  38699. } else {
  38700. return internal;
  38701. }
  38702. }
  38703. @JRubyMethod(name = "sync")
  38704. public IRubyObject sync() {
  38705. return getRuntime().getTrue();
  38706. }
  38707. @JRubyMethod(name = "sysread", optional = 2)
  38708. public IRubyObject sysread(IRubyObject[] args) {
  38709. IRubyObject obj = read(args);
  38710. if (isEOF()) {
  38711. if (obj.isNil() || ((RubyString) obj).getByteList().length() == 0) {
  38712. throw getRuntime().newEOFError();
  38713. }
  38714. }
  38715. return obj;
  38716. }
  38717. @JRubyMethod(name = "truncate", required = 1)
  38718. public IRubyObject truncate(IRubyObject arg) {
  38719. checkWritable();
  38720. int len = RubyFixnum.fix2int(arg);
  38721. if (len < 0) {
  38722. throw getRuntime().newErrnoEINVALError("negative legnth");
  38723. }
  38724. internal.modify();
  38725. internal.getByteList().length(len);
  38726. return arg;
  38727. }
  38728. @JRubyMethod(name = "ungetc", required = 1)
  38729. public IRubyObject ungetc(IRubyObject arg) {
  38730. checkReadable();
  38731. int c = RubyNumeric.num2int(arg);
  38732. if (pos == 0) return getRuntime().getNil();
  38733. internal.modify();
  38734. pos--;
  38735. ByteList bytes = internal.getByteList();
  38736. if (bytes.length() <= pos) {
  38737. bytes.length((int)pos + 1);
  38738. }
  38739. bytes.set((int) pos, c);
  38740. return getRuntime().getNil();
  38741. }
  38742. @JRubyMethod(name = {"write", "syswrite"}, required = 1)
  38743. public IRubyObject write(ThreadContext context, IRubyObject arg) {
  38744. return context.getRuntime().newFixnum(writeInternal(context, arg));
  38745. }
  38746. private int writeInternal(ThreadContext context, IRubyObject arg) {
  38747. checkWritable();
  38748. checkFrozen();
  38749. RubyString val = arg.asString();
  38750. internal.modify();
  38751. if (modes.isAppendable()) {
  38752. internal.getByteList().append(val.getByteList());
  38753. pos = internal.getByteList().length();
  38754. } else {
  38755. int left = internal.getByteList().length()-(int)pos;
  38756. internal.getByteList().replace((int)pos,Math.min(val.getByteList().length(),left),val.getByteList());
  38757. pos += val.getByteList().length();
  38758. }
  38759. if (val.isTaint()) {
  38760. internal.setTaint(true);
  38761. }
  38762. return val.getByteList().length();
  38763. }
  38764. /* rb: check_modifiable */
  38765. @Override
  38766. protected void checkFrozen() {
  38767. checkInitialized();
  38768. if (internal.isFrozen()) throw getRuntime().newIOError("not modifiable string");
  38769. }
  38770. /* rb: readable */
  38771. private void checkReadable() {
  38772. checkInitialized();
  38773. if (closedRead || !modes.isReadable()) {
  38774. throw getRuntime().newIOError("not opened for reading");
  38775. }
  38776. }
  38777. /* rb: writable */
  38778. private void checkWritable() {
  38779. checkInitialized();
  38780. if (closedWrite || !modes.isWritable()) {
  38781. throw getRuntime().newIOError("not opened for writing");
  38782. }
  38783. // Tainting here if we ever want it. (secure 4)
  38784. }
  38785. private void checkInitialized() {
  38786. if (modes == null) {
  38787. throw getRuntime().newIOError("uninitialized stream");
  38788. }
  38789. }
  38790. private void checkFinalized() {
  38791. if (internal == null) {
  38792. throw getRuntime().newIOError("not opened");
  38793. }
  38794. }
  38795. private void checkOpen() {
  38796. if (closedRead && closedWrite) {
  38797. throw getRuntime().newIOError("closed stream");
  38798. }
  38799. }
  38800. private void setupModes() {
  38801. closedWrite = false;
  38802. closedRead = false;
  38803. if (modes.isReadOnly()) closedWrite = true;
  38804. if (!modes.isReadable()) closedRead = true;
  38805. }
  38806. }
  38807. package org.jruby;
  38808. import org.joni.Matcher;
  38809. import org.joni.Option;
  38810. import org.joni.Regex;
  38811. import org.joni.Region;
  38812. import org.joni.encoding.Encoding;
  38813. import org.jruby.anno.JRubyClass;
  38814. import org.jruby.anno.JRubyMethod;
  38815. import org.jruby.common.IRubyWarnings.ID;
  38816. import org.jruby.exceptions.RaiseException;
  38817. import org.jruby.runtime.Block;
  38818. import org.jruby.runtime.ObjectAllocator;
  38819. import org.jruby.runtime.ThreadContext;
  38820. import org.jruby.runtime.Visibility;
  38821. import org.jruby.runtime.builtin.IRubyObject;
  38822. import org.jruby.util.ByteList;
  38823. /**
  38824. * @author kscott
  38825. *
  38826. */
  38827. @JRubyClass(name="StringScanner")
  38828. public class RubyStringScanner extends RubyObject {
  38829. private RubyString str;
  38830. private int pos = 0;
  38831. private int lastPos = -1;
  38832. private Region regs;
  38833. private int beg = -1;
  38834. private int end = -1;
  38835. // not to be confused with RubyObject's flags
  38836. private int scannerFlags;
  38837. private static final int MATCHED_STR_SCN_F = 1 << 11;
  38838. private static ObjectAllocator STRINGSCANNER_ALLOCATOR = new ObjectAllocator() {
  38839. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  38840. return new RubyStringScanner(runtime, klass);
  38841. }
  38842. };
  38843. public static RubyClass createScannerClass(final Ruby runtime) {
  38844. RubyClass scannerClass = runtime.defineClass("StringScanner", runtime.getObject(), STRINGSCANNER_ALLOCATOR);
  38845. scannerClass.defineAnnotatedMethods(RubyStringScanner.class);
  38846. ThreadContext context = runtime.getCurrentContext();
  38847. scannerClass.setConstant("Version", runtime.newString("0.7.0").freeze(context));
  38848. scannerClass.setConstant("Id", runtime.newString("$Id: strscan.c 13506 2007-09-24 08:56:24Z nobu $").freeze(context));
  38849. RubyClass standardError = runtime.getStandardError();
  38850. RubyClass error = scannerClass.defineClassUnder(
  38851. "Error", standardError, standardError.getAllocator());
  38852. RubyClass objClass = runtime.getObject();
  38853. if (!objClass.isConstantDefined("ScanError")) {
  38854. objClass.defineConstant("ScanError", error);
  38855. }
  38856. return scannerClass;
  38857. }
  38858. private void clearMatched() {
  38859. scannerFlags &= ~MATCHED_STR_SCN_F;
  38860. }
  38861. private void setMatched() {
  38862. scannerFlags |= MATCHED_STR_SCN_F;
  38863. }
  38864. private boolean isMatched() {
  38865. return (scannerFlags & MATCHED_STR_SCN_F) != 0;
  38866. }
  38867. private void check() {
  38868. if (str == null) throw getRuntime().newArgumentError("uninitialized StringScanner object");
  38869. }
  38870. protected RubyStringScanner(Ruby runtime, RubyClass type) {
  38871. super(runtime, type);
  38872. }
  38873. // second argument is allowed, but ignored (MRI)
  38874. @JRubyMethod(name = "initialize", required = 1, optional = 1, frame = true, visibility = Visibility.PRIVATE)
  38875. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  38876. str = args[0].convertToString();
  38877. return this;
  38878. }
  38879. @JRubyMethod(name = "initialize_copy", frame=true, visibility = Visibility.PRIVATE)
  38880. @Override
  38881. public IRubyObject initialize_copy(IRubyObject other) {
  38882. if (this == other) return this;
  38883. if (!(other instanceof RubyStringScanner)) {
  38884. throw getRuntime().newTypeError("wrong argument type "
  38885. + other.getMetaClass() + " (expected StringScanner)");
  38886. }
  38887. RubyStringScanner otherScanner = (RubyStringScanner)other;
  38888. str = otherScanner.str;
  38889. pos = otherScanner.pos;
  38890. lastPos = otherScanner.lastPos;
  38891. scannerFlags = otherScanner.scannerFlags;
  38892. regs = otherScanner.regs != null ? otherScanner.regs.clone() : null;
  38893. beg = otherScanner.beg;
  38894. end = otherScanner.end;
  38895. return this;
  38896. }
  38897. @JRubyMethod(name = "reset")
  38898. public IRubyObject reset() {
  38899. check();
  38900. pos = 0;
  38901. clearMatched();
  38902. return this;
  38903. }
  38904. @JRubyMethod(name = "terminate")
  38905. public IRubyObject terminate() {
  38906. check();
  38907. pos = str.getByteList().realSize;
  38908. clearMatched();
  38909. return this;
  38910. }
  38911. @JRubyMethod(name = "clear")
  38912. public IRubyObject clear() {
  38913. check();
  38914. getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#clear is obsolete; use #terminate instead", "StringScanner#clear", "#terminate");
  38915. return terminate();
  38916. }
  38917. @JRubyMethod(name = "string")
  38918. public RubyString string() {
  38919. return str;
  38920. }
  38921. @JRubyMethod(name = "string=", required = 1)
  38922. public IRubyObject set_string(ThreadContext context, IRubyObject str) {
  38923. this.str = (RubyString) str.convertToString().strDup(context.getRuntime()).freeze(context);
  38924. pos = 0;
  38925. clearMatched();
  38926. return str;
  38927. }
  38928. @JRubyMethod(name = {"concat", "<<"}, required = 1)
  38929. public IRubyObject concat(IRubyObject obj) {
  38930. check();
  38931. str.append(obj); // append will call convertToString()
  38932. return this;
  38933. }
  38934. @JRubyMethod(name = {"pos", "pointer"})
  38935. public RubyFixnum pos() {
  38936. check();
  38937. return RubyFixnum.newFixnum(getRuntime(), pos);
  38938. }
  38939. @JRubyMethod(name = {"pos=", "pointer="})
  38940. public IRubyObject set_pos(IRubyObject pos) {
  38941. check();
  38942. int i = RubyNumeric.num2int(pos);
  38943. int size = str.getByteList().realSize;
  38944. if (i < 0) i += size;
  38945. if (i < 0 || i > size) throw getRuntime().newRangeError("index out of range.");
  38946. this.pos = i;
  38947. return RubyFixnum.newFixnum(getRuntime(), i);
  38948. }
  38949. private IRubyObject extractRange(Ruby runtime, int beg, int end) {
  38950. int size = str.getByteList().realSize;
  38951. if (beg > size) return getRuntime().getNil();
  38952. if (end > size) end = size;
  38953. return str.makeShared(runtime, beg, end - beg);
  38954. }
  38955. private IRubyObject extractBegLen(Ruby runtime, int beg, int len) {
  38956. assert len >= 0;
  38957. int size = str.getByteList().realSize;
  38958. if (beg > size) return getRuntime().getNil();
  38959. if (beg + len > size) len = size - beg;
  38960. return str.makeShared(runtime, beg, len);
  38961. }
  38962. private IRubyObject scan(IRubyObject regex, boolean succptr, boolean getstr, boolean headonly) {
  38963. if (!(regex instanceof RubyRegexp)) throw getRuntime().newTypeError("wrong argument type " + regex.getMetaClass() + " (expected Regexp)");
  38964. check();
  38965. Regex pattern = ((RubyRegexp)regex).getPattern();
  38966. clearMatched();
  38967. int rest = str.getByteList().realSize - pos;
  38968. if (rest < 0) return getRuntime().getNil();
  38969. ByteList value = str.getByteList();
  38970. Matcher matcher = pattern.matcher(value.bytes, value.begin + pos, value.begin + value.realSize);
  38971. final int ret;
  38972. if (headonly) {
  38973. ret = matcher.match(value.begin + pos, value.begin + value.realSize, Option.NONE);
  38974. } else {
  38975. ret = matcher.search(value.begin + pos, value.begin + value.realSize, Option.NONE);
  38976. }
  38977. regs = matcher.getRegion();
  38978. if (regs == null) {
  38979. beg = matcher.getBegin();
  38980. end = matcher.getEnd();
  38981. } else {
  38982. beg = regs.beg[0];
  38983. end = regs.end[0];
  38984. }
  38985. if (ret < 0) return getRuntime().getNil();
  38986. setMatched();
  38987. lastPos = pos;
  38988. if (succptr) pos += end;
  38989. return getstr ? extractBegLen(getRuntime(), lastPos, end) : RubyFixnum.newFixnum(getRuntime(), end);
  38990. }
  38991. @JRubyMethod(name = "scan", required = 1)
  38992. public IRubyObject scan(IRubyObject regex) {
  38993. return scan(regex, true, true, true);
  38994. }
  38995. @JRubyMethod(name = "match?", required = 1)
  38996. public IRubyObject match_p(IRubyObject regex) {
  38997. return scan(regex, false, false, true);
  38998. }
  38999. @JRubyMethod(name = "skip", required = 1)
  39000. public IRubyObject skip(IRubyObject regex) {
  39001. return scan(regex, true, false, true);
  39002. }
  39003. @JRubyMethod(name = "check", required = 1)
  39004. public IRubyObject check(IRubyObject regex) {
  39005. return scan(regex, false, true, true);
  39006. }
  39007. @JRubyMethod(name = "scan_full", required = 3)
  39008. public IRubyObject scan_full(IRubyObject regex, IRubyObject s, IRubyObject f) {
  39009. return scan(regex, s.isTrue(), f.isTrue(), true);
  39010. }
  39011. @JRubyMethod(name = "scan_until", required = 1)
  39012. public IRubyObject scan_until(IRubyObject regex) {
  39013. return scan(regex, true, true, false);
  39014. }
  39015. @JRubyMethod(name = "exist?", required = 1)
  39016. public IRubyObject exist_p(IRubyObject regex) {
  39017. return scan(regex, false, false, false);
  39018. }
  39019. @JRubyMethod(name = "skip_until", required = 1)
  39020. public IRubyObject skip_until(IRubyObject regex) {
  39021. return scan(regex, true, false, false);
  39022. }
  39023. @JRubyMethod(name = "check_until", required = 1)
  39024. public IRubyObject check_until(IRubyObject regex) {
  39025. return scan(regex, false, true, false);
  39026. }
  39027. @JRubyMethod(name = "search_full", required = 3)
  39028. public IRubyObject search_full(IRubyObject regex, IRubyObject s, IRubyObject f) {
  39029. return scan(regex, s.isTrue(), f.isTrue(), false);
  39030. }
  39031. private void adjustRegisters() {
  39032. beg = 0;
  39033. end = pos - lastPos;
  39034. regs = null;
  39035. }
  39036. @JRubyMethod(name = "getch")
  39037. public IRubyObject getch(ThreadContext context) {
  39038. check();
  39039. clearMatched();
  39040. Ruby runtime = context.getRuntime();
  39041. ByteList value = str.getByteList();
  39042. if (pos >= value.realSize) return runtime.getNil();
  39043. Encoding enc = runtime.getKCode().getEncoding();
  39044. int len;
  39045. if (enc.isSingleByte()) {
  39046. len = 1;
  39047. } else {
  39048. len = enc.length(value.bytes[value.begin + pos]);
  39049. }
  39050. if (pos + len > value.realSize) len = value.realSize - pos;
  39051. lastPos = pos;
  39052. pos += len;
  39053. setMatched();
  39054. adjustRegisters();
  39055. return extractRange(runtime, lastPos + beg, lastPos + end);
  39056. }
  39057. @JRubyMethod(name = "get_byte")
  39058. public IRubyObject get_byte(ThreadContext context) {
  39059. check();
  39060. clearMatched();
  39061. if (pos >= str.getByteList().realSize) return getRuntime().getNil();
  39062. lastPos = pos;
  39063. pos++;
  39064. setMatched();
  39065. adjustRegisters();
  39066. return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
  39067. }
  39068. @JRubyMethod(name = "getbyte")
  39069. public IRubyObject getbyte(ThreadContext context) {
  39070. context.getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD,
  39071. "StringScanner#getbyte is obsolete; use #get_byte instead",
  39072. "StringScanner#getbyte", "#get_byte");
  39073. return get_byte(context);
  39074. }
  39075. @JRubyMethod(name = "peek", required = 1)
  39076. public IRubyObject peek(ThreadContext context, IRubyObject length) {
  39077. check();
  39078. int len = RubyNumeric.num2int(length);
  39079. if (len < 0) {
  39080. throw context.getRuntime().newArgumentError("negative string size (or size too big)");
  39081. }
  39082. ByteList value = str.getByteList();
  39083. if (pos >= value.realSize) return RubyString.newEmptyString(getRuntime()).infectBy(str);
  39084. if (pos + len > value.realSize) len = value.realSize - pos;
  39085. return extractBegLen(context.getRuntime(), pos, len);
  39086. }
  39087. @JRubyMethod(name = "peep", required = 1)
  39088. public IRubyObject peep(ThreadContext context, IRubyObject length) {
  39089. getRuntime().getWarnings().warning(
  39090. ID.DEPRECATED_METHOD, "StringScanner#peep is obsolete; use #peek instead",
  39091. "StringScanner#peep", "#peek");
  39092. return peek(context, length);
  39093. }
  39094. @JRubyMethod(name = "unscan")
  39095. public IRubyObject unscan() {
  39096. check();
  39097. Ruby runtime = getRuntime();
  39098. if (!isMatched()) {
  39099. RubyClass errorClass = runtime.fastGetClass("StringScanner").fastGetClass("Error");
  39100. throw new RaiseException(RubyException.newException(
  39101. runtime, errorClass, "unscan failed: previous match had failed"));
  39102. }
  39103. pos = lastPos;
  39104. clearMatched();
  39105. return this;
  39106. }
  39107. @JRubyMethod(name = "beginning_of_line?", alias = "bol?")
  39108. public IRubyObject bol_p() {
  39109. check();
  39110. ByteList value = str.getByteList();
  39111. if (pos > value.realSize) return getRuntime().getNil();
  39112. if (pos == 0) return getRuntime().getTrue();
  39113. return value.bytes[(value.begin + pos) - 1] == (byte)'\n' ? getRuntime().getTrue() : getRuntime().getFalse();
  39114. }
  39115. @JRubyMethod(name = "eos?")
  39116. public RubyBoolean eos_p(ThreadContext context) {
  39117. check();
  39118. return pos >= str.getByteList().realSize ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  39119. }
  39120. @JRubyMethod(name = "empty?")
  39121. public RubyBoolean empty_p(ThreadContext context) {
  39122. getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#empty? is obsolete; use #eos? instead", "StringScanner#empty?", "#eos?");
  39123. return eos_p(context);
  39124. }
  39125. @JRubyMethod(name = "rest?")
  39126. public RubyBoolean rest_p(ThreadContext context) {
  39127. check();
  39128. return pos >= str.getByteList().realSize ? context.getRuntime().getFalse() : context.getRuntime().getTrue();
  39129. }
  39130. @JRubyMethod(name = "matched?")
  39131. public RubyBoolean matched_p(ThreadContext context) {
  39132. check();
  39133. return isMatched() ? context.getRuntime().getTrue() : context.getRuntime().getFalse();
  39134. }
  39135. @JRubyMethod(name = "matched")
  39136. public IRubyObject matched(ThreadContext context) {
  39137. check();
  39138. if (!isMatched()) return getRuntime().getNil();
  39139. return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
  39140. }
  39141. @JRubyMethod(name = "matched_size")
  39142. public IRubyObject matched_size() {
  39143. check();
  39144. if (!isMatched()) return getRuntime().getNil();
  39145. return RubyFixnum.newFixnum(getRuntime(), end - beg);
  39146. }
  39147. @JRubyMethod(name = "matchedsize")
  39148. public IRubyObject matchedsize() {
  39149. getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#matchedsize is obsolete; use #matched_size instead",
  39150. "StringScanner#matchedize", "#matched_size");
  39151. return matched_size();
  39152. }
  39153. @JRubyMethod(name = "[]", required = 1)
  39154. public IRubyObject op_aref(ThreadContext context, IRubyObject idx) {
  39155. check();
  39156. if (!isMatched()) return context.getRuntime().getNil();
  39157. int i = RubyNumeric.num2int(idx);
  39158. int numRegs = regs == null ? 1 : regs.numRegs;
  39159. if (i < 0) i += numRegs;
  39160. if (i < 0 || i >= numRegs) return context.getRuntime().getNil();
  39161. if (regs == null) {
  39162. assert i == 0;
  39163. if (beg == -1) return getRuntime().getNil();
  39164. return extractRange(context.getRuntime(), lastPos + beg, lastPos + end);
  39165. } else {
  39166. if (regs.beg[i] == -1) return getRuntime().getNil();
  39167. return extractRange(context.getRuntime(), lastPos + regs.beg[i], lastPos + regs.end[i]);
  39168. }
  39169. }
  39170. @JRubyMethod(name = "pre_match")
  39171. public IRubyObject pre_match(ThreadContext context) {
  39172. check();
  39173. if (!isMatched()) return context.getRuntime().getNil();
  39174. return extractRange(context.getRuntime(), 0, lastPos + beg);
  39175. }
  39176. @JRubyMethod(name = "post_match")
  39177. public IRubyObject post_match(ThreadContext context) {
  39178. check();
  39179. if (!isMatched()) return context.getRuntime().getNil();
  39180. return extractRange(context.getRuntime(), lastPos + end, str.getByteList().realSize);
  39181. }
  39182. @JRubyMethod(name = "rest")
  39183. public IRubyObject rest(ThreadContext context) {
  39184. check();
  39185. ByteList value = str.getByteList();
  39186. if (pos >= value.realSize) return RubyString.newEmptyString(context.getRuntime()).infectBy(str);
  39187. return extractRange(context.getRuntime(), pos, value.realSize);
  39188. }
  39189. @JRubyMethod(name = "rest_size")
  39190. public RubyFixnum rest_size() {
  39191. check();
  39192. ByteList value = str.getByteList();
  39193. if (pos >= value.realSize) return RubyFixnum.zero(getRuntime());
  39194. return RubyFixnum.newFixnum(getRuntime(), value.realSize - pos);
  39195. }
  39196. @JRubyMethod(name = "restsize")
  39197. public RubyFixnum restsize() {
  39198. getRuntime().getWarnings().warning(ID.DEPRECATED_METHOD, "StringScanner#restsize is obsolete; use #rest_size instead", "StringScanner#restsize", "#rest_size");
  39199. return rest_size();
  39200. }
  39201. @JRubyMethod(name = "inspect")
  39202. @Override
  39203. public IRubyObject inspect() {
  39204. if (str == null) return inspect("(uninitialized)");
  39205. if (pos >= str.getByteList().realSize) return inspect("fin");
  39206. if (pos == 0) return inspect(pos + "/" + str.getByteList().realSize + " @ " + inspect2());
  39207. return inspect(pos + "/" + str.getByteList().realSize + " " + inspect1() + " @ " + inspect2());
  39208. }
  39209. private IRubyObject inspect(String msg) {
  39210. IRubyObject result = getRuntime().newString("#<" + getMetaClass() + " " + msg + ">");
  39211. if (str != null) result.infectBy(str);
  39212. return result;
  39213. }
  39214. private static final int INSPECT_LENGTH = 5;
  39215. private IRubyObject inspect1() {
  39216. if (pos == 0) return RubyString.newEmptyString(getRuntime());
  39217. if (pos > INSPECT_LENGTH) {
  39218. return RubyString.newString(getRuntime(), "...".getBytes()).append(str.substr(pos - INSPECT_LENGTH, INSPECT_LENGTH)).inspect();
  39219. } else {
  39220. return str.substr(0, pos).inspect();
  39221. }
  39222. }
  39223. private IRubyObject inspect2() {
  39224. if (pos >= str.getByteList().realSize) return RubyString.newEmptyString(getRuntime());
  39225. int len = str.getByteList().realSize - pos;
  39226. if (len > INSPECT_LENGTH) {
  39227. return ((RubyString)str.substr(pos, INSPECT_LENGTH)).cat("...".getBytes()).inspect();
  39228. } else {
  39229. return str.substr(pos, len).inspect();
  39230. }
  39231. }
  39232. @JRubyMethod(name = "must_C_version", meta = true)
  39233. public static IRubyObject mustCversion(IRubyObject recv) {
  39234. return recv;
  39235. }
  39236. }
  39237. /***** BEGIN LICENSE BLOCK *****
  39238. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  39239. *
  39240. * The contents of this file are subject to the Common Public
  39241. * License Version 1.0 (the "License"); you may not use this file
  39242. * except in compliance with the License. You may obtain a copy of
  39243. * the License at http://www.eclipse.org/legal/cpl-v10.html
  39244. *
  39245. * Software distributed under the License is distributed on an "AS
  39246. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  39247. * implied. See the License for the specific language governing
  39248. * rights and limitations under the License.
  39249. *
  39250. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  39251. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  39252. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  39253. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  39254. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  39255. * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
  39256. *
  39257. * Alternatively, the contents of this file may be used under the terms of
  39258. * either of the GNU General Public License Version 2 or later (the "GPL"),
  39259. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  39260. * in which case the provisions of the GPL or the LGPL are applicable instead
  39261. * of those above. If you wish to allow use of your version of this file only
  39262. * under the terms of either the GPL or the LGPL, and not to allow others to
  39263. * use your version of this file under the terms of the CPL, indicate your
  39264. * decision by deleting the provisions above and replace them with the notice
  39265. * and other provisions required by the GPL or the LGPL. If you do not delete
  39266. * the provisions above, a recipient may use your version of this file under
  39267. * the terms of any one of the CPL, the GPL or the LGPL.
  39268. ***** END LICENSE BLOCK *****/
  39269. package org.jruby;
  39270. import java.util.List;
  39271. import org.jruby.anno.JRubyMethod;
  39272. import org.jruby.anno.JRubyClass;
  39273. import org.jruby.runtime.Arity;
  39274. import org.jruby.runtime.Block;
  39275. import org.jruby.runtime.Frame;
  39276. import org.jruby.runtime.MethodIndex;
  39277. import org.jruby.runtime.ObjectAllocator;
  39278. import org.jruby.runtime.ThreadContext;
  39279. import org.jruby.runtime.Visibility;
  39280. import org.jruby.runtime.builtin.IRubyObject;
  39281. import org.jruby.runtime.marshal.MarshalStream;
  39282. import org.jruby.runtime.marshal.UnmarshalStream;
  39283. import org.jruby.util.ByteList;
  39284. import org.jruby.util.IdUtil;
  39285. import org.jruby.common.IRubyWarnings.ID;
  39286. import org.jruby.exceptions.RaiseException;
  39287. import org.jruby.internal.runtime.methods.CallConfiguration;
  39288. import org.jruby.internal.runtime.methods.DynamicMethod;
  39289. import org.jruby.runtime.ClassIndex;
  39290. /**
  39291. * @author jpetersen
  39292. */
  39293. @JRubyClass(name="Struct")
  39294. public class RubyStruct extends RubyObject {
  39295. private IRubyObject[] values;
  39296. /**
  39297. * Constructor for RubyStruct.
  39298. * @param runtime
  39299. * @param rubyClass
  39300. */
  39301. public RubyStruct(Ruby runtime, RubyClass rubyClass) {
  39302. super(runtime, rubyClass);
  39303. int size = RubyNumeric.fix2int(getInternalVariable((RubyClass)rubyClass, "__size__"));
  39304. values = new IRubyObject[size];
  39305. for (int i = 0; i < size; i++) {
  39306. values[i] = getRuntime().getNil();
  39307. }
  39308. }
  39309. public static RubyClass createStructClass(Ruby runtime) {
  39310. // TODO: NOT_ALLOCATABLE_ALLOCATOR may be ok here, but it's unclear how Structs
  39311. // work with marshalling. Confirm behavior and ensure we're doing this correctly. JRUBY-415
  39312. RubyClass structClass = runtime.defineClass("Struct", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  39313. runtime.setStructClass(structClass);
  39314. structClass.index = ClassIndex.STRUCT;
  39315. structClass.includeModule(runtime.getEnumerable());
  39316. structClass.defineAnnotatedMethods(RubyStruct.class);
  39317. return structClass;
  39318. }
  39319. @Override
  39320. public int getNativeTypeIndex() {
  39321. return ClassIndex.STRUCT;
  39322. }
  39323. private static IRubyObject getInternalVariable(RubyClass type, String internedName) {
  39324. RubyClass structClass = type.getRuntime().getStructClass();
  39325. IRubyObject variable;
  39326. while (type != null && type != structClass) {
  39327. if ((variable = type.fastGetInternalVariable(internedName)) != null) {
  39328. return variable;
  39329. }
  39330. type = type.getSuperClass();
  39331. }
  39332. return type.getRuntime().getNil();
  39333. }
  39334. private RubyClass classOf() {
  39335. return getMetaClass() instanceof MetaClass ? getMetaClass().getSuperClass() : getMetaClass();
  39336. }
  39337. private void modify() {
  39338. testFrozen("Struct is frozen");
  39339. if (!isTaint() && getRuntime().getSafeLevel() >= 4) {
  39340. throw getRuntime().newSecurityError("Insecure: can't modify struct");
  39341. }
  39342. }
  39343. @JRubyMethod
  39344. public RubyFixnum hash(ThreadContext context) {
  39345. Ruby runtime = getRuntime();
  39346. int h = getMetaClass().getRealClass().hashCode();
  39347. for (int i = 0; i < values.length; i++) {
  39348. h = (h << 1) | (h < 0 ? 1 : 0);
  39349. h ^= RubyNumeric.num2long(values[i].callMethod(context, MethodIndex.HASH, "hash"));
  39350. }
  39351. return runtime.newFixnum(h);
  39352. }
  39353. private IRubyObject setByName(String name, IRubyObject value) {
  39354. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39355. assert !member.isNil() : "uninitialized struct";
  39356. modify();
  39357. for (int i = 0,k=member.getLength(); i < k; i++) {
  39358. if (member.eltInternal(i).asJavaString().equals(name)) {
  39359. return values[i] = value;
  39360. }
  39361. }
  39362. throw notStructMemberError(name);
  39363. }
  39364. private IRubyObject getByName(String name) {
  39365. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39366. assert !member.isNil() : "uninitialized struct";
  39367. for (int i = 0,k=member.getLength(); i < k; i++) {
  39368. if (member.eltInternal(i).asJavaString().equals(name)) {
  39369. return values[i];
  39370. }
  39371. }
  39372. throw notStructMemberError(name);
  39373. }
  39374. // Struct methods
  39375. /** Create new Struct class.
  39376. *
  39377. * MRI: rb_struct_s_def / make_struct
  39378. *
  39379. */
  39380. @JRubyMethod(name = "new", required = 1, rest = true, frame = true, meta = true)
  39381. public static RubyClass newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
  39382. String name = null;
  39383. boolean nilName = false;
  39384. Ruby runtime = recv.getRuntime();
  39385. if (args.length > 0) {
  39386. IRubyObject firstArgAsString = args[0].checkStringType();
  39387. if (!firstArgAsString.isNil()) {
  39388. name = ((RubyString)firstArgAsString).getByteList().toString();
  39389. } else if (args[0].isNil()) {
  39390. nilName = true;
  39391. }
  39392. }
  39393. RubyArray member = runtime.newArray();
  39394. for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
  39395. member.append(runtime.newSymbol(args[i].asJavaString()));
  39396. }
  39397. RubyClass newStruct;
  39398. RubyClass superClass = (RubyClass)recv;
  39399. if (name == null || nilName) {
  39400. newStruct = RubyClass.newClass(runtime, superClass);
  39401. newStruct.setAllocator(STRUCT_INSTANCE_ALLOCATOR);
  39402. newStruct.makeMetaClass(superClass.getMetaClass());
  39403. newStruct.inherit(superClass);
  39404. } else {
  39405. if (!IdUtil.isConstant(name)) {
  39406. throw runtime.newNameError("identifier " + name + " needs to be constant", name);
  39407. }
  39408. IRubyObject type = superClass.getConstantAt(name);
  39409. if (type != null) {
  39410. ThreadContext context = runtime.getCurrentContext();
  39411. Frame frame = context.getCurrentFrame();
  39412. runtime.getWarnings().warn(ID.STRUCT_CONSTANT_REDEFINED, frame.getFile(), frame.getLine(), "redefining constant Struct::" + name, name);
  39413. superClass.remove_const(context, runtime.newString(name));
  39414. }
  39415. newStruct = superClass.defineClassUnder(name, superClass, STRUCT_INSTANCE_ALLOCATOR);
  39416. }
  39417. newStruct.index = ClassIndex.STRUCT;
  39418. newStruct.fastSetInternalVariable("__size__", member.length());
  39419. newStruct.fastSetInternalVariable("__member__", member);
  39420. newStruct.getSingletonClass().defineAnnotatedMethods(StructMethods.class);
  39421. // define access methods.
  39422. for (int i = (name == null && !nilName) ? 0 : 1; i < args.length; i++) {
  39423. final String memberName = args[i].asJavaString();
  39424. // if we are storing a name as well, index is one too high for values
  39425. final int index = (name == null && !nilName) ? i : i - 1;
  39426. newStruct.addMethod(memberName, new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.NO_FRAME_NO_SCOPE) {
  39427. @Override
  39428. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  39429. Arity.checkArgumentCount(self.getRuntime(), args, 0, 0);
  39430. return ((RubyStruct)self).get(index);
  39431. }
  39432. @Override
  39433. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
  39434. return ((RubyStruct)self).get(index);
  39435. }
  39436. @Override
  39437. public DynamicMethod dup() {
  39438. return this;
  39439. }
  39440. });
  39441. newStruct.addMethod(memberName + "=", new DynamicMethod(newStruct, Visibility.PUBLIC, CallConfiguration.NO_FRAME_NO_SCOPE) {
  39442. @Override
  39443. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
  39444. Arity.checkArgumentCount(self.getRuntime(), args, 1, 1);
  39445. return ((RubyStruct)self).set(args[0], index);
  39446. }
  39447. @Override
  39448. public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg) {
  39449. return ((RubyStruct)self).set(arg, index);
  39450. }
  39451. @Override
  39452. public DynamicMethod dup() {
  39453. return this;
  39454. }
  39455. });
  39456. }
  39457. if (block.isGiven()) {
  39458. // Struct bodies should be public by default, so set block visibility to public. JRUBY-1185.
  39459. block.getBinding().setVisibility(Visibility.PUBLIC);
  39460. block.yield(runtime.getCurrentContext(), null, newStruct, newStruct, false);
  39461. }
  39462. return newStruct;
  39463. }
  39464. // For binding purposes on the newly created struct types
  39465. public static class StructMethods {
  39466. @JRubyMethod(name = {"new", "[]"}, rest = true, frame = true)
  39467. public static IRubyObject newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
  39468. return RubyStruct.newStruct(recv, args, block);
  39469. }
  39470. @JRubyMethod
  39471. public static IRubyObject members(IRubyObject recv, Block block) {
  39472. return RubyStruct.members(recv, block);
  39473. }
  39474. }
  39475. /** Create new Structure.
  39476. *
  39477. * MRI: struct_alloc
  39478. *
  39479. */
  39480. public static RubyStruct newStruct(IRubyObject recv, IRubyObject[] args, Block block) {
  39481. RubyStruct struct = new RubyStruct(recv.getRuntime(), (RubyClass) recv);
  39482. struct.callInit(args, block);
  39483. return struct;
  39484. }
  39485. @JRubyMethod(rest = true, frame = true, visibility = Visibility.PRIVATE)
  39486. public IRubyObject initialize(IRubyObject[] args, Block unusedBlock) {
  39487. modify();
  39488. int size = RubyNumeric.fix2int(getInternalVariable(getMetaClass(), "__size__"));
  39489. if (args.length > size) {
  39490. throw getRuntime().newArgumentError("struct size differs (" + args.length +" for " + size + ")");
  39491. }
  39492. for (int i = 0; i < args.length; i++) {
  39493. values[i] = args[i];
  39494. }
  39495. return getRuntime().getNil();
  39496. }
  39497. public static RubyArray members(IRubyObject recv, Block block) {
  39498. RubyArray member = (RubyArray) getInternalVariable((RubyClass) recv, "__member__");
  39499. assert !member.isNil() : "uninitialized struct";
  39500. RubyArray result = recv.getRuntime().newArray(member.getLength());
  39501. for (int i = 0,k=member.getLength(); i < k; i++) {
  39502. result.append(recv.getRuntime().newString(member.eltInternal(i).asJavaString()));
  39503. }
  39504. return result;
  39505. }
  39506. @JRubyMethod
  39507. public RubyArray members() {
  39508. return members(classOf(), Block.NULL_BLOCK);
  39509. }
  39510. @JRubyMethod
  39511. public RubyArray select(ThreadContext context, Block block) {
  39512. RubyArray array = RubyArray.newArray(context.getRuntime());
  39513. for (int i = 0; i < values.length; i++) {
  39514. if (block.yield(context, values[i]).isTrue()) {
  39515. array.append(values[i]);
  39516. }
  39517. }
  39518. return array;
  39519. }
  39520. public IRubyObject set(IRubyObject value, int index) {
  39521. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39522. assert !member.isNil() : "uninitialized struct";
  39523. modify();
  39524. return values[index] = value;
  39525. }
  39526. private RaiseException notStructMemberError(String name) {
  39527. return getRuntime().newNameError(name + " is not struct member", name);
  39528. }
  39529. public IRubyObject get(int index) {
  39530. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39531. assert !member.isNil() : "uninitialized struct";
  39532. return values[index];
  39533. }
  39534. @Override
  39535. public void copySpecialInstanceVariables(IRubyObject clone) {
  39536. RubyStruct struct = (RubyStruct)clone;
  39537. struct.values = new IRubyObject[values.length];
  39538. System.arraycopy(values, 0, struct.values, 0, values.length);
  39539. }
  39540. @JRubyMethod(name = "==", required = 1)
  39541. public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
  39542. if (this == other) return getRuntime().getTrue();
  39543. if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
  39544. if (getMetaClass().getRealClass() != other.getMetaClass().getRealClass()) return getRuntime().getFalse();
  39545. Ruby runtime = getRuntime();
  39546. RubyStruct otherStruct = (RubyStruct)other;
  39547. for (int i = 0; i < values.length; i++) {
  39548. if (!equalInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
  39549. }
  39550. return runtime.getTrue();
  39551. }
  39552. @JRubyMethod(name = "eql?", required = 1)
  39553. public IRubyObject eql_p(ThreadContext context, IRubyObject other) {
  39554. if (this == other) return getRuntime().getTrue();
  39555. if (!(other instanceof RubyStruct)) return getRuntime().getFalse();
  39556. if (getMetaClass() != other.getMetaClass()) return getRuntime().getFalse();
  39557. Ruby runtime = getRuntime();
  39558. RubyStruct otherStruct = (RubyStruct)other;
  39559. for (int i = 0; i < values.length; i++) {
  39560. if (!eqlInternal(context, values[i], otherStruct.values[i])) return runtime.getFalse();
  39561. }
  39562. return runtime.getTrue();
  39563. }
  39564. /** inspect_struct
  39565. *
  39566. */
  39567. private IRubyObject inspectStruct(final ThreadContext context) {
  39568. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39569. assert !member.isNil() : "uninitialized struct";
  39570. ByteList buffer = new ByteList("#<struct ".getBytes());
  39571. buffer.append(getMetaClass().getRealClass().getRealClass().getName().getBytes());
  39572. buffer.append(' ');
  39573. for (int i = 0,k=member.getLength(); i < k; i++) {
  39574. if (i > 0) buffer.append(',').append(' ');
  39575. // FIXME: MRI has special case for constants here
  39576. buffer.append(RubyString.objAsString(context, member.eltInternal(i)).getByteList());
  39577. buffer.append('=');
  39578. buffer.append(inspect(context, values[i]).getByteList());
  39579. }
  39580. buffer.append('>');
  39581. return getRuntime().newString(buffer); // OBJ_INFECT
  39582. }
  39583. @JRubyMethod(name = {"inspect", "to_s"})
  39584. public IRubyObject inspect(ThreadContext context) {
  39585. if (getRuntime().isInspecting(this)) return getRuntime().newString("#<struct " + getMetaClass().getRealClass().getName() + ":...>");
  39586. try {
  39587. getRuntime().registerInspecting(this);
  39588. return inspectStruct(context);
  39589. } finally {
  39590. getRuntime().unregisterInspecting(this);
  39591. }
  39592. }
  39593. @JRubyMethod(name = {"to_a", "values"})
  39594. public RubyArray to_a() {
  39595. return getRuntime().newArray(values);
  39596. }
  39597. @JRubyMethod(name = {"size", "length"} )
  39598. public RubyFixnum size() {
  39599. return getRuntime().newFixnum(values.length);
  39600. }
  39601. @JRubyMethod(name = "each", backtrace = true)
  39602. public IRubyObject each(ThreadContext context, Block block) {
  39603. for (int i = 0; i < values.length; i++) {
  39604. block.yield(context, values[i]);
  39605. }
  39606. return this;
  39607. }
  39608. @JRubyMethod(frame = true)
  39609. public IRubyObject each_pair(ThreadContext context, Block block) {
  39610. RubyArray member = (RubyArray) getInternalVariable(classOf(), "__member__");
  39611. assert !member.isNil() : "uninitialized struct";
  39612. for (int i = 0; i < values.length; i++) {
  39613. block.yield(context, getRuntime().newArrayNoCopy(new IRubyObject[]{member.eltInternal(i), values[i]}));
  39614. }
  39615. return this;
  39616. }
  39617. @JRubyMethod(name = "[]", required = 1)
  39618. public IRubyObject aref(IRubyObject key) {
  39619. if (key instanceof RubyString || key instanceof RubySymbol) {
  39620. return getByName(key.asJavaString());
  39621. }
  39622. int idx = RubyNumeric.fix2int(key);
  39623. idx = idx < 0 ? values.length + idx : idx;
  39624. if (idx < 0) {
  39625. throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
  39626. } else if (idx >= values.length) {
  39627. throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
  39628. }
  39629. return values[idx];
  39630. }
  39631. @JRubyMethod(name = "[]=", required = 2)
  39632. public IRubyObject aset(IRubyObject key, IRubyObject value) {
  39633. if (key instanceof RubyString || key instanceof RubySymbol) {
  39634. return setByName(key.asJavaString(), value);
  39635. }
  39636. int idx = RubyNumeric.fix2int(key);
  39637. idx = idx < 0 ? values.length + idx : idx;
  39638. if (idx < 0) {
  39639. throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
  39640. } else if (idx >= values.length) {
  39641. throw getRuntime().newIndexError("offset " + idx + " too large for struct (size:" + values.length + ")");
  39642. }
  39643. modify();
  39644. return values[idx] = value;
  39645. }
  39646. // FIXME: This is copied code from RubyArray. Both RE, Struct, and Array should share one impl
  39647. // This is also hacky since I construct ruby objects to access ruby arrays through aref instead
  39648. // of something lower.
  39649. @JRubyMethod(rest = true)
  39650. public IRubyObject values_at(IRubyObject[] args) {
  39651. long olen = values.length;
  39652. RubyArray result = getRuntime().newArray(args.length);
  39653. for (int i = 0; i < args.length; i++) {
  39654. if (args[i] instanceof RubyFixnum) {
  39655. result.append(aref(args[i]));
  39656. continue;
  39657. }
  39658. long beglen[];
  39659. if (!(args[i] instanceof RubyRange)) {
  39660. } else if ((beglen = ((RubyRange) args[i]).begLen(olen, 0)) == null) {
  39661. continue;
  39662. } else {
  39663. int beg = (int) beglen[0];
  39664. int len = (int) beglen[1];
  39665. int end = len;
  39666. for (int j = 0; j < end; j++) {
  39667. result.append(aref(getRuntime().newFixnum(j + beg)));
  39668. }
  39669. continue;
  39670. }
  39671. result.append(aref(getRuntime().newFixnum(RubyNumeric.num2long(args[i]))));
  39672. }
  39673. return result;
  39674. }
  39675. public static void marshalTo(RubyStruct struct, MarshalStream output) throws java.io.IOException {
  39676. output.registerLinkTarget(struct);
  39677. output.dumpDefaultObjectHeader('S', struct.getMetaClass());
  39678. List members = ((RubyArray) getInternalVariable(struct.classOf(), "__member__")).getList();
  39679. output.writeInt(members.size());
  39680. for (int i = 0; i < members.size(); i++) {
  39681. RubySymbol name = (RubySymbol) members.get(i);
  39682. output.dumpObject(name);
  39683. output.dumpObject(struct.values[i]);
  39684. }
  39685. }
  39686. public static RubyStruct unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  39687. Ruby runtime = input.getRuntime();
  39688. RubySymbol className = (RubySymbol) input.unmarshalObject();
  39689. RubyClass rbClass = pathToClass(runtime, className.asJavaString());
  39690. if (rbClass == null) {
  39691. throw runtime.newNameError("uninitialized constant " + className, className.asJavaString());
  39692. }
  39693. RubyArray mem = members(rbClass, Block.NULL_BLOCK);
  39694. int len = input.unmarshalInt();
  39695. IRubyObject[] values = new IRubyObject[len];
  39696. for(int i = 0; i < len; i++) {
  39697. values[i] = runtime.getNil();
  39698. }
  39699. RubyStruct result = newStruct(rbClass, values, Block.NULL_BLOCK);
  39700. input.registerLinkTarget(result);
  39701. for(int i = 0; i < len; i++) {
  39702. IRubyObject slot = input.unmarshalObject();
  39703. if(!mem.eltInternal(i).toString().equals(slot.toString())) {
  39704. throw runtime.newTypeError("struct " + rbClass.getName() + " not compatible (:" + slot + " for :" + mem.eltInternal(i) + ")");
  39705. }
  39706. result.aset(runtime.newFixnum(i), input.unmarshalObject());
  39707. }
  39708. return result;
  39709. }
  39710. private static RubyClass pathToClass(Ruby runtime, String path) {
  39711. // FIXME: Throw the right ArgumentError's if the class is missing
  39712. // or if it's a module.
  39713. return (RubyClass) runtime.getClassFromPath(path);
  39714. }
  39715. private static ObjectAllocator STRUCT_INSTANCE_ALLOCATOR = new ObjectAllocator() {
  39716. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  39717. RubyStruct instance = new RubyStruct(runtime, klass);
  39718. instance.setMetaClass(klass);
  39719. return instance;
  39720. }
  39721. };
  39722. @Override
  39723. @JRubyMethod(required = 1)
  39724. public IRubyObject initialize_copy(IRubyObject arg) {
  39725. if (this == arg) return this;
  39726. RubyStruct original = (RubyStruct) arg;
  39727. values = new IRubyObject[original.values.length];
  39728. System.arraycopy(original.values, 0, values, 0, original.values.length);
  39729. return this;
  39730. }
  39731. }
  39732. /*
  39733. ***** BEGIN LICENSE BLOCK *****
  39734. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  39735. *
  39736. * The contents of this file are subject to the Common Public
  39737. * License Version 1.0 (the "License"); you may not use this file
  39738. * except in compliance with the License. You may obtain a copy of
  39739. * the License at http://www.eclipse.org/legal/cpl-v10.html
  39740. *
  39741. * Software distributed under the License is distributed on an "AS
  39742. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  39743. * implied. See the License for the specific language governing
  39744. * rights and limitations under the License.
  39745. *
  39746. * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
  39747. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  39748. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  39749. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  39750. * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
  39751. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  39752. * Copyright (C) 2006 Derek Berner <derek.berner@state.nm.us>
  39753. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  39754. * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
  39755. *
  39756. * Alternatively, the contents of this file may be used under the terms of
  39757. * either of the GNU General Public License Version 2 or later (the "GPL"),
  39758. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  39759. * in which case the provisions of the GPL or the LGPL are applicable instead
  39760. * of those above. If you wish to allow use of your version of this file only
  39761. * under the terms of either the GPL or the LGPL, and not to allow others to
  39762. * use your version of this file under the terms of the CPL, indicate your
  39763. * decision by deleting the provisions above and replace them with the notice
  39764. * and other provisions required by the GPL or the LGPL. If you do not delete
  39765. * the provisions above, a recipient may use your version of this file under
  39766. * the terms of any one of the CPL, the GPL or the LGPL.
  39767. ***** END LICENSE BLOCK *****/
  39768. package org.jruby;
  39769. import java.util.concurrent.locks.ReentrantLock;
  39770. import org.jruby.anno.JRubyMethod;
  39771. import org.jruby.anno.JRubyClass;
  39772. import org.jruby.common.IRubyWarnings.ID;
  39773. import org.jruby.javasupport.util.RuntimeHelpers;
  39774. import org.jruby.runtime.ClassIndex;
  39775. import org.jruby.runtime.ObjectAllocator;
  39776. import org.jruby.runtime.ThreadContext;
  39777. import org.jruby.runtime.Block;
  39778. import org.jruby.runtime.BlockCallback;
  39779. import org.jruby.runtime.builtin.IRubyObject;
  39780. import org.jruby.runtime.marshal.UnmarshalStream;
  39781. import org.jruby.util.ByteList;
  39782. /**
  39783. * Represents a Ruby symbol (e.g. :bar)
  39784. */
  39785. @JRubyClass(name="Symbol")
  39786. public class RubySymbol extends RubyObject {
  39787. private final String symbol;
  39788. private final int id;
  39789. private final ByteList symbolBytes;
  39790. /**
  39791. *
  39792. * @param runtime
  39793. * @param internedSymbol the String value of the new Symbol. This <em>must</em>
  39794. * have been previously interned
  39795. */
  39796. private RubySymbol(Ruby runtime, String internedSymbol) {
  39797. super(runtime, runtime.getSymbol(), false);
  39798. // symbol string *must* be interned
  39799. assert internedSymbol == internedSymbol.intern() : internedSymbol + " is not interned";
  39800. this.symbol = internedSymbol;
  39801. this.symbolBytes = ByteList.create(symbol);
  39802. this.id = runtime.allocSymbolId();
  39803. }
  39804. public static RubyClass createSymbolClass(Ruby runtime) {
  39805. RubyClass symbolClass = runtime.defineClass("Symbol", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  39806. runtime.setSymbol(symbolClass);
  39807. RubyClass symbolMetaClass = symbolClass.getMetaClass();
  39808. symbolClass.index = ClassIndex.SYMBOL;
  39809. symbolClass.kindOf = new RubyModule.KindOf() {
  39810. public boolean isKindOf(IRubyObject obj, RubyModule type) {
  39811. return obj instanceof RubySymbol;
  39812. }
  39813. };
  39814. symbolClass.defineAnnotatedMethods(RubySymbol.class);
  39815. symbolMetaClass.undefineMethod("new");
  39816. return symbolClass;
  39817. }
  39818. @Override
  39819. public int getNativeTypeIndex() {
  39820. return ClassIndex.SYMBOL;
  39821. }
  39822. /** rb_to_id
  39823. *
  39824. * @return a String representation of the symbol
  39825. */
  39826. @Override
  39827. public String asJavaString() {
  39828. return symbol;
  39829. }
  39830. /** short circuit for Symbol key comparison
  39831. *
  39832. */
  39833. @Override
  39834. public final boolean eql(IRubyObject other) {
  39835. return other == this;
  39836. }
  39837. @Override
  39838. public boolean isImmediate() {
  39839. return true;
  39840. }
  39841. @Override
  39842. public RubyClass getSingletonClass() {
  39843. throw getRuntime().newTypeError("can't define singleton");
  39844. }
  39845. public static RubySymbol getSymbolLong(Ruby runtime, long id) {
  39846. return runtime.getSymbolTable().lookup(id);
  39847. }
  39848. /* Symbol class methods.
  39849. *
  39850. */
  39851. public static RubySymbol newSymbol(Ruby runtime, String name) {
  39852. return runtime.getSymbolTable().getSymbol(name);
  39853. }
  39854. @JRubyMethod(name = "to_i")
  39855. public RubyFixnum to_i() {
  39856. return getRuntime().newFixnum(id);
  39857. }
  39858. @JRubyMethod(name = "to_int")
  39859. public RubyFixnum to_int() {
  39860. if (getRuntime().getVerbose().isTrue()) {
  39861. getRuntime().getWarnings().warn(ID.SYMBOL_AS_INTEGER, "treating Symbol as an integer");
  39862. }
  39863. return to_i();
  39864. }
  39865. @JRubyMethod(name = "inspect")
  39866. @Override
  39867. public IRubyObject inspect() {
  39868. Ruby runtime = getRuntime();
  39869. return runtime.newString(":" +
  39870. (isSymbolName(symbol) ? symbol : RubyString.newStringShared(runtime, symbolBytes).dump().toString()));
  39871. }
  39872. @JRubyMethod(name = "to_s")
  39873. @Override
  39874. public IRubyObject to_s() {
  39875. return RubyString.newStringShared(getRuntime(), symbolBytes);
  39876. }
  39877. @JRubyMethod(name = "id2name")
  39878. public IRubyObject id2name() {
  39879. return to_s();
  39880. }
  39881. @JRubyMethod(name = "===", required = 1)
  39882. @Override
  39883. public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
  39884. return super.op_equal(context, other);
  39885. }
  39886. @Override
  39887. public RubyFixnum hash() {
  39888. return getRuntime().newFixnum(hashCode());
  39889. }
  39890. @Override
  39891. public int hashCode() {
  39892. return id;
  39893. }
  39894. public int getId() {
  39895. return id;
  39896. }
  39897. @Override
  39898. public boolean equals(Object other) {
  39899. return other == this;
  39900. }
  39901. @JRubyMethod(name = "to_sym")
  39902. public IRubyObject to_sym() {
  39903. return this;
  39904. }
  39905. @Override
  39906. public IRubyObject freeze(ThreadContext context) {
  39907. return this;
  39908. }
  39909. @Override
  39910. public IRubyObject taint(ThreadContext context) {
  39911. return this;
  39912. }
  39913. private static class ToProcCallback implements BlockCallback {
  39914. private RubySymbol symbol;
  39915. public ToProcCallback(RubySymbol symbol) {
  39916. this.symbol = symbol;
  39917. }
  39918. public IRubyObject call(ThreadContext ctx, IRubyObject[] args, Block blk) {
  39919. IRubyObject[] currentArgs = args;
  39920. switch(currentArgs.length) {
  39921. case 0: throw symbol.getRuntime().newArgumentError("no receiver given");
  39922. case 1: {
  39923. if((currentArgs[0] instanceof RubyArray) && ((RubyArray)currentArgs[0]).getLength() != 0) {
  39924. // This is needed to unpack stuff
  39925. currentArgs = ((RubyArray)currentArgs[0]).toJavaArrayMaybeUnsafe();
  39926. IRubyObject[] args2 = new IRubyObject[currentArgs.length-1];
  39927. System.arraycopy(currentArgs, 1, args2, 0, args2.length);
  39928. return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol, args2);
  39929. } else {
  39930. return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol);
  39931. }
  39932. }
  39933. default: {
  39934. IRubyObject[] args2 = new IRubyObject[currentArgs.length-1];
  39935. System.arraycopy(currentArgs, 1, args2, 0, args2.length);
  39936. return RuntimeHelpers.invoke(ctx, currentArgs[0], symbol.symbol, args2);
  39937. }
  39938. }
  39939. }
  39940. }
  39941. /*
  39942. @JRubyMethod
  39943. public IRubyObject to_proc() {
  39944. return RubyProc.newProc(getRuntime(),
  39945. CallBlock.newCallClosure(this, getRuntime().getSymbol(), Arity.noArguments(), new ToProcCallback(this), getRuntime().getCurrentContext()),
  39946. Block.Type.PROC);
  39947. }
  39948. */
  39949. private static boolean isIdentStart(char c) {
  39950. return ((c >= 'a' && c <= 'z')|| (c >= 'A' && c <= 'Z')
  39951. || c == '_');
  39952. }
  39953. private static boolean isIdentChar(char c) {
  39954. return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
  39955. || c == '_');
  39956. }
  39957. private static boolean isIdentifier(String s) {
  39958. if (s == null || s.length() <= 0) {
  39959. return false;
  39960. }
  39961. if (!isIdentStart(s.charAt(0))) {
  39962. return false;
  39963. }
  39964. for (int i = 1; i < s.length(); i++) {
  39965. if (!isIdentChar(s.charAt(i))) {
  39966. return false;
  39967. }
  39968. }
  39969. return true;
  39970. }
  39971. /**
  39972. * is_special_global_name from parse.c.
  39973. * @param s
  39974. * @return
  39975. */
  39976. private static boolean isSpecialGlobalName(String s) {
  39977. if (s == null || s.length() <= 0) {
  39978. return false;
  39979. }
  39980. int length = s.length();
  39981. switch (s.charAt(0)) {
  39982. case '~': case '*': case '$': case '?': case '!': case '@': case '/': case '\\':
  39983. case ';': case ',': case '.': case '=': case ':': case '<': case '>': case '\"':
  39984. case '&': case '`': case '\'': case '+': case '0':
  39985. return length == 1;
  39986. case '-':
  39987. return (length == 1 || (length == 2 && isIdentChar(s.charAt(1))));
  39988. default:
  39989. // we already confirmed above that length > 0
  39990. for (int i = 0; i < length; i++) {
  39991. if (!Character.isDigit(s.charAt(i))) {
  39992. return false;
  39993. }
  39994. }
  39995. }
  39996. return true;
  39997. }
  39998. private static boolean isSymbolName(String s) {
  39999. if (s == null || s.length() < 1) {
  40000. return false;
  40001. }
  40002. int length = s.length();
  40003. char c = s.charAt(0);
  40004. switch (c) {
  40005. case '$':
  40006. if (length > 1 && isSpecialGlobalName(s.substring(1))) {
  40007. return true;
  40008. }
  40009. return isIdentifier(s.substring(1));
  40010. case '@':
  40011. int offset = 1;
  40012. if (length >= 2 && s.charAt(1) == '@') {
  40013. offset++;
  40014. }
  40015. return isIdentifier(s.substring(offset));
  40016. case '<':
  40017. return (length == 1 || (length == 2 && (s.equals("<<") || s.equals("<="))) ||
  40018. (length == 3 && s.equals("<=>")));
  40019. case '>':
  40020. return (length == 1) || (length == 2 && (s.equals(">>") || s.equals(">=")));
  40021. case '=':
  40022. return ((length == 2 && (s.equals("==") || s.equals("=~"))) ||
  40023. (length == 3 && s.equals("===")));
  40024. case '*':
  40025. return (length == 1 || (length == 2 && s.equals("**")));
  40026. case '+':
  40027. return (length == 1 || (length == 2 && s.equals("+@")));
  40028. case '-':
  40029. return (length == 1 || (length == 2 && s.equals("-@")));
  40030. case '|': case '^': case '&': case '/': case '%': case '~': case '`':
  40031. return length == 1;
  40032. case '[':
  40033. return s.equals("[]") || s.equals("[]=");
  40034. }
  40035. if (!isIdentStart(c)) {
  40036. return false;
  40037. }
  40038. boolean localID = (c >= 'a' && c <= 'z');
  40039. int last = 1;
  40040. for (; last < length; last++) {
  40041. char d = s.charAt(last);
  40042. if (!isIdentChar(d)) {
  40043. break;
  40044. }
  40045. }
  40046. if (last == length) {
  40047. return true;
  40048. } else if (localID && last == length - 1) {
  40049. char d = s.charAt(last);
  40050. return d == '!' || d == '?' || d == '=';
  40051. }
  40052. return false;
  40053. }
  40054. @JRubyMethod(name = "all_symbols", meta = true)
  40055. public static IRubyObject all_symbols(IRubyObject recv) {
  40056. return recv.getRuntime().getSymbolTable().all_symbols();
  40057. }
  40058. public static RubySymbol unmarshalFrom(UnmarshalStream input) throws java.io.IOException {
  40059. RubySymbol result = newSymbol(input.getRuntime(), RubyString.byteListToString(input.unmarshalString()));
  40060. input.registerLinkTarget(result);
  40061. return result;
  40062. }
  40063. public static class SymbolTable {
  40064. static final int DEFAULT_INITIAL_CAPACITY = 2048; // *must* be power of 2!
  40065. static final int MAXIMUM_CAPACITY = 1 << 30;
  40066. static final float DEFAULT_LOAD_FACTOR = 0.75f;
  40067. private final ReentrantLock tableLock = new ReentrantLock();
  40068. private volatile SymbolEntry[] symbolTable;
  40069. private int size;
  40070. private int threshold;
  40071. private final float loadFactor;
  40072. private final Ruby runtime;
  40073. public SymbolTable(Ruby runtime) {
  40074. this.runtime = runtime;
  40075. this.loadFactor = DEFAULT_LOAD_FACTOR;
  40076. this.threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
  40077. this.symbolTable = new SymbolEntry[DEFAULT_INITIAL_CAPACITY];
  40078. }
  40079. // note all fields are final -- rehash creates new entries when necessary.
  40080. // as documented in java.util.concurrent.ConcurrentHashMap.java, that will
  40081. // statistically affect only a small percentage (< 20%) of entries for a given rehash.
  40082. static class SymbolEntry {
  40083. final int hash;
  40084. final String name;
  40085. final RubySymbol symbol;
  40086. final SymbolEntry next;
  40087. SymbolEntry(int hash, String name, RubySymbol symbol, SymbolEntry next) {
  40088. this.hash = hash;
  40089. this.name = name;
  40090. this.symbol = symbol;
  40091. this.next = next;
  40092. }
  40093. }
  40094. public RubySymbol getSymbol(String name) {
  40095. int hash = name.hashCode();
  40096. SymbolEntry[] table;
  40097. for (SymbolEntry e = (table = symbolTable)[hash & (table.length - 1)]; e != null; e = e.next) {
  40098. if (hash == e.hash && name.equals(e.name)) {
  40099. return e.symbol;
  40100. }
  40101. }
  40102. ReentrantLock lock;
  40103. (lock = tableLock).lock();
  40104. try {
  40105. int potentialNewSize;
  40106. if ((potentialNewSize = size + 1) > threshold) {
  40107. table = rehash();
  40108. } else {
  40109. table = symbolTable;
  40110. }
  40111. int index;
  40112. // try lookup again under lock
  40113. for (SymbolEntry e = table[index = hash & (table.length - 1)]; e != null; e = e.next) {
  40114. if (hash == e.hash && name.equals(e.name)) {
  40115. return e.symbol;
  40116. }
  40117. }
  40118. String internedName;
  40119. RubySymbol symbol = new RubySymbol(runtime, internedName = name.intern());
  40120. table[index] = new SymbolEntry(hash, internedName, symbol, table[index]);
  40121. size = potentialNewSize;
  40122. // write-volatile
  40123. symbolTable = table;
  40124. return symbol;
  40125. } finally {
  40126. lock.unlock();
  40127. }
  40128. }
  40129. public RubySymbol fastGetSymbol(String internedName) {
  40130. assert internedName == internedName.intern() : internedName + " is not interned";
  40131. SymbolEntry[] table;
  40132. for (SymbolEntry e = (table = symbolTable)[internedName.hashCode() & (table.length - 1)]; e != null; e = e.next) {
  40133. if (internedName == e.name) {
  40134. return e.symbol;
  40135. }
  40136. }
  40137. ReentrantLock lock;
  40138. (lock = tableLock).lock();
  40139. try {
  40140. int potentialNewSize;
  40141. if ((potentialNewSize = size + 1) > threshold) {
  40142. table = rehash();
  40143. } else {
  40144. table = symbolTable;
  40145. }
  40146. int index;
  40147. int hash;
  40148. // try lookup again under lock
  40149. for (SymbolEntry e = table[index = (hash = internedName.hashCode()) & (table.length - 1)]; e != null; e = e.next) {
  40150. if (internedName == e.name) {
  40151. return e.symbol;
  40152. }
  40153. }
  40154. RubySymbol symbol = new RubySymbol(runtime, internedName);
  40155. table[index] = new SymbolEntry(hash, internedName, symbol, table[index]);
  40156. size = potentialNewSize;
  40157. // write-volatile
  40158. symbolTable = table;
  40159. return symbol;
  40160. } finally {
  40161. lock.unlock();
  40162. }
  40163. }
  40164. // backwards-compatibility, but threadsafe now
  40165. public RubySymbol lookup(String name) {
  40166. int hash = name.hashCode();
  40167. SymbolEntry[] table;
  40168. for (SymbolEntry e = (table = symbolTable)[hash & (table.length - 1)]; e != null; e = e.next) {
  40169. if (hash == e.hash && name.equals(e.name)) {
  40170. return e.symbol;
  40171. }
  40172. }
  40173. return null;
  40174. }
  40175. public RubySymbol lookup(long id) {
  40176. SymbolEntry[] table = symbolTable;
  40177. for (int i = table.length; --i >= 0; ) {
  40178. for (SymbolEntry e = table[i]; e != null; e = e.next) {
  40179. if (id == e.symbol.id) {
  40180. return e.symbol;
  40181. }
  40182. }
  40183. }
  40184. return null;
  40185. }
  40186. public RubyArray all_symbols() {
  40187. SymbolEntry[] table = this.symbolTable;
  40188. RubyArray array = runtime.newArray(this.size);
  40189. for (int i = table.length; --i >= 0; ) {
  40190. for (SymbolEntry e = table[i]; e != null; e = e.next) {
  40191. array.append(e.symbol);
  40192. }
  40193. }
  40194. return array;
  40195. }
  40196. // not so backwards-compatible here, but no one should have been
  40197. // calling this anyway.
  40198. @Deprecated
  40199. public void store(RubySymbol symbol) {
  40200. throw new UnsupportedOperationException();
  40201. }
  40202. private SymbolEntry[] rehash() {
  40203. SymbolEntry[] oldTable = symbolTable;
  40204. int oldCapacity;
  40205. if ((oldCapacity = oldTable.length) >= MAXIMUM_CAPACITY) {
  40206. return oldTable;
  40207. }
  40208. int newCapacity = oldCapacity << 1;
  40209. SymbolEntry[] newTable = new SymbolEntry[newCapacity];
  40210. threshold = (int)(newCapacity * loadFactor);
  40211. int sizeMask = newCapacity - 1;
  40212. SymbolEntry e;
  40213. for (int i = oldCapacity; --i >= 0; ) {
  40214. // We need to guarantee that any existing reads of old Map can
  40215. // proceed. So we cannot yet null out each bin.
  40216. e = oldTable[i];
  40217. if (e != null) {
  40218. SymbolEntry next = e.next;
  40219. int idx = e.hash & sizeMask;
  40220. // Single node on list
  40221. if (next == null)
  40222. newTable[idx] = e;
  40223. else {
  40224. // Reuse trailing consecutive sequence at same slot
  40225. SymbolEntry lastRun = e;
  40226. int lastIdx = idx;
  40227. for (SymbolEntry last = next;
  40228. last != null;
  40229. last = last.next) {
  40230. int k = last.hash & sizeMask;
  40231. if (k != lastIdx) {
  40232. lastIdx = k;
  40233. lastRun = last;
  40234. }
  40235. }
  40236. newTable[lastIdx] = lastRun;
  40237. // Clone all remaining nodes
  40238. for (SymbolEntry p = e; p != lastRun; p = p.next) {
  40239. int k = p.hash & sizeMask;
  40240. SymbolEntry n = newTable[k];
  40241. newTable[k] = new SymbolEntry(p.hash, p.name, p.symbol, n);
  40242. }
  40243. }
  40244. }
  40245. }
  40246. symbolTable = newTable;
  40247. return newTable;
  40248. }
  40249. }
  40250. }
  40251. package org.jruby;
  40252. import java.io.IOException;
  40253. import java.util.List;
  40254. import java.util.Map;
  40255. import java.util.HashMap;
  40256. import org.jruby.anno.JRubyMethod;
  40257. import org.jruby.anno.JRubyClass;
  40258. import org.jruby.runtime.Arity;
  40259. import org.jruby.runtime.Block;
  40260. import org.jruby.runtime.ObjectAllocator;
  40261. import org.jruby.runtime.ObjectMarshal;
  40262. import org.jruby.runtime.Visibility;
  40263. import org.jruby.runtime.builtin.IRubyObject;
  40264. import org.jruby.runtime.builtin.Variable;
  40265. import org.jruby.runtime.component.VariableEntry;
  40266. import org.jruby.runtime.marshal.MarshalStream;
  40267. import org.jruby.runtime.marshal.UnmarshalStream;
  40268. @JRubyClass(name="SystemCallError", parent="StandardError")
  40269. public class RubySystemCallError extends RubyException {
  40270. private IRubyObject errno = getRuntime().getNil();
  40271. private final static Map<String, String> defaultMessages = new HashMap<String, String>();
  40272. static {
  40273. defaultMessages.put("Errno::EPERM", "Operation not permitted");
  40274. defaultMessages.put("Errno::ENOENT", "No such file or directory");
  40275. defaultMessages.put("Errno::ESRCH", "No such process");
  40276. defaultMessages.put("Errno::EINTR", "Interrupted system call");
  40277. defaultMessages.put("Errno::EIO", "Input/output error");
  40278. defaultMessages.put("Errno::ENXIO", "Device not configured");
  40279. defaultMessages.put("Errno::E2BIG", "Argument list too long");
  40280. defaultMessages.put("Errno::ENOEXEC", "Exec format error");
  40281. defaultMessages.put("Errno::EBADF", "Bad file descriptor");
  40282. defaultMessages.put("Errno::ECHILD", "No child processes");
  40283. defaultMessages.put("Errno::EDEADLK", "Resource deadlock avoided");
  40284. defaultMessages.put("Errno::ENOMEM", "Cannot allocate memory");
  40285. defaultMessages.put("Errno::EACCES", "Permission denied");
  40286. defaultMessages.put("Errno::EFAULT", "Bad address");
  40287. defaultMessages.put("Errno::ENOTBLK", "Block device required");
  40288. defaultMessages.put("Errno::EBUSY", "Resource busy");
  40289. defaultMessages.put("Errno::EEXIST", "File exists");
  40290. defaultMessages.put("Errno::EXDEV", "Cross-device link");
  40291. defaultMessages.put("Errno::ENODEV", "Operation not supported by device");
  40292. defaultMessages.put("Errno::ENOTDIR", "Not a directory");
  40293. defaultMessages.put("Errno::EISDIR", "Is a directory");
  40294. defaultMessages.put("Errno::EINVAL", "Invalid argument");
  40295. defaultMessages.put("Errno::ENFILE", "Too many open files in system");
  40296. defaultMessages.put("Errno::EMFILE", "Too many open files");
  40297. defaultMessages.put("Errno::ENOTTY", "Inappropriate ioctl for device");
  40298. defaultMessages.put("Errno::ETXTBSY", "Text file busy");
  40299. defaultMessages.put("Errno::EFBIG", "File too large");
  40300. defaultMessages.put("Errno::ENOSPC", "No space left on device");
  40301. defaultMessages.put("Errno::ESPIPE", "Illegal seek");
  40302. defaultMessages.put("Errno::EROFS", "Read-only file system");
  40303. defaultMessages.put("Errno::EMLINK", "Too many links");
  40304. defaultMessages.put("Errno::EPIPE", "Broken pipe");
  40305. defaultMessages.put("Errno::EDOM", "Numerical argument out of domain");
  40306. defaultMessages.put("Errno::ERANGE", "Result too large");
  40307. defaultMessages.put("Errno::EAGAIN", "Resource temporarily unavailable");
  40308. defaultMessages.put("Errno::EWOULDBLOCK", "Resource temporarily unavailable");
  40309. defaultMessages.put("Errno::EINPROGRESS", "Operation now in progress");
  40310. defaultMessages.put("Errno::EALREADY", "Operation already in progress");
  40311. defaultMessages.put("Errno::ENOTSOCK", "Socket operation on non-socket");
  40312. defaultMessages.put("Errno::EDESTADDRREQ", "Destination address required");
  40313. defaultMessages.put("Errno::EMSGSIZE", "Message too long");
  40314. defaultMessages.put("Errno::EPROTOTYPE", "Protocol wrong type for socket");
  40315. defaultMessages.put("Errno::ENOPROTOOPT", "Protocol not available");
  40316. defaultMessages.put("Errno::EPROTONOSUPPORT", "Protocol not supported");
  40317. defaultMessages.put("Errno::ESOCKTNOSUPPORT", "Socket type not supported");
  40318. defaultMessages.put("Errno::EPFNOSUPPORT", "Protocol family not supported");
  40319. defaultMessages.put("Errno::EAFNOSUPPORT", "Address family not supported by protocol family");
  40320. defaultMessages.put("Errno::EADDRINUSE", "Address already in use");
  40321. defaultMessages.put("Errno::EADDRNOTAVAIL", "Can't assign requested address");
  40322. defaultMessages.put("Errno::ENETDOWN", "Network is down");
  40323. defaultMessages.put("Errno::ENETUNREACH", "Network is unreachable");
  40324. defaultMessages.put("Errno::ENETRESET", "Network dropped connection on reset");
  40325. defaultMessages.put("Errno::ECONNABORTED", "Software caused connection abort");
  40326. defaultMessages.put("Errno::ECONNRESET", "Connection reset by peer");
  40327. defaultMessages.put("Errno::ENOBUFS", "No buffer space available");
  40328. defaultMessages.put("Errno::EISCONN", "Socket is already connected");
  40329. defaultMessages.put("Errno::ENOTCONN", "Socket is not connected");
  40330. defaultMessages.put("Errno::ESHUTDOWN", "Can't send after socket shutdown");
  40331. defaultMessages.put("Errno::ETOOMANYREFS", "Too many references: can't splice");
  40332. defaultMessages.put("Errno::ETIMEDOUT", "Operation timed out");
  40333. defaultMessages.put("Errno::ECONNREFUSED", "Connection refused");
  40334. defaultMessages.put("Errno::ELOOP", "Too many levels of symbolic links");
  40335. defaultMessages.put("Errno::ENAMETOOLONG", "File name too long");
  40336. defaultMessages.put("Errno::EHOSTDOWN", "Host is down");
  40337. defaultMessages.put("Errno::EHOSTUNREACH", "No route to host");
  40338. defaultMessages.put("Errno::ENOTEMPTY", "Directory not empty");
  40339. defaultMessages.put("Errno::EUSERS", "Too many users");
  40340. defaultMessages.put("Errno::EDQUOT", "Disc quota exceeded");
  40341. defaultMessages.put("Errno::ESTALE", "Stale NFS file handle");
  40342. defaultMessages.put("Errno::EREMOTE", "Too many levels of remote in path");
  40343. defaultMessages.put("Errno::ENOLCK", "No locks available");
  40344. defaultMessages.put("Errno::ENOSYS", "Function not implemented");
  40345. defaultMessages.put("Errno::EOVERFLOW", "Value too large to be stored in data type");
  40346. defaultMessages.put("Errno::EIDRM", "Identifier removed");
  40347. defaultMessages.put("Errno::ENOMSG", "No message of desired type");
  40348. defaultMessages.put("Errno::EILSEQ", "Illegal byte sequence");
  40349. defaultMessages.put("Errno::EBADMSG", "Bad message");
  40350. defaultMessages.put("Errno::EMULTIHOP", "EMULTIHOP (Reserved)");
  40351. defaultMessages.put("Errno::ENODATA", "No message available on STREAM");
  40352. defaultMessages.put("Errno::ENOLINK", "ENOLINK (Reserved)");
  40353. defaultMessages.put("Errno::ENOSR", "No STREAM resources");
  40354. defaultMessages.put("Errno::ENOSTR", "Not a STREAM");
  40355. defaultMessages.put("Errno::EPROTO", "Protocol error");
  40356. defaultMessages.put("Errno::ETIME", "STREAM ioctl timeout");
  40357. defaultMessages.put("Errno::EOPNOTSUPP", "Operation not supported");
  40358. defaultMessages.put("Errno::EOPNOTSUPP_DARWIN", "Operation not supported");
  40359. }
  40360. protected RubySystemCallError(Ruby runtime, RubyClass rubyClass) {
  40361. super(runtime, rubyClass, null);
  40362. }
  40363. public RubySystemCallError(Ruby runtime, RubyClass rubyClass, String message, int errno) {
  40364. super(runtime, rubyClass, message);
  40365. this.errno = runtime.newFixnum(errno);
  40366. }
  40367. private static ObjectAllocator SYSTEM_CALL_ERROR_ALLOCATOR = new ObjectAllocator() {
  40368. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  40369. RubyException instance = new RubySystemCallError(runtime, klass);
  40370. instance.setMetaClass(klass);
  40371. return instance;
  40372. }
  40373. };
  40374. private static final ObjectMarshal SYSTEM_CALL_ERROR_MARSHAL = new ObjectMarshal() {
  40375. public void marshalTo(Ruby runtime, Object obj, RubyClass type,
  40376. MarshalStream marshalStream) throws IOException {
  40377. RubySystemCallError exc = (RubySystemCallError) obj;
  40378. marshalStream.registerLinkTarget(exc);
  40379. List<Variable<IRubyObject>> attrs = exc.getVariableList();
  40380. attrs.add(new VariableEntry<IRubyObject>(
  40381. "mesg", exc.message == null ? runtime.getNil() : exc.message));
  40382. attrs.add(new VariableEntry<IRubyObject>("errno", exc.errno));
  40383. attrs.add(new VariableEntry<IRubyObject>("bt", exc.getBacktrace()));
  40384. marshalStream.dumpVariables(attrs);
  40385. }
  40386. public Object unmarshalFrom(Ruby runtime, RubyClass type,
  40387. UnmarshalStream unmarshalStream) throws IOException {
  40388. RubySystemCallError exc = (RubySystemCallError) type.allocate();
  40389. unmarshalStream.registerLinkTarget(exc);
  40390. unmarshalStream.defaultVariablesUnmarshal(exc);
  40391. exc.message = exc.removeInternalVariable("mesg");
  40392. exc.errno = exc.removeInternalVariable("errno");
  40393. exc.set_backtrace(exc.removeInternalVariable("bt"));
  40394. return exc;
  40395. }
  40396. };
  40397. public static RubyClass createSystemCallErrorClass(Ruby runtime, RubyClass standardError) {
  40398. RubyClass exceptionClass = runtime.defineClass("SystemCallError", standardError, SYSTEM_CALL_ERROR_ALLOCATOR);
  40399. exceptionClass.setMarshal(SYSTEM_CALL_ERROR_MARSHAL);
  40400. runtime.callbackFactory(RubyClass.class);
  40401. exceptionClass.defineAnnotatedMethods(RubySystemCallError.class);
  40402. return exceptionClass;
  40403. }
  40404. @JRubyMethod(optional = 2, required=0, frame = true, visibility = Visibility.PRIVATE)
  40405. public IRubyObject initialize(IRubyObject[] args, Block block) {
  40406. RubyClass sCallErorrClass = getRuntime().getSystemCallError();
  40407. RubyClass klass = getMetaClass().getRealClass();
  40408. IRubyObject msg = getRuntime().getNil();
  40409. IRubyObject err = getRuntime().getNil();
  40410. boolean isErrnoClass = !klass.equals(sCallErorrClass);
  40411. if (!isErrnoClass) {
  40412. // one optional, one required args
  40413. Arity.checkArgumentCount(getRuntime(), args, 1, 2);
  40414. msg = args[0];
  40415. if (args.length == 2) {
  40416. err = args[1];
  40417. }
  40418. if (args.length == 1 && (msg instanceof RubyFixnum)) {
  40419. err = msg;
  40420. msg = getRuntime().getNil();
  40421. }
  40422. } else {
  40423. // one optional and no required args
  40424. Arity.checkArgumentCount(getRuntime(), args, 0, 1);
  40425. if (args.length == 1) {
  40426. msg = args[0];
  40427. }
  40428. // try to get errno out of the class
  40429. err = klass.fastGetConstant("Errno");
  40430. }
  40431. if (!err.isNil()) {
  40432. errno = err.convertToInteger();
  40433. }
  40434. String val = defaultMessages.get(klass.getName());
  40435. if (val == null) {
  40436. val = "Unknown error";
  40437. }
  40438. // MRI behavior: we don't print errno for actual Errno errors
  40439. if (!errno.isNil() && !isErrnoClass) {
  40440. val += " " + errno.toString();
  40441. }
  40442. if (!msg.isNil()) {
  40443. val += " - " + msg.convertToString();
  40444. }
  40445. message = getRuntime().newString(val);
  40446. return this;
  40447. }
  40448. @JRubyMethod
  40449. public IRubyObject errno() {
  40450. return errno;
  40451. }
  40452. }
  40453. /***** BEGIN LICENSE BLOCK *****
  40454. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  40455. *
  40456. * The contents of this file are subject to the Common Public
  40457. * License Version 1.0 (the "License"); you may not use this file
  40458. * except in compliance with the License. You may obtain a copy of
  40459. * the License at http://www.eclipse.org/legal/cpl-v10.html
  40460. *
  40461. * Software distributed under the License is distributed on an "AS
  40462. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  40463. * implied. See the License for the specific language governing
  40464. * rights and limitations under the License.
  40465. *
  40466. * Alternatively, the contents of this file may be used under the terms of
  40467. * either of the GNU General Public License Version 2 or later (the "GPL"),
  40468. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  40469. * in which case the provisions of the GPL or the LGPL are applicable instead
  40470. * of those above. If you wish to allow use of your version of this file only
  40471. * under the terms of either the GPL or the LGPL, and not to allow others to
  40472. * use your version of this file under the terms of the CPL, indicate your
  40473. * decision by deleting the provisions above and replace them with the notice
  40474. * and other provisions required by the GPL or the LGPL. If you do not delete
  40475. * the provisions above, a recipient may use your version of this file under
  40476. * the terms of any one of the CPL, the GPL or the LGPL.
  40477. ***** END LICENSE BLOCK *****/
  40478. package org.jruby;
  40479. import org.jruby.anno.JRubyMethod;
  40480. import org.jruby.anno.JRubyClass;
  40481. import org.jruby.runtime.Block;
  40482. import org.jruby.runtime.ObjectAllocator;
  40483. import org.jruby.runtime.Visibility;
  40484. import org.jruby.runtime.builtin.IRubyObject;
  40485. @JRubyClass(name="SystemExit", parent="Exception")
  40486. public class RubySystemExit extends RubyException {
  40487. IRubyObject status;
  40488. private static ObjectAllocator SYSTEMEXIT_ALLOCATOR = new ObjectAllocator() {
  40489. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  40490. return new RubySystemExit(runtime, klass);
  40491. }
  40492. };
  40493. public static RubyClass createSystemExitClass(Ruby runtime, RubyClass exceptionClass) {
  40494. RubyClass systemExitClass = runtime.defineClass("SystemExit", exceptionClass, SYSTEMEXIT_ALLOCATOR);
  40495. systemExitClass.defineAnnotatedMethods(RubySystemExit.class);
  40496. return systemExitClass;
  40497. }
  40498. public static RubySystemExit newInstance(Ruby runtime, int status) {
  40499. RubyClass exc = runtime.getSystemExit();
  40500. IRubyObject[] exArgs = new IRubyObject[] {
  40501. runtime.newFixnum(status),
  40502. runtime.newString("exit") };
  40503. return (RubySystemExit) exc.newInstance(runtime.getCurrentContext(), exArgs, Block.NULL_BLOCK);
  40504. }
  40505. protected RubySystemExit(Ruby runtime, RubyClass exceptionClass) {
  40506. super(runtime, exceptionClass);
  40507. status = runtime.getNil();
  40508. }
  40509. @JRubyMethod(name = "initialize", optional = 2, frame = true, visibility = Visibility.PRIVATE)
  40510. public IRubyObject initialize(IRubyObject[]args, Block block) {
  40511. status = RubyFixnum.zero(getRuntime());
  40512. if (args.length > 0 && args[0] instanceof RubyFixnum) {
  40513. status = args[0];
  40514. IRubyObject[]tmpArgs = new IRubyObject[args.length - 1];
  40515. System.arraycopy(args, 1, tmpArgs, 0, tmpArgs.length);
  40516. args = tmpArgs;
  40517. }
  40518. super.initialize(args, block);
  40519. return this;
  40520. }
  40521. @JRubyMethod(name = "status")
  40522. public IRubyObject status() {
  40523. return status;
  40524. }
  40525. @JRubyMethod(name = "success?")
  40526. public IRubyObject success_p() {
  40527. if (status.isNil()) return getRuntime().getTrue();
  40528. if (status.equals(RubyFixnum.zero(getRuntime()))) return getRuntime().getTrue();
  40529. return getRuntime().getFalse();
  40530. }
  40531. }
  40532. /***** BEGIN LICENSE BLOCK *****
  40533. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  40534. *
  40535. * The contents of this file are subject to the Common Public
  40536. * License Version 1.0 (the "License"); you may not use this file
  40537. * except in compliance with the License. You may obtain a copy of
  40538. * the License at http://www.eclipse.org/legal/cpl-v10.html
  40539. *
  40540. * Software distributed under the License is distributed on an "AS
  40541. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  40542. * implied. See the License for the specific language governing
  40543. * rights and limitations under the License.
  40544. *
  40545. * Copyright (C) 2002 Jason Voegele <jason@jvoegele.com>
  40546. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  40547. * Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  40548. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  40549. * Copyright (C) 2004-2005 Charles O Nutter <headius@headius.com>
  40550. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  40551. *
  40552. * Alternatively, the contents of this file may be used under the terms of
  40553. * either of the GNU General Public License Version 2 or later (the "GPL"),
  40554. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  40555. * in which case the provisions of the GPL or the LGPL are applicable instead
  40556. * of those above. If you wish to allow use of your version of this file only
  40557. * under the terms of either the GPL or the LGPL, and not to allow others to
  40558. * use your version of this file under the terms of the CPL, indicate your
  40559. * decision by deleting the provisions above and replace them with the notice
  40560. * and other provisions required by the GPL or the LGPL. If you do not delete
  40561. * the provisions above, a recipient may use your version of this file under
  40562. * the terms of any one of the CPL, the GPL or the LGPL.
  40563. ***** END LICENSE BLOCK *****/
  40564. package org.jruby;
  40565. import java.io.IOException;
  40566. import java.nio.channels.Channel;
  40567. import java.nio.channels.SelectableChannel;
  40568. import java.nio.channels.SelectionKey;
  40569. import java.nio.channels.Selector;
  40570. import java.util.HashMap;
  40571. import java.util.Map;
  40572. import java.util.Set;
  40573. import org.jruby.common.IRubyWarnings.ID;
  40574. import org.jruby.exceptions.RaiseException;
  40575. import org.jruby.exceptions.ThreadKill;
  40576. import org.jruby.internal.runtime.FutureThread;
  40577. import org.jruby.internal.runtime.NativeThread;
  40578. import org.jruby.internal.runtime.RubyRunnable;
  40579. import org.jruby.internal.runtime.ThreadLike;
  40580. import org.jruby.internal.runtime.ThreadService;
  40581. import org.jruby.runtime.Block;
  40582. import org.jruby.runtime.ObjectAllocator;
  40583. import org.jruby.runtime.ThreadContext;
  40584. import org.jruby.runtime.builtin.IRubyObject;
  40585. import java.util.concurrent.ExecutionException;
  40586. import java.util.concurrent.locks.ReentrantLock;
  40587. import org.jruby.anno.JRubyMethod;
  40588. import org.jruby.anno.JRubyClass;
  40589. import org.jruby.runtime.ObjectMarshal;
  40590. import org.jruby.runtime.Visibility;
  40591. /**
  40592. * Implementation of Ruby's <code>Thread</code> class. Each Ruby thread is
  40593. * mapped to an underlying Java Virtual Machine thread.
  40594. * <p>
  40595. * Thread encapsulates the behavior of a thread of execution, including the main
  40596. * thread of the Ruby script. In the descriptions that follow, the parameter
  40597. * <code>aSymbol</code> refers to a symbol, which is either a quoted string or a
  40598. * <code>Symbol</code> (such as <code>:name</code>).
  40599. *
  40600. * Note: For CVS history, see ThreadClass.java.
  40601. */
  40602. @JRubyClass(name="Thread")
  40603. public class RubyThread extends RubyObject {
  40604. private ThreadLike threadImpl;
  40605. private RubyFixnum priority;
  40606. private transient Map<IRubyObject, IRubyObject> threadLocalVariables;
  40607. private boolean abortOnException;
  40608. private IRubyObject finalResult;
  40609. private RaiseException exitingException;
  40610. private IRubyObject receivedException;
  40611. private RubyThreadGroup threadGroup;
  40612. private final ThreadService threadService;
  40613. private volatile boolean isStopped = false;
  40614. private volatile boolean isDead = false;
  40615. public Object stopLock = new Object();
  40616. private volatile boolean killed = false;
  40617. public Object killLock = new Object();
  40618. public final ReentrantLock lock = new ReentrantLock();
  40619. private static final boolean DEBUG = false;
  40620. protected RubyThread(Ruby runtime, RubyClass type) {
  40621. super(runtime, type);
  40622. this.threadService = runtime.getThreadService();
  40623. finalResult = runtime.getNil();
  40624. }
  40625. /**
  40626. * Dispose of the current thread by removing it from its parent ThreadGroup.
  40627. */
  40628. public void dispose() {
  40629. threadGroup.remove(this);
  40630. }
  40631. public static RubyClass createThreadClass(Ruby runtime) {
  40632. // FIXME: In order for Thread to play well with the standard 'new' behavior,
  40633. // it must provide an allocator that can create empty object instances which
  40634. // initialize then fills with appropriate data.
  40635. RubyClass threadClass = runtime.defineClass("Thread", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  40636. runtime.setThread(threadClass);
  40637. threadClass.defineAnnotatedMethods(RubyThread.class);
  40638. RubyThread rubyThread = new RubyThread(runtime, threadClass);
  40639. // TODO: need to isolate the "current" thread from class creation
  40640. rubyThread.threadImpl = new NativeThread(rubyThread, Thread.currentThread());
  40641. runtime.getThreadService().setMainThread(Thread.currentThread(), rubyThread);
  40642. // set to default thread group
  40643. runtime.getDefaultThreadGroup().addDirectly(rubyThread);
  40644. threadClass.setMarshal(ObjectMarshal.NOT_MARSHALABLE_MARSHAL);
  40645. return threadClass;
  40646. }
  40647. /**
  40648. * <code>Thread.new</code>
  40649. * <p>
  40650. * Thread.new( <i>[ arg ]*</i> ) {| args | block } -> aThread
  40651. * <p>
  40652. * Creates a new thread to execute the instructions given in block, and
  40653. * begins running it. Any arguments passed to Thread.new are passed into the
  40654. * block.
  40655. * <pre>
  40656. * x = Thread.new { sleep .1; print "x"; print "y"; print "z" }
  40657. * a = Thread.new { print "a"; print "b"; sleep .2; print "c" }
  40658. * x.join # Let the threads finish before
  40659. * a.join # main thread exits...
  40660. * </pre>
  40661. * <i>produces:</i> abxyzc
  40662. */
  40663. @JRubyMethod(name = {"new", "fork"}, rest = true, frame = true, meta = true)
  40664. public static IRubyObject newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
  40665. return startThread(recv, args, true, block);
  40666. }
  40667. /**
  40668. * Basically the same as Thread.new . However, if class Thread is
  40669. * subclassed, then calling start in that subclass will not invoke the
  40670. * subclass's initialize method.
  40671. */
  40672. @JRubyMethod(name = "start", rest = true, frame = true, meta = true)
  40673. public static RubyThread start(IRubyObject recv, IRubyObject[] args, Block block) {
  40674. return startThread(recv, args, false, block);
  40675. }
  40676. public static RubyThread adopt(IRubyObject recv, Thread t) {
  40677. return adoptThread(recv, t, Block.NULL_BLOCK);
  40678. }
  40679. private static RubyThread adoptThread(final IRubyObject recv, Thread t, Block block) {
  40680. final Ruby runtime = recv.getRuntime();
  40681. final RubyThread rubyThread = new RubyThread(runtime, (RubyClass) recv);
  40682. rubyThread.threadImpl = new NativeThread(rubyThread, t);
  40683. ThreadContext context = runtime.getThreadService().registerNewThread(rubyThread);
  40684. context.preAdoptThread();
  40685. // set to default thread group
  40686. runtime.getDefaultThreadGroup().addDirectly(rubyThread);
  40687. return rubyThread;
  40688. }
  40689. @JRubyMethod(name = "initialize", rest = true, frame = true, visibility = Visibility.PRIVATE)
  40690. public IRubyObject initialize(IRubyObject[] args, Block block) {
  40691. Ruby runtime = getRuntime();
  40692. if (!block.isGiven()) throw runtime.newThreadError("must be called with a block");
  40693. RubyRunnable runnable = new RubyRunnable(this, args, block);
  40694. if (RubyInstanceConfig.POOLING_ENABLED) {
  40695. threadImpl = new FutureThread(this, runnable);
  40696. } else {
  40697. Thread thread = new Thread(runnable);
  40698. thread.setDaemon(true);
  40699. threadImpl = new NativeThread(this, thread);
  40700. }
  40701. // set to default thread group
  40702. runtime.getDefaultThreadGroup().addDirectly(this);
  40703. threadImpl.start();
  40704. // We yield here to hopefully permit the target thread to schedule
  40705. // MRI immediately schedules it, so this is close but not exact
  40706. Thread.yield();
  40707. return this;
  40708. }
  40709. private static RubyThread startThread(final IRubyObject recv, final IRubyObject[] args, boolean callInit, Block block) {
  40710. RubyThread rubyThread = new RubyThread(recv.getRuntime(), (RubyClass) recv);
  40711. if (callInit) {
  40712. rubyThread.callInit(args, block);
  40713. } else {
  40714. // for Thread::start, which does not call the subclass's initialize
  40715. rubyThread.initialize(args, block);
  40716. }
  40717. return rubyThread;
  40718. }
  40719. private void ensureNotCurrent() {
  40720. if (this == getRuntime().getCurrentContext().getThread()) {
  40721. throw new RuntimeException("internal thread method called from another thread");
  40722. }
  40723. }
  40724. public synchronized void cleanTerminate(IRubyObject result) {
  40725. finalResult = result;
  40726. isStopped = true;
  40727. isDead = true;
  40728. }
  40729. public void pollThreadEvents() {
  40730. pollThreadEvents(getRuntime().getCurrentContext());
  40731. }
  40732. public void pollThreadEvents(ThreadContext context) {
  40733. // check for criticalization *before* locking ourselves
  40734. threadService.waitForCritical();
  40735. if (killed) throwThreadKill();
  40736. if (receivedException != null) receivedAnException(context);
  40737. }
  40738. private void throwThreadKill() {
  40739. throw new ThreadKill();
  40740. }
  40741. /**
  40742. * Returns the status of the global ``abort on exception'' condition. The
  40743. * default is false. When set to true, will cause all threads to abort (the
  40744. * process will exit(0)) if an exception is raised in any thread. See also
  40745. * Thread.abort_on_exception= .
  40746. */
  40747. @JRubyMethod(name = "abort_on_exception", meta = true)
  40748. public static RubyBoolean abort_on_exception_x(IRubyObject recv) {
  40749. Ruby runtime = recv.getRuntime();
  40750. return runtime.isGlobalAbortOnExceptionEnabled() ? runtime.getTrue() : runtime.getFalse();
  40751. }
  40752. @JRubyMethod(name = "abort_on_exception=", required = 1, meta = true)
  40753. public static IRubyObject abort_on_exception_set_x(IRubyObject recv, IRubyObject value) {
  40754. recv.getRuntime().setGlobalAbortOnExceptionEnabled(value.isTrue());
  40755. return value;
  40756. }
  40757. @JRubyMethod(name = "current", meta = true)
  40758. public static RubyThread current(IRubyObject recv) {
  40759. return recv.getRuntime().getCurrentContext().getThread();
  40760. }
  40761. @JRubyMethod(name = "main", meta = true)
  40762. public static RubyThread main(IRubyObject recv) {
  40763. return recv.getRuntime().getThreadService().getMainThread();
  40764. }
  40765. @JRubyMethod(name = "pass", meta = true)
  40766. public static IRubyObject pass(IRubyObject recv) {
  40767. Ruby runtime = recv.getRuntime();
  40768. ThreadService ts = runtime.getThreadService();
  40769. boolean critical = ts.getCritical();
  40770. ts.setCritical(false);
  40771. Thread.yield();
  40772. ts.setCritical(critical);
  40773. return recv.getRuntime().getNil();
  40774. }
  40775. @JRubyMethod(name = "list", meta = true)
  40776. public static RubyArray list(IRubyObject recv) {
  40777. RubyThread[] activeThreads = recv.getRuntime().getThreadService().getActiveRubyThreads();
  40778. return recv.getRuntime().newArrayNoCopy(activeThreads);
  40779. }
  40780. private IRubyObject getSymbolKey(IRubyObject originalKey) {
  40781. if (originalKey instanceof RubySymbol) {
  40782. return originalKey;
  40783. } else if (originalKey instanceof RubyString) {
  40784. return getRuntime().newSymbol(originalKey.asJavaString());
  40785. } else if (originalKey instanceof RubyFixnum) {
  40786. getRuntime().getWarnings().warn(ID.FIXNUMS_NOT_SYMBOLS, "Do not use Fixnums as Symbols");
  40787. throw getRuntime().newArgumentError(originalKey + " is not a symbol");
  40788. } else {
  40789. throw getRuntime().newTypeError(originalKey + " is not a symbol");
  40790. }
  40791. }
  40792. private synchronized Map<IRubyObject, IRubyObject> getThreadLocals() {
  40793. if (threadLocalVariables == null) {
  40794. threadLocalVariables = new HashMap<IRubyObject, IRubyObject>();
  40795. }
  40796. return threadLocalVariables;
  40797. }
  40798. @JRubyMethod(name = "[]", required = 1)
  40799. public IRubyObject op_aref(IRubyObject key) {
  40800. IRubyObject value;
  40801. if ((value = getThreadLocals().get(getSymbolKey(key))) != null) {
  40802. return value;
  40803. }
  40804. return getRuntime().getNil();
  40805. }
  40806. @JRubyMethod(name = "[]=", required = 2)
  40807. public IRubyObject op_aset(IRubyObject key, IRubyObject value) {
  40808. key = getSymbolKey(key);
  40809. getThreadLocals().put(key, value);
  40810. return value;
  40811. }
  40812. @JRubyMethod(name = "abort_on_exception")
  40813. public RubyBoolean abort_on_exception() {
  40814. return abortOnException ? getRuntime().getTrue() : getRuntime().getFalse();
  40815. }
  40816. @JRubyMethod(name = "abort_on_exception=", required = 1)
  40817. public IRubyObject abort_on_exception_set(IRubyObject val) {
  40818. abortOnException = val.isTrue();
  40819. return val;
  40820. }
  40821. @JRubyMethod(name = "alive?")
  40822. public RubyBoolean alive_p() {
  40823. return !isDead && threadImpl.isAlive() ? getRuntime().getTrue() : getRuntime().getFalse();
  40824. }
  40825. @JRubyMethod(name = "join", optional = 1, backtrace = true)
  40826. public IRubyObject join(IRubyObject[] args) {
  40827. long timeoutMillis = Long.MAX_VALUE;
  40828. if (args.length > 0) {
  40829. if (args.length > 1) {
  40830. throw getRuntime().newArgumentError(args.length,1);
  40831. }
  40832. // MRI behavior: value given in seconds; converted to Float; less
  40833. // than or equal to zero returns immediately; returns nil
  40834. timeoutMillis = (long)(1000.0D * args[0].convertToFloat().getValue());
  40835. if (timeoutMillis <= 0) {
  40836. // TODO: not sure that we should skip calling join() altogether.
  40837. // Thread.join() has some implications for Java Memory Model, etc.
  40838. if (threadImpl.isAlive()) {
  40839. return getRuntime().getNil();
  40840. } else {
  40841. return this;
  40842. }
  40843. }
  40844. }
  40845. if (isCurrent()) {
  40846. throw getRuntime().newThreadError("thread tried to join itself");
  40847. }
  40848. try {
  40849. if (threadService.getCritical()) {
  40850. // If the target thread is sleeping or stopped, wake it
  40851. synchronized (stopLock) {
  40852. stopLock.notify();
  40853. }
  40854. // interrupt the target thread in case it's blocking or waiting
  40855. // WARNING: We no longer interrupt the target thread, since this usually means
  40856. // interrupting IO and with NIO that means the channel is no longer usable.
  40857. // We either need a new way to handle waking a target thread that's waiting
  40858. // on IO, or we need to accept that we can't wake such threads and must wait
  40859. // for them to complete their operation.
  40860. //threadImpl.interrupt();
  40861. }
  40862. RubyThread currentThread = getRuntime().getCurrentContext().getThread();
  40863. final long timeToWait = Math.min(timeoutMillis, 200);
  40864. // We need this loop in order to be able to "unblock" the
  40865. // join call without actually calling interrupt.
  40866. long start = System.currentTimeMillis();
  40867. while(true) {
  40868. currentThread.pollThreadEvents();
  40869. threadImpl.join(timeToWait);
  40870. if (!threadImpl.isAlive()) {
  40871. break;
  40872. }
  40873. if (System.currentTimeMillis() - start > timeoutMillis) {
  40874. break;
  40875. }
  40876. }
  40877. } catch (InterruptedException ie) {
  40878. ie.printStackTrace();
  40879. assert false : ie;
  40880. } catch (ExecutionException ie) {
  40881. ie.printStackTrace();
  40882. assert false : ie;
  40883. }
  40884. if (exitingException != null) {
  40885. throw exitingException;
  40886. }
  40887. if (threadImpl.isAlive()) {
  40888. return getRuntime().getNil();
  40889. } else {
  40890. return this;
  40891. }
  40892. }
  40893. @JRubyMethod(name = "value")
  40894. public IRubyObject value() {
  40895. join(new IRubyObject[0]);
  40896. synchronized (this) {
  40897. return finalResult;
  40898. }
  40899. }
  40900. @JRubyMethod(name = "group")
  40901. public IRubyObject group() {
  40902. if (threadGroup == null) {
  40903. return getRuntime().getNil();
  40904. }
  40905. return threadGroup;
  40906. }
  40907. void setThreadGroup(RubyThreadGroup rubyThreadGroup) {
  40908. threadGroup = rubyThreadGroup;
  40909. }
  40910. @JRubyMethod(name = "inspect")
  40911. @Override
  40912. public IRubyObject inspect() {
  40913. // FIXME: There's some code duplication here with RubyObject#inspect
  40914. StringBuilder part = new StringBuilder();
  40915. String cname = getMetaClass().getRealClass().getName();
  40916. part.append("#<").append(cname).append(":0x");
  40917. part.append(Integer.toHexString(System.identityHashCode(this)));
  40918. if (threadImpl.isAlive()) {
  40919. if (isStopped) {
  40920. part.append(getRuntime().newString(" sleep"));
  40921. } else if (killed) {
  40922. part.append(getRuntime().newString(" aborting"));
  40923. } else {
  40924. part.append(getRuntime().newString(" run"));
  40925. }
  40926. } else {
  40927. part.append(" dead");
  40928. }
  40929. part.append(">");
  40930. return getRuntime().newString(part.toString());
  40931. }
  40932. @JRubyMethod(name = "key?", required = 1)
  40933. public RubyBoolean key_p(IRubyObject key) {
  40934. key = getSymbolKey(key);
  40935. return getRuntime().newBoolean(getThreadLocals().containsKey(key));
  40936. }
  40937. @JRubyMethod(name = "keys")
  40938. public RubyArray keys() {
  40939. IRubyObject[] keys = new IRubyObject[getThreadLocals().size()];
  40940. return RubyArray.newArrayNoCopy(getRuntime(), getThreadLocals().keySet().toArray(keys));
  40941. }
  40942. @JRubyMethod(name = "critical=", required = 1, meta = true)
  40943. public static IRubyObject critical_set(IRubyObject receiver, IRubyObject value) {
  40944. receiver.getRuntime().getThreadService().setCritical(value.isTrue());
  40945. return value;
  40946. }
  40947. @JRubyMethod(name = "critical", meta = true)
  40948. public static IRubyObject critical(IRubyObject receiver) {
  40949. return receiver.getRuntime().newBoolean(receiver.getRuntime().getThreadService().getCritical());
  40950. }
  40951. @JRubyMethod(name = "stop", meta = true)
  40952. public static IRubyObject stop(IRubyObject receiver) {
  40953. RubyThread rubyThread = receiver.getRuntime().getThreadService().getCurrentContext().getThread();
  40954. Object stopLock = rubyThread.stopLock;
  40955. synchronized (stopLock) {
  40956. rubyThread.pollThreadEvents();
  40957. try {
  40958. rubyThread.isStopped = true;
  40959. // attempt to decriticalize all if we're the critical thread
  40960. receiver.getRuntime().getThreadService().setCritical(false);
  40961. stopLock.wait();
  40962. } catch (InterruptedException ie) {
  40963. rubyThread.pollThreadEvents();
  40964. }
  40965. rubyThread.isStopped = false;
  40966. }
  40967. return receiver.getRuntime().getNil();
  40968. }
  40969. @JRubyMethod(name = "kill", required = 1, frame = true, meta = true)
  40970. public static IRubyObject kill(IRubyObject receiver, IRubyObject rubyThread, Block block) {
  40971. if (!(rubyThread instanceof RubyThread)) throw receiver.getRuntime().newTypeError(rubyThread, receiver.getRuntime().getThread());
  40972. return ((RubyThread)rubyThread).kill();
  40973. }
  40974. @JRubyMethod(name = "exit", frame = true, meta = true)
  40975. public static IRubyObject s_exit(IRubyObject receiver, Block block) {
  40976. RubyThread rubyThread = receiver.getRuntime().getThreadService().getCurrentContext().getThread();
  40977. rubyThread.killed = true;
  40978. // attempt to decriticalize all if we're the critical thread
  40979. receiver.getRuntime().getThreadService().setCritical(false);
  40980. throw new ThreadKill();
  40981. }
  40982. @JRubyMethod(name = "stop?")
  40983. public RubyBoolean stop_p() {
  40984. // not valid for "dead" state
  40985. return getRuntime().newBoolean(isStopped);
  40986. }
  40987. @JRubyMethod(name = "wakeup")
  40988. public RubyThread wakeup() {
  40989. synchronized (stopLock) {
  40990. stopLock.notifyAll();
  40991. }
  40992. return this;
  40993. }
  40994. @JRubyMethod(name = "priority")
  40995. public RubyFixnum priority() {
  40996. return priority;
  40997. }
  40998. @JRubyMethod(name = "priority=", required = 1)
  40999. public IRubyObject priority_set(IRubyObject priority) {
  41000. // FIXME: This should probably do some translation from Ruby priority levels to Java priority levels (until we have green threads)
  41001. int iPriority = RubyNumeric.fix2int(priority);
  41002. if (iPriority < Thread.MIN_PRIORITY) {
  41003. iPriority = Thread.MIN_PRIORITY;
  41004. } else if (iPriority > Thread.MAX_PRIORITY) {
  41005. iPriority = Thread.MAX_PRIORITY;
  41006. }
  41007. this.priority = RubyFixnum.newFixnum(getRuntime(), iPriority);
  41008. if (threadImpl.isAlive()) {
  41009. threadImpl.setPriority(iPriority);
  41010. }
  41011. return this.priority;
  41012. }
  41013. @JRubyMethod(name = "raise", optional = 2, frame = true)
  41014. public IRubyObject raise(IRubyObject[] args, Block block) {
  41015. ensureNotCurrent();
  41016. Ruby runtime = getRuntime();
  41017. if (DEBUG) System.out.println("thread " + Thread.currentThread() + " before raising");
  41018. RubyThread currentThread = getRuntime().getCurrentContext().getThread();
  41019. try {
  41020. while (!(currentThread.lock.tryLock() && this.lock.tryLock())) {
  41021. if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
  41022. }
  41023. currentThread.pollThreadEvents();
  41024. if (DEBUG) System.out.println("thread " + Thread.currentThread() + " raising");
  41025. receivedException = prepareRaiseException(runtime, args, block);
  41026. // If the target thread is sleeping or stopped, wake it
  41027. synchronized (stopLock) {
  41028. stopLock.notify();
  41029. }
  41030. // interrupt the target thread in case it's blocking or waiting
  41031. // WARNING: We no longer interrupt the target thread, since this usually means
  41032. // interrupting IO and with NIO that means the channel is no longer usable.
  41033. // We either need a new way to handle waking a target thread that's waiting
  41034. // on IO, or we need to accept that we can't wake such threads and must wait
  41035. // for them to complete their operation.
  41036. //threadImpl.interrupt();
  41037. // new interrupt, to hopefully wake it out of any blocking IO
  41038. this.interrupt();
  41039. } finally {
  41040. if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
  41041. if (this.lock.isHeldByCurrentThread()) this.lock.unlock();
  41042. }
  41043. return this;
  41044. }
  41045. private IRubyObject prepareRaiseException(Ruby runtime, IRubyObject[] args, Block block) {
  41046. if(args.length == 0) {
  41047. IRubyObject lastException = runtime.getGlobalVariables().get("$!");
  41048. if(lastException.isNil()) {
  41049. return new RaiseException(runtime, runtime.getRuntimeError(), "", false).getException();
  41050. }
  41051. return lastException;
  41052. }
  41053. IRubyObject exception;
  41054. ThreadContext context = getRuntime().getCurrentContext();
  41055. if(args.length == 1) {
  41056. if(args[0] instanceof RubyString) {
  41057. return runtime.getRuntimeError().newInstance(context, args, block);
  41058. }
  41059. if(!args[0].respondsTo("exception")) {
  41060. return runtime.newTypeError("exception class/object expected").getException();
  41061. }
  41062. exception = args[0].callMethod(context, "exception");
  41063. } else {
  41064. if (!args[0].respondsTo("exception")) {
  41065. return runtime.newTypeError("exception class/object expected").getException();
  41066. }
  41067. exception = args[0].callMethod(context, "exception", args[1]);
  41068. }
  41069. if (!runtime.getException().isInstance(exception)) {
  41070. return runtime.newTypeError("exception object expected").getException();
  41071. }
  41072. if (args.length == 3) {
  41073. ((RubyException) exception).set_backtrace(args[2]);
  41074. }
  41075. return exception;
  41076. }
  41077. @JRubyMethod(name = "run")
  41078. public IRubyObject run() {
  41079. // if stopped, unstop
  41080. synchronized (stopLock) {
  41081. if (isStopped) {
  41082. isStopped = false;
  41083. stopLock.notifyAll();
  41084. }
  41085. }
  41086. return this;
  41087. }
  41088. public void sleep(long millis) throws InterruptedException {
  41089. assert this == getRuntime().getCurrentContext().getThread();
  41090. synchronized (stopLock) {
  41091. pollThreadEvents();
  41092. try {
  41093. isStopped = true;
  41094. stopLock.wait(millis);
  41095. } finally {
  41096. isStopped = false;
  41097. pollThreadEvents();
  41098. }
  41099. }
  41100. }
  41101. @JRubyMethod(name = "status")
  41102. public IRubyObject status() {
  41103. if (threadImpl.isAlive()) {
  41104. if (isStopped || currentSelector != null && currentSelector.isOpen()) {
  41105. return getRuntime().newString("sleep");
  41106. } else if (killed) {
  41107. return getRuntime().newString("aborting");
  41108. }
  41109. return getRuntime().newString("run");
  41110. } else if (exitingException != null) {
  41111. return getRuntime().getNil();
  41112. } else {
  41113. return getRuntime().getFalse();
  41114. }
  41115. }
  41116. @JRubyMethod(name = {"kill", "exit", "terminate"})
  41117. public IRubyObject kill() {
  41118. // need to reexamine this
  41119. RubyThread currentThread = getRuntime().getCurrentContext().getThread();
  41120. // If the killee thread is the same as the killer thread, just die
  41121. if (currentThread == this) throwThreadKill();
  41122. try {
  41123. if (DEBUG) System.out.println("thread " + Thread.currentThread() + " trying to kill");
  41124. while (!(currentThread.lock.tryLock() && this.lock.tryLock())) {
  41125. if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
  41126. }
  41127. currentThread.pollThreadEvents();
  41128. if (DEBUG) System.out.println("thread " + Thread.currentThread() + " succeeded with kill");
  41129. killed = true;
  41130. // If the target thread is sleeping or stopped, wake it
  41131. synchronized (stopLock) {
  41132. stopLock.notify();
  41133. }
  41134. // interrupt the target thread in case it's blocking or waiting
  41135. // WARNING: We no longer interrupt the target thread, since this usually means
  41136. // interrupting IO and with NIO that means the channel is no longer usable.
  41137. // We either need a new way to handle waking a target thread that's waiting
  41138. // on IO, or we need to accept that we can't wake such threads and must wait
  41139. // for them to complete their operation.
  41140. //threadImpl.interrupt();
  41141. // new interrupt, to hopefully wake it out of any blocking IO
  41142. this.interrupt();
  41143. } finally {
  41144. if (currentThread.lock.isHeldByCurrentThread()) currentThread.lock.unlock();
  41145. if (this.lock.isHeldByCurrentThread()) this.lock.unlock();
  41146. }
  41147. try {
  41148. threadImpl.join();
  41149. } catch (InterruptedException ie) {
  41150. // we were interrupted, check thread events again
  41151. currentThread.pollThreadEvents();
  41152. } catch (ExecutionException ie) {
  41153. // we were interrupted, check thread events again
  41154. currentThread.pollThreadEvents();
  41155. }
  41156. return this;
  41157. }
  41158. @JRubyMethod(name = {"kill!", "exit!", "terminate!"})
  41159. public IRubyObject kill_bang() {
  41160. throw getRuntime().newNotImplementedError("Thread#kill!, exit!, and terminate! are not safe and not supported");
  41161. }
  41162. @JRubyMethod(name = "safe_level")
  41163. public IRubyObject safe_level() {
  41164. throw getRuntime().newNotImplementedError("Thread-specific SAFE levels are not supported");
  41165. }
  41166. private boolean isCurrent() {
  41167. return threadImpl.isCurrent();
  41168. }
  41169. public void exceptionRaised(RaiseException exception) {
  41170. assert isCurrent();
  41171. RubyException rubyException = exception.getException();
  41172. Ruby runtime = rubyException.getRuntime();
  41173. if (runtime.getSystemExit().isInstance(rubyException)) {
  41174. threadService.getMainThread().raise(new IRubyObject[] {rubyException}, Block.NULL_BLOCK);
  41175. } else if (abortOnException(runtime)) {
  41176. runtime.printError(rubyException);
  41177. RubyException systemExit = RubySystemExit.newInstance(runtime, 1);
  41178. systemExit.message = rubyException.message;
  41179. systemExit.set_backtrace(rubyException.backtrace());
  41180. threadService.getMainThread().raise(new IRubyObject[] {systemExit}, Block.NULL_BLOCK);
  41181. return;
  41182. } else if (runtime.getDebug().isTrue()) {
  41183. runtime.printError(exception.getException());
  41184. }
  41185. exitingException = exception;
  41186. }
  41187. private boolean abortOnException(Ruby runtime) {
  41188. return (runtime.isGlobalAbortOnExceptionEnabled() || abortOnException);
  41189. }
  41190. public static RubyThread mainThread(IRubyObject receiver) {
  41191. return receiver.getRuntime().getThreadService().getMainThread();
  41192. }
  41193. private Selector currentSelector;
  41194. @Deprecated
  41195. public boolean selectForAccept(RubyIO io) {
  41196. return select(io, SelectionKey.OP_ACCEPT);
  41197. }
  41198. public boolean select(RubyIO io, int ops) {
  41199. Channel channel = io.getChannel();
  41200. if (channel instanceof SelectableChannel) {
  41201. SelectableChannel selectable = (SelectableChannel)channel;
  41202. synchronized (selectable.blockingLock()) {
  41203. boolean oldBlocking = selectable.isBlocking();
  41204. try {
  41205. selectable.configureBlocking(false);
  41206. io.addBlockingThread(this);
  41207. currentSelector = selectable.provider().openSelector();
  41208. SelectionKey key = selectable.register(currentSelector, ops);
  41209. int result = currentSelector.select();
  41210. // check for thread events, in case we've been woken up to die
  41211. pollThreadEvents();
  41212. if (result == 1) {
  41213. Set<SelectionKey> keySet = currentSelector.selectedKeys();
  41214. if (keySet.iterator().next() == key) {
  41215. return true;
  41216. }
  41217. }
  41218. return false;
  41219. } catch (IOException ioe) {
  41220. throw io.getRuntime().newRuntimeError("Error with selector: " + ioe);
  41221. } finally {
  41222. if (currentSelector != null) {
  41223. try {
  41224. currentSelector.close();
  41225. } catch (IOException ioe) {
  41226. throw io.getRuntime().newRuntimeError("Could not close selector");
  41227. }
  41228. }
  41229. currentSelector = null;
  41230. io.removeBlockingThread(this);
  41231. try {
  41232. selectable.configureBlocking(oldBlocking);
  41233. } catch (IOException ioe) {
  41234. // ignore; I don't like doing it, but it seems like we
  41235. // really just need to make all channels non-blocking by
  41236. // default and use select when implementing blocking ops,
  41237. // so if this remains set non-blocking, perhaps it's not
  41238. // such a big deal...
  41239. }
  41240. }
  41241. }
  41242. } else {
  41243. // can't select, just have to do a blocking call
  41244. return true;
  41245. }
  41246. }
  41247. public void interrupt() {
  41248. if (currentSelector != null) {
  41249. currentSelector.wakeup();
  41250. }
  41251. }
  41252. public void beforeBlockingCall() {
  41253. isStopped = true;
  41254. }
  41255. public void afterBlockingCall() {
  41256. isStopped = false;
  41257. }
  41258. private void receivedAnException(ThreadContext context) {
  41259. // clear this so we don't keep re-throwing
  41260. IRubyObject raiseException = receivedException;
  41261. receivedException = null;
  41262. RubyModule kernelModule = getRuntime().getKernel();
  41263. if (DEBUG) {
  41264. System.out.println("thread " + Thread.currentThread() + " before propagating exception: " + killed);
  41265. }
  41266. kernelModule.callMethod(context, "raise", raiseException);
  41267. }
  41268. }
  41269. /***** BEGIN LICENSE BLOCK *****
  41270. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  41271. *
  41272. * The contents of this file are subject to the Common Public
  41273. * License Version 1.0 (the "License"); you may not use this file
  41274. * except in compliance with the License. You may obtain a copy of
  41275. * the License at http://www.eclipse.org/legal/cpl-v10.html
  41276. *
  41277. * Software distributed under the License is distributed on an "AS
  41278. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  41279. * implied. See the License for the specific language governing
  41280. * rights and limitations under the License.
  41281. *
  41282. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  41283. * Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  41284. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  41285. *
  41286. * Alternatively, the contents of this file may be used under the terms of
  41287. * either of the GNU General Public License Version 2 or later (the "GPL"),
  41288. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  41289. * in which case the provisions of the GPL or the LGPL are applicable instead
  41290. * of those above. If you wish to allow use of your version of this file only
  41291. * under the terms of either the GPL or the LGPL, and not to allow others to
  41292. * use your version of this file under the terms of the CPL, indicate your
  41293. * decision by deleting the provisions above and replace them with the notice
  41294. * and other provisions required by the GPL or the LGPL. If you do not delete
  41295. * the provisions above, a recipient may use your version of this file under
  41296. * the terms of any one of the CPL, the GPL or the LGPL.
  41297. ***** END LICENSE BLOCK *****/
  41298. package org.jruby;
  41299. import java.util.HashMap;
  41300. import java.util.Map;
  41301. import org.jruby.anno.JRubyMethod;
  41302. import org.jruby.anno.JRubyClass;
  41303. import org.jruby.runtime.Block;
  41304. import org.jruby.runtime.ObjectAllocator;
  41305. import org.jruby.runtime.builtin.IRubyObject;
  41306. /**
  41307. * Implementation of Ruby's <code>ThreadGroup</code> class. This is currently
  41308. * just a stub.
  41309. * <p>
  41310. *
  41311. * @author Charles O Nutter (headius@headius.com)
  41312. */
  41313. @JRubyClass(name="ThreadGroup")
  41314. public class RubyThreadGroup extends RubyObject {
  41315. private Map<Integer, IRubyObject> rubyThreadList = new HashMap<Integer, IRubyObject>();
  41316. private boolean enclosed = false;
  41317. // ENEBO: Can these be fast?
  41318. public static RubyClass createThreadGroupClass(Ruby runtime) {
  41319. RubyClass threadGroupClass = runtime.defineClass("ThreadGroup", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  41320. runtime.setThreadGroup(threadGroupClass);
  41321. threadGroupClass.defineAnnotatedMethods(RubyThreadGroup.class);
  41322. // create the default thread group
  41323. RubyThreadGroup defaultThreadGroup = new RubyThreadGroup(runtime, threadGroupClass);
  41324. runtime.setDefaultThreadGroup(defaultThreadGroup);
  41325. threadGroupClass.defineConstant("Default", defaultThreadGroup);
  41326. return threadGroupClass;
  41327. }
  41328. @JRubyMethod(name = "new", frame = true, meta = true)
  41329. public static IRubyObject newInstance(IRubyObject recv, Block block) {
  41330. return new RubyThreadGroup(recv.getRuntime(), (RubyClass)recv);
  41331. }
  41332. @JRubyMethod(name = "add", required = 1, frame = true)
  41333. public synchronized IRubyObject add(IRubyObject rubyThread, Block block) {
  41334. if (!(rubyThread instanceof RubyThread)) throw getRuntime().newTypeError(rubyThread, getRuntime().getThread());
  41335. // synchronize on the RubyThread for threadgroup updates
  41336. if (isFrozen()) {
  41337. throw getRuntime().newTypeError("can't add to frozen ThreadGroup");
  41338. }
  41339. RubyThread thread = (RubyThread)rubyThread;
  41340. // we only add live threads
  41341. if (thread.alive_p().isTrue()) {
  41342. addDirectly(thread);
  41343. }
  41344. return this;
  41345. }
  41346. void addDirectly(RubyThread rubyThread) {
  41347. synchronized (rubyThread) {
  41348. IRubyObject oldGroup = rubyThread.group();
  41349. if (oldGroup != getRuntime().getNil()) {
  41350. RubyThreadGroup threadGroup = (RubyThreadGroup) oldGroup;
  41351. threadGroup.rubyThreadList.remove(System.identityHashCode(rubyThread));
  41352. }
  41353. rubyThread.setThreadGroup(this);
  41354. rubyThreadList.put(System.identityHashCode(rubyThread), rubyThread);
  41355. }
  41356. }
  41357. public synchronized void remove(RubyThread rubyThread) {
  41358. rubyThread.setThreadGroup(null);
  41359. rubyThreadList.remove(System.identityHashCode(rubyThread));
  41360. }
  41361. @JRubyMethod(name = "enclose", frame = true)
  41362. public IRubyObject enclose(Block block) {
  41363. enclosed = true;
  41364. return this;
  41365. }
  41366. @JRubyMethod(name = "enclosed?", frame = true)
  41367. public IRubyObject enclosed_p(Block block) {
  41368. return new RubyBoolean(getRuntime(), enclosed);
  41369. }
  41370. @JRubyMethod(name = "list", frame = true)
  41371. public synchronized IRubyObject list(Block block) {
  41372. return getRuntime().newArrayNoCopy((IRubyObject[]) rubyThreadList.values().toArray(new IRubyObject[rubyThreadList.size()]));
  41373. }
  41374. private RubyThreadGroup(Ruby runtime, RubyClass type) {
  41375. super(runtime, type);
  41376. }
  41377. }
  41378. /***** BEGIN LICENSE BLOCK *****
  41379. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  41380. *
  41381. * The contents of this file are subject to the Common Public
  41382. * License Version 1.0 (the "License"); you may not use this file
  41383. * except in compliance with the License. You may obtain a copy of
  41384. * the License at http://www.eclipse.org/legal/cpl-v10.html
  41385. *
  41386. * Software distributed under the License is distributed on an "AS
  41387. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  41388. * implied. See the License for the specific language governing
  41389. * rights and limitations under the License.
  41390. *
  41391. * Copyright (C) 2001 Chad Fowler <chadfowler@chadfowler.com>
  41392. * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
  41393. * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
  41394. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  41395. * Copyright (C) 2004 Joey Gibson <joey@joeygibson.com>
  41396. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  41397. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  41398. * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
  41399. * Copyright (C) 2006 Ola Bini <ola.bini@ki.se>
  41400. * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
  41401. *
  41402. * Alternatively, the contents of this file may be used under the terms of
  41403. * either of the GNU General Public License Version 2 or later (the "GPL"),
  41404. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  41405. * in which case the provisions of the GPL or the LGPL are applicable instead
  41406. * of those above. If you wish to allow use of your version of this file only
  41407. * under the terms of either the GPL or the LGPL, and not to allow others to
  41408. * use your version of this file under the terms of the CPL, indicate your
  41409. * decision by deleting the provisions above and replace them with the notice
  41410. * and other provisions required by the GPL or the LGPL. If you do not delete
  41411. * the provisions above, a recipient may use your version of this file under
  41412. * the terms of any one of the CPL, the GPL or the LGPL.
  41413. ***** END LICENSE BLOCK *****/
  41414. package org.jruby;
  41415. import java.lang.ref.SoftReference;
  41416. import java.util.Date;
  41417. import java.util.HashMap;
  41418. import java.util.Locale;
  41419. import java.util.Map;
  41420. import java.util.TimeZone;
  41421. import java.util.regex.Matcher;
  41422. import java.util.regex.Pattern;
  41423. import org.joda.time.DateTime;
  41424. import org.joda.time.DateTimeZone;
  41425. import org.joda.time.format.DateTimeFormat;
  41426. import org.joda.time.format.DateTimeFormatter;
  41427. import org.jruby.anno.JRubyClass;
  41428. import org.jruby.anno.JRubyMethod;
  41429. import org.jruby.runtime.Block;
  41430. import org.jruby.runtime.ClassIndex;
  41431. import org.jruby.runtime.MethodIndex;
  41432. import org.jruby.runtime.ObjectAllocator;
  41433. import org.jruby.runtime.ThreadContext;
  41434. import org.jruby.runtime.Visibility;
  41435. import org.jruby.runtime.builtin.IRubyObject;
  41436. import org.jruby.util.ByteList;
  41437. import org.jruby.util.RubyDateFormat;
  41438. /** The Time class.
  41439. *
  41440. * @author chadfowler, jpetersen
  41441. */
  41442. @JRubyClass(name="Time", include="Comparable")
  41443. public class RubyTime extends RubyObject {
  41444. public static final String UTC = "UTC";
  41445. private DateTime dt;
  41446. private long usec;
  41447. private final static DateTimeFormatter ONE_DAY_CTIME_FORMATTER = DateTimeFormat.forPattern("EEE MMM d HH:mm:ss yyyy").withLocale(Locale.ENGLISH);
  41448. private final static DateTimeFormatter TWO_DAY_CTIME_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss yyyy").withLocale(Locale.ENGLISH);
  41449. private final static DateTimeFormatter TO_S_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss Z yyyy").withLocale(Locale.ENGLISH);
  41450. private final static DateTimeFormatter TO_S_UTC_FORMATTER = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss 'UTC' yyyy").withLocale(Locale.ENGLISH);
  41451. // There are two different popular TZ formats: legacy (AST+3:00:00, GMT-3), and
  41452. // newer one (US/Pacific, America/Los_Angeles). This pattern is to detect
  41453. // the legacy TZ format in order to convert it to the newer format
  41454. // understood by Java API.
  41455. private static final Pattern TZ_PATTERN
  41456. = Pattern.compile("(\\D+?)([\\+-]?)(\\d+)(:\\d+)?(:\\d+)?");
  41457. private static final ByteList TZ_STRING = ByteList.create("TZ");
  41458. public static DateTimeZone getLocalTimeZone(Ruby runtime) {
  41459. RubyString tzVar = runtime.newString(TZ_STRING);
  41460. RubyHash h = ((RubyHash)runtime.getObject().fastGetConstant("ENV"));
  41461. IRubyObject tz = h.op_aref(runtime.getCurrentContext(), tzVar);
  41462. if (tz == null || ! (tz instanceof RubyString)) {
  41463. return DateTimeZone.getDefault();
  41464. } else {
  41465. String zone = tz.toString();
  41466. DateTimeZone cachedZone = runtime.getLocalTimezoneCache().get(zone);
  41467. if (cachedZone != null) return cachedZone;
  41468. String originalZone = zone;
  41469. // Value of "TZ" property is of a bit different format,
  41470. // which confuses the Java's TimeZone.getTimeZone(id) method,
  41471. // and so, we need to convert it.
  41472. Matcher tzMatcher = TZ_PATTERN.matcher(zone);
  41473. if (tzMatcher.matches()) {
  41474. String sign = tzMatcher.group(2);
  41475. String hours = tzMatcher.group(3);
  41476. String minutes = tzMatcher.group(4);
  41477. // GMT+00:00 --> Etc/GMT, see "MRI behavior"
  41478. // comment below.
  41479. if (("00".equals(hours) || "0".equals(hours))
  41480. && (minutes == null || ":00".equals(minutes) || ":0".equals(minutes))) {
  41481. zone = "Etc/GMT";
  41482. } else {
  41483. // Invert the sign, since TZ format and Java format
  41484. // use opposite signs, sigh... Also, Java API requires
  41485. // the sign to be always present, be it "+" or "-".
  41486. sign = ("-".equals(sign)? "+" : "-");
  41487. // Always use "GMT" since that's required by Java API.
  41488. zone = "GMT" + sign + hours;
  41489. if (minutes != null) {
  41490. zone += minutes;
  41491. }
  41492. }
  41493. }
  41494. // MRI behavior: With TZ equal to "GMT" or "UTC", Time.now
  41495. // is *NOT* considered as a proper GMT/UTC time:
  41496. // ENV['TZ']="GMT"
  41497. // Time.now.gmt? ==> false
  41498. // ENV['TZ']="UTC"
  41499. // Time.now.utc? ==> false
  41500. // Hence, we need to adjust for that.
  41501. if ("GMT".equalsIgnoreCase(zone) || "UTC".equalsIgnoreCase(zone)) {
  41502. zone = "Etc/" + zone;
  41503. }
  41504. DateTimeZone dtz = DateTimeZone.forTimeZone(TimeZone.getTimeZone(zone));
  41505. runtime.getLocalTimezoneCache().put(originalZone, dtz);
  41506. return dtz;
  41507. }
  41508. }
  41509. public RubyTime(Ruby runtime, RubyClass rubyClass) {
  41510. super(runtime, rubyClass);
  41511. }
  41512. public RubyTime(Ruby runtime, RubyClass rubyClass, DateTime dt) {
  41513. super(runtime, rubyClass);
  41514. this.dt = dt;
  41515. }
  41516. // We assume that these two time instances
  41517. // occurred at the same time.
  41518. private static final long BASE_TIME_MILLIS = System.currentTimeMillis();
  41519. private static final long BASE_TIME_NANOS = System.nanoTime();
  41520. private static ObjectAllocator TIME_ALLOCATOR = new ObjectAllocator() {
  41521. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  41522. long usecsPassed = (System.nanoTime() - BASE_TIME_NANOS) / 1000L;
  41523. long millisTime = BASE_TIME_MILLIS + usecsPassed / 1000L;
  41524. long usecs = usecsPassed % 1000L;
  41525. DateTimeZone dtz = getLocalTimeZone(runtime);
  41526. DateTime dt = new DateTime(millisTime, dtz);
  41527. RubyTime rt = new RubyTime(runtime, klass, dt);
  41528. rt.setUSec(usecs);
  41529. return rt;
  41530. }
  41531. };
  41532. public static RubyClass createTimeClass(Ruby runtime) {
  41533. RubyClass timeClass = runtime.defineClass("Time", runtime.getObject(), TIME_ALLOCATOR);
  41534. timeClass.index = ClassIndex.TIME;
  41535. runtime.setTime(timeClass);
  41536. timeClass.includeModule(runtime.getComparable());
  41537. timeClass.defineAnnotatedMethods(RubyTime.class);
  41538. return timeClass;
  41539. }
  41540. public void setUSec(long usec) {
  41541. this.usec = usec;
  41542. }
  41543. public long getUSec() {
  41544. return usec;
  41545. }
  41546. public void updateCal(DateTime dt) {
  41547. this.dt = dt;
  41548. }
  41549. protected long getTimeInMillis() {
  41550. return dt.getMillis(); // For JDK 1.4 we can use "cal.getTimeInMillis()"
  41551. }
  41552. public static RubyTime newTime(Ruby runtime, long milliseconds) {
  41553. return newTime(runtime, new DateTime(milliseconds));
  41554. }
  41555. public static RubyTime newTime(Ruby runtime, DateTime dt) {
  41556. return new RubyTime(runtime, runtime.getTime(), dt);
  41557. }
  41558. public static RubyTime newTime(Ruby runtime, DateTime dt, long usec) {
  41559. RubyTime t = new RubyTime(runtime, runtime.getTime(), dt);
  41560. t.setUSec(usec);
  41561. return t;
  41562. }
  41563. @Override
  41564. public Class<?> getJavaClass() {
  41565. return Date.class;
  41566. }
  41567. @JRubyMethod(name = "initialize_copy", required = 1)
  41568. @Override
  41569. public IRubyObject initialize_copy(IRubyObject original) {
  41570. if (!(original instanceof RubyTime)) {
  41571. throw getRuntime().newTypeError("Expecting an instance of class Time");
  41572. }
  41573. RubyTime originalTime = (RubyTime) original;
  41574. // We can just use dt, since it is immutable
  41575. dt = originalTime.dt;
  41576. usec = originalTime.usec;
  41577. return this;
  41578. }
  41579. @JRubyMethod(name = "succ")
  41580. public RubyTime succ() {
  41581. return newTime(getRuntime(),dt.plusSeconds(1));
  41582. }
  41583. @JRubyMethod(name = {"gmtime", "utc"})
  41584. public RubyTime gmtime() {
  41585. dt = dt.withZone(DateTimeZone.UTC);
  41586. return this;
  41587. }
  41588. @JRubyMethod(name = "localtime")
  41589. public RubyTime localtime() {
  41590. dt = dt.withZone(getLocalTimeZone(getRuntime()));
  41591. return this;
  41592. }
  41593. @JRubyMethod(name = {"gmt?", "utc?", "gmtime?"})
  41594. public RubyBoolean gmt() {
  41595. return getRuntime().newBoolean(dt.getZone().getID().equals("UTC"));
  41596. }
  41597. @JRubyMethod(name = {"getgm", "getutc"})
  41598. public RubyTime getgm() {
  41599. return newTime(getRuntime(), dt.withZone(DateTimeZone.UTC), getUSec());
  41600. }
  41601. @JRubyMethod(name = "getlocal")
  41602. public RubyTime getlocal() {
  41603. return newTime(getRuntime(), dt.withZone(getLocalTimeZone(getRuntime())), getUSec());
  41604. }
  41605. @JRubyMethod(name = "strftime", required = 1)
  41606. public RubyString strftime(IRubyObject format) {
  41607. final RubyDateFormat rubyDateFormat = new RubyDateFormat("-", Locale.US);
  41608. rubyDateFormat.applyPattern(format.toString());
  41609. rubyDateFormat.setDateTime(dt);
  41610. String result = rubyDateFormat.format(null);
  41611. return getRuntime().newString(result);
  41612. }
  41613. @JRubyMethod(name = ">=", required = 1)
  41614. public IRubyObject op_ge(ThreadContext context, IRubyObject other) {
  41615. if (other instanceof RubyTime) {
  41616. return getRuntime().newBoolean(cmp((RubyTime) other) >= 0);
  41617. }
  41618. return RubyComparable.op_ge(context, this, other);
  41619. }
  41620. @JRubyMethod(name = ">", required = 1)
  41621. public IRubyObject op_gt(ThreadContext context, IRubyObject other) {
  41622. if (other instanceof RubyTime) {
  41623. return getRuntime().newBoolean(cmp((RubyTime) other) > 0);
  41624. }
  41625. return RubyComparable.op_gt(context, this, other);
  41626. }
  41627. @JRubyMethod(name = "<=", required = 1)
  41628. public IRubyObject op_le(ThreadContext context, IRubyObject other) {
  41629. if (other instanceof RubyTime) {
  41630. return getRuntime().newBoolean(cmp((RubyTime) other) <= 0);
  41631. }
  41632. return RubyComparable.op_le(context, this, other);
  41633. }
  41634. @JRubyMethod(name = "<", required = 1)
  41635. public IRubyObject op_lt(ThreadContext context, IRubyObject other) {
  41636. if (other instanceof RubyTime) {
  41637. return getRuntime().newBoolean(cmp((RubyTime) other) < 0);
  41638. }
  41639. return RubyComparable.op_lt(context, this, other);
  41640. }
  41641. private int cmp(RubyTime other) {
  41642. long millis = getTimeInMillis();
  41643. long millis_other = other.getTimeInMillis();
  41644. long usec_other = other.usec;
  41645. if (millis > millis_other || (millis == millis_other && usec > usec_other)) {
  41646. return 1;
  41647. } else if (millis < millis_other || (millis == millis_other && usec < usec_other)) {
  41648. return -1;
  41649. }
  41650. return 0;
  41651. }
  41652. @JRubyMethod(name = "+", required = 1)
  41653. public IRubyObject op_plus(IRubyObject other) {
  41654. long time = getTimeInMillis();
  41655. if (other instanceof RubyTime) {
  41656. throw getRuntime().newTypeError("time + time ?");
  41657. }
  41658. long adjustment = (long) (RubyNumeric.num2dbl(other) * 1000000);
  41659. int micro = (int) (adjustment % 1000);
  41660. adjustment = adjustment / 1000;
  41661. time += adjustment;
  41662. RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
  41663. newTime.dt = new DateTime(time).withZone(dt.getZone());
  41664. newTime.setUSec(micro);
  41665. return newTime;
  41666. }
  41667. private IRubyObject opMinus(RubyTime other) {
  41668. long time = getTimeInMillis() * 1000 + getUSec();
  41669. time -= other.getTimeInMillis() * 1000 + other.getUSec();
  41670. return RubyFloat.newFloat(getRuntime(), time / 1000000.0); // float number of seconds
  41671. }
  41672. @JRubyMethod(name = "-", required = 1)
  41673. public IRubyObject op_minus(IRubyObject other) {
  41674. if (other instanceof RubyTime) return opMinus((RubyTime) other);
  41675. long time = getTimeInMillis();
  41676. long adjustment = (long) (RubyNumeric.num2dbl(other) * 1000000);
  41677. int micro = (int) (adjustment % 1000);
  41678. adjustment = adjustment / 1000;
  41679. time -= adjustment;
  41680. RubyTime newTime = new RubyTime(getRuntime(), getMetaClass());
  41681. newTime.dt = new DateTime(time).withZone(dt.getZone());
  41682. newTime.setUSec(micro);
  41683. return newTime;
  41684. }
  41685. @JRubyMethod(name = "===", required = 1)
  41686. @Override
  41687. public IRubyObject op_eqq(ThreadContext context, IRubyObject other) {
  41688. return (RubyNumeric.fix2int(callMethod(context, MethodIndex.OP_SPACESHIP, "<=>", other)) == 0) ? getRuntime().getTrue() : getRuntime().getFalse();
  41689. }
  41690. @JRubyMethod(name = "<=>", required = 1)
  41691. public IRubyObject op_cmp(ThreadContext context, IRubyObject other) {
  41692. if (other instanceof RubyTime) {
  41693. return context.getRuntime().newFixnum(cmp((RubyTime) other));
  41694. }
  41695. return context.getRuntime().getNil();
  41696. }
  41697. @JRubyMethod(name = "eql?", required = 1)
  41698. @Override
  41699. public IRubyObject eql_p(IRubyObject other) {
  41700. if (other instanceof RubyTime) {
  41701. RubyTime otherTime = (RubyTime)other;
  41702. return (usec == otherTime.usec && getTimeInMillis() == otherTime.getTimeInMillis()) ? getRuntime().getTrue() : getRuntime().getFalse();
  41703. }
  41704. return getRuntime().getFalse();
  41705. }
  41706. @JRubyMethod(name = {"asctime", "ctime"})
  41707. public RubyString asctime() {
  41708. DateTimeFormatter simpleDateFormat;
  41709. if (dt.getDayOfMonth() < 10) {
  41710. simpleDateFormat = ONE_DAY_CTIME_FORMATTER;
  41711. } else {
  41712. simpleDateFormat = TWO_DAY_CTIME_FORMATTER;
  41713. }
  41714. String result = simpleDateFormat.print(dt);
  41715. return getRuntime().newString(result);
  41716. }
  41717. @JRubyMethod(name = {"to_s", "inspect"})
  41718. @Override
  41719. public IRubyObject to_s() {
  41720. DateTimeFormatter simpleDateFormat;
  41721. if (dt.getZone() == DateTimeZone.UTC) {
  41722. simpleDateFormat = TO_S_UTC_FORMATTER;
  41723. } else {
  41724. simpleDateFormat = TO_S_FORMATTER;
  41725. }
  41726. String result = simpleDateFormat.print(dt);
  41727. return getRuntime().newString(result);
  41728. }
  41729. @JRubyMethod(name = "to_a")
  41730. @Override
  41731. public RubyArray to_a() {
  41732. return getRuntime().newArrayNoCopy(new IRubyObject[] { sec(), min(), hour(), mday(), month(),
  41733. year(), wday(), yday(), isdst(), zone() });
  41734. }
  41735. @JRubyMethod(name = "to_f")
  41736. public RubyFloat to_f() {
  41737. long time = getTimeInMillis();
  41738. time = time * 1000 + usec;
  41739. return RubyFloat.newFloat(getRuntime(), time / 1000000.0);
  41740. }
  41741. @JRubyMethod(name = {"to_i", "tv_sec"})
  41742. public RubyInteger to_i() {
  41743. return getRuntime().newFixnum(getTimeInMillis() / 1000);
  41744. }
  41745. @JRubyMethod(name = {"usec", "tv_usec"})
  41746. public RubyInteger usec() {
  41747. return getRuntime().newFixnum(dt.getMillisOfSecond() * 1000 + getUSec());
  41748. }
  41749. public void setMicroseconds(long mic) {
  41750. long millis = getTimeInMillis() % 1000;
  41751. long withoutMillis = getTimeInMillis() - millis;
  41752. withoutMillis += (mic / 1000);
  41753. dt = dt.withMillis(withoutMillis);
  41754. usec = mic % 1000;
  41755. }
  41756. public long microseconds() {
  41757. return getTimeInMillis() % 1000 * 1000 + usec;
  41758. }
  41759. @JRubyMethod(name = "sec")
  41760. public RubyInteger sec() {
  41761. return getRuntime().newFixnum(dt.getSecondOfMinute());
  41762. }
  41763. @JRubyMethod(name = "min")
  41764. public RubyInteger min() {
  41765. return getRuntime().newFixnum(dt.getMinuteOfHour());
  41766. }
  41767. @JRubyMethod(name = "hour")
  41768. public RubyInteger hour() {
  41769. return getRuntime().newFixnum(dt.getHourOfDay());
  41770. }
  41771. @JRubyMethod(name = {"mday", "day"})
  41772. public RubyInteger mday() {
  41773. return getRuntime().newFixnum(dt.getDayOfMonth());
  41774. }
  41775. @JRubyMethod(name = {"month", "mon"})
  41776. public RubyInteger month() {
  41777. return getRuntime().newFixnum(dt.getMonthOfYear());
  41778. }
  41779. @JRubyMethod(name = "year")
  41780. public RubyInteger year() {
  41781. return getRuntime().newFixnum(dt.getYear());
  41782. }
  41783. @JRubyMethod(name = "wday")
  41784. public RubyInteger wday() {
  41785. return getRuntime().newFixnum((dt.getDayOfWeek()%7));
  41786. }
  41787. @JRubyMethod(name = "yday")
  41788. public RubyInteger yday() {
  41789. return getRuntime().newFixnum(dt.getDayOfYear());
  41790. }
  41791. @JRubyMethod(name = {"gmt_offset", "gmtoff", "utc_offset"})
  41792. public RubyInteger gmt_offset() {
  41793. int offset = dt.getZone().getOffsetFromLocal(dt.getMillis());
  41794. return getRuntime().newFixnum((int)(offset/1000));
  41795. }
  41796. @JRubyMethod(name = {"isdst", "dst?"})
  41797. public RubyBoolean isdst() {
  41798. return getRuntime().newBoolean(!dt.getZone().isStandardOffset(dt.getMillis()));
  41799. }
  41800. @JRubyMethod(name = "zone")
  41801. public RubyString zone() {
  41802. String zone = dt.getZone().getShortName(dt.getMillis());
  41803. if(zone.equals("+00:00")) {
  41804. zone = "GMT";
  41805. }
  41806. return getRuntime().newString(zone);
  41807. }
  41808. public void setDateTime(DateTime dt) {
  41809. this.dt = dt;
  41810. }
  41811. public DateTime getDateTime() {
  41812. return this.dt;
  41813. }
  41814. public Date getJavaDate() {
  41815. return this.dt.toDate();
  41816. }
  41817. @JRubyMethod(name = "hash")
  41818. @Override
  41819. public RubyFixnum hash() {
  41820. // modified to match how hash is calculated in 1.8.2
  41821. return getRuntime().newFixnum((int)(((dt.getMillis() / 1000) ^ microseconds()) << 1) >> 1);
  41822. }
  41823. @JRubyMethod(name = "_dump", optional = 1, frame = true)
  41824. public RubyString dump(IRubyObject[] args, Block unusedBlock) {
  41825. RubyString str = (RubyString) mdump(new IRubyObject[] { this });
  41826. str.syncVariables(this.getVariableList());
  41827. return str;
  41828. }
  41829. public RubyObject mdump(final IRubyObject[] args) {
  41830. RubyTime obj = (RubyTime)args[0];
  41831. DateTime dateTime = obj.dt.withZone(DateTimeZone.UTC);
  41832. byte dumpValue[] = new byte[8];
  41833. int pe =
  41834. 0x1 << 31 |
  41835. (dateTime.getYear()-1900) << 14 |
  41836. (dateTime.getMonthOfYear()-1) << 10 |
  41837. dateTime.getDayOfMonth() << 5 |
  41838. dateTime.getHourOfDay();
  41839. int se =
  41840. dateTime.getMinuteOfHour() << 26 |
  41841. dateTime.getSecondOfMinute() << 20 |
  41842. (dateTime.getMillisOfSecond() * 1000 + (int)usec); // dump usec, not msec
  41843. for(int i = 0; i < 4; i++) {
  41844. dumpValue[i] = (byte)(pe & 0xFF);
  41845. pe >>>= 8;
  41846. }
  41847. for(int i = 4; i < 8 ;i++) {
  41848. dumpValue[i] = (byte)(se & 0xFF);
  41849. se >>>= 8;
  41850. }
  41851. return RubyString.newString(obj.getRuntime(), new ByteList(dumpValue,false));
  41852. }
  41853. @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
  41854. public IRubyObject initialize(Block block) {
  41855. return this;
  41856. }
  41857. /* Time class methods */
  41858. public static IRubyObject s_new(IRubyObject recv, IRubyObject[] args, Block block) {
  41859. Ruby runtime = recv.getRuntime();
  41860. RubyTime time = new RubyTime(runtime, (RubyClass) recv, new DateTime(getLocalTimeZone(runtime)));
  41861. time.callInit(args,block);
  41862. return time;
  41863. }
  41864. /**
  41865. * @deprecated Use {@link #newInstance(ThreadContext, IRubyObject)}
  41866. */
  41867. @Deprecated
  41868. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) {
  41869. return newInstance(context, recv);
  41870. }
  41871. @JRubyMethod(name = "now", backtrace = true, meta = true)
  41872. public static IRubyObject newInstance(ThreadContext context, IRubyObject recv) {
  41873. IRubyObject obj = ((RubyClass) recv).allocate();
  41874. obj.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, obj);
  41875. return obj;
  41876. }
  41877. @JRubyMethod(name = "at", meta = true)
  41878. public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObject arg) {
  41879. Ruby runtime = context.getRuntime();
  41880. final RubyTime time;
  41881. if (arg instanceof RubyTime) {
  41882. RubyTime other = (RubyTime) arg;
  41883. time = new RubyTime(runtime, (RubyClass) recv, other.dt);
  41884. time.setUSec(other.getUSec());
  41885. } else {
  41886. time = new RubyTime(runtime, (RubyClass) recv,
  41887. new DateTime(0L, getLocalTimeZone(runtime)));
  41888. long seconds = RubyNumeric.num2long(arg);
  41889. long millisecs = 0;
  41890. long microsecs = 0;
  41891. // In the case of two arguments, MRI will discard the portion of
  41892. // the first argument after a decimal point (i.e., "floor").
  41893. // However in the case of a single argument, any portion after
  41894. // the decimal point is honored.
  41895. if (arg instanceof RubyFloat) {
  41896. double dbl = ((RubyFloat) arg).getDoubleValue();
  41897. long micro = (long) ((dbl - seconds) * 1000000);
  41898. millisecs = micro / 1000;
  41899. microsecs = micro % 1000;
  41900. }
  41901. time.setUSec(microsecs);
  41902. time.dt = time.dt.withMillis(seconds * 1000 + millisecs);
  41903. }
  41904. time.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, time);
  41905. return time;
  41906. }
  41907. @JRubyMethod(name = "at", meta = true)
  41908. public static IRubyObject at(ThreadContext context, IRubyObject recv, IRubyObject arg1, IRubyObject arg2) {
  41909. Ruby runtime = context.getRuntime();
  41910. RubyTime time = new RubyTime(runtime, (RubyClass) recv,
  41911. new DateTime(0L, getLocalTimeZone(runtime)));
  41912. long seconds = RubyNumeric.num2long(arg1);
  41913. long millisecs = 0;
  41914. long microsecs = 0;
  41915. long tmp = RubyNumeric.num2long(arg2);
  41916. millisecs = tmp / 1000;
  41917. microsecs = tmp % 1000;
  41918. time.setUSec(microsecs);
  41919. time.dt = time.dt.withMillis(seconds * 1000 + millisecs);
  41920. time.getMetaClass().getBaseCallSites()[RubyClass.CS_IDX_INITIALIZE].call(context, time);
  41921. return time;
  41922. }
  41923. @JRubyMethod(name = {"local", "mktime"}, required = 1, optional = 9, meta = true)
  41924. public static RubyTime new_local(IRubyObject recv, IRubyObject[] args) {
  41925. return createTime(recv, args, false);
  41926. }
  41927. @JRubyMethod(name = {"utc", "gm"}, required = 1, optional = 9, meta = true)
  41928. public static RubyTime new_utc(IRubyObject recv, IRubyObject[] args) {
  41929. return createTime(recv, args, true);
  41930. }
  41931. @JRubyMethod(name = "_load", required = 1, frame = true, meta = true)
  41932. public static RubyTime load(IRubyObject recv, IRubyObject from, Block block) {
  41933. return s_mload(recv, (RubyTime)(((RubyClass)recv).allocate()), from);
  41934. }
  41935. protected static RubyTime s_mload(IRubyObject recv, RubyTime time, IRubyObject from) {
  41936. Ruby runtime = recv.getRuntime();
  41937. DateTime dt = new DateTime(DateTimeZone.UTC);
  41938. byte[] fromAsBytes = null;
  41939. fromAsBytes = from.convertToString().getBytes();
  41940. if(fromAsBytes.length != 8) {
  41941. throw runtime.newTypeError("marshaled time format differ");
  41942. }
  41943. int p=0;
  41944. int s=0;
  41945. for (int i = 0; i < 4; i++) {
  41946. p |= ((int)fromAsBytes[i] & 0xFF) << (8 * i);
  41947. }
  41948. for (int i = 4; i < 8; i++) {
  41949. s |= ((int)fromAsBytes[i] & 0xFF) << (8 * (i - 4));
  41950. }
  41951. if ((p & (1<<31)) == 0) {
  41952. dt = dt.withMillis(p * 1000L + s);
  41953. } else {
  41954. p &= ~(1<<31);
  41955. dt = dt.withYear(((p >>> 14) & 0xFFFF) + 1900);
  41956. dt = dt.withMonthOfYear(((p >>> 10) & 0xF) + 1);
  41957. dt = dt.withDayOfMonth(((p >>> 5) & 0x1F));
  41958. dt = dt.withHourOfDay((p & 0x1F));
  41959. dt = dt.withMinuteOfHour(((s >>> 26) & 0x3F));
  41960. dt = dt.withSecondOfMinute(((s >>> 20) & 0x3F));
  41961. // marsaling dumps usec, not msec
  41962. dt = dt.withMillisOfSecond((s & 0xFFFFF) / 1000);
  41963. dt = dt.withZone(getLocalTimeZone(runtime));
  41964. time.setUSec((s & 0xFFFFF) % 1000);
  41965. }
  41966. time.setDateTime(dt);
  41967. return time;
  41968. }
  41969. private static final String[] MONTHS = {"jan", "feb", "mar", "apr", "may", "jun",
  41970. "jul", "aug", "sep", "oct", "nov", "dec"};
  41971. private static final Map<String, Integer> MONTHS_MAP = new HashMap<String, Integer>();
  41972. static {
  41973. for (int i = 0; i < MONTHS.length; i++) {
  41974. MONTHS_MAP.put(MONTHS[i], i + 1);
  41975. }
  41976. }
  41977. private static final int[] time_min = {1, 0, 0, 0, Integer.MIN_VALUE};
  41978. private static final int[] time_max = {31, 23, 59, 60, Integer.MAX_VALUE};
  41979. private static final int ARG_SIZE = 7;
  41980. private static RubyTime createTime(IRubyObject recv, IRubyObject[] args, boolean gmt) {
  41981. Ruby runtime = recv.getRuntime();
  41982. int len = ARG_SIZE;
  41983. if (args.length == 10) {
  41984. args = new IRubyObject[] { args[5], args[4], args[3], args[2], args[1], args[0], runtime.getNil() };
  41985. } else {
  41986. // MRI accepts additional wday argument which appears to be ignored.
  41987. len = args.length;
  41988. if (len < ARG_SIZE) {
  41989. IRubyObject[] newArgs = new IRubyObject[ARG_SIZE];
  41990. System.arraycopy(args, 0, newArgs, 0, args.length);
  41991. for (int i = len; i < ARG_SIZE; i++) {
  41992. newArgs[i] = runtime.getNil();
  41993. }
  41994. args = newArgs;
  41995. len = ARG_SIZE;
  41996. }
  41997. }
  41998. if (args[0] instanceof RubyString) {
  41999. args[0] = RubyNumeric.str2inum(runtime, (RubyString) args[0], 10, false);
  42000. }
  42001. int year = (int) RubyNumeric.num2long(args[0]);
  42002. int month = 1;
  42003. if (len > 1) {
  42004. if (!args[1].isNil()) {
  42005. IRubyObject tmp = args[1].checkStringType();
  42006. if (!tmp.isNil()) {
  42007. String monthString = tmp.toString().toLowerCase();
  42008. Integer monthInt = MONTHS_MAP.get(monthString);
  42009. if (monthInt != null) {
  42010. month = monthInt;
  42011. } else {
  42012. try {
  42013. month = Integer.parseInt(monthString);
  42014. } catch (NumberFormatException nfExcptn) {
  42015. throw runtime.newArgumentError("Argument out of range.");
  42016. }
  42017. }
  42018. } else {
  42019. month = (int) RubyNumeric.num2long(args[1]);
  42020. }
  42021. }
  42022. if (1 > month || month > 12) {
  42023. throw runtime.newArgumentError("Argument out of range: for month: " + month);
  42024. }
  42025. }
  42026. int[] int_args = { 1, 0, 0, 0, 0, 0 };
  42027. for (int i = 0; int_args.length >= i + 2; i++) {
  42028. if (!args[i + 2].isNil()) {
  42029. if (!(args[i + 2] instanceof RubyNumeric)) {
  42030. args[i + 2] = args[i + 2].callMethod(
  42031. runtime.getCurrentContext(), "to_i");
  42032. }
  42033. long value = RubyNumeric.num2long(args[i + 2]);
  42034. if (time_min[i] > value || value > time_max[i]) {
  42035. throw runtime.newArgumentError("argument out of range.");
  42036. }
  42037. int_args[i] = (int) value;
  42038. }
  42039. }
  42040. if (0 <= year && year < 39) {
  42041. year += 2000;
  42042. } else if (69 <= year && year < 139) {
  42043. year += 1900;
  42044. }
  42045. DateTimeZone dtz;
  42046. if (gmt) {
  42047. dtz = DateTimeZone.UTC;
  42048. } else {
  42049. dtz = getLocalTimeZone(runtime);
  42050. }
  42051. DateTime dt;
  42052. // set up with min values and then add to allow rolling over
  42053. try {
  42054. dt = new DateTime(year, 1, 1, 0, 0 , 0, 0, dtz);
  42055. dt = dt.plusMonths(month - 1)
  42056. .plusDays(int_args[0] - 1)
  42057. .plusHours(int_args[1])
  42058. .plusMinutes(int_args[2])
  42059. .plusSeconds(int_args[3]);
  42060. } catch (org.joda.time.IllegalFieldValueException e) {
  42061. throw runtime.newArgumentError("time out of range");
  42062. }
  42063. RubyTime time = new RubyTime(runtime, (RubyClass) recv, dt);
  42064. // Ignores usec if 8 args (for compatibility with parsedate) or if not supplied.
  42065. if (args.length != 8 && !args[6].isNil()) {
  42066. int usec = int_args[4] % 1000;
  42067. int msec = int_args[4] / 1000;
  42068. if (int_args[4] < 0) {
  42069. msec -= 1;
  42070. usec += 1000;
  42071. }
  42072. time.dt = dt.withMillis(dt.getMillis() + msec);
  42073. time.setUSec(usec);
  42074. }
  42075. time.callInit(IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
  42076. return time;
  42077. }
  42078. }
  42079. /***** BEGIN LICENSE BLOCK *****
  42080. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  42081. *
  42082. * The contents of this file are subject to the Common Public
  42083. * License Version 1.0 (the "License"); you may not use this file
  42084. * except in compliance with the License. You may obtain a copy of
  42085. * the License at http://www.eclipse.org/legal/cpl-v10.html
  42086. *
  42087. * Software distributed under the License is distributed on an "AS
  42088. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  42089. * implied. See the License for the specific language governing
  42090. * rights and limitations under the License.
  42091. *
  42092. * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
  42093. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  42094. *
  42095. * Alternatively, the contents of this file may be used under the terms of
  42096. * either of the GNU General Public License Version 2 or later (the "GPL"),
  42097. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  42098. * in which case the provisions of the GPL or the LGPL are applicable instead
  42099. * of those above. If you wish to allow use of your version of this file only
  42100. * under the terms of either the GPL or the LGPL, and not to allow others to
  42101. * use your version of this file under the terms of the CPL, indicate your
  42102. * decision by deleting the provisions above and replace them with the notice
  42103. * and other provisions required by the GPL or the LGPL. If you do not delete
  42104. * the provisions above, a recipient may use your version of this file under
  42105. * the terms of any one of the CPL, the GPL or the LGPL.
  42106. ***** END LICENSE BLOCK *****/
  42107. package org.jruby;
  42108. import org.jruby.anno.JRubyMethod;
  42109. import org.jruby.anno.JRubyClass;
  42110. import org.jruby.internal.runtime.methods.DynamicMethod;
  42111. import org.jruby.runtime.Block;
  42112. import org.jruby.runtime.ObjectAllocator;
  42113. import org.jruby.runtime.ThreadContext;
  42114. import org.jruby.runtime.builtin.IRubyObject;
  42115. /**
  42116. *
  42117. * Note: This was renamed from UnboundMethod.java
  42118. *
  42119. * @author jpetersen
  42120. */
  42121. @JRubyClass(name="UnboundMethod", parent="Method")
  42122. public class RubyUnboundMethod extends RubyMethod {
  42123. protected RubyUnboundMethod(Ruby runtime) {
  42124. super(runtime, runtime.getUnboundMethod());
  42125. }
  42126. public static RubyUnboundMethod newUnboundMethod(
  42127. RubyModule implementationModule,
  42128. String methodName,
  42129. RubyModule originModule,
  42130. String originName,
  42131. DynamicMethod method) {
  42132. RubyUnboundMethod newMethod = new RubyUnboundMethod(implementationModule.getRuntime());
  42133. newMethod.implementationModule = implementationModule;
  42134. newMethod.methodName = methodName;
  42135. newMethod.originModule = originModule;
  42136. newMethod.originName = originName;
  42137. newMethod.method = method;
  42138. return newMethod;
  42139. }
  42140. public static RubyClass defineUnboundMethodClass(Ruby runtime) {
  42141. // TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here. Confirm. JRUBY-415
  42142. RubyClass newClass =
  42143. runtime.defineClass("UnboundMethod", runtime.getMethod(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  42144. runtime.setUnboundMethod(newClass);
  42145. newClass.defineAnnotatedMethods(RubyUnboundMethod.class);
  42146. return newClass;
  42147. }
  42148. /**
  42149. * @see org.jruby.RubyMethod#call(IRubyObject[])
  42150. */
  42151. @JRubyMethod(name = {"call", "[]"}, rest = true, frame = true)
  42152. @Override
  42153. public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) {
  42154. throw context.getRuntime().newTypeError("you cannot call unbound method; bind first");
  42155. }
  42156. /**
  42157. * @see org.jruby.RubyMethod#unbind()
  42158. */
  42159. @JRubyMethod(name = "unbind", frame = true)
  42160. @Override
  42161. public RubyUnboundMethod unbind(Block block) {
  42162. return this;
  42163. }
  42164. @JRubyMethod(name = "bind", required = 1, frame = true)
  42165. public RubyMethod bind(ThreadContext context, IRubyObject aReceiver, Block block) {
  42166. RubyClass receiverClass = aReceiver.getMetaClass();
  42167. if (!originModule.isInstance(aReceiver)) {
  42168. if (originModule instanceof MetaClass) {
  42169. throw context.getRuntime().newTypeError("singleton method called for a different object");
  42170. } else if (receiverClass instanceof MetaClass && receiverClass.getMethods().containsKey(originName)) {
  42171. throw context.getRuntime().newTypeError("method `" + originName + "' overridden");
  42172. } else if (
  42173. !(originModule.isModule() ? originModule.isInstance(aReceiver) : aReceiver.getType() == originModule)) {
  42174. // FIX replace type() == ... with isInstanceOf(...)
  42175. throw context.getRuntime().newTypeError("bind argument must be an instance of " + originModule.getName());
  42176. }
  42177. }
  42178. return RubyMethod.newMethod(implementationModule, methodName, receiverClass, originName, method, aReceiver);
  42179. }
  42180. @JRubyMethod(name = "clone")
  42181. @Override
  42182. public RubyMethod rbClone() {
  42183. return newUnboundMethod(implementationModule, methodName, originModule, originName, method);
  42184. }
  42185. @JRubyMethod(name = "to_proc", frame = true)
  42186. @Override
  42187. public IRubyObject to_proc(ThreadContext context, Block unusedBlock) {
  42188. return super.to_proc(context, unusedBlock);
  42189. }
  42190. }
  42191. /***** BEGIN LICENSE BLOCK *****
  42192. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  42193. *
  42194. * The contents of this file are subject to the Common Public
  42195. * License Version 1.0 (the "License"); you may not use this file
  42196. * except in compliance with the License. You may obtain a copy of
  42197. * the License at http://www.eclipse.org/legal/cpl-v10.html
  42198. *
  42199. * Software distributed under the License is distributed on an "AS
  42200. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  42201. * implied. See the License for the specific language governing
  42202. * rights and limitations under the License.
  42203. *
  42204. * Copyright (C) 2007 Ola Bini <ola.bini@gmail.com>
  42205. *
  42206. * Alternatively, the contents of this file may be used under the terms of
  42207. * either of the GNU General Public License Version 2 or later (the "GPL"),
  42208. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  42209. * in which case the provisions of the GPL or the LGPL are applicable instead
  42210. * of those above. If you wish to allow use of your version of this file only
  42211. * under the terms of either the GPL or the LGPL, and not to allow others to
  42212. * use your version of this file under the terms of the CPL, indicate your
  42213. * decision by deleting the provisions above and replace them with the notice
  42214. * and other provisions required by the GPL or the LGPL. If you do not delete
  42215. * the provisions above, a recipient may use your version of this file under
  42216. * the terms of any one of the CPL, the GPL or the LGPL.
  42217. ***** END LICENSE BLOCK *****/
  42218. package org.jruby;
  42219. import java.io.IOException;
  42220. import java.util.Iterator;
  42221. import java.util.List;
  42222. import java.util.Map;
  42223. import java.util.regex.Pattern;
  42224. import org.jruby.anno.JRubyMethod;
  42225. import org.jruby.anno.JRubyClass;
  42226. import org.jruby.anno.JRubyModule;
  42227. import org.jruby.runtime.Block;
  42228. import org.jruby.runtime.ThreadContext;
  42229. import org.jruby.runtime.builtin.IRubyObject;
  42230. import org.jruby.javasupport.JavaEmbedUtils;
  42231. import org.jruby.javasupport.JavaUtil;
  42232. import org.jruby.javasupport.util.RuntimeHelpers;
  42233. import org.jruby.runtime.MethodIndex;
  42234. import org.jruby.runtime.Visibility;
  42235. import org.jruby.yaml.JRubyRepresenter;
  42236. import org.jruby.yaml.JRubyConstructor;
  42237. import org.jruby.yaml.JRubySerializer;
  42238. import org.jruby.util.IOInputStream;
  42239. import org.jruby.util.IOOutputStream;
  42240. import org.jvyamlb.Representer;
  42241. import org.jvyamlb.Constructor;
  42242. import org.jvyamlb.ParserImpl;
  42243. import org.jvyamlb.PositioningParserImpl;
  42244. import org.jvyamlb.Scanner;
  42245. import org.jvyamlb.ScannerImpl;
  42246. import org.jvyamlb.Composer;
  42247. import org.jvyamlb.ComposerImpl;
  42248. import org.jvyamlb.PositioningScannerImpl;
  42249. import org.jvyamlb.PositioningComposerImpl;
  42250. import org.jvyamlb.Serializer;
  42251. import org.jvyamlb.ResolverImpl;
  42252. import org.jvyamlb.EmitterImpl;
  42253. import org.jvyamlb.exceptions.YAMLException;
  42254. import org.jvyamlb.YAMLConfig;
  42255. import org.jvyamlb.YAML;
  42256. import org.jvyamlb.PositioningScanner;
  42257. import org.jvyamlb.Positionable;
  42258. import org.jvyamlb.Position;
  42259. /**
  42260. * @author <a href="mailto:ola.bini@ki.se">Ola Bini</a>
  42261. */
  42262. @JRubyModule(name="YAML")
  42263. public class RubyYAML {
  42264. public static RubyModule createYAMLModule(Ruby runtime) {
  42265. RubyModule result = runtime.defineModule("YAML");
  42266. runtime.getKernel().callMethod(runtime.getCurrentContext(),"require", runtime.newString("stringio"));
  42267. result.defineAnnotatedMethods(RubyYAML.class);
  42268. RubyClass obj = runtime.getObject();
  42269. RubyClass clazz = runtime.getClassClass();
  42270. RubyClass hash = runtime.getHash();
  42271. RubyClass array = runtime.getArray();
  42272. RubyClass struct = runtime.getStructClass();
  42273. RubyClass exception = runtime.getException();
  42274. RubyClass string = runtime.getString();
  42275. RubyClass symbol = runtime.getSymbol();
  42276. RubyClass range = runtime.getRange();
  42277. RubyClass regexp = runtime.getRegexp();
  42278. RubyClass time = runtime.getTime();
  42279. RubyClass date = runtime.fastGetClass("Date");
  42280. RubyClass fixnum = runtime.getFixnum();
  42281. RubyClass bignum = runtime.getBignum();
  42282. RubyClass flt = runtime.getFloat();
  42283. RubyClass trueClass = runtime.getTrueClass();
  42284. RubyClass falseClass = runtime.getFalseClass();
  42285. RubyClass nilClass = runtime.getNilClass();
  42286. clazz.defineAnnotatedMethods(YAMLClassMethods.class);
  42287. obj.defineAnnotatedMethods(YAMLObjectMethods.class);
  42288. hash.defineAnnotatedMethods(YAMLHashMethods.class);
  42289. array.defineAnnotatedMethods(YAMLArrayMethods.class);
  42290. struct.defineAnnotatedMethods(YAMLStructMethods.class);
  42291. exception.defineAnnotatedMethods(YAMLExceptionMethods.class);
  42292. string.defineAnnotatedMethods(YAMLStringMethods.class);
  42293. symbol.defineAnnotatedMethods(YAMLSymbolMethods.class);
  42294. range.defineAnnotatedMethods(YAMLRangeMethods.class);
  42295. regexp.defineAnnotatedMethods(YAMLRegexpMethods.class);
  42296. time.defineAnnotatedMethods(YAMLTimeMethods.class);
  42297. date.defineAnnotatedMethods(YAMLDateMethods.class);
  42298. bignum.defineAnnotatedMethods(YAMLNumericMethods.class);
  42299. fixnum.defineAnnotatedMethods(YAMLNumericMethods.class);
  42300. flt.defineAnnotatedMethods(YAMLNumericMethods.class);
  42301. trueClass.defineAnnotatedMethods(YAMLTrueMethods.class);
  42302. falseClass.defineAnnotatedMethods(YAMLFalseMethods.class);
  42303. nilClass.defineAnnotatedMethods(YAMLNilMethods.class);
  42304. runtime.setObjectToYamlMethod(runtime.getObject().searchMethod("to_yaml"));
  42305. return result;
  42306. }
  42307. @JRubyMethod(name = "dump", required = 1, optional = 1, module = true, visibility = Visibility.PRIVATE)
  42308. public static IRubyObject dump(IRubyObject self, IRubyObject[] args) {
  42309. IRubyObject obj = args[0];
  42310. Ruby runtime = self.getRuntime();
  42311. IRubyObject val = runtime.newArray(obj);
  42312. if(args.length>1) {
  42313. return RuntimeHelpers.invoke(runtime.getCurrentContext(), self,"dump_all", val, args[1]);
  42314. } else {
  42315. return self.callMethod(runtime.getCurrentContext(),"dump_all", val);
  42316. }
  42317. }
  42318. @JRubyMethod(name = "dump_all", required = 1, optional = 1, module = true, visibility = Visibility.PRIVATE)
  42319. public static IRubyObject dump_all(IRubyObject self, IRubyObject[] args) {
  42320. ThreadContext context = self.getRuntime().getCurrentContext();
  42321. RubyArray objs = (RubyArray)args[0];
  42322. IRubyObject io = null;
  42323. IRubyObject io2 = null;
  42324. if(args.length == 2 && args[1] != null && !args[1].isNil()) {
  42325. io = args[1];
  42326. }
  42327. YAMLConfig cfg = YAML.config().version("1.0");
  42328. IOOutputStream iox = null;
  42329. if(null == io) {
  42330. io2 = self.getRuntime().fastGetClass("StringIO").callMethod(context, "new");
  42331. iox = new IOOutputStream(io2);
  42332. } else {
  42333. iox = new IOOutputStream(io);
  42334. }
  42335. Serializer ser = new JRubySerializer(new EmitterImpl(iox,cfg),new ResolverImpl(),cfg);
  42336. try {
  42337. ser.open();
  42338. Representer r = new JRubyRepresenter(ser, cfg);
  42339. for(Iterator iter = objs.getList().iterator();iter.hasNext();) {
  42340. r.represent(iter.next());
  42341. }
  42342. ser.close();
  42343. } catch(IOException e) {
  42344. throw self.getRuntime().newIOErrorFromException(e);
  42345. }
  42346. if(null == io) {
  42347. io2.callMethod(context, "rewind");
  42348. return io2.callMethod(context, "read");
  42349. } else {
  42350. return io;
  42351. }
  42352. }
  42353. @JRubyMethod(name = "_parse_internal", required = 1, module = true, visibility = Visibility.PRIVATE)
  42354. public static IRubyObject parse_internal(IRubyObject self, IRubyObject arg) {
  42355. boolean debug = self.getRuntime().getDebug().isTrue();
  42356. IRubyObject io = check_yaml_port(arg);
  42357. Scanner scn = null;
  42358. try {
  42359. if(io instanceof RubyString) {
  42360. scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
  42361. } else {
  42362. scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
  42363. }
  42364. Composer ctor =
  42365. debug ?
  42366. new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl()) :
  42367. new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl())
  42368. ;
  42369. if(ctor.checkNode()) {
  42370. return JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getNode());
  42371. }
  42372. return self.getRuntime().getNil();
  42373. } catch(YAMLException e) {
  42374. if(self.getRuntime().getDebug().isTrue()) {
  42375. Position.Range range = ((Positionable)e).getRange();
  42376. throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
  42377. } else {
  42378. throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
  42379. }
  42380. }
  42381. }
  42382. @JRubyMethod(name = "load", required = 1, module = true, visibility = Visibility.PRIVATE)
  42383. public static IRubyObject load(IRubyObject self, IRubyObject arg) {
  42384. boolean debug = self.getRuntime().getDebug().isTrue();
  42385. IRubyObject io = check_yaml_port(arg);
  42386. Scanner scn = null;
  42387. try {
  42388. if(io instanceof RubyString) {
  42389. scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
  42390. } else {
  42391. scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
  42392. }
  42393. Constructor ctor =
  42394. debug ?
  42395. new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
  42396. new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
  42397. ;
  42398. if(ctor.checkData()) {
  42399. return JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData());
  42400. }
  42401. return self.getRuntime().getNil();
  42402. } catch(YAMLException e) {
  42403. if(self.getRuntime().getDebug().isTrue()) {
  42404. Position.Range range = ((Positionable)e).getRange();
  42405. throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
  42406. } else {
  42407. throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
  42408. }
  42409. }
  42410. }
  42411. @JRubyMethod(name = "load_file", required = 1, module = true, visibility = Visibility.PRIVATE)
  42412. public static IRubyObject load_file(IRubyObject self, IRubyObject arg) {
  42413. Ruby runtime = self.getRuntime();
  42414. ThreadContext context = runtime.getCurrentContext();
  42415. IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(),"open", arg, runtime.newString("r"));
  42416. IRubyObject val = self.callMethod(context,"load", io);
  42417. io.callMethod(context, "close");
  42418. return val;
  42419. }
  42420. @JRubyMethod(name = "each_document", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
  42421. public static IRubyObject each_document(IRubyObject self, IRubyObject arg, Block block) {
  42422. boolean debug = self.getRuntime().getDebug().isTrue();
  42423. ThreadContext context = self.getRuntime().getCurrentContext();
  42424. IRubyObject io = arg;
  42425. Scanner scn = null;
  42426. try {
  42427. if(io instanceof RubyString) {
  42428. scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
  42429. } else {
  42430. scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
  42431. }
  42432. Constructor ctor =
  42433. debug ?
  42434. new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
  42435. new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
  42436. ;
  42437. while(ctor.checkData()) {
  42438. block.yield(context, JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
  42439. }
  42440. return self.getRuntime().getNil();
  42441. } catch(YAMLException e) {
  42442. if(self.getRuntime().getDebug().isTrue()) {
  42443. Position.Range range = ((Positionable)e).getRange();
  42444. throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
  42445. } else {
  42446. throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
  42447. }
  42448. }
  42449. }
  42450. @JRubyMethod(name = "load_documents", required = 1, frame = true, module = true, visibility = Visibility.PRIVATE)
  42451. public static IRubyObject load_documents(IRubyObject self, IRubyObject arg, Block block) {
  42452. boolean debug = self.getRuntime().getDebug().isTrue();
  42453. ThreadContext context = self.getRuntime().getCurrentContext();
  42454. IRubyObject io = check_yaml_port(arg);
  42455. Scanner scn = null;
  42456. try {
  42457. if(io instanceof RubyString) {
  42458. scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
  42459. } else {
  42460. scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
  42461. }
  42462. Constructor ctor =
  42463. debug ?
  42464. new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
  42465. new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
  42466. ;
  42467. while(ctor.checkData()) {
  42468. block.yield(context, JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
  42469. }
  42470. return self.getRuntime().getNil();
  42471. } catch(YAMLException e) {
  42472. if(self.getRuntime().getDebug().isTrue()) {
  42473. Position.Range range = ((Positionable)e).getRange();
  42474. throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
  42475. } else {
  42476. throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
  42477. }
  42478. }
  42479. }
  42480. @JRubyMethod(name = "load_stream", required = 1, module = true, visibility = Visibility.PRIVATE)
  42481. public static IRubyObject load_stream(IRubyObject self, IRubyObject arg) {
  42482. boolean debug = self.getRuntime().getDebug().isTrue();
  42483. ThreadContext context = self.getRuntime().getCurrentContext();
  42484. IRubyObject d = self.getRuntime().getNil();
  42485. IRubyObject io = arg;
  42486. Scanner scn = null;
  42487. try {
  42488. if(io instanceof RubyString) {
  42489. scn = debug ? new PositioningScannerImpl(((RubyString)io).getByteList()) : new ScannerImpl(((RubyString)io).getByteList());
  42490. } else {
  42491. scn = debug ? new PositioningScannerImpl(new IOInputStream(io)) : new ScannerImpl(new IOInputStream(io));
  42492. }
  42493. Constructor ctor =
  42494. debug ?
  42495. new JRubyConstructor(self, new PositioningComposerImpl(new PositioningParserImpl((PositioningScanner)scn,YAML.config().version("1.0")),new ResolverImpl())) :
  42496. new JRubyConstructor(self, new ComposerImpl(new ParserImpl(scn,YAML.config().version("1.0")),new ResolverImpl()))
  42497. ;
  42498. while(ctor.checkData()) {
  42499. if(d.isNil()) {
  42500. d = self.getRuntime().fastGetModule("YAML").fastGetClass("Stream").callMethod(context,"new", d);
  42501. }
  42502. d.callMethod(context,"add", JavaEmbedUtils.javaToRuby(self.getRuntime(),ctor.getData()));
  42503. }
  42504. return d;
  42505. } catch(YAMLException e) {
  42506. if(self.getRuntime().getDebug().isTrue()) {
  42507. Position.Range range = ((Positionable)e).getRange();
  42508. throw self.getRuntime().newArgumentError("syntax error on " + range.start + ":" + range.end + ": " + e.getMessage());
  42509. } else {
  42510. throw self.getRuntime().newArgumentError("syntax error:" + e.getMessage());
  42511. }
  42512. }
  42513. }
  42514. @JRubyMethod(name = "dump_stream", rest = true, module = true, visibility = Visibility.PRIVATE)
  42515. public static IRubyObject dump_stream(IRubyObject self, IRubyObject[] args) {
  42516. ThreadContext context = self.getRuntime().getCurrentContext();
  42517. IRubyObject stream = self.getRuntime().fastGetModule("YAML").fastGetClass("Stream").callMethod(context, "new");
  42518. for(int i=0,j=args.length;i<j;i++) {
  42519. stream.callMethod(context,"add", args[i]);
  42520. }
  42521. return stream.callMethod(context, "emit");
  42522. }
  42523. @JRubyMethod(name = "quick_emit_node", required = 1, rest = true, frame = true, module = true, visibility = Visibility.PRIVATE)
  42524. public static IRubyObject quick_emit_node(IRubyObject self, IRubyObject[] args, Block block) {
  42525. return block.yield(self.getRuntime().getCurrentContext(), args[0]);
  42526. }
  42527. // @JRubyMethod(name = "quick_emit_node", rest = true, module = true, visibility = Visibility.PRIVATE)
  42528. public static IRubyObject quick_emit(IRubyObject self, IRubyObject[] args) {
  42529. return self.getRuntime().getNil();
  42530. }
  42531. // prepares IO port type for load (ported from ext/syck/rubyext.c)
  42532. private static IRubyObject check_yaml_port(IRubyObject port) {
  42533. if (port instanceof RubyString) {
  42534. // OK
  42535. }
  42536. else if (port.respondsTo("read")) {
  42537. if (port.respondsTo("binmode")) {
  42538. ThreadContext context = port.getRuntime().getCurrentContext();
  42539. port.callMethod(context, "binmode");
  42540. }
  42541. }
  42542. else {
  42543. throw port.getRuntime().newTypeError("instance of IO needed");
  42544. }
  42545. return port;
  42546. }
  42547. @JRubyClass(name="Hash")
  42548. public static class YAMLHashMethods {
  42549. @JRubyMethod(name = "to_yaml_node", required = 1)
  42550. public static IRubyObject hash_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42551. Ruby runtime = self.getRuntime();
  42552. ThreadContext context = runtime.getCurrentContext();
  42553. return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), self, self.callMethod(context, "to_yaml_style"));
  42554. }
  42555. }
  42556. @JRubyClass(name="Object")
  42557. public static class YAMLObjectMethods {
  42558. @JRubyMethod(name = "to_yaml_properties")
  42559. public static IRubyObject obj_to_yaml_properties(IRubyObject self) {
  42560. ThreadContext context = self.getRuntime().getCurrentContext();
  42561. return self.callMethod(context, "instance_variables").callMethod(context, "sort");
  42562. }
  42563. @JRubyMethod(name = "to_yaml_style")
  42564. public static IRubyObject obj_to_yaml_style(IRubyObject self) {
  42565. return self.getRuntime().getNil();
  42566. }
  42567. @JRubyMethod(name = "to_yaml_node", required = 1)
  42568. public static IRubyObject obj_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42569. ThreadContext context = self.getRuntime().getCurrentContext();
  42570. Map mep = (Map)(new RubyHash(self.getRuntime()));
  42571. RubyArray props = (RubyArray)self.callMethod(context, "to_yaml_properties");
  42572. for(Iterator iter = props.getList().iterator(); iter.hasNext();) {
  42573. String m = iter.next().toString();
  42574. mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
  42575. }
  42576. return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
  42577. }
  42578. @JRubyMethod(name = "to_yaml", rest = true)
  42579. public static IRubyObject obj_to_yaml(IRubyObject self, IRubyObject[] args) {
  42580. ThreadContext context = self.getRuntime().getCurrentContext();
  42581. return self.getRuntime().fastGetModule("YAML").callMethod(context,"dump", self);
  42582. }
  42583. @JRubyMethod(name = "taguri")
  42584. public static IRubyObject obj_taguri(IRubyObject self) {
  42585. return self.getRuntime().newString("!ruby/object:" + self.getType().getName());
  42586. }
  42587. }
  42588. @JRubyClass(name="Class")
  42589. public static class YAMLClassMethods {
  42590. @JRubyMethod(name = "to_yaml", rest = true)
  42591. public static IRubyObject class_to_yaml(IRubyObject self, IRubyObject[] args) {
  42592. throw self.getRuntime().newTypeError("can't dump anonymous class " + self.getType().getName());
  42593. }
  42594. }
  42595. @JRubyClass(name="Array")
  42596. public static class YAMLArrayMethods {
  42597. @JRubyMethod(name = "to_yaml_node", required = 1)
  42598. public static IRubyObject array_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42599. ThreadContext context = self.getRuntime().getCurrentContext();
  42600. return RuntimeHelpers.invoke(context, arg, "seq", self.callMethod(context, "taguri"), self, self.callMethod(context, "to_yaml_style"));
  42601. }
  42602. }
  42603. @JRubyClass(name="Struct")
  42604. public static class YAMLStructMethods {
  42605. @JRubyMethod(name = "to_yaml_node", required = 1)
  42606. public static IRubyObject struct_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42607. ThreadContext context = self.getRuntime().getCurrentContext();
  42608. Map mep = (Map)(new RubyHash(self.getRuntime()));
  42609. for(Iterator iter = ((RubyArray)self.callMethod(context, "members")).getList().iterator();iter.hasNext();) {
  42610. IRubyObject key = self.getRuntime().newString(iter.next().toString());
  42611. mep.put(key,self.callMethod(context,MethodIndex.AREF, "[]", key));
  42612. }
  42613. for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
  42614. String m = iter.next().toString();
  42615. mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
  42616. }
  42617. return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
  42618. }
  42619. @JRubyMethod(name = "taguri")
  42620. public static IRubyObject struct_taguri(IRubyObject self) {
  42621. return self.getRuntime().newString("!ruby/struct:" + self.getType().getName());
  42622. }
  42623. }
  42624. @JRubyClass(name="Exception")
  42625. public static class YAMLExceptionMethods {
  42626. @JRubyMethod(name = "to_yaml_node", required = 1)
  42627. public static IRubyObject exception_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42628. ThreadContext context = self.getRuntime().getCurrentContext();
  42629. Map mep = (Map)(new RubyHash(self.getRuntime()));
  42630. mep.put(self.getRuntime().newString("message"),self.callMethod(context, "message"));
  42631. for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
  42632. String m = iter.next().toString();
  42633. mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
  42634. }
  42635. return RuntimeHelpers.invoke(context, arg,"map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
  42636. }
  42637. @JRubyMethod(name = "taguri")
  42638. public static IRubyObject exception_taguri(IRubyObject self) {
  42639. return self.getRuntime().newString("!ruby/exception:" + self.getType().getName());
  42640. }
  42641. }
  42642. private static final Pattern AFTER_NEWLINE = Pattern.compile("\n.+", Pattern.DOTALL);
  42643. @JRubyClass(name="String")
  42644. public static class YAMLStringMethods {
  42645. @JRubyMethod(name = "is_complex_yaml?")
  42646. public static IRubyObject string_is_complex(IRubyObject self) {
  42647. ThreadContext context = self.getRuntime().getCurrentContext();
  42648. return (self.callMethod(context, "to_yaml_style").isTrue() ||
  42649. ((List)self.callMethod(context, "to_yaml_properties")).isEmpty() ||
  42650. AFTER_NEWLINE.matcher(self.toString()).find()) ? self.getRuntime().getTrue() : self.getRuntime().getFalse();
  42651. }
  42652. @JRubyMethod(name = "is_binary_data?")
  42653. public static IRubyObject string_is_binary(IRubyObject self) {
  42654. ThreadContext context = self.getRuntime().getCurrentContext();
  42655. if(self.callMethod(context, MethodIndex.EMPTY_P, "empty?").isTrue()) {
  42656. return self.getRuntime().getNil();
  42657. }
  42658. return self.toString().indexOf('\0') != -1 ? self.getRuntime().getTrue() : self.getRuntime().getFalse();
  42659. }
  42660. private static JRubyRepresenter into(IRubyObject arg) {
  42661. IRubyObject jobj = arg.getInstanceVariables().fastGetInstanceVariable("@java_object");
  42662. if(jobj != null) {
  42663. return (JRubyRepresenter)(((org.jruby.javasupport.JavaObject)jobj).getValue());
  42664. }
  42665. return null;
  42666. }
  42667. @JRubyMethod(name = "to_yaml_node", required = 1)
  42668. public static IRubyObject string_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42669. ThreadContext context = self.getRuntime().getCurrentContext();
  42670. Ruby rt = self.getRuntime();
  42671. if(self.callMethod(context, "is_binary_data?").isTrue()) {
  42672. return RuntimeHelpers.invoke(context, arg, "scalar", rt.newString("tag:yaml.org,2002:binary"), rt.newArray(self).callMethod(context, "pack", rt.newString("m")), rt.newString("|"));
  42673. }
  42674. if(((List)self.callMethod(context, "to_yaml_properties")).isEmpty()) {
  42675. JRubyRepresenter rep = into(arg);
  42676. if(rep != null) {
  42677. try {
  42678. return JavaUtil.convertJavaToRuby(rt,rep.scalar(self.callMethod(context, "taguri").toString(),self.convertToString().getByteList(),self.toString().startsWith(":") ? "\"" : self.callMethod(context, "to_yaml_style").toString()));
  42679. } catch(IOException e) {
  42680. throw rt.newIOErrorFromException(e);
  42681. }
  42682. } else {
  42683. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self, self.toString().startsWith(":") ? rt.newString("\"") : self.callMethod(context, "to_yaml_style"));
  42684. }
  42685. }
  42686. Map mep = (Map)(new RubyHash(self.getRuntime()));
  42687. mep.put(self.getRuntime().newString("str"),rt.newString(self.toString()));
  42688. for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
  42689. String m = iter.next().toString();
  42690. mep.put(self.getRuntime().newString(m), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
  42691. }
  42692. return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
  42693. }
  42694. }
  42695. @JRubyClass(name="Symbol")
  42696. public static class YAMLSymbolMethods {
  42697. @JRubyMethod(name = "to_yaml_node", required = 1)
  42698. public static IRubyObject symbol_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42699. ThreadContext context = self.getRuntime().getCurrentContext();
  42700. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, "inspect"), self.callMethod(context, "to_yaml_style"));
  42701. }
  42702. @JRubyMethod(name = "taguri")
  42703. public static IRubyObject symbol_taguri(IRubyObject self) {
  42704. return self.getRuntime().newString("tag:yaml.org,2002:str");
  42705. }
  42706. }
  42707. @JRubyClass(name="Numeric")
  42708. public static class YAMLNumericMethods {
  42709. @JRubyMethod(name = "to_yaml_node", required = 1)
  42710. public static IRubyObject numeric_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42711. ThreadContext context = self.getRuntime().getCurrentContext();
  42712. String val = self.toString();
  42713. if("Infinity".equals(val)) {
  42714. val = ".Inf";
  42715. } else if("-Infinity".equals(val)) {
  42716. val = "-.Inf";
  42717. } else if("NaN".equals(val)) {
  42718. val = ".NaN";
  42719. }
  42720. return RuntimeHelpers.invoke(context, arg,"scalar", self.callMethod(context, "taguri"), self.getRuntime().newString(val), self.callMethod(context, "to_yaml_style"));
  42721. }
  42722. }
  42723. @JRubyClass(name="Range")
  42724. public static class YAMLRangeMethods {
  42725. @JRubyMethod(name = "to_yaml_node", required = 1)
  42726. public static IRubyObject range_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42727. ThreadContext context = self.getRuntime().getCurrentContext();
  42728. Map mep = (Map)(new RubyHash(self.getRuntime()));
  42729. mep.put(self.getRuntime().newString("begin"),self.callMethod(context, "begin"));
  42730. mep.put(self.getRuntime().newString("end"),self.callMethod(context, "end"));
  42731. mep.put(self.getRuntime().newString("excl"),self.callMethod(context, "exclude_end?"));
  42732. for(Iterator iter = ((RubyArray)self.callMethod(context, "to_yaml_properties")).getList().iterator(); iter.hasNext();) {
  42733. String m = iter.next().toString();
  42734. mep.put(self.getRuntime().newString(m.substring(1)), self.callMethod(context,"instance_variable_get", self.getRuntime().newString(m)));
  42735. }
  42736. return RuntimeHelpers.invoke(context, arg, "map", self.callMethod(context, "taguri"), (IRubyObject)mep, self.callMethod(context, "to_yaml_style"));
  42737. }
  42738. }
  42739. @JRubyClass(name="Regexp")
  42740. public static class YAMLRegexpMethods {
  42741. @JRubyMethod(name = "to_yaml_node", required = 1)
  42742. public static IRubyObject regexp_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42743. ThreadContext context = self.getRuntime().getCurrentContext();
  42744. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, "inspect"), self.callMethod(context, "to_yaml_style"));
  42745. }
  42746. }
  42747. @JRubyClass(name="Time")
  42748. public static class YAMLTimeMethods {
  42749. @JRubyMethod(name = "to_yaml_node", required = 1)
  42750. public static IRubyObject time_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42751. ThreadContext context = self.getRuntime().getCurrentContext();
  42752. IRubyObject tz = self.getRuntime().newString("Z");
  42753. IRubyObject difference_sign = self.getRuntime().newString("-");
  42754. self = self.dup();
  42755. if(!self.callMethod(context, "utc?").isTrue()) {
  42756. IRubyObject utc_same_instant = self.callMethod(context, "utc");
  42757. IRubyObject utc_same_writing = RuntimeHelpers.invoke(context, self.getRuntime().getTime(), "utc", new IRubyObject[]{
  42758. self.callMethod(context, "year"),self.callMethod(context, "month"),self.callMethod(context, "day"),self.callMethod(context, "hour"),
  42759. self.callMethod(context, "min"),self.callMethod(context, "sec"),self.callMethod(context, "usec")});
  42760. IRubyObject difference_to_utc = utc_same_writing.callMethod(context,MethodIndex.OP_MINUS, "-", utc_same_instant);
  42761. IRubyObject absolute_difference;
  42762. if(difference_to_utc.callMethod(context,MethodIndex.OP_LT, "<", RubyFixnum.zero(self.getRuntime())).isTrue()) {
  42763. difference_sign = self.getRuntime().newString("-");
  42764. absolute_difference = RubyFixnum.zero(self.getRuntime()).callMethod(context,MethodIndex.OP_MINUS, "-", difference_to_utc);
  42765. } else {
  42766. difference_sign = self.getRuntime().newString("+");
  42767. absolute_difference = difference_to_utc;
  42768. }
  42769. IRubyObject difference_minutes = absolute_difference.callMethod(context,"/", self.getRuntime().newFixnum(60)).callMethod(context, "round");
  42770. 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))}));
  42771. }
  42772. IRubyObject standard = self.callMethod(context,"strftime", self.getRuntime().newString("%Y-%m-%d %H:%M:%S"));
  42773. if(self.callMethod(context, "usec").callMethod(context, "nonzero?").isTrue()) {
  42774. standard = standard.callMethod(context, MethodIndex.OP_PLUS, "+", self.getRuntime().newString(".%06d").callMethod(context,"%", self.getRuntime().newArray(self.callMethod(context, "usec"))));
  42775. }
  42776. standard = standard.callMethod(context,MethodIndex.OP_PLUS, "+", self.getRuntime().newString(" %s").callMethod(context,"%", self.getRuntime().newArray(tz)));
  42777. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), standard, self.callMethod(context, "to_yaml_style"));
  42778. }
  42779. }
  42780. @JRubyClass(name="Date")
  42781. public static class YAMLDateMethods {
  42782. @JRubyMethod(name = "to_yaml_node", required = 1)
  42783. public static IRubyObject date_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42784. ThreadContext context = self.getRuntime().getCurrentContext();
  42785. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
  42786. }
  42787. }
  42788. @JRubyClass(name="TrueClass")
  42789. public static class YAMLTrueMethods {
  42790. @JRubyMethod(name = "to_yaml_node", required = 1)
  42791. public static IRubyObject true_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42792. ThreadContext context = self.getRuntime().getCurrentContext();
  42793. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
  42794. }
  42795. @JRubyMethod(name = "taguri")
  42796. public static IRubyObject true_taguri(IRubyObject self) {
  42797. return self.getRuntime().newString("tag:yaml.org,2002:bool");
  42798. }
  42799. }
  42800. @JRubyClass(name="FalseClass")
  42801. public static class YAMLFalseMethods {
  42802. @JRubyMethod(name = "to_yaml_node", required = 1)
  42803. public static IRubyObject false_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42804. ThreadContext context = self.getRuntime().getCurrentContext();
  42805. return RuntimeHelpers.invoke(context, arg, "scalar", self.callMethod(context, "taguri"), self.callMethod(context, MethodIndex.TO_S, "to_s"), self.callMethod(context, "to_yaml_style"));
  42806. }
  42807. @JRubyMethod(name = "taguri")
  42808. public static IRubyObject false_taguri(IRubyObject self) {
  42809. return self.getRuntime().newString("tag:yaml.org,2002:bool");
  42810. }
  42811. }
  42812. @JRubyClass(name="NilClass")
  42813. public static class YAMLNilMethods {
  42814. @JRubyMethod(name = "to_yaml_node", required = 1)
  42815. public static IRubyObject nil_to_yaml_node(IRubyObject self, IRubyObject arg) {
  42816. ThreadContext context = self.getRuntime().getCurrentContext();
  42817. return RuntimeHelpers.invoke(context, arg,"scalar", self.callMethod(context, "taguri"), self.getRuntime().newString(""), self.callMethod(context, "to_yaml_style"));
  42818. }
  42819. }
  42820. }// RubyYAML
  42821. /***** BEGIN LICENSE BLOCK *****
  42822. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  42823. *
  42824. * The contents of this file are subject to the Common Public
  42825. * License Version 1.0 (the "License"); you may not use this file
  42826. * except in compliance with the License. You may obtain a copy of
  42827. * the License at http://www.eclipse.org/legal/cpl-v10.html
  42828. *
  42829. * Software distributed under the License is distributed on an "AS
  42830. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  42831. * implied. See the License for the specific language governing
  42832. * rights and limitations under the License.
  42833. *
  42834. * Copyright (C) 2006 Ola Bini <ola@ologix.com>
  42835. *
  42836. * Alternatively, the contents of this file may be used under the terms of
  42837. * either of the GNU General Public License Version 2 or later (the "GPL"),
  42838. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  42839. * in which case the provisions of the GPL or the LGPL are applicable instead
  42840. * of those above. If you wish to allow use of your version of this file only
  42841. * under the terms of either the GPL or the LGPL, and not to allow others to
  42842. * use your version of this file under the terms of the CPL, indicate your
  42843. * decision by deleting the provisions above and replace them with the notice
  42844. * and other provisions required by the GPL or the LGPL. If you do not delete
  42845. * the provisions above, a recipient may use your version of this file under
  42846. * the terms of any one of the CPL, the GPL or the LGPL.
  42847. ***** END LICENSE BLOCK *****/
  42848. package org.jruby;
  42849. import java.io.InputStream;
  42850. import java.io.IOException;
  42851. import java.util.List;
  42852. import java.util.ArrayList;
  42853. import java.util.zip.GZIPInputStream;
  42854. import java.util.zip.GZIPOutputStream;
  42855. import org.jruby.anno.FrameField;
  42856. import org.jruby.anno.JRubyMethod;
  42857. import org.jruby.anno.JRubyClass;
  42858. import org.jruby.anno.JRubyModule;
  42859. import org.jruby.exceptions.RaiseException;
  42860. import org.jruby.javasupport.util.RuntimeHelpers;
  42861. import org.jruby.runtime.Arity;
  42862. import org.jruby.runtime.Block;
  42863. import org.jruby.runtime.ObjectAllocator;
  42864. import org.jruby.runtime.ThreadContext;
  42865. import org.jruby.runtime.Visibility;
  42866. import org.jruby.runtime.builtin.IRubyObject;
  42867. import org.jruby.util.IOInputStream;
  42868. import org.jruby.util.IOOutputStream;
  42869. import org.jruby.util.CRC32Ext;
  42870. import org.jruby.util.Adler32Ext;
  42871. import org.jruby.util.ZlibInflate;
  42872. import org.jruby.util.ZlibDeflate;
  42873. import org.jruby.util.ByteList;
  42874. @JRubyModule(name="Zlib")
  42875. public class RubyZlib {
  42876. /** Create the Zlib module and add it to the Ruby runtime.
  42877. *
  42878. */
  42879. public static RubyModule createZlibModule(Ruby runtime) {
  42880. RubyModule result = runtime.defineModule("Zlib");
  42881. RubyClass gzfile = result.defineClassUnder("GzipFile", runtime.getObject(), RubyGzipFile.GZIPFILE_ALLOCATOR);
  42882. gzfile.defineAnnotatedMethods(RubyGzipFile.class);
  42883. RubyClass gzreader = result.defineClassUnder("GzipReader", gzfile, RubyGzipReader.GZIPREADER_ALLOCATOR);
  42884. gzreader.includeModule(runtime.getEnumerable());
  42885. gzreader.defineAnnotatedMethods(RubyGzipReader.class);
  42886. RubyClass standardError = runtime.getStandardError();
  42887. RubyClass zlibError = result.defineClassUnder("Error", standardError, standardError.getAllocator());
  42888. gzreader.defineClassUnder("Error", zlibError, zlibError.getAllocator());
  42889. RubyClass gzwriter = result.defineClassUnder("GzipWriter", gzfile, RubyGzipWriter.GZIPWRITER_ALLOCATOR);
  42890. gzwriter.defineAnnotatedMethods(RubyGzipWriter.class);
  42891. result.defineConstant("ZLIB_VERSION",runtime.newString("1.2.1"));
  42892. result.defineConstant("VERSION",runtime.newString("0.6.0"));
  42893. result.defineConstant("BINARY",runtime.newFixnum(0));
  42894. result.defineConstant("ASCII",runtime.newFixnum(1));
  42895. result.defineConstant("UNKNOWN",runtime.newFixnum(2));
  42896. result.defineConstant("DEF_MEM_LEVEL",runtime.newFixnum(8));
  42897. result.defineConstant("MAX_MEM_LEVEL",runtime.newFixnum(9));
  42898. result.defineConstant("OS_UNIX",runtime.newFixnum(3));
  42899. result.defineConstant("OS_UNKNOWN",runtime.newFixnum(255));
  42900. result.defineConstant("OS_CODE",runtime.newFixnum(11));
  42901. result.defineConstant("OS_ZSYSTEM",runtime.newFixnum(8));
  42902. result.defineConstant("OS_VMCMS",runtime.newFixnum(4));
  42903. result.defineConstant("OS_VMS",runtime.newFixnum(2));
  42904. result.defineConstant("OS_RISCOS",runtime.newFixnum(13));
  42905. result.defineConstant("OS_MACOS",runtime.newFixnum(7));
  42906. result.defineConstant("OS_OS2",runtime.newFixnum(6));
  42907. result.defineConstant("OS_AMIGA",runtime.newFixnum(1));
  42908. result.defineConstant("OS_QDOS",runtime.newFixnum(12));
  42909. result.defineConstant("OS_WIN32",runtime.newFixnum(11));
  42910. result.defineConstant("OS_ATARI",runtime.newFixnum(5));
  42911. result.defineConstant("OS_MSDOS",runtime.newFixnum(0));
  42912. result.defineConstant("OS_CPM",runtime.newFixnum(9));
  42913. result.defineConstant("OS_TOPS20",runtime.newFixnum(10));
  42914. result.defineConstant("DEFAULT_STRATEGY",runtime.newFixnum(0));
  42915. result.defineConstant("FILTERED",runtime.newFixnum(1));
  42916. result.defineConstant("HUFFMAN_ONLY",runtime.newFixnum(2));
  42917. result.defineConstant("NO_FLUSH",runtime.newFixnum(0));
  42918. result.defineConstant("SYNC_FLUSH",runtime.newFixnum(2));
  42919. result.defineConstant("FULL_FLUSH",runtime.newFixnum(3));
  42920. result.defineConstant("FINISH",runtime.newFixnum(4));
  42921. result.defineConstant("NO_COMPRESSION",runtime.newFixnum(0));
  42922. result.defineConstant("BEST_SPEED",runtime.newFixnum(1));
  42923. result.defineConstant("DEFAULT_COMPRESSION",runtime.newFixnum(-1));
  42924. result.defineConstant("BEST_COMPRESSION",runtime.newFixnum(9));
  42925. result.defineConstant("MAX_WBITS",runtime.newFixnum(15));
  42926. result.defineAnnotatedMethods(RubyZlib.class);
  42927. result.defineClassUnder("StreamEnd",zlibError, zlibError.getAllocator());
  42928. result.defineClassUnder("StreamError",zlibError, zlibError.getAllocator());
  42929. result.defineClassUnder("BufError",zlibError, zlibError.getAllocator());
  42930. result.defineClassUnder("NeedDict",zlibError, zlibError.getAllocator());
  42931. result.defineClassUnder("MemError",zlibError, zlibError.getAllocator());
  42932. result.defineClassUnder("VersionError",zlibError, zlibError.getAllocator());
  42933. result.defineClassUnder("DataError",zlibError, zlibError.getAllocator());
  42934. RubyClass gzError = gzfile.defineClassUnder("Error",zlibError, zlibError.getAllocator());
  42935. gzfile.defineClassUnder("CRCError",gzError, gzError.getAllocator());
  42936. gzfile.defineClassUnder("NoFooter",gzError, gzError.getAllocator());
  42937. gzfile.defineClassUnder("LengthError",gzError, gzError.getAllocator());
  42938. // ZStream actually *isn't* allocatable
  42939. RubyClass zstream = result.defineClassUnder("ZStream", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
  42940. zstream.defineAnnotatedMethods(ZStream.class);
  42941. zstream.undefineMethod("new");
  42942. RubyClass infl = result.defineClassUnder("Inflate", zstream, Inflate.INFLATE_ALLOCATOR);
  42943. infl.defineAnnotatedMethods(Inflate.class);
  42944. RubyClass defl = result.defineClassUnder("Deflate", zstream, Deflate.DEFLATE_ALLOCATOR);
  42945. defl.defineAnnotatedMethods(Deflate.class);
  42946. runtime.getKernel().callMethod(runtime.getCurrentContext(),"require",runtime.newString("stringio"));
  42947. return result;
  42948. }
  42949. @JRubyClass(name="Zlib::Error", parent="StandardError")
  42950. public static class Error {}
  42951. @JRubyClass(name="Zlib::StreamEnd", parent="Zlib::Error")
  42952. public static class StreamEnd extends Error {}
  42953. @JRubyClass(name="Zlib::StreamError", parent="Zlib::Error")
  42954. public static class StreamError extends Error {}
  42955. @JRubyClass(name="Zlib::BufError", parent="Zlib::Error")
  42956. public static class BufError extends Error {}
  42957. @JRubyClass(name="Zlib::NeedDict", parent="Zlib::Error")
  42958. public static class NeedDict extends Error {}
  42959. @JRubyClass(name="Zlib::MemError", parent="Zlib::Error")
  42960. public static class MemError extends Error {}
  42961. @JRubyClass(name="Zlib::VersionError", parent="Zlib::Error")
  42962. public static class VersionError extends Error {}
  42963. @JRubyClass(name="Zlib::DataError", parent="Zlib::Error")
  42964. public static class DataError extends Error {}
  42965. @JRubyMethod(name = "zlib_version", module = true, visibility = Visibility.PRIVATE)
  42966. public static IRubyObject zlib_version(IRubyObject recv) {
  42967. return ((RubyModule)recv).fastGetConstant("ZLIB_VERSION");
  42968. }
  42969. @JRubyMethod(name = "version", module = true, visibility = Visibility.PRIVATE)
  42970. public static IRubyObject version(IRubyObject recv) {
  42971. return ((RubyModule)recv).fastGetConstant("VERSION");
  42972. }
  42973. @JRubyMethod(name = "crc32", optional = 2, module = true, visibility = Visibility.PRIVATE)
  42974. public static IRubyObject crc32(IRubyObject recv, IRubyObject[] args) throws Exception {
  42975. args = Arity.scanArgs(recv.getRuntime(),args,0,2);
  42976. long crc = 0;
  42977. ByteList bytes = null;
  42978. if (!args[0].isNil()) bytes = args[0].convertToString().getByteList();
  42979. if (!args[1].isNil()) crc = RubyNumeric.num2long(args[1]);
  42980. CRC32Ext ext = new CRC32Ext((int)crc);
  42981. if (bytes != null) {
  42982. ext.update(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  42983. }
  42984. return recv.getRuntime().newFixnum(ext.getValue());
  42985. }
  42986. @JRubyMethod(name = "adler32", optional = 2, module = true, visibility = Visibility.PRIVATE)
  42987. public static IRubyObject adler32(IRubyObject recv, IRubyObject[] args) throws Exception {
  42988. args = Arity.scanArgs(recv.getRuntime(),args,0,2);
  42989. int adler = 1;
  42990. ByteList bytes = null;
  42991. if (!args[0].isNil()) bytes = args[0].convertToString().getByteList();
  42992. if (!args[1].isNil()) adler = RubyNumeric.fix2int(args[1]);
  42993. Adler32Ext ext = new Adler32Ext(adler);
  42994. if (bytes != null) {
  42995. ext.update(bytes.unsafeBytes(), bytes.begin(), bytes.length()); // it's safe since adler.update doesn't modify the array
  42996. }
  42997. return recv.getRuntime().newFixnum(ext.getValue());
  42998. }
  42999. private final static long[] crctab = new long[]{
  43000. 0L, 1996959894L, 3993919788L, 2567524794L, 124634137L, 1886057615L, 3915621685L, 2657392035L, 249268274L, 2044508324L, 3772115230L, 2547177864L, 162941995L,
  43001. 2125561021L, 3887607047L, 2428444049L, 498536548L, 1789927666L, 4089016648L, 2227061214L, 450548861L, 1843258603L, 4107580753L, 2211677639L, 325883990L,
  43002. 1684777152L, 4251122042L, 2321926636L, 335633487L, 1661365465L, 4195302755L, 2366115317L, 997073096L, 1281953886L, 3579855332L, 2724688242L, 1006888145L,
  43003. 1258607687L, 3524101629L, 2768942443L, 901097722L, 1119000684L, 3686517206L, 2898065728L, 853044451L, 1172266101L, 3705015759L, 2882616665L, 651767980L,
  43004. 1373503546L, 3369554304L, 3218104598L, 565507253L, 1454621731L, 3485111705L, 3099436303L, 671266974L, 1594198024L, 3322730930L, 2970347812L, 795835527L,
  43005. 1483230225L, 3244367275L, 3060149565L, 1994146192L, 31158534L, 2563907772L, 4023717930L, 1907459465L, 112637215L, 2680153253L, 3904427059L, 2013776290L,
  43006. 251722036L, 2517215374L, 3775830040L, 2137656763L, 141376813L, 2439277719L, 3865271297L, 1802195444L, 476864866L, 2238001368L, 4066508878L, 1812370925L,
  43007. 453092731L, 2181625025L, 4111451223L, 1706088902L, 314042704L, 2344532202L, 4240017532L, 1658658271L, 366619977L, 2362670323L, 4224994405L, 1303535960L,
  43008. 984961486L, 2747007092L, 3569037538L, 1256170817L, 1037604311L, 2765210733L, 3554079995L, 1131014506L, 879679996L, 2909243462L, 3663771856L, 1141124467L,
  43009. 855842277L, 2852801631L, 3708648649L, 1342533948L, 654459306L, 3188396048L, 3373015174L, 1466479909L, 544179635L, 3110523913L, 3462522015L, 1591671054L,
  43010. 702138776L, 2966460450L, 3352799412L, 1504918807L, 783551873L, 3082640443L, 3233442989L, 3988292384L, 2596254646L, 62317068L, 1957810842L, 3939845945L,
  43011. 2647816111L, 81470997L, 1943803523L, 3814918930L, 2489596804L, 225274430L, 2053790376L, 3826175755L, 2466906013L, 167816743L, 2097651377L, 4027552580L,
  43012. 2265490386L, 503444072L, 1762050814L, 4150417245L, 2154129355L, 426522225L, 1852507879L, 4275313526L, 2312317920L, 282753626L, 1742555852L, 4189708143L,
  43013. 2394877945L, 397917763L, 1622183637L, 3604390888L, 2714866558L, 953729732L, 1340076626L, 3518719985L, 2797360999L, 1068828381L, 1219638859L, 3624741850L,
  43014. 2936675148L, 906185462L, 1090812512L, 3747672003L, 2825379669L, 829329135L, 1181335161L, 3412177804L, 3160834842L, 628085408L, 1382605366L, 3423369109L,
  43015. 3138078467L, 570562233L, 1426400815L, 3317316542L, 2998733608L, 733239954L, 1555261956L, 3268935591L, 3050360625L, 752459403L, 1541320221L, 2607071920L,
  43016. 3965973030L, 1969922972L, 40735498L, 2617837225L, 3943577151L, 1913087877L, 83908371L, 2512341634L, 3803740692L, 2075208622L, 213261112L, 2463272603L,
  43017. 3855990285L, 2094854071L, 198958881L, 2262029012L, 4057260610L, 1759359992L, 534414190L, 2176718541L, 4139329115L, 1873836001L, 414664567L, 2282248934L,
  43018. 4279200368L, 1711684554L, 285281116L, 2405801727L, 4167216745L, 1634467795L, 376229701L, 2685067896L, 3608007406L, 1308918612L, 956543938L, 2808555105L,
  43019. 3495958263L, 1231636301L, 1047427035L, 2932959818L, 3654703836L, 1088359270L, 936918000L, 2847714899L, 3736837829L, 1202900863L, 817233897L, 3183342108L,
  43020. 3401237130L, 1404277552L, 615818150L, 3134207493L, 3453421203L, 1423857449L, 601450431L, 3009837614L, 3294710456L, 1567103746L, 711928724L, 3020668471L,
  43021. 3272380065L, 1510334235L, 755167117};
  43022. @JRubyMethod(name = "crc_table", module = true, visibility = Visibility.PRIVATE)
  43023. public static IRubyObject crc_table(IRubyObject recv) {
  43024. List<IRubyObject> ll = new ArrayList<IRubyObject>(crctab.length);
  43025. for(int i=0;i<crctab.length;i++) {
  43026. ll.add(recv.getRuntime().newFixnum(crctab[i]));
  43027. }
  43028. return recv.getRuntime().newArray(ll);
  43029. }
  43030. @JRubyClass(name="Zlib::ZStream")
  43031. public static abstract class ZStream extends RubyObject {
  43032. protected boolean closed = false;
  43033. protected boolean ended = false;
  43034. protected boolean finished = false;
  43035. protected abstract int internalTotalOut();
  43036. protected abstract boolean internalStreamEndP();
  43037. protected abstract void internalEnd();
  43038. protected abstract void internalReset();
  43039. protected abstract int internalAdler();
  43040. protected abstract IRubyObject internalFinish() throws Exception;
  43041. protected abstract int internalTotalIn();
  43042. protected abstract void internalClose();
  43043. public ZStream(Ruby runtime, RubyClass type) {
  43044. super(runtime, type);
  43045. }
  43046. @JRubyMethod(name = "initialize", frame = true, visibility = Visibility.PRIVATE)
  43047. public IRubyObject initialize(Block unusedBlock) {
  43048. return this;
  43049. }
  43050. @JRubyMethod(name = "flust_next_out")
  43051. public IRubyObject flush_next_out() {
  43052. return getRuntime().getNil();
  43053. }
  43054. @JRubyMethod(name = "total_out")
  43055. public IRubyObject total_out() {
  43056. return getRuntime().newFixnum(internalTotalOut());
  43057. }
  43058. @JRubyMethod(name = "stream_end?")
  43059. public IRubyObject stream_end_p() {
  43060. return internalStreamEndP() ? getRuntime().getTrue() : getRuntime().getFalse();
  43061. }
  43062. @JRubyMethod(name = "data_type")
  43063. public IRubyObject data_type() {
  43064. return getRuntime().fastGetModule("Zlib").fastGetConstant("UNKNOWN");
  43065. }
  43066. @JRubyMethod(name = "closed?")
  43067. public IRubyObject closed_p() {
  43068. return closed ? getRuntime().getTrue() : getRuntime().getFalse();
  43069. }
  43070. @JRubyMethod(name = "ended?")
  43071. public IRubyObject ended_p() {
  43072. return ended ? getRuntime().getTrue() : getRuntime().getFalse();
  43073. }
  43074. @JRubyMethod(name = "end")
  43075. public IRubyObject end() {
  43076. if(!ended) {
  43077. internalEnd();
  43078. ended = true;
  43079. }
  43080. return getRuntime().getNil();
  43081. }
  43082. @JRubyMethod(name = "reset")
  43083. public IRubyObject reset() {
  43084. internalReset();
  43085. return getRuntime().getNil();
  43086. }
  43087. @JRubyMethod(name = "avail_out")
  43088. public IRubyObject avail_out() {
  43089. return RubyFixnum.zero(getRuntime());
  43090. }
  43091. @JRubyMethod(name = "avail_out=", required = 1)
  43092. public IRubyObject set_avail_out(IRubyObject p1) {
  43093. return p1;
  43094. }
  43095. @JRubyMethod(name = "adler")
  43096. public IRubyObject adler() {
  43097. return getRuntime().newFixnum(internalAdler());
  43098. }
  43099. @JRubyMethod(name = "finish")
  43100. public IRubyObject finish() throws Exception {
  43101. if(!finished) {
  43102. finished = true;
  43103. return internalFinish();
  43104. }
  43105. return RubyString.newEmptyString(getRuntime());
  43106. }
  43107. @JRubyMethod(name = "avail_in")
  43108. public IRubyObject avail_in() {
  43109. return RubyFixnum.zero(getRuntime());
  43110. }
  43111. @JRubyMethod(name = "flush_next_in")
  43112. public IRubyObject flush_next_in() {
  43113. return getRuntime().getNil();
  43114. }
  43115. @JRubyMethod(name = "total_in")
  43116. public IRubyObject total_in() {
  43117. return getRuntime().newFixnum(internalTotalIn());
  43118. }
  43119. @JRubyMethod(name = "finished?")
  43120. public IRubyObject finished_p() {
  43121. return finished ? getRuntime().getTrue() : getRuntime().getFalse();
  43122. }
  43123. @JRubyMethod(name = "close")
  43124. public IRubyObject close() {
  43125. if(!closed) {
  43126. internalClose();
  43127. closed = true;
  43128. }
  43129. return getRuntime().getNil();
  43130. }
  43131. }
  43132. @JRubyClass(name="Zlib::Inflate", parent="Zlib::ZStream")
  43133. public static class Inflate extends ZStream {
  43134. protected static final ObjectAllocator INFLATE_ALLOCATOR = new ObjectAllocator() {
  43135. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  43136. return new Inflate(runtime, klass);
  43137. }
  43138. };
  43139. @JRubyMethod(name = "inflate", required = 1, meta = true)
  43140. public static IRubyObject s_inflate(IRubyObject recv, IRubyObject string) throws Exception {
  43141. return ZlibInflate.s_inflate(recv,string.convertToString().getByteList());
  43142. }
  43143. public Inflate(Ruby runtime, RubyClass type) {
  43144. super(runtime, type);
  43145. }
  43146. private ZlibInflate infl;
  43147. @JRubyMethod(name = "initialize", rest = true, visibility = Visibility.PRIVATE)
  43148. public IRubyObject _initialize(IRubyObject[] args) throws Exception {
  43149. infl = new ZlibInflate(this);
  43150. return this;
  43151. }
  43152. @JRubyMethod(name = "<<", required = 1)
  43153. public IRubyObject append(IRubyObject arg) {
  43154. infl.append(arg);
  43155. return this;
  43156. }
  43157. @JRubyMethod(name = "sync_point?")
  43158. public IRubyObject sync_point_p() {
  43159. return infl.sync_point();
  43160. }
  43161. @JRubyMethod(name = "set_dictionary", required = 1)
  43162. public IRubyObject set_dictionary(IRubyObject arg) throws Exception {
  43163. return infl.set_dictionary(arg);
  43164. }
  43165. @JRubyMethod(name = "inflate", required = 1)
  43166. public IRubyObject inflate(IRubyObject string) throws Exception {
  43167. return infl.inflate(string.convertToString().getByteList());
  43168. }
  43169. @JRubyMethod(name = "sync", required = 1)
  43170. public IRubyObject sync(IRubyObject string) {
  43171. return infl.sync(string);
  43172. }
  43173. protected int internalTotalOut() {
  43174. return infl.getInflater().getTotalOut();
  43175. }
  43176. protected boolean internalStreamEndP() {
  43177. return infl.getInflater().finished();
  43178. }
  43179. protected void internalEnd() {
  43180. infl.getInflater().end();
  43181. }
  43182. protected void internalReset() {
  43183. infl.getInflater().reset();
  43184. }
  43185. protected int internalAdler() {
  43186. return infl.getInflater().getAdler();
  43187. }
  43188. protected IRubyObject internalFinish() throws Exception {
  43189. infl.finish();
  43190. return getRuntime().getNil();
  43191. }
  43192. public IRubyObject finished_p() {
  43193. return infl.getInflater().finished() ? getRuntime().getTrue() : getRuntime().getFalse();
  43194. }
  43195. protected int internalTotalIn() {
  43196. return infl.getInflater().getTotalIn();
  43197. }
  43198. protected void internalClose() {
  43199. infl.close();
  43200. }
  43201. }
  43202. @JRubyClass(name="Zlib::Deflate", parent="Zlib::ZStream")
  43203. public static class Deflate extends ZStream {
  43204. protected static final ObjectAllocator DEFLATE_ALLOCATOR = new ObjectAllocator() {
  43205. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  43206. return new Deflate(runtime, klass);
  43207. }
  43208. };
  43209. @JRubyMethod(name = "deflate", required = 1, optional = 1, meta = true)
  43210. public static IRubyObject s_deflate(IRubyObject recv, IRubyObject[] args) throws Exception {
  43211. args = Arity.scanArgs(recv.getRuntime(),args,1,1);
  43212. int level = -1;
  43213. if(!args[1].isNil()) {
  43214. level = RubyNumeric.fix2int(args[1]);
  43215. }
  43216. return ZlibDeflate.s_deflate(recv,args[0].convertToString().getByteList(),level);
  43217. }
  43218. public Deflate(Ruby runtime, RubyClass type) {
  43219. super(runtime, type);
  43220. }
  43221. private ZlibDeflate defl;
  43222. @JRubyMethod(name = "initialize", optional = 4, visibility = Visibility.PRIVATE)
  43223. public IRubyObject _initialize(IRubyObject[] args) throws Exception {
  43224. args = Arity.scanArgs(getRuntime(),args,0,4);
  43225. int level = -1;
  43226. int window_bits = 15;
  43227. int memlevel = 8;
  43228. int strategy = 0;
  43229. if(!args[0].isNil()) {
  43230. level = RubyNumeric.fix2int(args[0]);
  43231. }
  43232. if(!args[1].isNil()) {
  43233. window_bits = RubyNumeric.fix2int(args[1]);
  43234. }
  43235. if(!args[2].isNil()) {
  43236. memlevel = RubyNumeric.fix2int(args[2]);
  43237. }
  43238. if(!args[3].isNil()) {
  43239. strategy = RubyNumeric.fix2int(args[3]);
  43240. }
  43241. defl = new ZlibDeflate(this,level,window_bits,memlevel,strategy);
  43242. return this;
  43243. }
  43244. @JRubyMethod(name = "<<", required = 1)
  43245. public IRubyObject append(IRubyObject arg) throws Exception {
  43246. defl.append(arg);
  43247. return this;
  43248. }
  43249. @JRubyMethod(name = "params", required = 2)
  43250. public IRubyObject params(IRubyObject level, IRubyObject strategy) {
  43251. defl.params(RubyNumeric.fix2int(level),RubyNumeric.fix2int(strategy));
  43252. return getRuntime().getNil();
  43253. }
  43254. @JRubyMethod(name = "set_dictionary", required = 1)
  43255. public IRubyObject set_dictionary(IRubyObject arg) throws Exception {
  43256. return defl.set_dictionary(arg);
  43257. }
  43258. @JRubyMethod(name = "flush", optional = 1)
  43259. public IRubyObject flush(IRubyObject[] args) throws Exception {
  43260. int flush = 2; // SYNC_FLUSH
  43261. if(args.length == 1) {
  43262. if(!args[0].isNil()) {
  43263. flush = RubyNumeric.fix2int(args[0]);
  43264. }
  43265. }
  43266. return defl.flush(flush);
  43267. }
  43268. @JRubyMethod(name = "deflate", required = 1, optional = 1)
  43269. public IRubyObject deflate(IRubyObject[] args) throws Exception {
  43270. args = Arity.scanArgs(getRuntime(),args,1,1);
  43271. int flush = 0; // NO_FLUSH
  43272. if(!args[1].isNil()) {
  43273. flush = RubyNumeric.fix2int(args[1]);
  43274. }
  43275. return defl.deflate(args[0].convertToString().getByteList(),flush);
  43276. }
  43277. protected int internalTotalOut() {
  43278. return defl.getDeflater().getTotalOut();
  43279. }
  43280. protected boolean internalStreamEndP() {
  43281. return defl.getDeflater().finished();
  43282. }
  43283. protected void internalEnd() {
  43284. defl.getDeflater().end();
  43285. }
  43286. protected void internalReset() {
  43287. defl.getDeflater().reset();
  43288. }
  43289. protected int internalAdler() {
  43290. return defl.getDeflater().getAdler();
  43291. }
  43292. protected IRubyObject internalFinish() throws Exception {
  43293. return defl.finish();
  43294. }
  43295. protected int internalTotalIn() {
  43296. return defl.getDeflater().getTotalIn();
  43297. }
  43298. protected void internalClose() {
  43299. defl.close();
  43300. }
  43301. }
  43302. @JRubyClass(name="Zlib::GzipFile")
  43303. public static class RubyGzipFile extends RubyObject {
  43304. @JRubyClass(name="Zlib::GzipFile::Error", parent="Zlib::Error")
  43305. public static class Error {}
  43306. @JRubyClass(name="Zlib::GzipFile::CRCError", parent="Zlib::GzipFile::Error")
  43307. public static class CRCError extends Error {}
  43308. @JRubyClass(name="Zlib::GzipFile::NoFooter", parent="Zlib::GzipFile::Error")
  43309. public static class NoFooter extends Error {}
  43310. @JRubyClass(name="Zlib::GzipFile::LengthError", parent="Zlib::GzipFile::Error")
  43311. public static class LengthError extends Error {}
  43312. private static IRubyObject wrap(ThreadContext context, RubyGzipFile instance,
  43313. IRubyObject io, Block block) throws IOException {
  43314. if (block.isGiven()) {
  43315. try {
  43316. block.yield(context, instance);
  43317. return instance.getRuntime().getNil();
  43318. } finally {
  43319. if (!instance.isClosed()) instance.close();
  43320. }
  43321. }
  43322. return io;
  43323. }
  43324. @JRubyMethod(name = "wrap", required = 1, frame = true, meta = true)
  43325. public static IRubyObject wrap(ThreadContext context, IRubyObject recv, IRubyObject io, Block block) throws IOException {
  43326. Ruby runtime = recv.getRuntime();
  43327. RubyGzipFile instance;
  43328. // TODO: People extending GzipWriter/reader will break. Find better way here.
  43329. if (recv == runtime.getModule("Zlib").getClass("GzipWriter")) {
  43330. instance = RubyGzipWriter.newGzipWriter(recv, new IRubyObject[] { io }, block);
  43331. } else {
  43332. instance = RubyGzipReader.newInstance(recv, new IRubyObject[] { io }, block);
  43333. }
  43334. return wrap(context, instance, io, block);
  43335. }
  43336. protected static final ObjectAllocator GZIPFILE_ALLOCATOR = new ObjectAllocator() {
  43337. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  43338. return new RubyGzipFile(runtime, klass);
  43339. }
  43340. };
  43341. @JRubyMethod(name = "new", frame = true, meta = true)
  43342. public static RubyGzipFile newInstance(IRubyObject recv, Block block) {
  43343. RubyClass klass = (RubyClass)recv;
  43344. RubyGzipFile result = (RubyGzipFile) klass.allocate();
  43345. result.callInit(new IRubyObject[0], block);
  43346. return result;
  43347. }
  43348. protected boolean closed = false;
  43349. protected boolean finished = false;
  43350. private int os_code = 255;
  43351. private int level = -1;
  43352. private String orig_name;
  43353. private String comment;
  43354. protected IRubyObject realIo;
  43355. private IRubyObject mtime;
  43356. public RubyGzipFile(Ruby runtime, RubyClass type) {
  43357. super(runtime, type);
  43358. mtime = runtime.getNil();
  43359. }
  43360. @JRubyMethod(name = "os_code")
  43361. public IRubyObject os_code() {
  43362. return getRuntime().newFixnum(os_code);
  43363. }
  43364. @JRubyMethod(name = "closed?")
  43365. public IRubyObject closed_p() {
  43366. return closed ? getRuntime().getTrue() : getRuntime().getFalse();
  43367. }
  43368. protected boolean isClosed() {
  43369. return closed;
  43370. }
  43371. @JRubyMethod(name = "orig_name")
  43372. public IRubyObject orig_name() {
  43373. return orig_name == null ? getRuntime().getNil() : getRuntime().newString(orig_name);
  43374. }
  43375. @JRubyMethod(name = "to_io")
  43376. public IRubyObject to_io() {
  43377. return realIo;
  43378. }
  43379. @JRubyMethod(name = "comment")
  43380. public IRubyObject comment() {
  43381. return comment == null ? getRuntime().getNil() : getRuntime().newString(comment);
  43382. }
  43383. @JRubyMethod(name = "crc")
  43384. public IRubyObject crc() {
  43385. return RubyFixnum.zero(getRuntime());
  43386. }
  43387. @JRubyMethod(name = "mtime")
  43388. public IRubyObject mtime() {
  43389. return mtime;
  43390. }
  43391. @JRubyMethod(name = "sync")
  43392. public IRubyObject sync() {
  43393. return getRuntime().getNil();
  43394. }
  43395. @JRubyMethod(name = "finish")
  43396. public IRubyObject finish() throws IOException {
  43397. if (!finished) {
  43398. //io.finish();
  43399. }
  43400. finished = true;
  43401. return realIo;
  43402. }
  43403. @JRubyMethod(name = "close")
  43404. public IRubyObject close() throws IOException {
  43405. return null;
  43406. }
  43407. @JRubyMethod(name = "level")
  43408. public IRubyObject level() {
  43409. return getRuntime().newFixnum(level);
  43410. }
  43411. @JRubyMethod(name = "sync=", required = 1)
  43412. public IRubyObject set_sync(IRubyObject ignored) {
  43413. return getRuntime().getNil();
  43414. }
  43415. }
  43416. @JRubyClass(name="Zlib::GzipReader", parent="Zlib::GzipFile", include="Enumerable")
  43417. public static class RubyGzipReader extends RubyGzipFile {
  43418. @JRubyClass(name="Zlib::GzipReader::Error", parent="Zlib::GzipReader")
  43419. public static class Error {}
  43420. protected static final ObjectAllocator GZIPREADER_ALLOCATOR = new ObjectAllocator() {
  43421. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  43422. return new RubyGzipReader(runtime, klass);
  43423. }
  43424. };
  43425. @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
  43426. public static RubyGzipReader newInstance(IRubyObject recv, IRubyObject[] args, Block block) {
  43427. RubyClass klass = (RubyClass)recv;
  43428. RubyGzipReader result = (RubyGzipReader)klass.allocate();
  43429. result.callInit(args, block);
  43430. return result;
  43431. }
  43432. @JRubyMethod(name = "open", required = 1, frame = true, meta = true)
  43433. public static IRubyObject open(final ThreadContext context, IRubyObject recv, IRubyObject filename, Block block) throws IOException {
  43434. Ruby runtime = recv.getRuntime();
  43435. IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", filename, runtime.newString("rb"));
  43436. RubyGzipFile instance = newInstance(recv, new IRubyObject[]{io}, Block.NULL_BLOCK);
  43437. return RubyGzipFile.wrap(context, instance, io, block);
  43438. }
  43439. public RubyGzipReader(Ruby runtime, RubyClass type) {
  43440. super(runtime, type);
  43441. }
  43442. private int line;
  43443. private InputStream io;
  43444. @JRubyMethod(name = "initialize", required = 1, frame = true, visibility = Visibility.PRIVATE)
  43445. public IRubyObject initialize(IRubyObject io, Block unusedBlock) {
  43446. realIo = io;
  43447. try {
  43448. this.io = new GZIPInputStream(new IOInputStream(io));
  43449. } catch (IOException e) {
  43450. Ruby runtime = io.getRuntime();
  43451. RubyClass errorClass = runtime.fastGetModule("Zlib").fastGetClass("GzipReader").fastGetClass("Error");
  43452. throw new RaiseException(RubyException.newException(runtime, errorClass, e.getMessage()));
  43453. }
  43454. line = 1;
  43455. return this;
  43456. }
  43457. @JRubyMethod(name = "rewind")
  43458. public IRubyObject rewind() {
  43459. return getRuntime().getNil();
  43460. }
  43461. @JRubyMethod(name = "lineno")
  43462. public IRubyObject lineno() {
  43463. return getRuntime().newFixnum(line);
  43464. }
  43465. @JRubyMethod(name = "readline", writes = FrameField.LASTLINE)
  43466. public IRubyObject readline(ThreadContext context) throws IOException {
  43467. IRubyObject dst = gets(context, new IRubyObject[0]);
  43468. if (dst.isNil()) {
  43469. throw getRuntime().newEOFError();
  43470. }
  43471. return dst;
  43472. }
  43473. public IRubyObject internalGets(IRubyObject[] args) throws IOException {
  43474. ByteList sep = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
  43475. if (args.length > 0) {
  43476. sep = args[0].convertToString().getByteList();
  43477. }
  43478. return internalSepGets(sep);
  43479. }
  43480. private IRubyObject internalSepGets(ByteList sep) throws IOException {
  43481. ByteList result = new ByteList();
  43482. int ce = io.read();
  43483. while (ce != -1 && sep.indexOf(ce) == -1) {
  43484. result.append((byte)ce);
  43485. ce = io.read();
  43486. }
  43487. line++;
  43488. result.append(sep);
  43489. return RubyString.newString(getRuntime(),result);
  43490. }
  43491. @JRubyMethod(name = "gets", optional = 1, writes = FrameField.LASTLINE)
  43492. public IRubyObject gets(ThreadContext context, IRubyObject[] args) throws IOException {
  43493. IRubyObject result = internalGets(args);
  43494. if (!result.isNil()) {
  43495. context.getCurrentFrame().setLastLine(result);
  43496. }
  43497. return result;
  43498. }
  43499. private final static int BUFF_SIZE = 4096;
  43500. @JRubyMethod(name = "read", optional = 1)
  43501. public IRubyObject read(IRubyObject[] args) throws IOException {
  43502. if (args.length == 0 || args[0].isNil()) {
  43503. ByteList val = new ByteList(10);
  43504. byte[] buffer = new byte[BUFF_SIZE];
  43505. int read = io.read(buffer);
  43506. while (read != -1) {
  43507. val.append(buffer,0,read);
  43508. read = io.read(buffer);
  43509. }
  43510. return RubyString.newString(getRuntime(),val);
  43511. }
  43512. int len = RubyNumeric.fix2int(args[0]);
  43513. if (len < 0) {
  43514. throw getRuntime().newArgumentError("negative length " + len + " given");
  43515. } else if (len > 0) {
  43516. byte[] buffer = new byte[len];
  43517. int toRead = len;
  43518. int offset = 0;
  43519. int read = 0;
  43520. while (toRead > 0) {
  43521. read = io.read(buffer,offset,toRead);
  43522. if (read == -1) {
  43523. break;
  43524. }
  43525. toRead -= read;
  43526. offset += read;
  43527. } // hmm...
  43528. return RubyString.newString(getRuntime(),new ByteList(buffer,0,len-toRead,false));
  43529. }
  43530. return RubyString.newEmptyString(getRuntime());
  43531. }
  43532. @JRubyMethod(name = "lineno=", required = 1)
  43533. public IRubyObject set_lineno(IRubyObject lineArg) {
  43534. line = RubyNumeric.fix2int(lineArg);
  43535. return lineArg;
  43536. }
  43537. @JRubyMethod(name = "pos")
  43538. public IRubyObject pos() {
  43539. return RubyFixnum.zero(getRuntime());
  43540. }
  43541. @JRubyMethod(name = "readchar")
  43542. public IRubyObject readchar() throws IOException {
  43543. int value = io.read();
  43544. if (value == -1) {
  43545. throw getRuntime().newEOFError();
  43546. }
  43547. return getRuntime().newFixnum(value);
  43548. }
  43549. @JRubyMethod(name = "getc")
  43550. public IRubyObject getc() throws IOException {
  43551. int value = io.read();
  43552. return value == -1 ? getRuntime().getNil() : getRuntime().newFixnum(value);
  43553. }
  43554. private boolean isEof() throws IOException {
  43555. return ((GZIPInputStream)io).available() != 1;
  43556. }
  43557. @JRubyMethod(name = "close")
  43558. public IRubyObject close() throws IOException {
  43559. if (!closed) {
  43560. io.close();
  43561. }
  43562. this.closed = true;
  43563. return getRuntime().getNil();
  43564. }
  43565. @JRubyMethod(name = "eof")
  43566. public IRubyObject eof() throws IOException {
  43567. return isEof() ? getRuntime().getTrue() : getRuntime().getFalse();
  43568. }
  43569. @JRubyMethod(name = "eof?")
  43570. public IRubyObject eof_p() throws IOException {
  43571. return eof();
  43572. }
  43573. @JRubyMethod(name = "unused")
  43574. public IRubyObject unused() {
  43575. return getRuntime().getNil();
  43576. }
  43577. @JRubyMethod(name = "tell")
  43578. public IRubyObject tell() {
  43579. return getRuntime().getNil();
  43580. }
  43581. @JRubyMethod(name = "each", optional = 1, frame = true)
  43582. public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) throws IOException {
  43583. ByteList sep = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
  43584. if (args.length > 0 && !args[0].isNil()) {
  43585. sep = args[0].convertToString().getByteList();
  43586. }
  43587. while (!isEof()) {
  43588. block.yield(context, internalSepGets(sep));
  43589. }
  43590. return getRuntime().getNil();
  43591. }
  43592. @JRubyMethod(name = "ungetc", required = 1)
  43593. public IRubyObject ungetc(IRubyObject arg) {
  43594. return getRuntime().getNil();
  43595. }
  43596. @JRubyMethod(name = "readlines", optional = 1)
  43597. public IRubyObject readlines(IRubyObject[] args) throws IOException {
  43598. List<IRubyObject> array = new ArrayList<IRubyObject>();
  43599. if (args.length != 0 && args[0].isNil()) {
  43600. array.add(read(new IRubyObject[0]));
  43601. } else {
  43602. ByteList seperator = ((RubyString)getRuntime().getGlobalVariables().get("$/")).getByteList();
  43603. if (args.length > 0) {
  43604. seperator = args[0].convertToString().getByteList();
  43605. }
  43606. while (!isEof()) {
  43607. array.add(internalSepGets(seperator));
  43608. }
  43609. }
  43610. return getRuntime().newArray(array);
  43611. }
  43612. @JRubyMethod(name = "each_byte", frame = true)
  43613. public IRubyObject each_byte(ThreadContext context, Block block) throws IOException {
  43614. int value = io.read();
  43615. while (value != -1) {
  43616. block.yield(context, getRuntime().newFixnum(value));
  43617. value = io.read();
  43618. }
  43619. return getRuntime().getNil();
  43620. }
  43621. }
  43622. @JRubyClass(name="Zlib::GzipWriter", parent="Zlib::GzipFile")
  43623. public static class RubyGzipWriter extends RubyGzipFile {
  43624. protected static final ObjectAllocator GZIPWRITER_ALLOCATOR = new ObjectAllocator() {
  43625. public IRubyObject allocate(Ruby runtime, RubyClass klass) {
  43626. return new RubyGzipWriter(runtime, klass);
  43627. }
  43628. };
  43629. @JRubyMethod(name = "new", rest = true, frame = true, meta = true)
  43630. public static RubyGzipWriter newGzipWriter(IRubyObject recv, IRubyObject[] args, Block block) {
  43631. RubyClass klass = (RubyClass)recv;
  43632. RubyGzipWriter result = (RubyGzipWriter)klass.allocate();
  43633. result.callInit(args, block);
  43634. return result;
  43635. }
  43636. @JRubyMethod(name = "open", required = 1, optional = 2, frame = true, meta = true)
  43637. public static IRubyObject open(final ThreadContext context, IRubyObject recv, IRubyObject[] args, Block block) throws IOException {
  43638. Ruby runtime = recv.getRuntime();
  43639. IRubyObject level = runtime.getNil();
  43640. IRubyObject strategy = runtime.getNil();
  43641. if (args.length > 1) {
  43642. level = args[1];
  43643. if (args.length > 2) strategy = args[2];
  43644. }
  43645. IRubyObject io = RuntimeHelpers.invoke(context, runtime.getFile(), "open", args[0], runtime.newString("wb"));
  43646. RubyGzipFile instance = newGzipWriter(recv, new IRubyObject[]{io, level, strategy}, Block.NULL_BLOCK);
  43647. return RubyGzipFile.wrap(context, instance, io, block);
  43648. }
  43649. public RubyGzipWriter(Ruby runtime, RubyClass type) {
  43650. super(runtime, type);
  43651. }
  43652. private GZIPOutputStream io;
  43653. @JRubyMethod(name = "initialize", required = 1, rest = true, frame = true, visibility = Visibility.PRIVATE)
  43654. public IRubyObject initialize2(IRubyObject[] args, Block unusedBlock) throws IOException {
  43655. realIo = (RubyObject)args[0];
  43656. this.io = new GZIPOutputStream(new IOOutputStream(args[0]));
  43657. return this;
  43658. }
  43659. @JRubyMethod(name = "close")
  43660. public IRubyObject close() throws IOException {
  43661. if (!closed) {
  43662. io.close();
  43663. }
  43664. this.closed = true;
  43665. return getRuntime().getNil();
  43666. }
  43667. @JRubyMethod(name = "append", required = 1)
  43668. public IRubyObject append(IRubyObject p1) throws IOException {
  43669. this.write(p1);
  43670. return this;
  43671. }
  43672. @JRubyMethod(name = "printf", required = 1, rest = true)
  43673. public IRubyObject printf(ThreadContext context, IRubyObject[] args) throws IOException {
  43674. write(RubyKernel.sprintf(context, this, args));
  43675. return context.getRuntime().getNil();
  43676. }
  43677. @JRubyMethod(name = "print", rest = true)
  43678. public IRubyObject print(IRubyObject[] args) throws IOException {
  43679. if (args.length != 0) {
  43680. for (int i = 0, j = args.length; i < j; i++) {
  43681. write(args[i]);
  43682. }
  43683. }
  43684. IRubyObject sep = getRuntime().getGlobalVariables().get("$\\");
  43685. if (!sep.isNil()) {
  43686. write(sep);
  43687. }
  43688. return getRuntime().getNil();
  43689. }
  43690. @JRubyMethod(name = "pos")
  43691. public IRubyObject pos() {
  43692. return getRuntime().getNil();
  43693. }
  43694. @JRubyMethod(name = "orig_name=", required = 1)
  43695. public IRubyObject set_orig_name(IRubyObject ignored) {
  43696. return getRuntime().getNil();
  43697. }
  43698. @JRubyMethod(name = "comment=", required = 1)
  43699. public IRubyObject set_comment(IRubyObject ignored) {
  43700. return getRuntime().getNil();
  43701. }
  43702. @JRubyMethod(name = "putc", required = 1)
  43703. public IRubyObject putc(IRubyObject p1) throws IOException {
  43704. io.write(RubyNumeric.fix2int(p1));
  43705. return p1;
  43706. }
  43707. @JRubyMethod(name = "puts", rest = true)
  43708. public IRubyObject puts(ThreadContext context, IRubyObject[] args) throws IOException {
  43709. RubyStringIO sio = (RubyStringIO)getRuntime().fastGetClass("StringIO").newInstance(context, new IRubyObject[0], Block.NULL_BLOCK);
  43710. sio.puts(context, args);
  43711. write(sio.string());
  43712. return getRuntime().getNil();
  43713. }
  43714. public IRubyObject finish() throws IOException {
  43715. if (!finished) {
  43716. io.finish();
  43717. }
  43718. finished = true;
  43719. return realIo;
  43720. }
  43721. @JRubyMethod(name = "flush", optional = 1)
  43722. public IRubyObject flush(IRubyObject[] args) throws IOException {
  43723. if (args.length == 0 || args[0].isNil() || RubyNumeric.fix2int(args[0]) != 0) { // Zlib::NO_FLUSH
  43724. io.flush();
  43725. }
  43726. return getRuntime().getNil();
  43727. }
  43728. @JRubyMethod(name = "mtime=", required = 1)
  43729. public IRubyObject set_mtime(IRubyObject ignored) {
  43730. return getRuntime().getNil();
  43731. }
  43732. @JRubyMethod(name = "tell")
  43733. public IRubyObject tell() {
  43734. return getRuntime().getNil();
  43735. }
  43736. @JRubyMethod(name = "write", required = 1)
  43737. public IRubyObject write(IRubyObject p1) throws IOException {
  43738. ByteList bytes = p1.convertToString().getByteList();
  43739. io.write(bytes.unsafeBytes(), bytes.begin(), bytes.length());
  43740. return getRuntime().newFixnum(bytes.length());
  43741. }
  43742. }
  43743. }
  43744. /***** BEGIN LICENSE BLOCK *****
  43745. * Version: CPL 1.0/GPL 2.0/LGPL 2.1
  43746. *
  43747. * The contents of this file are subject to the Common Public
  43748. * License Version 1.0 (the "License"); you may not use this file
  43749. * except in compliance with the License. You may obtain a copy of
  43750. * the License at http://www.eclipse.org/legal/cpl-v10.html
  43751. *
  43752. * Software distributed under the License is distributed on an "AS
  43753. * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  43754. * implied. See the License for the specific language governing
  43755. * rights and limitations under the License.
  43756. *
  43757. * Copyright (C) 2002 Jan Arne Petersen <jpetersen@uni-bonn.de>
  43758. * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
  43759. * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
  43760. * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
  43761. *
  43762. * Alternatively, the contents of this file may be used under the terms of
  43763. * either of the GNU General Public License Version 2 or later (the "GPL"),
  43764. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  43765. * in which case the provisions of the GPL or the LGPL are applicable instead
  43766. * of those above. If you wish to allow use of your version of this file only
  43767. * under the terms of either the GPL or the LGPL, and not to allow others to
  43768. * use your version of this file under the terms of the CPL, indicate your
  43769. * decision by deleting the provisions above and replace them with the notice
  43770. * and other provisions required by the GPL or the LGPL. If you do not delete
  43771. * the provisions above, a recipient may use your version of this file under
  43772. * the terms of any one of the CPL, the GPL or the LGPL.
  43773. ***** END LICENSE BLOCK *****/
  43774. package org.jruby;
  43775. import org.jruby.runtime.Arity;
  43776. import org.jruby.runtime.Block;
  43777. import org.jruby.runtime.builtin.IRubyObject;
  43778. import org.jruby.runtime.callback.Callback;
  43779. /**
  43780. *
  43781. * @author jpetersen
  43782. */
  43783. public final class TopSelfFactory {
  43784. /**
  43785. * Constructor for TopSelfFactory.
  43786. */
  43787. private TopSelfFactory() {
  43788. super();
  43789. }
  43790. public static IRubyObject createTopSelf(final Ruby runtime) {
  43791. IRubyObject topSelf = new RubyObject(runtime, runtime.getObject());
  43792. topSelf.getSingletonClass().defineFastMethod("to_s", new Callback() {
  43793. /**
  43794. * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
  43795. */
  43796. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
  43797. return runtime.newString("main");
  43798. }
  43799. /**
  43800. * @see org.jruby.runtime.callback.Callback#getArity()
  43801. */
  43802. public Arity getArity() {
  43803. return Arity.noArguments();
  43804. }
  43805. });
  43806. // The following three methods must be defined fast, since they expect to modify the current frame
  43807. // (i.e. they expect no frame will be allocated for them). JRUBY-1185.
  43808. topSelf.getSingletonClass().defineFastPrivateMethod("include", new Callback() {
  43809. /**
  43810. * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
  43811. */
  43812. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block block) {
  43813. runtime.secure(4);
  43814. return runtime.getObject().include(args);
  43815. }
  43816. /**
  43817. * @see org.jruby.runtime.callback.Callback#getArity()
  43818. */
  43819. public Arity getArity() {
  43820. return Arity.optional();
  43821. }
  43822. });
  43823. topSelf.getSingletonClass().defineFastPrivateMethod("public", new Callback() {
  43824. /**
  43825. * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
  43826. */
  43827. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  43828. return runtime.getObject().rbPublic(recv.getRuntime().getCurrentContext(), args);
  43829. }
  43830. /**
  43831. * @see org.jruby.runtime.callback.Callback#getArity()
  43832. */
  43833. public Arity getArity() {
  43834. return Arity.optional();
  43835. }
  43836. });
  43837. topSelf.getSingletonClass().defineFastPrivateMethod("private", new Callback() {
  43838. /**
  43839. * @see org.jruby.runtime.callback.Callback#execute(IRubyObject, IRubyObject[])
  43840. */
  43841. public IRubyObject execute(IRubyObject recv, IRubyObject[] args, Block unusedBlock) {
  43842. return runtime.getObject().rbPrivate(recv.getRuntime().getCurrentContext(), args);
  43843. }
  43844. /**
  43845. * @see org.jruby.runtime.callback.Callback#getArity()
  43846. */
  43847. public Arity getArity() {
  43848. return Arity.optional();
  43849. }
  43850. });
  43851. return topSelf;
  43852. }
  43853. }