Utils.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * Protocol Buffers - Google's data interchange format
  3. * Copyright 2014 Google Inc. All rights reserved.
  4. * https://developers.google.com/protocol-buffers/
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are
  8. * met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above
  13. * copyright notice, this list of conditions and the following disclaimer
  14. * in the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of Google Inc. nor the names of its
  17. * contributors may be used to endorse or promote products derived from
  18. * this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. package com.google.protobuf.jruby;
  33. import com.google.protobuf.ByteString;
  34. import com.google.protobuf.DescriptorProtos;
  35. import com.google.protobuf.Descriptors;
  36. import org.jcodings.Encoding;
  37. import org.jcodings.specific.ASCIIEncoding;
  38. import org.jcodings.specific.USASCIIEncoding;
  39. import org.jcodings.specific.UTF8Encoding;
  40. import org.jruby.*;
  41. import org.jruby.runtime.Block;
  42. import org.jruby.runtime.ThreadContext;
  43. import org.jruby.runtime.builtin.IRubyObject;
  44. import java.math.BigInteger;
  45. public class Utils {
  46. public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
  47. return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
  48. }
  49. public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) {
  50. return fieldTypeToRuby(context, type.name());
  51. }
  52. public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) {
  53. return fieldTypeToRuby(context, type.name());
  54. }
  55. private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
  56. return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
  57. }
  58. public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType,
  59. IRubyObject value, RubyModule typeClass) {
  60. Ruby runtime = context.runtime;
  61. Object val;
  62. switch(fieldType) {
  63. case INT32:
  64. case INT64:
  65. case UINT32:
  66. case UINT64:
  67. if (!isRubyNum(value)) {
  68. throw runtime.newTypeError("Expected number type for integral field.");
  69. }
  70. switch(fieldType) {
  71. case INT32:
  72. RubyNumeric.num2int(value);
  73. break;
  74. case INT64:
  75. RubyNumeric.num2long(value);
  76. break;
  77. case UINT32:
  78. num2uint(value);
  79. break;
  80. default:
  81. num2ulong(context.runtime, value);
  82. break;
  83. }
  84. checkIntTypePrecision(context, fieldType, value);
  85. break;
  86. case FLOAT:
  87. if (!isRubyNum(value))
  88. throw runtime.newTypeError("Expected number type for float field.");
  89. break;
  90. case DOUBLE:
  91. if (!isRubyNum(value))
  92. throw runtime.newTypeError("Expected number type for double field.");
  93. break;
  94. case BOOL:
  95. if (!(value instanceof RubyBoolean))
  96. throw runtime.newTypeError("Invalid argument for boolean field.");
  97. break;
  98. case BYTES:
  99. case STRING:
  100. validateStringEncoding(context.runtime, fieldType, value);
  101. break;
  102. case MESSAGE:
  103. if (value.getMetaClass() != typeClass) {
  104. throw runtime.newTypeError(value, typeClass);
  105. }
  106. break;
  107. case ENUM:
  108. if (value instanceof RubySymbol) {
  109. Descriptors.EnumDescriptor enumDescriptor =
  110. ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor();
  111. val = enumDescriptor.findValueByName(value.asJavaString());
  112. if (val == null)
  113. throw runtime.newRangeError("Enum value " + value + " is not found.");
  114. } else if(!isRubyNum(value)) {
  115. throw runtime.newTypeError("Expected number or symbol type for enum field.");
  116. }
  117. break;
  118. default:
  119. break;
  120. }
  121. }
  122. public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) {
  123. Ruby runtime = context.runtime;
  124. switch (fieldType) {
  125. case INT32:
  126. return runtime.newFixnum((Integer) value);
  127. case INT64:
  128. return runtime.newFixnum((Long) value);
  129. case UINT32:
  130. return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
  131. case UINT64:
  132. long ret = (Long) value;
  133. return ret >= 0 ? runtime.newFixnum(ret) :
  134. RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
  135. case FLOAT:
  136. return runtime.newFloat((Float) value);
  137. case DOUBLE:
  138. return runtime.newFloat((Double) value);
  139. case BOOL:
  140. return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
  141. case BYTES:
  142. return runtime.newString(((ByteString) value).toStringUtf8());
  143. case STRING:
  144. return runtime.newString(value.toString());
  145. default:
  146. return runtime.getNil();
  147. }
  148. }
  149. public static int num2uint(IRubyObject value) {
  150. long longVal = RubyNumeric.num2long(value);
  151. if (longVal > UINT_MAX)
  152. throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
  153. long num = longVal;
  154. if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
  155. // encode to UINT32
  156. num = (-longVal ^ (-1l >>> 32) ) + 1;
  157. RubyNumeric.checkInt(value, num);
  158. return (int) num;
  159. }
  160. public static long num2ulong(Ruby runtime, IRubyObject value) {
  161. if (value instanceof RubyFloat) {
  162. RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
  163. return RubyBignum.big2ulong(bignum);
  164. } else if (value instanceof RubyBignum) {
  165. return RubyBignum.big2ulong((RubyBignum) value);
  166. } else {
  167. return RubyNumeric.num2long(value);
  168. }
  169. }
  170. public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
  171. if (!(value instanceof RubyString))
  172. throw runtime.newTypeError("Invalid argument for string field.");
  173. Encoding encoding = ((RubyString) value).getEncoding();
  174. switch(type) {
  175. case BYTES:
  176. if (encoding != ASCIIEncoding.INSTANCE)
  177. throw runtime.newTypeError("Encoding for bytes fields" +
  178. " must be \"ASCII-8BIT\", but was " + encoding);
  179. break;
  180. case STRING:
  181. if (encoding != UTF8Encoding.INSTANCE
  182. && encoding != USASCIIEncoding.INSTANCE)
  183. throw runtime.newTypeError("Encoding for string fields" +
  184. " must be \"UTF-8\" or \"ASCII\", but was " + encoding);
  185. break;
  186. default:
  187. break;
  188. }
  189. }
  190. public static void checkNameAvailability(ThreadContext context, String name) {
  191. if (context.runtime.getObject().getConstantAt(name) != null)
  192. throw context.runtime.newNameError(name + " is already defined", name);
  193. }
  194. /**
  195. * Replace invalid "." in descriptor with __DOT__
  196. * @param name
  197. * @return
  198. */
  199. public static String escapeIdentifier(String name) {
  200. return name.replace(".", BADNAME_REPLACEMENT);
  201. }
  202. /**
  203. * Replace __DOT__ in descriptor name with "."
  204. * @param name
  205. * @return
  206. */
  207. public static String unescapeIdentifier(String name) {
  208. return name.replace(BADNAME_REPLACEMENT, ".");
  209. }
  210. public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) {
  211. return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
  212. fieldDescriptor.isRepeated() &&
  213. fieldDescriptor.getMessageType().getOptions().getMapEntry();
  214. }
  215. public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name,
  216. IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) {
  217. Ruby runtime = context.runtime;
  218. RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
  219. fieldDef.setLabel(context, runtime.newString(label));
  220. fieldDef.setName(context, name);
  221. fieldDef.setType(context, type);
  222. fieldDef.setNumber(context, number);
  223. if (!typeClass.isNil()) {
  224. if (!(typeClass instanceof RubyString)) {
  225. throw runtime.newArgumentError("expected string for type class");
  226. }
  227. fieldDef.setSubmsgName(context, typeClass);
  228. }
  229. return fieldDef;
  230. }
  231. protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
  232. if (value instanceof RubyFloat) {
  233. double doubleVal = RubyNumeric.num2dbl(value);
  234. if (Math.floor(doubleVal) != doubleVal) {
  235. throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field.");
  236. }
  237. }
  238. if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) {
  239. if (RubyNumeric.num2dbl(value) < 0) {
  240. throw context.runtime.newRangeError("Assigning negative value to unsigned integer field.");
  241. }
  242. }
  243. }
  244. protected static boolean isRubyNum(Object value) {
  245. return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
  246. }
  247. protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
  248. Ruby runtime = context.runtime;
  249. if (!(value instanceof RubyModule)) {
  250. throw runtime.newArgumentError("TypeClass has incorrect type");
  251. }
  252. RubyModule klass = (RubyModule) value;
  253. IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
  254. if (descriptor.isNil()) {
  255. throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
  256. "class or enum as returned by the DescriptorPool.");
  257. }
  258. if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
  259. if (! (descriptor instanceof RubyDescriptor)) {
  260. throw runtime.newArgumentError("Descriptor has an incorrect type");
  261. }
  262. } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
  263. if (! (descriptor instanceof RubyEnumDescriptor)) {
  264. throw runtime.newArgumentError("Descriptor has an incorrect type");
  265. }
  266. }
  267. }
  268. public static String BADNAME_REPLACEMENT = "__DOT__";
  269. public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
  270. public static String EQUAL_SIGN = "=";
  271. private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
  272. private static long UINT_MAX = 0xffffffffl;
  273. }