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