json 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of json might be problematic. Click here for more details.

Files changed (107) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +23 -0
  4. data/CHANGES.md +391 -0
  5. data/Gemfile +14 -0
  6. data/README-json-jruby.md +33 -0
  7. data/README.md +409 -0
  8. data/Rakefile +408 -0
  9. data/VERSION +1 -0
  10. data/diagrams/.keep +0 -0
  11. data/ext/json/ext/fbuffer/fbuffer.h +187 -0
  12. data/ext/json/ext/generator/depend +1 -0
  13. data/ext/json/ext/generator/extconf.rb +4 -0
  14. data/ext/json/ext/generator/generator.c +1444 -0
  15. data/ext/json/ext/generator/generator.h +171 -0
  16. data/ext/json/ext/parser/depend +1 -0
  17. data/ext/json/ext/parser/extconf.rb +6 -0
  18. data/ext/json/ext/parser/parser.c +2131 -0
  19. data/ext/json/ext/parser/parser.h +91 -0
  20. data/ext/json/ext/parser/parser.rl +891 -0
  21. data/ext/json/extconf.rb +2 -0
  22. data/install.rb +23 -0
  23. data/java/src/json/ext/ByteListTranscoder.java +166 -0
  24. data/java/src/json/ext/Generator.java +443 -0
  25. data/java/src/json/ext/GeneratorMethods.java +231 -0
  26. data/java/src/json/ext/GeneratorService.java +42 -0
  27. data/java/src/json/ext/GeneratorState.java +490 -0
  28. data/java/src/json/ext/OptionsReader.java +113 -0
  29. data/java/src/json/ext/Parser.java +2362 -0
  30. data/java/src/json/ext/Parser.rl +893 -0
  31. data/java/src/json/ext/ParserService.java +34 -0
  32. data/java/src/json/ext/RuntimeInfo.java +116 -0
  33. data/java/src/json/ext/StringDecoder.java +166 -0
  34. data/java/src/json/ext/StringEncoder.java +111 -0
  35. data/java/src/json/ext/Utils.java +88 -0
  36. data/json-java.gemspec +38 -0
  37. data/json.gemspec +0 -0
  38. data/json_pure.gemspec +38 -0
  39. data/lib/json.rb +63 -0
  40. data/lib/json/add/bigdecimal.rb +29 -0
  41. data/lib/json/add/complex.rb +29 -0
  42. data/lib/json/add/core.rb +12 -0
  43. data/lib/json/add/date.rb +34 -0
  44. data/lib/json/add/date_time.rb +50 -0
  45. data/lib/json/add/exception.rb +31 -0
  46. data/lib/json/add/ostruct.rb +31 -0
  47. data/lib/json/add/range.rb +29 -0
  48. data/lib/json/add/rational.rb +28 -0
  49. data/lib/json/add/regexp.rb +30 -0
  50. data/lib/json/add/set.rb +29 -0
  51. data/lib/json/add/struct.rb +30 -0
  52. data/lib/json/add/symbol.rb +25 -0
  53. data/lib/json/add/time.rb +38 -0
  54. data/lib/json/common.rb +456 -0
  55. data/lib/json/ext.rb +15 -0
  56. data/lib/json/ext/.keep +0 -0
  57. data/lib/json/generic_object.rb +71 -0
  58. data/lib/json/pure.rb +15 -0
  59. data/lib/json/pure/generator.rb +458 -0
  60. data/lib/json/pure/parser.rb +311 -0
  61. data/lib/json/version.rb +9 -0
  62. data/references/rfc7159.txt +899 -0
  63. data/tests/fixtures/fail10.json +1 -0
  64. data/tests/fixtures/fail11.json +1 -0
  65. data/tests/fixtures/fail12.json +1 -0
  66. data/tests/fixtures/fail13.json +1 -0
  67. data/tests/fixtures/fail14.json +1 -0
  68. data/tests/fixtures/fail18.json +1 -0
  69. data/tests/fixtures/fail19.json +1 -0
  70. data/tests/fixtures/fail2.json +1 -0
  71. data/tests/fixtures/fail20.json +1 -0
  72. data/tests/fixtures/fail21.json +1 -0
  73. data/tests/fixtures/fail22.json +1 -0
  74. data/tests/fixtures/fail23.json +1 -0
  75. data/tests/fixtures/fail24.json +1 -0
  76. data/tests/fixtures/fail25.json +1 -0
  77. data/tests/fixtures/fail27.json +2 -0
  78. data/tests/fixtures/fail28.json +2 -0
  79. data/tests/fixtures/fail3.json +1 -0
  80. data/tests/fixtures/fail4.json +1 -0
  81. data/tests/fixtures/fail5.json +1 -0
  82. data/tests/fixtures/fail6.json +1 -0
  83. data/tests/fixtures/fail7.json +1 -0
  84. data/tests/fixtures/fail8.json +1 -0
  85. data/tests/fixtures/fail9.json +1 -0
  86. data/tests/fixtures/obsolete_fail1.json +1 -0
  87. data/tests/fixtures/pass1.json +56 -0
  88. data/tests/fixtures/pass15.json +1 -0
  89. data/tests/fixtures/pass16.json +1 -0
  90. data/tests/fixtures/pass17.json +1 -0
  91. data/tests/fixtures/pass2.json +1 -0
  92. data/tests/fixtures/pass26.json +1 -0
  93. data/tests/fixtures/pass3.json +6 -0
  94. data/tests/json_addition_test.rb +203 -0
  95. data/tests/json_common_interface_test.rb +126 -0
  96. data/tests/json_encoding_test.rb +107 -0
  97. data/tests/json_ext_parser_test.rb +15 -0
  98. data/tests/json_fixtures_test.rb +32 -0
  99. data/tests/json_generator_test.rb +377 -0
  100. data/tests/json_generic_object_test.rb +82 -0
  101. data/tests/json_parser_test.rb +472 -0
  102. data/tests/json_string_matching_test.rb +38 -0
  103. data/tests/test_helper.rb +17 -0
  104. data/tools/diff.sh +18 -0
  105. data/tools/fuzz.rb +131 -0
  106. data/tools/server.rb +62 -0
  107. metadata +185 -0
