• 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 IRubyObject 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                 value = validateStringEncoding(context, 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         return value;
131     }
132 
wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value)133     public static IRubyObject wrapPrimaryValue(ThreadContext context, Descriptors.FieldDescriptor.Type fieldType, Object value) {
134         Ruby runtime = context.runtime;
135         switch (fieldType) {
136             case INT32:
137                 return runtime.newFixnum((Integer) value);
138             case INT64:
139                 return runtime.newFixnum((Long) value);
140             case UINT32:
141                 return runtime.newFixnum(((Integer) value) & (-1l >>> 32));
142             case UINT64:
143                 long ret = (Long) value;
144                 return ret >= 0 ? runtime.newFixnum(ret) :
145                         RubyBignum.newBignum(runtime, UINT64_COMPLEMENTARY.add(new BigInteger(ret + "")));
146             case FLOAT:
147                 return runtime.newFloat((Float) value);
148             case DOUBLE:
149                 return runtime.newFloat((Double) value);
150             case BOOL:
151                 return (Boolean) value ? runtime.getTrue() : runtime.getFalse();
152             case BYTES: {
153                 IRubyObject wrapped = runtime.newString(((ByteString) value).toStringUtf8());
154                 wrapped.setFrozen(true);
155                 return wrapped;
156             }
157             case STRING: {
158                 IRubyObject wrapped = runtime.newString(value.toString());
159                 wrapped.setFrozen(true);
160                 return wrapped;
161             }
162             default:
163                 return runtime.getNil();
164         }
165     }
166 
num2uint(IRubyObject value)167     public static int num2uint(IRubyObject value) {
168         long longVal = RubyNumeric.num2long(value);
169         if (longVal > UINT_MAX)
170             throw value.getRuntime().newRangeError("Integer " + longVal + " too big to convert to 'unsigned int'");
171         long num = longVal;
172         if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE)
173             // encode to UINT32
174             num = (-longVal ^ (-1l >>> 32) ) + 1;
175         RubyNumeric.checkInt(value, num);
176         return (int) num;
177     }
178 
num2ulong(Ruby runtime, IRubyObject value)179     public static long num2ulong(Ruby runtime, IRubyObject value) {
180         if (value instanceof RubyFloat) {
181             RubyBignum bignum = RubyBignum.newBignum(runtime, ((RubyFloat) value).getDoubleValue());
182             return RubyBignum.big2ulong(bignum);
183         } else if (value instanceof RubyBignum) {
184             return RubyBignum.big2ulong((RubyBignum) value);
185         } else {
186             return RubyNumeric.num2long(value);
187         }
188     }
189 
validateStringEncoding(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)190     public static IRubyObject validateStringEncoding(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
191         if (!(value instanceof RubyString))
192             throw context.runtime.newTypeError("Invalid argument for string field.");
193         switch(type) {
194             case BYTES:
195                 value = ((RubyString)value).encode(context, context.runtime.evalScriptlet("Encoding::ASCII_8BIT"));
196                 break;
197             case STRING:
198                 value = ((RubyString)value).encode(context, context.runtime.evalScriptlet("Encoding::UTF_8"));
199                 break;
200             default:
201                 break;
202         }
203         value.setFrozen(true);
204         return value;
205     }
206 
checkNameAvailability(ThreadContext context, String name)207     public static void checkNameAvailability(ThreadContext context, String name) {
208         if (context.runtime.getObject().getConstantAt(name) != null)
209             throw context.runtime.newNameError(name + " is already defined", name);
210     }
211 
212     /**
213      * Replace invalid "." in descriptor with __DOT__
214      * @param name
215      * @return
216      */
escapeIdentifier(String name)217     public static String escapeIdentifier(String name) {
218         return name.replace(".", BADNAME_REPLACEMENT);
219     }
220 
221     /**
222      * Replace __DOT__ in descriptor name with "."
223      * @param name
224      * @return
225      */
unescapeIdentifier(String name)226     public static String unescapeIdentifier(String name) {
227         return name.replace(BADNAME_REPLACEMENT, ".");
228     }
229 
isMapEntry(Descriptors.FieldDescriptor fieldDescriptor)230     public static boolean isMapEntry(Descriptors.FieldDescriptor fieldDescriptor) {
231         return fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE &&
232                 fieldDescriptor.isRepeated() &&
233                 fieldDescriptor.getMessageType().getOptions().getMapEntry();
234     }
235 
msgdefCreateField(ThreadContext context, String label, IRubyObject name, IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor)236     public static RubyFieldDescriptor msgdefCreateField(ThreadContext context, String label, IRubyObject name,
237                                       IRubyObject type, IRubyObject number, IRubyObject typeClass, RubyClass cFieldDescriptor) {
238         Ruby runtime = context.runtime;
239         RubyFieldDescriptor fieldDef = (RubyFieldDescriptor) cFieldDescriptor.newInstance(context, Block.NULL_BLOCK);
240         fieldDef.setLabel(context, runtime.newString(label));
241         fieldDef.setName(context, name);
242         fieldDef.setType(context, type);
243         fieldDef.setNumber(context, number);
244 
245         if (!typeClass.isNil()) {
246             if (!(typeClass instanceof RubyString)) {
247                 throw runtime.newArgumentError("expected string for type class");
248             }
249             fieldDef.setSubmsgName(context, typeClass);
250         }
251         return fieldDef;
252     }
253 
checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)254     protected static void checkIntTypePrecision(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
255         if (value instanceof RubyFloat) {
256             double doubleVal = RubyNumeric.num2dbl(value);
257             if (Math.floor(doubleVal) != doubleVal) {
258                 throw context.runtime.newRangeError("Non-integral floating point value assigned to integer field.");
259             }
260         }
261         if (type == Descriptors.FieldDescriptor.Type.UINT32 || type == Descriptors.FieldDescriptor.Type.UINT64) {
262             if (RubyNumeric.num2dbl(value) < 0) {
263                 throw context.runtime.newRangeError("Assigning negative value to unsigned integer field.");
264             }
265         }
266     }
267 
isRubyNum(Object value)268     protected static boolean isRubyNum(Object value) {
269         return value instanceof RubyFixnum || value instanceof RubyFloat || value instanceof RubyBignum;
270     }
271 
validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value)272     protected static void validateTypeClass(ThreadContext context, Descriptors.FieldDescriptor.Type type, IRubyObject value) {
273         Ruby runtime = context.runtime;
274         if (!(value instanceof RubyModule)) {
275             throw runtime.newArgumentError("TypeClass has incorrect type");
276         }
277         RubyModule klass = (RubyModule) value;
278         IRubyObject descriptor = klass.getInstanceVariable(DESCRIPTOR_INSTANCE_VAR);
279         if (descriptor.isNil()) {
280             throw runtime.newArgumentError("Type class has no descriptor. Please pass a " +
281                     "class or enum as returned by the DescriptorPool.");
282         }
283         if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
284             if (! (descriptor instanceof RubyDescriptor)) {
285                 throw runtime.newArgumentError("Descriptor has an incorrect type");
286             }
287         } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
288             if (! (descriptor instanceof RubyEnumDescriptor)) {
289                 throw runtime.newArgumentError("Descriptor has an incorrect type");
290             }
291         }
292     }
293 
294     public static String BADNAME_REPLACEMENT = "__DOT__";
295 
296     public static String DESCRIPTOR_INSTANCE_VAR = "@descriptor";
297 
298     public static String EQUAL_SIGN = "=";
299 
300     private static BigInteger UINT64_COMPLEMENTARY = new BigInteger("18446744073709551616"); //Math.pow(2, 64)
301 
302     private static long UINT_MAX = 0xffffffffl;
303 }
304