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