@@ -0,0 +1,34 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
6
+ package json.ext;
7
+
8
+ import java.io.IOException;
9
+ import java.lang.ref.WeakReference;
10
+
11
+ import org.jruby.Ruby;
12
+ import org.jruby.RubyClass;
13
+ import org.jruby.RubyModule;
14
+ import org.jruby.runtime.load.BasicLibraryService;
15
+
16
+ /**
17
+ * The service invoked by JRuby's {@link org.jruby.runtime.load.LoadService LoadService}.
18
+ * Defines the <code>JSON::Ext::Parser</code> class.
19
+ * @author mernen
20
+ */
21
+ public class ParserService implements BasicLibraryService {
22
+ public boolean basicLoad(Ruby runtime) throws IOException {
23
+ runtime.getLoadService().require("json/common");
24
+ RuntimeInfo info = RuntimeInfo.initRuntime(runtime);
25
+
26
+ info.jsonModule = new WeakReference<RubyModule>(runtime.defineModule("JSON"));
27
+ RubyModule jsonExtModule = info.jsonModule.get().defineModuleUnder("Ext");
28
+ RubyClass parserClass =
29
+ jsonExtModule.defineClassUnder("Parser", runtime.getObject(),
30
+ Parser.ALLOCATOR);
31
+ parserClass.defineAnnotatedMethods(Parser.class);
32
+ return true;
33
+ }
34
+ }
@@ -0,0 +1,116 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
6
+ package json.ext;
7
+
8
+ import java.lang.ref.WeakReference;
9
+ import java.util.HashMap;
10
+ import java.util.Map;
11
+ import java.util.WeakHashMap;
12
+ import org.jruby.Ruby;
13
+ import org.jruby.RubyClass;
14
+ import org.jruby.RubyEncoding;
15
+ import org.jruby.RubyModule;
16
+ import org.jruby.runtime.ThreadContext;
17
+ import org.jruby.runtime.builtin.IRubyObject;
18
+
19
+
20
+ final class RuntimeInfo {
21
+ // since the vast majority of cases runs just one runtime,
22
+ // we optimize for that
23
+ private static WeakReference<Ruby> runtime1 = new WeakReference<Ruby>(null);
24
+ private static RuntimeInfo info1;
25
+ // store remaining runtimes here (does not include runtime1)
26
+ private static Map<Ruby, RuntimeInfo> runtimes;
27
+
28
+ // these fields are filled by the service loaders
29
+ // Use WeakReferences so that RuntimeInfo doesn't indirectly hold a hard reference to
30
+ // the Ruby runtime object, which would cause memory leaks in the runtimes map above.
31
+ /** JSON */
32
+ WeakReference<RubyModule> jsonModule;
33
+ /** JSON::Ext::Generator::GeneratorMethods::String::Extend */
34
+ WeakReference<RubyModule> stringExtendModule;
35
+ /** JSON::Ext::Generator::State */
36
+ WeakReference<RubyClass> generatorStateClass;
37
+ /** JSON::SAFE_STATE_PROTOTYPE */
38
+ WeakReference<GeneratorState> safeStatePrototype;
39
+
40
+ final WeakReference<RubyEncoding> utf8;
41
+ final WeakReference<RubyEncoding> ascii8bit;
42
+ // other encodings
43
+ private final Map<String, WeakReference<RubyEncoding>> encodings;
44
+
45
+ private RuntimeInfo(Ruby runtime) {
46
+ RubyClass encodingClass = runtime.getEncoding();
47
+ if (encodingClass == null) { // 1.8 mode
48
+ utf8 = ascii8bit = null;
49
+ encodings = null;
50
+ } else {
51
+ ThreadContext context = runtime.getCurrentContext();
52
+
53
+ utf8 = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
54
+ encodingClass, runtime.newString("utf-8")));
55
+ ascii8bit = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
56
+ encodingClass, runtime.newString("ascii-8bit")));
57
+ encodings = new HashMap<String, WeakReference<RubyEncoding>>();
58
+ }
59
+ }
60
+
61
+ static RuntimeInfo initRuntime(Ruby runtime) {
62
+ synchronized (RuntimeInfo.class) {
63
+ if (runtime1.get() == runtime) {
64
+ return info1;
65
+ } else if (runtime1.get() == null) {
66
+ runtime1 = new WeakReference<Ruby>(runtime);
67
+ info1 = new RuntimeInfo(runtime);
68
+ return info1;
69
+ } else {
70
+ if (runtimes == null) {
71
+ runtimes = new WeakHashMap<Ruby, RuntimeInfo>(1);
72
+ }
73
+ RuntimeInfo cache = runtimes.get(runtime);
74
+ if (cache == null) {
75
+ cache = new RuntimeInfo(runtime);
76
+ runtimes.put(runtime, cache);
77
+ }
78
+ return cache;
79
+ }
80
+ }
81
+ }
82
+
83
+ public static RuntimeInfo forRuntime(Ruby runtime) {
84
+ synchronized (RuntimeInfo.class) {
85
+ if (runtime1.get() == runtime) return info1;
86
+ RuntimeInfo cache = null;
87
+ if (runtimes != null) cache = runtimes.get(runtime);
88
+ assert cache != null : "Runtime given has not initialized JSON::Ext";
89
+ return cache;
90
+ }
91
+ }
92
+
93
+ public RubyEncoding getEncoding(ThreadContext context, String name) {
94
+ synchronized (encodings) {
95
+ WeakReference<RubyEncoding> encoding = encodings.get(name);
96
+ if (encoding == null) {
97
+ Ruby runtime = context.getRuntime();
98
+ encoding = new WeakReference<RubyEncoding>((RubyEncoding)RubyEncoding.find(context,
99
+ runtime.getEncoding(), runtime.newString(name)));
100
+ encodings.put(name, encoding);
101
+ }
102
+ return encoding.get();
103
+ }
104
+ }
105
+
106
+ public GeneratorState getSafeStatePrototype(ThreadContext context) {
107
+ if (safeStatePrototype == null) {
108
+ IRubyObject value = jsonModule.get().getConstant("SAFE_STATE_PROTOTYPE");
109
+ if (!(value instanceof GeneratorState)) {
110
+ throw context.getRuntime().newTypeError(value, generatorStateClass.get());
111
+ }
112
+ safeStatePrototype = new WeakReference<GeneratorState>((GeneratorState)value);
113
+ }
114
+ return safeStatePrototype.get();
115
+ }
116
+ }
@@ -0,0 +1,166 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
6
+ package json.ext;
7
+
8
+ import org.jruby.exceptions.RaiseException;
9
+ import org.jruby.runtime.ThreadContext;
10
+ import org.jruby.util.ByteList;
11
+
12
+ /**
13
+ * A decoder that reads a JSON-encoded string from the given sources and
14
+ * returns its decoded form on a new ByteList. Escaped Unicode characters
15
+ * are encoded as UTF-8.
16
+ */
17
+ final class StringDecoder extends ByteListTranscoder {
18
+ /**
19
+ * Stores the offset of the high surrogate when reading a surrogate pair,
20
+ * or -1 when not.
21
+ */
22
+ private int surrogatePairStart = -1;
23
+
24
+ // Array used for writing multi-byte characters into the buffer at once
25
+ private final byte[] aux = new byte[4];
26
+
27
+ StringDecoder(ThreadContext context) {
28
+ super(context);
29
+ }
30
+
31
+ ByteList decode(ByteList src, int start, int end) {
32
+ ByteList out = new ByteList(end - start);
33
+ out.setEncoding(src.getEncoding());
34
+ init(src, start, end, out);
35
+ while (hasNext()) {
36
+ handleChar(readUtf8Char());
37
+ }
38
+ quoteStop(pos);
39
+ return out;
40
+ }
41
+
42
+ private void handleChar(int c) {
43
+ if (c == '\\') {
44
+ quoteStop(charStart);
45
+ handleEscapeSequence();
46
+ } else {
47
+ quoteStart();
48
+ }
49
+ }
50
+
51
+ private void handleEscapeSequence() {
52
+ ensureMin(1);
53
+ switch (readUtf8Char()) {
54
+ case 'b':
55
+ append('\b');
56
+ break;
57
+ case 'f':
58
+ append('\f');
59
+ break;
60
+ case 'n':
61
+ append('\n');
62
+ break;
63
+ case 'r':
64
+ append('\r');
65
+ break;
66
+ case 't':
67
+ append('\t');
68
+ break;
69
+ case 'u':
70
+ ensureMin(4);
71
+ int cp = readHex();
72
+ if (Character.isHighSurrogate((char)cp)) {
73
+ handleLowSurrogate((char)cp);
74
+ } else if (Character.isLowSurrogate((char)cp)) {
75
+ // low surrogate with no high surrogate
76
+ throw invalidUtf8();
77
+ } else {
78
+ writeUtf8Char(cp);
79
+ }
80
+ break;
81
+ default: // '\\', '"', '/'...
82
+ quoteStart();
83
+ }
84
+ }
85
+
86
+ private void handleLowSurrogate(char highSurrogate) {
87
+ surrogatePairStart = charStart;
88
+ ensureMin(1);
89
+ int lowSurrogate = readUtf8Char();
90
+
91
+ if (lowSurrogate == '\\') {
92
+ ensureMin(5);
93
+ if (readUtf8Char() != 'u') throw invalidUtf8();
94
+ lowSurrogate = readHex();
95
+ }
96
+
97
+ if (Character.isLowSurrogate((char)lowSurrogate)) {
98
+ writeUtf8Char(Character.toCodePoint(highSurrogate,
99
+ (char)lowSurrogate));
100
+ surrogatePairStart = -1;
101
+ } else {
102
+ throw invalidUtf8();
103
+ }
104
+ }
105
+
106
+ private void writeUtf8Char(int codePoint) {
107
+ if (codePoint < 0x80) {
108
+ append(codePoint);
109
+ } else if (codePoint < 0x800) {
110
+ aux[0] = (byte)(0xc0 | (codePoint >>> 6));
111
+ aux[1] = tailByte(codePoint & 0x3f);
112
+ append(aux, 0, 2);
113
+ } else if (codePoint < 0x10000) {
114
+ aux[0] = (byte)(0xe0 | (codePoint >>> 12));
115
+ aux[1] = tailByte(codePoint >>> 6);
116
+ aux[2] = tailByte(codePoint);
117
+ append(aux, 0, 3);
118
+ } else {
119
+ aux[0] = (byte)(0xf0 | codePoint >>> 18);
120
+ aux[1] = tailByte(codePoint >>> 12);
121
+ aux[2] = tailByte(codePoint >>> 6);
122
+ aux[3] = tailByte(codePoint);
123
+ append(aux, 0, 4);
124
+ }
125
+ }
126
+
127
+ private byte tailByte(int value) {
128
+ return (byte)(0x80 | (value & 0x3f));
129
+ }
130
+
131
+ /**
132
+ * Reads a 4-digit unsigned hexadecimal number from the source.
133
+ */
134
+ private int readHex() {
135
+ int numberStart = pos;
136
+ int result = 0;
137
+ int length = 4;
138
+ for (int i = 0; i < length; i++) {
139
+ int digit = readUtf8Char();
140
+ int digitValue;
141
+ if (digit >= '0' && digit <= '9') {
142
+ digitValue = digit - '0';
143
+ } else if (digit >= 'a' && digit <= 'f') {
144
+ digitValue = 10 + digit - 'a';
145
+ } else if (digit >= 'A' && digit <= 'F') {
146
+ digitValue = 10 + digit - 'A';
147
+ } else {
148
+ throw new NumberFormatException("Invalid base 16 number "
149
+ + src.subSequence(numberStart, numberStart + length));
150
+ }
151
+ result = result * 16 + digitValue;
152
+ }
153
+ return result;
154
+ }
155
+
156
+ @Override
157
+ protected RaiseException invalidUtf8() {
158
+ ByteList message = new ByteList(
159
+ ByteList.plain("partial character in source, " +
160
+ "but hit end near "));
161
+ int start = surrogatePairStart != -1 ? surrogatePairStart : charStart;
162
+ message.append(src, start, srcEnd - start);
163
+ return Utils.newException(context, Utils.M_PARSER_ERROR,
164
+ context.getRuntime().newString(message));
165
+ }
166
+ }
@@ -0,0 +1,111 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
6
+ package json.ext;
7
+
8
+ import org.jruby.exceptions.RaiseException;
9
+ import org.jruby.runtime.ThreadContext;
10
+ import org.jruby.util.ByteList;
11
+
12
+ /**
13
+ * An encoder that reads from the given source and outputs its representation
14
+ * to another ByteList. The source string is fully checked for UTF-8 validity,
15
+ * and throws a GeneratorError if any problem is found.
16
+ */
17
+ final class StringEncoder extends ByteListTranscoder {
18
+ private final boolean asciiOnly;
19
+
20
+ // Escaped characters will reuse this array, to avoid new allocations
21
+ // or appending them byte-by-byte
22
+ private final byte[] aux =
23
+ new byte[] {/* First unicode character */
24
+ '\\', 'u', 0, 0, 0, 0,
25
+ /* Second unicode character (for surrogate pairs) */
26
+ '\\', 'u', 0, 0, 0, 0,
27
+ /* "\X" characters */
28
+ '\\', 0};
29
+ // offsets on the array above
30
+ private static final int ESCAPE_UNI1_OFFSET = 0;
31
+ private static final int ESCAPE_UNI2_OFFSET = ESCAPE_UNI1_OFFSET + 6;
32
+ private static final int ESCAPE_CHAR_OFFSET = ESCAPE_UNI2_OFFSET + 6;
33
+ /** Array used for code point decomposition in surrogates */
34
+ private final char[] utf16 = new char[2];
35
+
36
+ private static final byte[] HEX =
37
+ new byte[] {'0', '1', '2', '3', '4', '5', '6', '7',
38
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
39
+
40
+ StringEncoder(ThreadContext context, boolean asciiOnly) {
41
+ super(context);
42
+ this.asciiOnly = asciiOnly;
43
+ }
44
+
45
+ void encode(ByteList src, ByteList out) {
46
+ init(src, out);
47
+ append('"');
48
+ while (hasNext()) {
49
+ handleChar(readUtf8Char());
50
+ }
51
+ quoteStop(pos);
52
+ append('"');
53
+ }
54
+
55
+ private void handleChar(int c) {
56
+ switch (c) {
57
+ case '"':
58
+ case '\\':
59
+ escapeChar((char)c);
60
+ break;
61
+ case '\n':
62
+ escapeChar('n');
63
+ break;
64
+ case '\r':
65
+ escapeChar('r');
66
+ break;
67
+ case '\t':
68
+ escapeChar('t');
69
+ break;
70
+ case '\f':
71
+ escapeChar('f');
72
+ break;
73
+ case '\b':
74
+ escapeChar('b');
75
+ break;
76
+ default:
77
+ if (c >= 0x20 && c <= 0x7f ||
78
+ (c >= 0x80 && !asciiOnly)) {
79
+ quoteStart();
80
+ } else {
81
+ quoteStop(charStart);
82
+ escapeUtf8Char(c);
83
+ }
84
+ }
85
+ }
86
+
87
+ private void escapeChar(char c) {
88
+ quoteStop(charStart);
89
+ aux[ESCAPE_CHAR_OFFSET + 1] = (byte)c;
90
+ append(aux, ESCAPE_CHAR_OFFSET, 2);
91
+ }
92
+
93
+ private void escapeUtf8Char(int codePoint) {
94
+ int numChars = Character.toChars(codePoint, utf16, 0);
95
+ escapeCodeUnit(utf16[0], ESCAPE_UNI1_OFFSET + 2);
96
+ if (numChars > 1) escapeCodeUnit(utf16[1], ESCAPE_UNI2_OFFSET + 2);
97
+ append(aux, ESCAPE_UNI1_OFFSET, 6 * numChars);
98
+ }
99
+
100
+ private void escapeCodeUnit(char c, int auxOffset) {
101
+ for (int i = 0; i < 4; i++) {
102
+ aux[auxOffset + i] = HEX[(c >>> (12 - 4 * i)) & 0xf];
103
+ }
104
+ }
105
+
106
+ @Override
107
+ protected RaiseException invalidUtf8() {
108
+ return Utils.newException(context, Utils.M_GENERATOR_ERROR,
109
+ "source sequence is illegal/malformed utf-8");
110
+ }
111
+ }
@@ -0,0 +1,88 @@
1
+ /*
2
+ * This code is copyrighted work by Daniel Luz <dev at mernen dot com>.
3
+ *
4
+ * Distributed under the Ruby license: https://www.ruby-lang.org/en/about/license.txt
5
+ */
6
+ package json.ext;
7
+
8
+ import org.jruby.Ruby;
9
+ import org.jruby.RubyArray;
10
+ import org.jruby.RubyClass;
11
+ import org.jruby.RubyException;
12
+ import org.jruby.RubyHash;
13
+ import org.jruby.RubyString;
14
+ import org.jruby.exceptions.RaiseException;
15
+ import org.jruby.runtime.Block;
16
+ import org.jruby.runtime.ThreadContext;
17
+ import org.jruby.runtime.builtin.IRubyObject;
18
+ import org.jruby.util.ByteList;
19
+
20
+ /**
21
+ * Library of miscellaneous utility functions
22
+ */
23
+ final class Utils {
24
+ public static final String M_GENERATOR_ERROR = "GeneratorError";
25
+ public static final String M_NESTING_ERROR = "NestingError";
26
+ public static final String M_PARSER_ERROR = "ParserError";
27
+
28
+ private Utils() {
29
+ throw new RuntimeException();
30
+ }
31
+
32
+ /**
33
+ * Safe {@link RubyArray} type-checking.
34
+ * Returns the given object if it is an <code>Array</code>,
35
+ * or throws an exception if not.
36
+ * @param object The object to test
37
+ * @return The given object if it is an <code>Array</code>
38
+ * @throws RaiseException <code>TypeError</code> if the object is not
39
+ * of the expected type
40
+ */
41
+ static RubyArray ensureArray(IRubyObject object) throws RaiseException {
42
+ if (object instanceof RubyArray) return (RubyArray)object;
43
+ Ruby runtime = object.getRuntime();
44
+ throw runtime.newTypeError(object, runtime.getArray());
45
+ }
46
+
47
+ static RubyHash ensureHash(IRubyObject object) throws RaiseException {
48
+ if (object instanceof RubyHash) return (RubyHash)object;
49
+ Ruby runtime = object.getRuntime();
50
+ throw runtime.newTypeError(object, runtime.getHash());
51
+ }
52
+
53
+ static RubyString ensureString(IRubyObject object) throws RaiseException {
54
+ if (object instanceof RubyString) return (RubyString)object;
55
+ Ruby runtime = object.getRuntime();
56
+ throw runtime.newTypeError(object, runtime.getString());
57
+ }
58
+
59
+ static RaiseException newException(ThreadContext context,
60
+ String className, String message) {
61
+ return newException(context, className,
62
+ context.getRuntime().newString(message));
63
+ }
64
+
65
+ static RaiseException newException(ThreadContext context,
66
+ String className, RubyString message) {
67
+ RuntimeInfo info = RuntimeInfo.forRuntime(context.getRuntime());
68
+ RubyClass klazz = info.jsonModule.get().getClass(className);
69
+ RubyException excptn =
70
+ (RubyException)klazz.newInstance(context,
71
+ new IRubyObject[] {message}, Block.NULL_BLOCK);
72
+ return new RaiseException(excptn);
73
+ }
74
+
75
+ static byte[] repeat(ByteList a, int n) {
76
+ return repeat(a.unsafeBytes(), a.begin(), a.length(), n);
77
+ }
78
+
79
+ static byte[] repeat(byte[] a, int begin, int length, int n) {
80
+ if (length == 0) return ByteList.NULL_ARRAY;
81
+ int resultLen = length * n;
82
+ byte[] result = new byte[resultLen];
83
+ for (int pos = 0; pos < resultLen; pos += length) {
84
+ System.arraycopy(a, begin, result, pos, length);
85
+ }
86
+ return result;
87
+ }
88
+ }