• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
33 package com.google.protobuf.jruby;
34 
35 import com.google.protobuf.ByteString;
36 import com.google.protobuf.DescriptorProtos;
37 import com.google.protobuf.Descriptors;
38 import org.jcodings.Encoding;
39 import org.jcodings.specific.ASCIIEncoding;
40 import org.jcodings.specific.USASCIIEncoding;
41 import org.jcodings.specific.UTF8Encoding;
42 import org.jruby.*;
43 import org.jruby.runtime.Block;
44 import org.jruby.runtime.ThreadContext;
45 import org.jruby.runtime.builtin.IRubyObject;
46 
47 import java.math.BigInteger;
48 
49 public class Utils {
rubyToFieldType(IRubyObject typeClass)50     public static Descriptors.FieldDescriptor.Type rubyToFieldType(IRubyObject typeClass) {
51         return Descriptors.FieldDescriptor.Type.valueOf(typeClass.asJavaString().toUpperCase());
52     }
53 
fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type)54     public static IRubyObject fieldTypeToRuby(ThreadContext context, Descriptors.FieldDescriptor.Type type) {
55         return fieldTypeToRuby(context, type.name());
56     }
57 
fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type)58     public static IRubyObject fieldTypeToRuby(ThreadContext context, DescriptorProtos.FieldDescriptorProto.Type type) {
59         return fieldTypeToRuby(context, type.name());
60     }
61 
fieldTypeToRuby(ThreadContext context, String typeName)62     private static IRubyObject fieldTypeToRuby(ThreadContext context, String typeName) {
63 
64         return context.runtime.newSymbol(typeName.replace("TYPE_", "").toLowerCase());
65     }
66 
checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, IRubyObject value, RubyModule typeClass)67     public static void checkType(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType,
68                             IRubyObject value, RubyModule typeClass) {
69         Ruby runtime = context.runtime;
70         Object val;
71         switch(fieldType) {
72             case INT32:
73             case INT64:
74             case UINT32:
75             case UINT64:
76                 if (!isRubyNum(value)) {
77                     throw runtime.newTypeError("Expected number type for integral field.");
78                 }
79                 switch(fieldType) {
80                     case INT32:
81                         RubyNumeric.num2int(value);
82                         break;
83                     case INT64:
84                         RubyNumeric.num2long(value);
85                         break;
86                     case UINT32:
87                         num2uint(value);
88                         break;
89                     default:
90                         num2ulong(context.runtime, value);
91                         break;
92                 }
93                 checkIntTypePrecision(context, fieldType, value);
94                 break;
95             case FLOAT:
96                 if (!isRubyNum(value))
97                     throw runtime.newTypeError("Expected number type for float field.");
98                 break;
99             case DOUBLE:
100                 if (!isRubyNum(value))
101                     throw runtime.newTypeError("Expected number type for double field.");
102                 break;
103             case BOOL:
104                 if (!(value instanceof RubyBoolean))
105                     throw runtime.newTypeError("Invalid argument for boolean field.");
106                 break;
107             case BYTES:
108             case STRING:
109                 validateStringEncoding(context.runtime, fieldType, value);
110                 break;
111             case MESSAGE:
112                 if (value.getMetaClass() != typeClass) {
113                     throw runtime.newTypeError(value, typeClass);
114                 }
115                 break;
116             case ENUM:
117                 if (value instanceof RubySymbol) {
118                     Descriptors.EnumDescriptor enumDescriptor =
119                             ((RubyEnumDescriptor) typeClass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR)).getDescriptor();
120                     val = enumDescriptor.findValueByName(value.asJavaString());
121                     if (val == null)
122                         throw runtime.newRangeError("Enum value " + value + " is not found.");
123                 } else if(!isRubyNum(value)) {
124                     throw runtime.newTypeError("Expected number or symbol type for enum field.");
125                 }
126                 break;
127             default:
128                 break;
129         }
130     }
131 
wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value)132     public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) {
133         Ruby runtime = context.runtime;
134         switch (fieldType) {
135             case INT32:
136                 return runtime.newFixnum((Integer) value);
137             case INT64:
138                 return runtime.newFixnum((Long) value);
139             case UINT32:
140                 return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
141             case UINT64:
142                 long ret = (Long) value;
143                 return ret >= 0 ? runtime.newFixnum(ret) :
144                         RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
145             case FLOAT:
146                 return runtime.newFloat((Float) value);
147             case DOUBLE:
148                 return runtime.newFloat((Double) value);
149             case BOOL:
150                 return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
151             case BYTES:
152                 return runtime.newString(((ByteString) value).toStringUtf8());
153             case STRING:
154                 return runtime.newString(value.toString());
155             default:
156                 return runtime.getNil();
157         }
158     }
159 
num2uint(IRubyObject value)160     public static int num2uint(IRubyObject value) {
161         long longVal = RubyNumeric.num2long(value);
162         if (longVal > UINT_MAX)
163             throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
164         long num = longVal;
165         if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
166             // encode to UINT32
167             num = (-longVal ^ (-1l >>> 32) ) + 1;
168         RubyNumeric.checkInt(value, num);
169         return (int) num;
170     }
171 
num2ulong(Ruby runtime, IRubyObject value)172     public static long num2ulong(Ruby runtime, IRubyObject value) {
173         if (value instanceof RubyFloat) {
174             RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
175             return RubyBignum.big2ulong(bignum);
176         } else if (value instanceof RubyBignum) {
177             return RubyBignum.big2ulong((RubyBignum) value);
178         } else {
179             return RubyNumeric.num2long(value);
180         }
181     }
182 
validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value)183     public static void validateStringEncoding(Ruby runtime, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
184         if (!(value instanceof RubyString))
185             throw runtime.newTypeError("Invalid argument for string field.");
186         Encoding encoding = ((RubyString) value).getEncoding();
187         switch(type) {
188             case BYTES:
189                 if (encoding != ASCIIEncoding.INSTANCE)
190                     throw runtime.newTypeError("Encoding for bytes fields" +
191                             " must be \"ASCII-8BIT\", but was " + encoding);
192                 break;
193             case STRING:
194                 if (encoding != UTF8Encoding.INSTANCE
195                         && encoding != USASCIIEncoding.INSTANCE)
196                     throw runtime.newTypeError("Encoding for string fields" +
197                             " must be \"UTF-8\" or \"ASCII\", but was " + encoding);
198                 break;
199             default:
200                 break;
201         }
202     }
203 
checkNameAvailability(ThreadContext context, String name)204     public static void checkNameAvailability(ThreadContext context, String name) {
205         if (context.runtime.getObject().getConstantAt(name) != null)
206             throw context.runtime.newNameError(name + " is already defined", name);
207     }
208 
209     /**
210      * Replace invalid "." in descriptor with __DOT__
211      * @param name
212      * @return
213      */
escapeIdentifier(String name)214     public static String escapeIdentifier(String name) {
215         return name.replace(".", BADNAME_REPLACEMENT);
216     }
217 
218     /**
219      * Replace __DOT__ in descriptor name with "."
220      * @param name
221      * @return
222      */
unescapeIdentifier(String name)223     public static String unescapeIdentifier(String name) {
224         return name.replace(BADNAME_REPLACEMENT, ".");
225     }
226 
isMapEntry(Descriptors.FieldDescriptor fieldDescriptor)227     public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) {
228         return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
229                 fieldDescriptor.isRepeated() &&
230                 fieldDescriptor.getMessageType().getOptions().getMapEntry();
231     }
232 
msgdefCreateField(ThreadContext context, String label, IRubyObject name, IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor)233     public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name,
234                                       IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) {
235         Ruby runtime = context.runtime;
236         RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
237         fieldDef.setLabel(context, runtime.newString(label));
238         fieldDef.setName(context, name);
239         fieldDef.setType(context, type);
240         fieldDef.setNumber(context, number);
241 
242         if (!typeClass.isNil()) {
243             if (!(typeClass instanceof RubyString)) {
244                 throw runtime.newArgumentError("expected string for type class");
245             }
246             fieldDef.setSubmsgName(context, typeClass);
247         }
248         return fieldDef;
249     }
250 
checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)251     protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
252         if (value instanceof RubyFloat) {
253             double doubleVal = RubyNumeric.num2dbl(value);
254             if (Math.floor(doubleVal) != doubleVal) {
255                 throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field.");
256             }
257         }
258         if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) {
259             if (RubyNumeric.num2dbl(value) < 0) {
260                 throw context.runtime.newRangeError("Assigning negative value to unsigned integer field.");
261             }
262         }
263     }
264 
isRubyNum(Object value)265     protected static boolean isRubyNum(Object value) {
266         return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
267     }
268 
validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)269     protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
270         Ruby runtime = context.runtime;
271         if (!(value instanceof RubyModule)) {
272             throw runtime.newArgumentError("TypeClass has incorrect type");
273         }
274         RubyModule klass = (RubyModule) value;
275         IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
276         if (descriptor.isNil()) {
277             throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
278                     "class or enum as returned by the DescriptorPool.");
279         }
280         if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
281             if (! (descriptor instanceof RubyDescriptor)) {
282                 throw runtime.newArgumentError("Descriptor has an incorrect type");
283             }
284         } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
285             if (! (descriptor instanceof RubyEnumDescriptor)) {
286                 throw runtime.newArgumentError("Descriptor has an incorrect type");
287             }
288         }
289     }
290 
291     public static String BADNAME_REPLACEMENT = "__DOT__";
292 
293     public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
294 
295     public static String EQUAL_SIGN = "=";
296 
297     private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
298 
299     private static long UINT_MAX = 0xffffffffl;
300 }
301