• 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.*;
36 import org.jruby.*;
37 import org.jruby.anno.JRubyMethod;
38 import org.jruby.runtime.Block;
39 import org.jruby.runtime.Helpers;
40 import org.jruby.runtime.ThreadContext;
41 import org.jruby.runtime.builtin.IRubyObject;
42 import org.jruby.util.ByteList;
43 
44 import java.security.MessageDigest;
45 import java.security.NoSuchAlgorithmException;
46 import java.util.HashMap;
47 import java.util.Map;
48 
49 public class RubyMessage extends RubyObject {
RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor)50     public RubyMessage(Ruby ruby, RubyClass klazz, Descriptors.Descriptor descriptor) {
51         super(ruby, klazz);
52         this.descriptor = descriptor;
53     }
54 
55     /*
56      * call-seq:
57      *     Message.new(kwargs) => new_message
58      *
59      * Creates a new instance of the given message class. Keyword arguments may be
60      * provided with keywords corresponding to field names.
61      *
62      * Note that no literal Message class exists. Only concrete classes per message
63      * type exist, as provided by the #msgclass method on Descriptors after they
64      * have been added to a pool. The method definitions described here on the
65      * Message class are provided on each concrete message class.
66      */
67     @JRubyMethod(optional = 1)
initialize(final ThreadContext context, IRubyObject[] args)68     public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) {
69         final Ruby runtime = context.runtime;
70         this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField");
71         this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map");
72         this.builder = DynamicMessage.newBuilder(this.descriptor);
73         this.repeatedFields = new HashMap<Descriptors.FieldDescriptor, RubyRepeatedField>();
74         this.maps = new HashMap<Descriptors.FieldDescriptor, RubyMap>();
75         this.fields = new HashMap<Descriptors.FieldDescriptor, IRubyObject>();
76         this.oneofCases = new HashMap<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor>();
77         if (args.length == 1) {
78             if (!(args[0] instanceof RubyHash)) {
79                 throw runtime.newArgumentError("expected Hash arguments.");
80             }
81             RubyHash hash = args[0].convertToHash();
82             hash.visitAll(new RubyHash.Visitor() {
83                 @Override
84                 public void visit(IRubyObject key, IRubyObject value) {
85                     if (!(key instanceof RubySymbol) && !(key instanceof RubyString))
86                         throw runtime.newTypeError("Expected string or symbols as hash keys in initialization map.");
87                     final Descriptors.FieldDescriptor fieldDescriptor = findField(context, key);
88 
89                     if (value.isNil()) return;
90 
91                     if (Utils.isMapEntry(fieldDescriptor)) {
92                         if (!(value instanceof RubyHash))
93                             throw runtime.newArgumentError("Expected Hash object as initializer value for map field '" +  key.asJavaString() + "'.");
94 
95                         final RubyMap map = newMapForField(context, fieldDescriptor);
96                         map.mergeIntoSelf(context, value);
97                         maps.put(fieldDescriptor, map);
98                     } else if (fieldDescriptor.isRepeated()) {
99                         if (!(value instanceof RubyArray))
100                             throw runtime.newArgumentError("Expected array as initializer value for repeated field '" +  key.asJavaString() + "'.");
101                         RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, value);
102                         addRepeatedField(fieldDescriptor, repeatedField);
103                     } else {
104                         Descriptors.OneofDescriptor oneof = fieldDescriptor.getContainingOneof();
105                         if (oneof != null) {
106                             oneofCases.put(oneof, fieldDescriptor);
107                         }
108 
109                         if (value instanceof RubyHash && fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
110                             RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
111                             RubyClass typeClass = (RubyClass) descriptor.msgclass(context);
112                             value = (IRubyObject) typeClass.newInstance(context, value, Block.NULL_BLOCK);
113                         }
114 
115                         fields.put(fieldDescriptor, value);
116                     }
117                 }
118             });
119         }
120         return this;
121     }
122 
123     /*
124      * call-seq:
125      *     Message.[]=(index, value)
126      *
127      * Sets a field's value by field name. The provided field name should be a
128      * string.
129      */
130     @JRubyMethod(name = "[]=")
indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value)131     public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) {
132         Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
133         return setField(context, fieldDescriptor, value);
134     }
135 
136     /*
137      * call-seq:
138      *     Message.[](index) => value
139      *
140      * Accesses a field's value by field name. The provided field name should be a
141      * string.
142      */
143     @JRubyMethod(name = "[]")
index(ThreadContext context, IRubyObject fieldName)144     public IRubyObject index(ThreadContext context, IRubyObject fieldName) {
145         Descriptors.FieldDescriptor fieldDescriptor = findField(context, fieldName);
146         return getField(context, fieldDescriptor);
147     }
148 
149     /*
150      * call-seq:
151      *     Message.inspect => string
152      *
153      * Returns a human-readable string representing this message. It will be
154      * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each
155      * field's value is represented according to its own #inspect method.
156      */
157     @JRubyMethod
inspect()158     public IRubyObject inspect() {
159         String cname = metaClass.getName();
160         StringBuilder sb = new StringBuilder("<");
161         sb.append(cname);
162         sb.append(": ");
163         sb.append(this.layoutInspect());
164         sb.append(">");
165 
166         return getRuntime().newString(sb.toString());
167     }
168 
169     /*
170      * call-seq:
171      *     Message.hash => hash_value
172      *
173      * Returns a hash value that represents this message's field values.
174      */
175     @JRubyMethod
hash(ThreadContext context)176     public IRubyObject hash(ThreadContext context) {
177         try {
178             MessageDigest digest = MessageDigest.getInstance("SHA-256");
179             for (RubyMap map : maps.values()) {
180                 digest.update((byte) map.hashCode());
181             }
182             for (RubyRepeatedField repeatedField : repeatedFields.values()) {
183                 digest.update((byte) repeatedFields.hashCode());
184             }
185             for (IRubyObject field : fields.values()) {
186                 digest.update((byte) field.hashCode());
187             }
188             return context.runtime.newString(new ByteList(digest.digest()));
189         } catch (NoSuchAlgorithmException ignore) {
190             return context.runtime.newFixnum(System.identityHashCode(this));
191         }
192     }
193 
194     /*
195      * call-seq:
196      *     Message.==(other) => boolean
197      *
198      * Performs a deep comparison of this message with another. Messages are equal
199      * if they have the same type and if each field is equal according to the :==
200      * method's semantics (a more efficient comparison may actually be done if the
201      * field is of a primitive type).
202      */
203     @JRubyMethod(name = "==")
eq(ThreadContext context, IRubyObject other)204     public IRubyObject eq(ThreadContext context, IRubyObject other) {
205         Ruby runtime = context.runtime;
206         if (!(other instanceof RubyMessage))
207             return runtime.getFalse();
208         RubyMessage message = (RubyMessage) other;
209         if (descriptor != message.descriptor) {
210             return runtime.getFalse();
211         }
212 
213         for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
214             IRubyObject thisVal = getField(context, fdef);
215             IRubyObject thatVal = message.getField(context, fdef);
216             IRubyObject ret = thisVal.callMethod(context, "==", thatVal);
217             if (!ret.isTrue()) {
218                 return runtime.getFalse();
219             }
220         }
221         return runtime.getTrue();
222     }
223 
224     /*
225      * call-seq:
226      *     Message.method_missing(*args)
227      *
228      * Provides accessors and setters for message fields according to their field
229      * names. For any field whose name does not conflict with a built-in method, an
230      * accessor is provided with the same name as the field, and a setter is
231      * provided with the name of the field plus the '=' suffix. Thus, given a
232      * message instance 'msg' with field 'foo', the following code is valid:
233      *
234      *     msg.foo = 42
235      *     puts msg.foo
236      */
237     @JRubyMethod(name = "method_missing", rest = true)
methodMissing(ThreadContext context, IRubyObject[] args)238     public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) {
239         if (args.length == 1) {
240             RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
241             IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]);
242             if (oneofDescriptor.isNil()) {
243                 if (!hasField(args[0])) {
244                     return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
245                 }
246                 return index(context, args[0]);
247             }
248             RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor;
249             Descriptors.FieldDescriptor fieldDescriptor =
250                     oneofCases.get(rubyOneofDescriptor.getOneofDescriptor());
251             if (fieldDescriptor == null)
252                 return context.runtime.getNil();
253 
254             return context.runtime.newSymbol(fieldDescriptor.getName());
255         } else {
256             // fieldName is RubySymbol
257             RubyString field = args[0].asString();
258             RubyString equalSign = context.runtime.newString(Utils.EQUAL_SIGN);
259             if (field.end_with_p(context, equalSign).isTrue()) {
260                 field.chomp_bang(context, equalSign);
261             }
262 
263             if (!hasField(field)) {
264                 return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK);
265             }
266             return indexSet(context, field, args[1]);
267         }
268     }
269 
270     /**
271      * call-seq:
272      * Message.dup => new_message
273      * Performs a shallow copy of this message and returns the new copy.
274      */
275     @JRubyMethod
dup(ThreadContext context)276     public IRubyObject dup(ThreadContext context) {
277         RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
278         IRubyObject value;
279         for (Descriptors.FieldDescriptor fieldDescriptor : this.descriptor.getFields()) {
280             if (fieldDescriptor.isRepeated()) {
281                 dup.addRepeatedField(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor));
282             } else if (fields.containsKey(fieldDescriptor)) {
283                 dup.fields.put(fieldDescriptor, fields.get(fieldDescriptor));
284             } else if (this.builder.hasField(fieldDescriptor)) {
285                 dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor)));
286             }
287         }
288         for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
289             dup.maps.put(fieldDescriptor, maps.get(fieldDescriptor));
290         }
291         return dup;
292     }
293 
294     /*
295      * call-seq:
296      *     Message.descriptor => descriptor
297      *
298      * Class method that returns the Descriptor instance corresponding to this
299      * message class's type.
300      */
301     @JRubyMethod(name = "descriptor", meta = true)
getDescriptor(ThreadContext context, IRubyObject recv)302     public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) {
303         return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR);
304     }
305 
306     /*
307      * call-seq:
308      *     MessageClass.encode(msg) => bytes
309      *
310      * Encodes the given message object to its serialized form in protocol buffers
311      * wire format.
312      */
313     @JRubyMethod(meta = true)
encode(ThreadContext context, IRubyObject recv, IRubyObject value)314     public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject value) {
315         RubyMessage message = (RubyMessage) value;
316         return context.runtime.newString(new ByteList(message.build(context).toByteArray()));
317     }
318 
319     /*
320      * call-seq:
321      *     MessageClass.decode(data) => message
322      *
323      * Decodes the given data (as a string containing bytes in protocol buffers wire
324      * format) under the interpretration given by this message class's definition
325      * and returns a message object with the corresponding field values.
326      */
327     @JRubyMethod(meta = true)
decode(ThreadContext context, IRubyObject recv, IRubyObject data)328     public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject data) {
329         byte[] bin = data.convertToString().getBytes();
330         RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
331         try {
332             ret.builder.mergeFrom(bin);
333         } catch (InvalidProtocolBufferException e) {
334             throw context.runtime.newRuntimeError(e.getMessage());
335         }
336         return ret;
337     }
338 
339     /*
340      * call-seq:
341      *     MessageClass.encode_json(msg) => json_string
342      *
343      * Encodes the given message object into its serialized JSON representation.
344      */
345     @JRubyMethod(name = "encode_json", meta = true)
encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb)346     public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject msgRb) {
347         RubyMessage message = (RubyMessage) msgRb;
348         return Helpers.invoke(context, message.toHash(context), "to_json");
349     }
350 
351     /*
352      * call-seq:
353      *     MessageClass.decode_json(data) => message
354      *
355      * Decodes the given data (as a string containing bytes in protocol buffers wire
356      * format) under the interpretration given by this message class's definition
357      * and returns a message object with the corresponding field values.
358      */
359     @JRubyMethod(name = "decode_json", meta = true)
decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json)360     public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject json) {
361         Ruby runtime = context.runtime;
362         RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK);
363         RubyModule jsonModule = runtime.getClassFromPath("JSON");
364         RubyHash opts = RubyHash.newHash(runtime);
365         opts.fastASet(runtime.newSymbol("symbolize_names"), runtime.getTrue());
366         IRubyObject[] args = new IRubyObject[] { Helpers.invoke(context, jsonModule, "parse", json, opts) };
367         ret.initialize(context, args);
368         return ret;
369     }
370 
371     @JRubyMethod(name = "to_h")
toHash(ThreadContext context)372     public IRubyObject toHash(ThreadContext context) {
373         Ruby runtime = context.runtime;
374         RubyHash ret = RubyHash.newHash(runtime);
375         for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
376             IRubyObject value = getField(context, fdef);
377             if (!value.isNil()) {
378                 if (fdef.isRepeated() && !fdef.isMapField()) {
379                     if (fdef.getType() != Descriptors.FieldDescriptor.Type.MESSAGE) {
380                         value = Helpers.invoke(context, value, "to_a");
381                     } else {
382                         RubyArray ary = value.convertToArray();
383                         for (int i = 0; i < ary.size(); i++) {
384                             IRubyObject submsg = Helpers.invoke(context, ary.eltInternal(i), "to_h");
385                             ary.eltInternalSet(i, submsg);
386                         }
387 
388                         value = ary.to_ary();
389                     }
390                 } else if (value.respondsTo("to_h")) {
391                     value = Helpers.invoke(context, value, "to_h");
392                 } else if (value.respondsTo("to_a")) {
393                     value = Helpers.invoke(context, value, "to_a");
394                 }
395             }
396             ret.fastASet(runtime.newSymbol(fdef.getName()), value);
397         }
398         return ret;
399     }
400 
build(ThreadContext context)401     protected DynamicMessage build(ThreadContext context) {
402         return build(context, 0);
403     }
404 
build(ThreadContext context, int depth)405     protected DynamicMessage build(ThreadContext context, int depth) {
406         if (depth > SINK_MAXIMUM_NESTING) {
407             throw context.runtime.newRuntimeError("Maximum recursion depth exceeded during encoding.");
408         }
409         for (Descriptors.FieldDescriptor fieldDescriptor : maps.keySet()) {
410             this.builder.clearField(fieldDescriptor);
411             RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
412             for (DynamicMessage kv : maps.get(fieldDescriptor).build(context, mapDescriptor)) {
413                 this.builder.addRepeatedField(fieldDescriptor, kv);
414             }
415         }
416         for (Descriptors.FieldDescriptor fieldDescriptor : repeatedFields.keySet()) {
417             RubyRepeatedField repeatedField = repeatedFields.get(fieldDescriptor);
418             this.builder.clearField(fieldDescriptor);
419             for (int i = 0; i < repeatedField.size(); i++) {
420                 Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth);
421                 this.builder.addRepeatedField(fieldDescriptor, item);
422             }
423         }
424         for (Descriptors.FieldDescriptor fieldDescriptor : fields.keySet()) {
425             IRubyObject value = fields.get(fieldDescriptor);
426             this.builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth));
427         }
428         return this.builder.build();
429     }
430 
getDescriptor()431     protected Descriptors.Descriptor getDescriptor() {
432         return this.descriptor;
433     }
434 
435     // Internal use only, called by Google::Protobuf.deep_copy
deepCopy(ThreadContext context)436     protected IRubyObject deepCopy(ThreadContext context) {
437         RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK);
438         for (Descriptors.FieldDescriptor fdef : this.descriptor.getFields()) {
439             if (fdef.isRepeated()) {
440                 copy.addRepeatedField(fdef, this.getRepeatedField(context, fdef).deepCopy(context));
441             } else if (fields.containsKey(fdef)) {
442                 copy.fields.put(fdef, fields.get(fdef));
443             } else if (this.builder.hasField(fdef)) {
444                 copy.fields.put(fdef, wrapField(context, fdef, this.builder.getField(fdef)));
445             }
446         }
447         return copy;
448     }
449 
getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor)450     private RubyRepeatedField getRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
451         if (this.repeatedFields.containsKey(fieldDescriptor)) {
452             return this.repeatedFields.get(fieldDescriptor);
453         }
454         int count = this.builder.getRepeatedFieldCount(fieldDescriptor);
455         RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
456         for (int i = 0; i < count; i++) {
457             ret.push(context, wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)));
458         }
459         addRepeatedField(fieldDescriptor, ret);
460         return ret;
461     }
462 
addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField)463     private void addRepeatedField(Descriptors.FieldDescriptor fieldDescriptor, RubyRepeatedField repeatedField) {
464         this.repeatedFields.put(fieldDescriptor, repeatedField);
465     }
466 
buildFrom(ThreadContext context, DynamicMessage dynamicMessage)467     private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) {
468         this.builder.mergeFrom(dynamicMessage);
469         return this;
470     }
471 
findField(ThreadContext context, IRubyObject fieldName)472     private Descriptors.FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) {
473         String nameStr = fieldName.asJavaString();
474         Descriptors.FieldDescriptor ret = this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr));
475         if (ret == null)
476             throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found");
477         return ret;
478     }
479 
hasField(IRubyObject fieldName)480     private boolean hasField(IRubyObject fieldName) {
481         String nameStr = fieldName.asJavaString();
482         return this.descriptor.findFieldByName(Utils.escapeIdentifier(nameStr)) != null;
483     }
484 
checkRepeatedFieldType(ThreadContext context, IRubyObject value, Descriptors.FieldDescriptor fieldDescriptor)485     private void checkRepeatedFieldType(ThreadContext context, IRubyObject value,
486                                         Descriptors.FieldDescriptor fieldDescriptor) {
487         Ruby runtime = context.runtime;
488         if (!(value instanceof RubyRepeatedField)) {
489             throw runtime.newTypeError("Expected repeated field array");
490         }
491     }
492 
493     // convert a ruby object to protobuf type, with type check
convert(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value, int depth)494     private Object convert(ThreadContext context,
495                            Descriptors.FieldDescriptor fieldDescriptor,
496                            IRubyObject value, int depth) {
497         Ruby runtime = context.runtime;
498         Object val = null;
499         switch (fieldDescriptor.getType()) {
500             case INT32:
501             case INT64:
502             case UINT32:
503             case UINT64:
504                 if (!Utils.isRubyNum(value)) {
505                     throw runtime.newTypeError("Expected number type for integral field.");
506                 }
507                 Utils.checkIntTypePrecision(context, fieldDescriptor.getType(), value);
508                 switch (fieldDescriptor.getType()) {
509                     case INT32:
510                         val = RubyNumeric.num2int(value);
511                         break;
512                     case INT64:
513                         val = RubyNumeric.num2long(value);
514                         break;
515                     case UINT32:
516                         val = Utils.num2uint(value);
517                         break;
518                     case UINT64:
519                         val = Utils.num2ulong(context.runtime, value);
520                         break;
521                     default:
522                         break;
523                 }
524                 break;
525             case FLOAT:
526                 if (!Utils.isRubyNum(value))
527                     throw runtime.newTypeError("Expected number type for float field.");
528                 val = (float) RubyNumeric.num2dbl(value);
529                 break;
530             case DOUBLE:
531                 if (!Utils.isRubyNum(value))
532                     throw runtime.newTypeError("Expected number type for double field.");
533                 val = RubyNumeric.num2dbl(value);
534                 break;
535             case BOOL:
536                 if (!(value instanceof RubyBoolean))
537                     throw runtime.newTypeError("Invalid argument for boolean field.");
538                 val = value.isTrue();
539                 break;
540             case BYTES:
541                 Utils.validateStringEncoding(context, fieldDescriptor.getType(), value);
542                 val = ByteString.copyFrom(((RubyString) value).getBytes());
543                 break;
544             case STRING:
545                 Utils.validateStringEncoding(context, fieldDescriptor.getType(), value);
546                 val = ((RubyString) value).asJavaString();
547                 break;
548             case MESSAGE:
549                 RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
550                 if (!value.getMetaClass().equals(typeClass))
551                     throw runtime.newTypeError(value, "Invalid type to assign to submessage field.");
552                 val = ((RubyMessage) value).build(context, depth + 1);
553                 break;
554             case ENUM:
555                 Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
556 
557                 if (Utils.isRubyNum(value)) {
558                     val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
559                 } else if (value instanceof RubySymbol || value instanceof RubyString) {
560                     val = enumDescriptor.findValueByName(value.asJavaString());
561                 } else {
562                     throw runtime.newTypeError("Expected number or symbol type for enum field.");
563                 }
564                 if (val == null) {
565                     throw runtime.newRangeError("Enum value " + value + " is not found.");
566                 }
567                 break;
568             default:
569                 break;
570         }
571         return val;
572     }
573 
wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value)574     private IRubyObject wrapField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, Object value) {
575         if (value == null) {
576             return context.runtime.getNil();
577         }
578         Ruby runtime = context.runtime;
579         switch (fieldDescriptor.getType()) {
580             case INT32:
581             case INT64:
582             case UINT32:
583             case UINT64:
584             case FLOAT:
585             case DOUBLE:
586             case BOOL:
587             case BYTES:
588             case STRING:
589                 return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value);
590             case MESSAGE:
591                 RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
592                 RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK);
593                 return msg.buildFrom(context, (DynamicMessage) value);
594             case ENUM:
595                 Descriptors.EnumValueDescriptor enumValueDescriptor = (Descriptors.EnumValueDescriptor) value;
596                 if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE
597                     return runtime.newFixnum(enumValueDescriptor.getNumber());
598                 }
599                 return runtime.newSymbol(enumValueDescriptor.getName());
600             default:
601                 return runtime.newString(value.toString());
602         }
603     }
604 
repeatedFieldForFieldDescriptor(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor)605     private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context,
606                                                               Descriptors.FieldDescriptor fieldDescriptor) {
607         IRubyObject typeClass = context.runtime.getNilClass();
608 
609         IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor);
610         Descriptors.FieldDescriptor.Type type = fieldDescriptor.getType();
611         if (type == Descriptors.FieldDescriptor.Type.MESSAGE) {
612             typeClass = ((RubyDescriptor) descriptor).msgclass(context);
613 
614         } else if (type == Descriptors.FieldDescriptor.Type.ENUM) {
615             typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context);
616         }
617         return new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass);
618     }
619 
getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor)620     protected IRubyObject getField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
621         Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
622         if (oneofDescriptor != null) {
623             if (oneofCases.get(oneofDescriptor) == fieldDescriptor) {
624                 return fields.get(fieldDescriptor);
625             } else {
626                 Descriptors.FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor);
627                 if (oneofCase != fieldDescriptor) {
628                   if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
629                     return context.runtime.getNil();
630                   } else {
631                     return wrapField(context, fieldDescriptor, fieldDescriptor.getDefaultValue());
632                   }
633                 }
634                 IRubyObject value = wrapField(context, oneofCase, builder.getField(oneofCase));
635                 fields.put(fieldDescriptor, value);
636                 return value;
637             }
638         }
639 
640         if (Utils.isMapEntry(fieldDescriptor)) {
641             RubyMap map = maps.get(fieldDescriptor);
642             if (map == null) {
643                 map = newMapForField(context, fieldDescriptor);
644                 int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor);
645                 Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
646                 Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
647                 RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
648                 RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context);
649                 for (int i = 0; i < mapSize; i++) {
650                     RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK);
651                     DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i);
652                     kvMessage.buildFrom(context, message);
653                     map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField));
654                 }
655                 maps.put(fieldDescriptor, map);
656             }
657             return map;
658         }
659         if (fieldDescriptor.isRepeated()) {
660             return getRepeatedField(context, fieldDescriptor);
661         }
662         if (fieldDescriptor.getType() != Descriptors.FieldDescriptor.Type.MESSAGE ||
663                 this.builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) {
664             if (fields.containsKey(fieldDescriptor)) {
665                 return fields.get(fieldDescriptor);
666             } else {
667                 IRubyObject value = wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor));
668                 if (this.builder.hasField(fieldDescriptor)) {
669                     fields.put(fieldDescriptor, value);
670                 }
671                 return value;
672             }
673         }
674         return context.runtime.getNil();
675     }
676 
setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value)677     protected IRubyObject setField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
678         if (Utils.isMapEntry(fieldDescriptor)) {
679             if (!(value instanceof RubyMap)) {
680                 throw context.runtime.newTypeError("Expected Map instance");
681             }
682             RubyMap thisMap = (RubyMap) getField(context, fieldDescriptor);
683             thisMap.mergeIntoSelf(context, value);
684         } else if (fieldDescriptor.isRepeated()) {
685             checkRepeatedFieldType(context, value, fieldDescriptor);
686             if (value instanceof RubyRepeatedField) {
687                 addRepeatedField(fieldDescriptor, (RubyRepeatedField) value);
688             } else {
689                 RubyArray ary = value.convertToArray();
690                 RubyRepeatedField repeatedField = rubyToRepeatedField(context, fieldDescriptor, ary);
691                 addRepeatedField(fieldDescriptor, repeatedField);
692             }
693         } else {
694             Descriptors.OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof();
695             if (oneofDescriptor != null) {
696                 Descriptors.FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor);
697                 if (oneofCase != null && oneofCase != fieldDescriptor) {
698                     fields.remove(oneofCase);
699                 }
700                 if (value.isNil()) {
701                     oneofCases.remove(oneofDescriptor);
702                     fields.remove(fieldDescriptor);
703                 } else {
704                     oneofCases.put(oneofDescriptor, fieldDescriptor);
705                     fields.put(fieldDescriptor, value);
706                 }
707             } else {
708                 Descriptors.FieldDescriptor.Type fieldType = fieldDescriptor.getType();
709                 IRubyObject typeClass = context.runtime.getObject();
710                 boolean addValue = true;
711                 if (fieldType == Descriptors.FieldDescriptor.Type.MESSAGE) {
712                     typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context);
713                     if (value.isNil()){
714                         addValue = false;
715                     }
716                 } else if (fieldType == Descriptors.FieldDescriptor.Type.ENUM) {
717                     typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context);
718                     Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType();
719                     if (Utils.isRubyNum(value)) {
720                         Descriptors.EnumValueDescriptor val =
721                                 enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value));
722                         if (val.getIndex() != -1) value = context.runtime.newSymbol(val.getName());
723                     }
724                 }
725                 if (addValue) {
726                     value = Utils.checkType(context, fieldType, value, (RubyModule) typeClass);
727                     this.fields.put(fieldDescriptor, value);
728                 } else {
729                     this.fields.remove(fieldDescriptor);
730                 }
731             }
732         }
733         return context.runtime.getNil();
734     }
735 
layoutInspect()736     private String layoutInspect() {
737         ThreadContext context = getRuntime().getCurrentContext();
738         StringBuilder sb = new StringBuilder();
739         for (Descriptors.FieldDescriptor fdef : descriptor.getFields()) {
740             sb.append(Utils.unescapeIdentifier(fdef.getName()));
741             sb.append(": ");
742             sb.append(getField(context, fdef).inspect());
743             sb.append(", ");
744         }
745         return sb.substring(0, sb.length() - 2);
746     }
747 
getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor)748     private IRubyObject getDescriptorForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
749         RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass);
750         return thisRbDescriptor.lookup(fieldDescriptor.getName()).getSubType(context);
751     }
752 
rubyToRepeatedField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value)753     private RubyRepeatedField rubyToRepeatedField(ThreadContext context,
754                                                   Descriptors.FieldDescriptor fieldDescriptor, IRubyObject value) {
755         RubyArray arr = value.convertToArray();
756         RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor);
757 
758         RubyClass typeClass = null;
759         if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
760             RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
761             typeClass = (RubyClass) descriptor.msgclass(context);
762         }
763 
764         for (int i = 0; i < arr.size(); i++) {
765             IRubyObject row = arr.eltInternal(i);
766             if (row instanceof RubyHash && typeClass != null) {
767                 row = (IRubyObject) typeClass.newInstance(context, row, Block.NULL_BLOCK);
768             }
769 
770             repeatedField.push(context, row);
771         }
772         return repeatedField;
773     }
774 
newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor)775     private RubyMap newMapForField(ThreadContext context, Descriptors.FieldDescriptor fieldDescriptor) {
776         RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor);
777         Descriptors.FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1);
778         Descriptors.FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2);
779         IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name());
780         IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name());
781         if (valueField.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) {
782             RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context,
783                     context.runtime.newString("value"));
784             RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubType(context);
785             return (RubyMap) cMap.newInstance(context, keyType, valueType,
786                     rubyDescriptor.msgclass(context), Block.NULL_BLOCK);
787         } else {
788             return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK);
789         }
790     }
791 
getOneofCase(Descriptors.OneofDescriptor oneof)792     private Descriptors.FieldDescriptor getOneofCase(Descriptors.OneofDescriptor oneof) {
793         if (oneofCases.containsKey(oneof)) {
794             return oneofCases.get(oneof);
795         }
796         return builder.getOneofFieldDescriptor(oneof);
797     }
798 
799     private Descriptors.Descriptor descriptor;
800     private DynamicMessage.Builder builder;
801     private RubyClass cRepeatedField;
802     private RubyClass cMap;
803     private Map<Descriptors.FieldDescriptor, RubyRepeatedField> repeatedFields;
804     private Map<Descriptors.FieldDescriptor, RubyMap> maps;
805     private Map<Descriptors.FieldDescriptor, IRubyObject> fields;
806     private Map<Descriptors.OneofDescriptor, Descriptors.FieldDescriptor> oneofCases;
807 
808     private static final int SINK_MAXIMUM_NESTING = 64;
809 }
810