1 /* 2 * Protocol Buffers - Google's data interchange format 3 * Copyright 2014 Google Inc. All rights reserved. 4 * https://developers.google.com/protocol-buffers/ 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are 8 * met: 9 * 10 * * Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * * Redistributions in binary form must reproduce the above 13 * copyright notice, this list of conditions and the following disclaimer 14 * in the documentation and/or other materials provided with the 15 * distribution. 16 * * Neither the name of Google Inc. nor the names of its 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.google.protobuf.jruby; 34 35 import com.google.protobuf.ByteString; 36 import com.google.protobuf.CodedInputStream; 37 import com.google.protobuf.Descriptors.Descriptor; 38 import com.google.protobuf.Descriptors.EnumDescriptor; 39 import com.google.protobuf.Descriptors.EnumValueDescriptor; 40 import com.google.protobuf.Descriptors.FieldDescriptor; 41 import com.google.protobuf.Descriptors.OneofDescriptor; 42 import com.google.protobuf.DynamicMessage; 43 import com.google.protobuf.InvalidProtocolBufferException; 44 import com.google.protobuf.Message; 45 import com.google.protobuf.UnknownFieldSet; 46 import com.google.protobuf.util.JsonFormat; 47 import java.nio.ByteBuffer; 48 import java.security.MessageDigest; 49 import java.security.NoSuchAlgorithmException; 50 import java.util.HashMap; 51 import java.util.List; 52 import java.util.Map; 53 import org.jruby.*; 54 import org.jruby.anno.JRubyMethod; 55 import org.jruby.exceptions.RaiseException; 56 import org.jruby.runtime.Block; 57 import org.jruby.runtime.Helpers; 58 import org.jruby.runtime.ThreadContext; 59 import org.jruby.runtime.builtin.IRubyObject; 60 import org.jruby.util.ByteList; 61 62 public class RubyMessage extends RubyObject { 63 private final String DEFAULT_VALUE = "google.protobuf.FieldDescriptorProto.default_value"; 64 private final String TYPE = "type"; 65 RubyMessage(Ruby runtime, RubyClass klazz, Descriptor descriptor)66 public RubyMessage(Ruby runtime, RubyClass klazz, Descriptor descriptor) { 67 super(runtime, klazz); 68 69 this.descriptor = descriptor; 70 this.cRepeatedField = (RubyClass) runtime.getClassFromPath("Google::Protobuf::RepeatedField"); 71 this.cMap = (RubyClass) runtime.getClassFromPath("Google::Protobuf::Map"); 72 this.builder = DynamicMessage.newBuilder(descriptor); 73 this.fields = new HashMap<FieldDescriptor, IRubyObject>(); 74 this.oneofCases = new HashMap<OneofDescriptor, FieldDescriptor>(); 75 } 76 77 /* 78 * call-seq: 79 * Message.new(kwargs) => new_message 80 * 81 * Creates a new instance of the given message class. Keyword arguments may be 82 * provided with keywords corresponding to field names. 83 * 84 * Note that no literal Message class exists. Only concrete classes per message 85 * type exist, as provided by the #msgclass method on Descriptors after they 86 * have been added to a pool. The method definitions described here on the 87 * Message class are provided on each concrete message class. 88 */ 89 @JRubyMethod(optional = 1) initialize(final ThreadContext context, IRubyObject[] args)90 public IRubyObject initialize(final ThreadContext context, IRubyObject[] args) { 91 final Ruby runtime = context.runtime; 92 if (args.length == 1) { 93 if (!(args[0] instanceof RubyHash)) { 94 throw runtime.newArgumentError("expected Hash arguments."); 95 } 96 RubyHash hash = args[0].convertToHash(); 97 hash.visitAll( 98 context, 99 new RubyHash.Visitor() { 100 @Override 101 public void visit(IRubyObject key, IRubyObject value) { 102 if (!(key instanceof RubySymbol) && !(key instanceof RubyString)) { 103 throw Utils.createTypeError( 104 context, "Expected string or symbols as hash keys in initialization map."); 105 } 106 final FieldDescriptor fieldDescriptor = 107 findField(context, key, ignoreUnknownFieldsOnInit); 108 109 if (value == null || value.isNil()) return; 110 111 if (fieldDescriptor.isMapField()) { 112 if (!(value instanceof RubyHash)) 113 throw runtime.newArgumentError( 114 "Expected Hash object as initializer value for map field '" 115 + key.asJavaString() 116 + "' (given " 117 + value.getMetaClass() 118 + ")."); 119 120 final RubyMap map = newMapForField(context, fieldDescriptor); 121 map.mergeIntoSelf(context, value); 122 fields.put(fieldDescriptor, map); 123 } else if (fieldDescriptor.isRepeated()) { 124 if (!(value instanceof RubyArray)) 125 throw runtime.newArgumentError( 126 "Expected array as initializer value for repeated field '" 127 + key.asJavaString() 128 + "' (given " 129 + value.getMetaClass() 130 + ")."); 131 fields.put(fieldDescriptor, rubyToRepeatedField(context, fieldDescriptor, value)); 132 } else { 133 OneofDescriptor oneof = fieldDescriptor.getContainingOneof(); 134 if (oneof != null) { 135 oneofCases.put(oneof, fieldDescriptor); 136 } 137 138 if (value instanceof RubyHash 139 && fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE) { 140 RubyDescriptor descriptor = 141 (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 142 RubyClass typeClass = (RubyClass) descriptor.msgclass(context); 143 value = (IRubyObject) typeClass.newInstance(context, value, Block.NULL_BLOCK); 144 fields.put(fieldDescriptor, value); 145 } else { 146 indexSet(context, key, value); 147 } 148 } 149 } 150 }, 151 null); 152 } 153 return this; 154 } 155 156 /* 157 * call-seq: 158 * Message.[]=(index, value) 159 * 160 * Sets a field's value by field name. The provided field name should be a 161 * string. 162 */ 163 @JRubyMethod(name = "[]=") indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value)164 public IRubyObject indexSet(ThreadContext context, IRubyObject fieldName, IRubyObject value) { 165 FieldDescriptor fieldDescriptor = findField(context, fieldName); 166 return setFieldInternal(context, fieldDescriptor, value); 167 } 168 169 /* 170 * call-seq: 171 * Message.[](index) => value 172 * 173 * Accesses a field's value by field name. The provided field name should be a 174 * string. 175 */ 176 @JRubyMethod(name = "[]") index(ThreadContext context, IRubyObject fieldName)177 public IRubyObject index(ThreadContext context, IRubyObject fieldName) { 178 FieldDescriptor fieldDescriptor = findField(context, fieldName); 179 return getFieldInternal(context, fieldDescriptor); 180 } 181 182 /* 183 * call-seq: 184 * Message.inspect => string 185 * 186 * Returns a human-readable string representing this message. It will be 187 * formatted as "<MessageType: field1: value1, field2: value2, ...>". Each 188 * field's value is represented according to its own #inspect method. 189 */ 190 @JRubyMethod(name = {"inspect", "to_s"}) inspect()191 public IRubyObject inspect() { 192 ThreadContext context = getRuntime().getCurrentContext(); 193 String cname = metaClass.getName(); 194 String colon = ": "; 195 String comma = ", "; 196 StringBuilder sb = new StringBuilder("<"); 197 boolean addComma = false; 198 199 sb.append(cname).append(colon); 200 201 for (FieldDescriptor fd : descriptor.getFields()) { 202 if (fd.hasPresence() && !(fields.containsKey(fd) || builder.hasField(fd))) { 203 continue; 204 } 205 if (addComma) { 206 sb.append(comma); 207 } else { 208 addComma = true; 209 } 210 211 sb.append(fd.getName()).append(colon); 212 213 IRubyObject value = getFieldInternal(context, fd); 214 if (value instanceof RubyBoolean) { 215 // Booleans don't implement internal "inspect" methods so have to call handle them manually 216 sb.append(value.isTrue() ? "true" : "false"); 217 } else { 218 sb.append(value.inspect()); 219 } 220 } 221 sb.append(">"); 222 223 return context.runtime.newString(sb.toString()); 224 } 225 226 /* 227 * call-seq: 228 * Message.hash => hash_value 229 * 230 * Returns a hash value that represents this message's field values. 231 */ 232 @JRubyMethod hash(ThreadContext context)233 public IRubyObject hash(ThreadContext context) { 234 try { 235 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 236 for (FieldDescriptor fd : descriptor.getFields()) { 237 digest.update((byte) getFieldInternal(context, fd).hashCode()); 238 } 239 return context.runtime.newFixnum(ByteBuffer.wrap(digest.digest()).getLong()); 240 } catch (NoSuchAlgorithmException ignore) { 241 return context.runtime.newFixnum(System.identityHashCode(this)); 242 } 243 } 244 245 /* 246 * call-seq: 247 * Message.==(other) => boolean 248 * 249 * Performs a deep comparison of this message with another. Messages are equal 250 * if they have the same type and if each field is equal according to the :== 251 * method's semantics (a more efficient comparison may actually be done if the 252 * field is of a primitive type). 253 */ 254 @JRubyMethod(name = {"==", "eql?"}) eq(ThreadContext context, IRubyObject other)255 public IRubyObject eq(ThreadContext context, IRubyObject other) { 256 Ruby runtime = context.runtime; 257 if (!(other instanceof RubyMessage)) return runtime.getFalse(); 258 RubyMessage message = (RubyMessage) other; 259 if (descriptor != message.descriptor) { 260 return runtime.getFalse(); 261 } 262 263 for (FieldDescriptor fdef : descriptor.getFields()) { 264 IRubyObject thisVal = getFieldInternal(context, fdef); 265 IRubyObject thatVal = message.getFieldInternal(context, fdef); 266 IRubyObject ret = thisVal.callMethod(context, "==", thatVal); 267 if (!ret.isTrue()) { 268 return runtime.getFalse(); 269 } 270 } 271 return runtime.getTrue(); 272 } 273 274 /* 275 * call-seq: 276 * Message.respond_to?(method_name, search_private_and_protected) => boolean 277 * 278 * Parallels method_missing, returning true when this object implements a method with the given 279 * method_name. 280 */ 281 @JRubyMethod(name = "respond_to?", required = 1, optional = 1) respondTo(ThreadContext context, IRubyObject[] args)282 public IRubyObject respondTo(ThreadContext context, IRubyObject[] args) { 283 String methodName = args[0].asJavaString(); 284 if (descriptor.findFieldByName(methodName) != null) { 285 return context.runtime.getTrue(); 286 } 287 RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); 288 IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]); 289 if (!oneofDescriptor.isNil()) { 290 return context.runtime.getTrue(); 291 } 292 if (methodName.startsWith(CLEAR_PREFIX)) { 293 String strippedMethodName = methodName.substring(6); 294 oneofDescriptor = 295 rubyDescriptor.lookupOneof(context, context.runtime.newSymbol(strippedMethodName)); 296 if (!oneofDescriptor.isNil()) { 297 return context.runtime.getTrue(); 298 } 299 300 if (descriptor.findFieldByName(strippedMethodName) != null) { 301 return context.runtime.getTrue(); 302 } 303 } 304 if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) { 305 String strippedMethodName = methodName.substring(4, methodName.length() - 1); 306 FieldDescriptor fieldDescriptor = descriptor.findFieldByName(strippedMethodName); 307 if (fieldDescriptor != null && fieldDescriptor.hasPresence()) { 308 return context.runtime.getTrue(); 309 } 310 oneofDescriptor = 311 rubyDescriptor.lookupOneof( 312 context, RubyString.newString(context.runtime, strippedMethodName)); 313 if (!oneofDescriptor.isNil()) { 314 return context.runtime.getTrue(); 315 } 316 } 317 if (methodName.endsWith(AS_VALUE_SUFFIX)) { 318 FieldDescriptor fieldDescriptor = 319 descriptor.findFieldByName(methodName.substring(0, methodName.length() - 9)); 320 if (fieldDescriptor != null && isWrappable(fieldDescriptor)) { 321 return context.runtime.getTrue(); 322 } 323 } 324 if (methodName.endsWith(CONST_SUFFIX)) { 325 FieldDescriptor fieldDescriptor = 326 descriptor.findFieldByName(methodName.substring(0, methodName.length() - 6)); 327 if (fieldDescriptor != null) { 328 if (fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) { 329 return context.runtime.getTrue(); 330 } 331 } 332 } 333 if (methodName.endsWith(Utils.EQUAL_SIGN)) { 334 String strippedMethodName = methodName.substring(0, methodName.length() - 1); 335 FieldDescriptor fieldDescriptor = descriptor.findFieldByName(strippedMethodName); 336 if (fieldDescriptor != null) { 337 return context.runtime.getTrue(); 338 } 339 if (strippedMethodName.endsWith(AS_VALUE_SUFFIX)) { 340 strippedMethodName = methodName.substring(0, strippedMethodName.length() - 9); 341 fieldDescriptor = descriptor.findFieldByName(strippedMethodName); 342 if (fieldDescriptor != null && isWrappable(fieldDescriptor)) { 343 return context.runtime.getTrue(); 344 } 345 } 346 } 347 boolean includePrivate = false; 348 if (args.length == 2) { 349 includePrivate = context.runtime.getTrue().equals(args[1]); 350 } 351 return metaClass.respondsToMethod(methodName, includePrivate) 352 ? context.runtime.getTrue() 353 : context.runtime.getFalse(); 354 } 355 356 /* 357 * call-seq: 358 * Message.method_missing(*args) 359 * 360 * Provides accessors and setters and methods to clear and check for presence of 361 * message fields according to their field names. 362 * 363 * For any field whose name does not conflict with a built-in method, an 364 * accessor is provided with the same name as the field, and a setter is 365 * provided with the name of the field plus the '=' suffix. Thus, given a 366 * message instance 'msg' with field 'foo', the following code is valid: 367 * 368 * msg.foo = 42 369 * puts msg.foo 370 * 371 * This method also provides read-only accessors for oneofs. If a oneof exists 372 * with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to 373 * the name of the field in that oneof that is currently set, or nil if none. 374 * 375 * It also provides methods of the form 'clear_fieldname' to clear the value 376 * of the field 'fieldname'. For basic data types, this will set the default 377 * value of the field. 378 * 379 * Additionally, it provides methods of the form 'has_fieldname?', which returns 380 * true if the field 'fieldname' is set in the message object, else false. For 381 * 'proto3' syntax, calling this for a basic type field will result in an error. 382 */ 383 @JRubyMethod(name = "method_missing", rest = true) methodMissing(ThreadContext context, IRubyObject[] args)384 public IRubyObject methodMissing(ThreadContext context, IRubyObject[] args) { 385 Ruby runtime = context.runtime; 386 String methodName = args[0].asJavaString(); 387 RubyDescriptor rubyDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); 388 389 if (args.length == 1) { 390 // If we find a Oneof return it's name (use lookupOneof because it has an index) 391 IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, args[0]); 392 393 if (!oneofDescriptor.isNil()) { 394 RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor; 395 OneofDescriptor ood = rubyOneofDescriptor.getDescriptor(); 396 397 // Check to see if we set this through ruby 398 FieldDescriptor fieldDescriptor = oneofCases.get(ood); 399 400 if (fieldDescriptor == null) { 401 // See if we set this from decoding a message 402 fieldDescriptor = builder.getOneofFieldDescriptor(ood); 403 404 if (fieldDescriptor == null) { 405 return context.nil; 406 } else { 407 // Cache it so we don't need to do multiple checks next time 408 oneofCases.put(ood, fieldDescriptor); 409 return runtime.newSymbol(fieldDescriptor.getName()); 410 } 411 } else { 412 return runtime.newSymbol(fieldDescriptor.getName()); 413 } 414 } 415 416 // If we find a field return its value 417 FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName); 418 419 if (fieldDescriptor != null) { 420 return getFieldInternal(context, fieldDescriptor); 421 } 422 423 if (methodName.startsWith(CLEAR_PREFIX)) { 424 methodName = methodName.substring(6); 425 oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName)); 426 if (!oneofDescriptor.isNil()) { 427 fieldDescriptor = oneofCases.get(((RubyOneofDescriptor) oneofDescriptor).getDescriptor()); 428 if (fieldDescriptor == null) { 429 // Clearing an already cleared oneof; return here to avoid NoMethodError. 430 return context.nil; 431 } 432 } 433 434 if (fieldDescriptor == null) { 435 fieldDescriptor = descriptor.findFieldByName(methodName); 436 } 437 438 if (fieldDescriptor != null) { 439 return clearFieldInternal(context, fieldDescriptor); 440 } 441 442 } else if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) { 443 methodName = 444 methodName.substring( 445 4, methodName.length() - 1); // Trim "has_" and "?" off the field name 446 oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName)); 447 if (!oneofDescriptor.isNil()) { 448 RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor; 449 return oneofCases.containsKey(rubyOneofDescriptor.getDescriptor()) 450 ? runtime.getTrue() 451 : runtime.getFalse(); 452 } 453 454 fieldDescriptor = descriptor.findFieldByName(methodName); 455 456 if (fieldDescriptor != null && fieldDescriptor.hasPresence()) { 457 return fields.containsKey(fieldDescriptor) ? runtime.getTrue() : runtime.getFalse(); 458 } 459 460 } else if (methodName.endsWith(AS_VALUE_SUFFIX)) { 461 methodName = methodName.substring(0, methodName.length() - 9); 462 fieldDescriptor = descriptor.findFieldByName(methodName); 463 464 if (fieldDescriptor != null && isWrappable(fieldDescriptor)) { 465 IRubyObject value = getFieldInternal(context, fieldDescriptor); 466 467 if (!value.isNil() && value instanceof RubyMessage) { 468 return ((RubyMessage) value).index(context, runtime.newString("value")); 469 } 470 471 return value; 472 } 473 474 } else if (methodName.endsWith(CONST_SUFFIX)) { 475 methodName = methodName.substring(0, methodName.length() - 6); 476 fieldDescriptor = descriptor.findFieldByName(methodName); 477 if (fieldDescriptor != null && fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) { 478 IRubyObject enumValue = getFieldInternal(context, fieldDescriptor); 479 480 if (!enumValue.isNil()) { 481 EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); 482 if (enumValue instanceof RubyRepeatedField) { 483 RubyArray values = (RubyArray) ((RubyRepeatedField) enumValue).toArray(context); 484 RubyArray retValues = runtime.newArray(values.getLength()); 485 for (int i = 0; i < values.getLength(); i++) { 486 String val = values.eltInternal(i).toString(); 487 retValues.store( 488 (long) i, runtime.newFixnum(enumDescriptor.findValueByName(val).getNumber())); 489 } 490 return retValues; 491 } 492 493 return runtime.newFixnum( 494 enumDescriptor.findValueByName(enumValue.asJavaString()).getNumber()); 495 } 496 } 497 } 498 499 } else if (args.length == 2 && methodName.endsWith(Utils.EQUAL_SIGN)) { 500 501 methodName = methodName.substring(0, methodName.length() - 1); // Trim equals sign 502 FieldDescriptor fieldDescriptor = descriptor.findFieldByName(methodName); 503 if (fieldDescriptor != null) { 504 return setFieldInternal(context, fieldDescriptor, args[1]); 505 } 506 507 IRubyObject oneofDescriptor = 508 rubyDescriptor.lookupOneof(context, RubyString.newString(context.runtime, methodName)); 509 if (!oneofDescriptor.isNil()) { 510 throw runtime.newRuntimeError("Oneof accessors are read-only."); 511 } 512 513 if (methodName.endsWith(AS_VALUE_SUFFIX)) { 514 methodName = methodName.substring(0, methodName.length() - 9); 515 516 fieldDescriptor = descriptor.findFieldByName(methodName); 517 518 if (fieldDescriptor != null && isWrappable(fieldDescriptor)) { 519 if (args[1].isNil()) { 520 return setFieldInternal(context, fieldDescriptor, args[1]); 521 } 522 523 RubyClass typeClass = 524 (RubyClass) 525 ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)) 526 .msgclass(context); 527 RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK); 528 msg.indexSet(context, runtime.newString("value"), args[1]); 529 return setFieldInternal(context, fieldDescriptor, msg); 530 } 531 } 532 } 533 534 return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK); 535 } 536 537 /** 538 * call-seq: Message.dup => new_message Performs a shallow copy of this message and returns the 539 * new copy. 540 */ 541 @JRubyMethod dup(ThreadContext context)542 public IRubyObject dup(ThreadContext context) { 543 RubyMessage dup = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK); 544 for (FieldDescriptor fieldDescriptor : this.descriptor.getFields()) { 545 if (fieldDescriptor.isRepeated()) { 546 dup.fields.put(fieldDescriptor, this.getRepeatedField(context, fieldDescriptor)); 547 } else if (fields.containsKey(fieldDescriptor)) { 548 dup.setFieldInternal(context, fieldDescriptor, fields.get(fieldDescriptor)); 549 } else if (this.builder.hasField(fieldDescriptor)) { 550 dup.setFieldInternal( 551 context, 552 fieldDescriptor, 553 wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor))); 554 } 555 } 556 return dup; 557 } 558 559 /* 560 * call-seq: 561 * Message.descriptor => descriptor 562 * 563 * Class method that returns the Descriptor instance corresponding to this 564 * message class's type. 565 */ 566 @JRubyMethod(name = "descriptor", meta = true) getDescriptor(ThreadContext context, IRubyObject recv)567 public static IRubyObject getDescriptor(ThreadContext context, IRubyObject recv) { 568 return ((RubyClass) recv).getInstanceVariable(Utils.DESCRIPTOR_INSTANCE_VAR); 569 } 570 571 /* 572 * call-seq: 573 * MessageClass.encode(msg, options = {}) => bytes 574 * 575 * Encodes the given message object to its serialized form in protocol buffers 576 * wire format. 577 * @param options [Hash] options for the encoder 578 * recursion_limit: set to maximum encoding depth for message (default is 64) 579 */ 580 @JRubyMethod(required = 1, optional = 1, meta = true) encode(ThreadContext context, IRubyObject recv, IRubyObject[] args)581 public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject[] args) { 582 if (recv != args[0].getMetaClass()) { 583 throw context.runtime.newArgumentError( 584 "Tried to encode a " + args[0].getMetaClass() + " message with " + recv); 585 } 586 RubyMessage message = (RubyMessage) args[0]; 587 int recursionLimitInt = SINK_MAXIMUM_NESTING; 588 589 if (args.length > 1) { 590 RubyHash options = (RubyHash) args[1]; 591 IRubyObject recursionLimit = options.fastARef(context.runtime.newSymbol("recursion_limit")); 592 593 if (recursionLimit != null) { 594 recursionLimitInt = ((RubyNumeric) recursionLimit).getIntValue(); 595 } 596 } 597 return context.runtime.newString( 598 new ByteList(message.build(context, 0, recursionLimitInt).toByteArray())); 599 } 600 601 /* 602 * call-seq: 603 * MessageClass.decode(data, options = {}) => message 604 * 605 * Decodes the given data (as a string containing bytes in protocol buffers wire 606 * format) under the interpretation given by this message class's definition 607 * and returns a message object with the corresponding field values. 608 * @param options [Hash] options for the decoder 609 * recursion_limit: set to maximum decoding depth for message (default is 100) 610 */ 611 @JRubyMethod(required = 1, optional = 1, meta = true) decode(ThreadContext context, IRubyObject recv, IRubyObject[] args)612 public static IRubyObject decode(ThreadContext context, IRubyObject recv, IRubyObject[] args) { 613 IRubyObject data = args[0]; 614 byte[] bin = data.convertToString().getBytes(); 615 CodedInputStream input = CodedInputStream.newInstance(bin); 616 RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK); 617 618 if (args.length == 2) { 619 if (!(args[1] instanceof RubyHash)) { 620 throw context.runtime.newArgumentError("Expected hash arguments."); 621 } 622 623 IRubyObject recursionLimit = 624 ((RubyHash) args[1]).fastARef(context.runtime.newSymbol("recursion_limit")); 625 if (recursionLimit != null) { 626 input.setRecursionLimit(((RubyNumeric) recursionLimit).getIntValue()); 627 } 628 } 629 return decodeBytes(context, ret, input, /*freeze*/ false); 630 } 631 decodeBytes( ThreadContext context, RubyMessage ret, CodedInputStream input, boolean freeze)632 public static IRubyObject decodeBytes( 633 ThreadContext context, RubyMessage ret, CodedInputStream input, boolean freeze) { 634 try { 635 ret.builder.mergeFrom(input, RubyDescriptorPool.registry); 636 } catch (Exception e) { 637 throw RaiseException.from( 638 context.runtime, 639 (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"), 640 e.getMessage()); 641 } 642 643 ret.builder 644 .getUnknownFields() 645 .asMap() 646 .forEach( 647 (i, values) -> { 648 FieldDescriptor fd = ret.builder.getDescriptorForType().findFieldByNumber(i); 649 if (fd != null && fd.isRepeated() && fd.getType() == FieldDescriptor.Type.ENUM) { 650 // Need to reset unknown values in repeated enum fields 651 if (fd.legacyEnumFieldTreatedAsClosed()) { 652 EnumDescriptor ed = fd.getEnumType(); 653 values 654 .getVarintList() 655 .forEach( 656 value -> { 657 ret.builder.addRepeatedField( 658 fd, ed.findValueByNumberCreatingIfUnknown(value.intValue())); 659 }); 660 } 661 } 662 }); 663 if (freeze) { 664 ret.freeze(context); 665 } 666 return ret; 667 } 668 669 /* 670 * call-seq: 671 * MessageClass.encode_json(msg, options = {}) => json_string 672 * 673 * Encodes the given message object into its serialized JSON representation. 674 * @param options [Hash] options for the decoder 675 * preserve_proto_fieldnames: set true to use original fieldnames (default is to camelCase) 676 * emit_defaults: set true to emit 0/false values (default is to omit them) 677 * format_enums_as_integers: set true to emit enum values as integer (default is string) 678 */ 679 @JRubyMethod(name = "encode_json", required = 1, optional = 1, meta = true) encodeJson( ThreadContext context, IRubyObject recv, IRubyObject[] args)680 public static IRubyObject encodeJson( 681 ThreadContext context, IRubyObject recv, IRubyObject[] args) { 682 Ruby runtime = context.runtime; 683 RubyMessage message = (RubyMessage) args[0]; 684 JsonFormat.Printer printer = JsonFormat.printer().omittingInsignificantWhitespace(); 685 String result; 686 687 if (args.length > 1) { 688 RubyHash options; 689 if (args[1] instanceof RubyHash) { 690 options = (RubyHash) args[1]; 691 } else if (args[1].respondsTo("to_h")) { 692 options = (RubyHash) args[1].callMethod(context, "to_h"); 693 } else { 694 throw runtime.newArgumentError("Expected hash arguments."); 695 } 696 697 IRubyObject emitDefaults = options.fastARef(runtime.newSymbol("emit_defaults")); 698 IRubyObject preserveNames = options.fastARef(runtime.newSymbol("preserve_proto_fieldnames")); 699 IRubyObject printingEnumsAsInts = 700 options.fastARef(runtime.newSymbol("format_enums_as_integers")); 701 702 if (emitDefaults != null && emitDefaults.isTrue()) { 703 printer = printer.alwaysPrintFieldsWithNoPresence(); 704 } 705 706 if (preserveNames != null && preserveNames.isTrue()) { 707 printer = printer.preservingProtoFieldNames(); 708 } 709 710 if (printingEnumsAsInts != null && printingEnumsAsInts.isTrue()) { 711 printer = printer.printingEnumsAsInts(); 712 } 713 } 714 printer = 715 printer.usingTypeRegistry( 716 JsonFormat.TypeRegistry.newBuilder().add(message.descriptor).build()); 717 718 try { 719 result = printer.print(message.build(context, 0, SINK_MAXIMUM_NESTING)); 720 } catch (InvalidProtocolBufferException e) { 721 throw runtime.newRuntimeError(e.getMessage()); 722 } catch (IllegalArgumentException e) { 723 throw createParseError(context, e.getMessage()); 724 } 725 726 return runtime.newString(result); 727 } 728 729 /* 730 * call-seq: 731 * MessageClass.decode_json(data, options = {}) => message 732 * 733 * Decodes the given data (as a string containing bytes in protocol buffers wire 734 * format) under the interpretation given by this message class's definition 735 * and returns a message object with the corresponding field values. 736 * 737 * @param options [Hash] options for the decoder 738 * ignore_unknown_fields: set true to ignore unknown fields (default is to 739 * raise an error) 740 */ 741 @JRubyMethod(name = "decode_json", required = 1, optional = 1, meta = true) decodeJson( ThreadContext context, IRubyObject recv, IRubyObject[] args)742 public static IRubyObject decodeJson( 743 ThreadContext context, IRubyObject recv, IRubyObject[] args) { 744 Ruby runtime = context.runtime; 745 boolean ignoreUnknownFields = false; 746 IRubyObject data = args[0]; 747 JsonFormat.Parser parser = JsonFormat.parser(); 748 749 if (args.length == 2) { 750 if (!(args[1] instanceof RubyHash)) { 751 throw runtime.newArgumentError("Expected hash arguments."); 752 } 753 754 IRubyObject ignoreSetting = 755 ((RubyHash) args[1]).fastARef(runtime.newSymbol("ignore_unknown_fields")); 756 if (ignoreSetting != null && ignoreSetting.isTrue()) { 757 parser = parser.ignoringUnknownFields(); 758 } 759 } 760 761 if (!(data instanceof RubyString)) { 762 throw runtime.newArgumentError("Expected string for JSON data."); 763 } 764 765 RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK); 766 parser = 767 parser.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(ret.descriptor).build()); 768 769 try { 770 parser.merge(data.asJavaString(), ret.builder); 771 } catch (InvalidProtocolBufferException e) { 772 throw createParseError(context, e.getMessage().replace("Cannot find", "No such")); 773 } 774 775 if (isWrapper(ret.descriptor)) { 776 throw runtime.newRuntimeError( 777 "Parsing a wrapper type from JSON at the top level does not work."); 778 } 779 780 return ret; 781 } 782 783 @JRubyMethod(name = "to_h") toHash(ThreadContext context)784 public IRubyObject toHash(ThreadContext context) { 785 Ruby runtime = context.runtime; 786 RubyHash ret = RubyHash.newHash(runtime); 787 build(context, 0, SINK_MAXIMUM_NESTING); // Sync Ruby data to the Builder object. 788 for (Map.Entry<FieldDescriptor, Object> field : builder.getAllFields().entrySet()) { 789 FieldDescriptor fdef = field.getKey(); 790 IRubyObject value = getFieldInternal(context, fdef, !fdef.hasPresence()); 791 792 if (fdef.isRepeated() && !fdef.isMapField()) { 793 if (fdef.getType() != FieldDescriptor.Type.MESSAGE) { 794 value = Helpers.invoke(context, value, "to_a"); 795 } else { 796 RubyArray ary = value.convertToArray(); 797 for (int i = 0; i < ary.size(); i++) { 798 IRubyObject submsg = Helpers.invoke(context, ary.eltInternal(i), "to_h"); 799 ary.eltInternalSet(i, submsg); 800 } 801 802 value = ary.to_ary(); 803 } 804 } else if (value.respondsTo("to_h")) { 805 value = Helpers.invoke(context, value, "to_h"); 806 } else if (value.respondsTo("to_a")) { 807 value = Helpers.invoke(context, value, "to_a"); 808 } 809 ret.fastASet(runtime.newSymbol(fdef.getName()), value); 810 } 811 return ret; 812 } 813 814 @JRubyMethod freeze(ThreadContext context)815 public IRubyObject freeze(ThreadContext context) { 816 if (isFrozen()) { 817 return this; 818 } 819 setFrozen(true); 820 for (FieldDescriptor fdef : descriptor.getFields()) { 821 if (fdef.isMapField()) { 822 ((RubyMap) fields.get(fdef)).freeze(context); 823 } else if (fdef.isRepeated()) { 824 this.getRepeatedField(context, fdef).freeze(context); 825 } else if (fields.containsKey(fdef)) { 826 if (fdef.getType() == FieldDescriptor.Type.MESSAGE) { 827 ((RubyMessage) fields.get(fdef)).freeze(context); 828 } 829 } 830 } 831 return this; 832 } 833 build(ThreadContext context, int depth, int recursionLimit)834 protected DynamicMessage build(ThreadContext context, int depth, int recursionLimit) { 835 if (depth >= recursionLimit) { 836 throw context.runtime.newRuntimeError("Recursion limit exceeded during encoding."); 837 } 838 839 RubySymbol typeBytesSymbol = RubySymbol.newSymbol(context.runtime, "TYPE_BYTES"); 840 841 // Handle the typical case where the fields.keySet contain the fieldDescriptors 842 for (FieldDescriptor fieldDescriptor : fields.keySet()) { 843 IRubyObject value = fields.get(fieldDescriptor); 844 845 if (value instanceof RubyMap) { 846 builder.clearField(fieldDescriptor); 847 RubyDescriptor mapDescriptor = 848 (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 849 for (DynamicMessage kv : 850 ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { 851 builder.addRepeatedField(fieldDescriptor, kv); 852 } 853 854 } else if (value instanceof RubyRepeatedField) { 855 RubyRepeatedField repeatedField = (RubyRepeatedField) value; 856 857 builder.clearField(fieldDescriptor); 858 for (int i = 0; i < repeatedField.size(); i++) { 859 Object item = 860 convert( 861 context, 862 fieldDescriptor, 863 repeatedField.get(i), 864 depth, 865 recursionLimit, 866 /*isDefaultValueForBytes*/ false); 867 builder.addRepeatedField(fieldDescriptor, item); 868 } 869 870 } else if (!value.isNil()) { 871 /** 872 * Detect the special case where default_value strings are provided for byte fields. If so, 873 * disable normal string encoding behavior within convert. For a more detailed explanation 874 * of other possible workarounds, see the comments above {@code 875 * com.google.protobuf.Internal#stringDefaultValue() stringDefaultValue}. 876 */ 877 boolean isDefaultStringForBytes = false; 878 if (DEFAULT_VALUE.equals(fieldDescriptor.getFullName())) { 879 FieldDescriptor enumFieldDescriptorForType = 880 this.builder.getDescriptorForType().findFieldByName(TYPE); 881 if (typeBytesSymbol.equals(fields.get(enumFieldDescriptorForType))) { 882 isDefaultStringForBytes = true; 883 } 884 } 885 builder.setField( 886 fieldDescriptor, 887 convert( 888 context, fieldDescriptor, value, depth, recursionLimit, isDefaultStringForBytes)); 889 } 890 } 891 892 // Handle cases where {@code fields} doesn't contain the value until after getFieldInternal 893 // is called - typical of a deserialized message. Skip non-maps and descriptors that already 894 // have an entry in {@code fields}. 895 for (FieldDescriptor fieldDescriptor : descriptor.getFields()) { 896 if (!fieldDescriptor.isMapField()) { 897 continue; 898 } 899 IRubyObject value = fields.get(fieldDescriptor); 900 if (value != null) { 901 continue; 902 } 903 value = getFieldInternal(context, fieldDescriptor); 904 if (value instanceof RubyMap) { 905 builder.clearField(fieldDescriptor); 906 RubyDescriptor mapDescriptor = 907 (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 908 for (DynamicMessage kv : 909 ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { 910 builder.addRepeatedField(fieldDescriptor, kv); 911 } 912 } 913 } 914 return builder.build(); 915 } 916 917 // Internal use only, called by Google::Protobuf.deep_copy deepCopy(ThreadContext context)918 protected IRubyObject deepCopy(ThreadContext context) { 919 RubyMessage copy = (RubyMessage) metaClass.newInstance(context, Block.NULL_BLOCK); 920 for (FieldDescriptor fdef : descriptor.getFields()) { 921 if (fdef.isRepeated()) { 922 copy.fields.put(fdef, this.getRepeatedField(context, fdef).deepCopy(context)); 923 } else if (fields.containsKey(fdef)) { 924 if (fdef.getType() == FieldDescriptor.Type.MESSAGE) { 925 copy.setFieldInternal(context, fdef, ((RubyMessage) fields.get(fdef)).deepCopy(context)); 926 } else { 927 copy.setFieldInternal(context, fdef, fields.get(fdef)); 928 } 929 } else if (builder.hasField(fdef)) { 930 copy.fields.put(fdef, wrapField(context, fdef, builder.getField(fdef))); 931 } 932 } 933 return copy; 934 } 935 clearField(ThreadContext context, FieldDescriptor fieldDescriptor)936 protected IRubyObject clearField(ThreadContext context, FieldDescriptor fieldDescriptor) { 937 validateMessageType(context, fieldDescriptor, "clear"); 938 return clearFieldInternal(context, fieldDescriptor); 939 } 940 discardUnknownFields(ThreadContext context)941 protected void discardUnknownFields(ThreadContext context) { 942 discardUnknownFields(context, builder); 943 } 944 getField(ThreadContext context, FieldDescriptor fieldDescriptor)945 protected IRubyObject getField(ThreadContext context, FieldDescriptor fieldDescriptor) { 946 validateMessageType(context, fieldDescriptor, "get"); 947 return getFieldInternal(context, fieldDescriptor); 948 } 949 hasField(ThreadContext context, FieldDescriptor fieldDescriptor)950 protected IRubyObject hasField(ThreadContext context, FieldDescriptor fieldDescriptor) { 951 validateMessageType(context, fieldDescriptor, "has?"); 952 if (!fieldDescriptor.hasPresence()) { 953 throw context.runtime.newArgumentError("does not track presence"); 954 } 955 return fields.containsKey(fieldDescriptor) 956 ? context.runtime.getTrue() 957 : context.runtime.getFalse(); 958 } 959 setField( ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value)960 protected IRubyObject setField( 961 ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { 962 validateMessageType(context, fieldDescriptor, "set"); 963 return setFieldInternal(context, fieldDescriptor, value); 964 } 965 setField( ThreadContext context, RubyFieldDescriptor fieldDescriptor, IRubyObject value)966 protected IRubyObject setField( 967 ThreadContext context, RubyFieldDescriptor fieldDescriptor, IRubyObject value) { 968 validateMessageType(context, fieldDescriptor.getDescriptor(), "set"); 969 return setFieldInternal(context, fieldDescriptor.getDescriptor(), fieldDescriptor, value); 970 } 971 getRepeatedField( ThreadContext context, FieldDescriptor fieldDescriptor)972 private RubyRepeatedField getRepeatedField( 973 ThreadContext context, FieldDescriptor fieldDescriptor) { 974 if (fields.containsKey(fieldDescriptor)) { 975 return (RubyRepeatedField) fields.get(fieldDescriptor); 976 } 977 int count = this.builder.getRepeatedFieldCount(fieldDescriptor); 978 RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor); 979 for (int i = 0; i < count; i++) { 980 ret.push( 981 context, 982 new IRubyObject[] { 983 wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)) 984 }); 985 } 986 fields.put(fieldDescriptor, ret); 987 return ret; 988 } 989 buildFrom(ThreadContext context, DynamicMessage dynamicMessage)990 private IRubyObject buildFrom(ThreadContext context, DynamicMessage dynamicMessage) { 991 this.builder.mergeFrom(dynamicMessage); 992 return this; 993 } 994 clearFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor)995 private IRubyObject clearFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) { 996 OneofDescriptor ood = fieldDescriptor.getContainingOneof(); 997 if (ood != null) oneofCases.remove(ood); 998 fields.remove(fieldDescriptor); 999 builder.clearField(fieldDescriptor); 1000 return context.nil; 1001 } 1002 discardUnknownFields(ThreadContext context, Message.Builder messageBuilder)1003 private void discardUnknownFields(ThreadContext context, Message.Builder messageBuilder) { 1004 messageBuilder.setUnknownFields(UnknownFieldSet.getDefaultInstance()); 1005 messageBuilder 1006 .getAllFields() 1007 .forEach( 1008 (fd, value) -> { 1009 if (fd.getType() == FieldDescriptor.Type.MESSAGE) { 1010 if (fd.isRepeated()) { 1011 messageBuilder.clearField(fd); 1012 ((List) value) 1013 .forEach( 1014 (val) -> { 1015 Message.Builder submessageBuilder = ((DynamicMessage) val).toBuilder(); 1016 discardUnknownFields(context, submessageBuilder); 1017 messageBuilder.addRepeatedField(fd, submessageBuilder.build()); 1018 }); 1019 } else { 1020 Message.Builder submessageBuilder = ((DynamicMessage) value).toBuilder(); 1021 discardUnknownFields(context, submessageBuilder); 1022 messageBuilder.setField(fd, submessageBuilder.build()); 1023 } 1024 } 1025 }); 1026 } 1027 findField(ThreadContext context, IRubyObject fieldName)1028 private FieldDescriptor findField(ThreadContext context, IRubyObject fieldName) { 1029 return findField(context, fieldName, false); 1030 } 1031 findField( ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField)1032 private FieldDescriptor findField( 1033 ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField) { 1034 String nameStr = fieldName.asJavaString(); 1035 FieldDescriptor ret = this.descriptor.findFieldByName(nameStr); 1036 if (ret == null && !ignoreUnknownField) { 1037 throw context.runtime.newArgumentError("field " + fieldName.asJavaString() + " is not found"); 1038 } 1039 return ret; 1040 } 1041 1042 // convert a ruby object to protobuf type, skip type check since it is checked on the way in convert( ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value, int depth, int recursionLimit, boolean isDefaultStringForBytes)1043 private Object convert( 1044 ThreadContext context, 1045 FieldDescriptor fieldDescriptor, 1046 IRubyObject value, 1047 int depth, 1048 int recursionLimit, 1049 boolean isDefaultStringForBytes) { 1050 Object val = null; 1051 switch (fieldDescriptor.getType()) { 1052 case INT32: 1053 case SFIXED32: 1054 case SINT32: 1055 val = RubyNumeric.num2int(value); 1056 break; 1057 case INT64: 1058 case SFIXED64: 1059 case SINT64: 1060 val = RubyNumeric.num2long(value); 1061 break; 1062 case FIXED32: 1063 case UINT32: 1064 val = Utils.num2uint(value); 1065 break; 1066 case FIXED64: 1067 case UINT64: 1068 val = Utils.num2ulong(context.runtime, value); 1069 break; 1070 case FLOAT: 1071 val = (float) RubyNumeric.num2dbl(value); 1072 break; 1073 case DOUBLE: 1074 val = (double) RubyNumeric.num2dbl(value); 1075 break; 1076 case BOOL: 1077 val = value.isTrue(); 1078 break; 1079 case BYTES: 1080 val = ByteString.copyFrom(((RubyString) value).getBytes()); 1081 break; 1082 case STRING: 1083 if (isDefaultStringForBytes) { 1084 val = ((RubyString) value).getByteList().toString(); 1085 } else { 1086 val = value.asJavaString(); 1087 } 1088 break; 1089 case MESSAGE: 1090 val = ((RubyMessage) value).build(context, depth + 1, recursionLimit); 1091 break; 1092 case ENUM: 1093 EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); 1094 if (Utils.isRubyNum(value)) { 1095 val = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); 1096 } else { 1097 val = enumDescriptor.findValueByName(value.asJavaString()); 1098 } 1099 break; 1100 default: 1101 break; 1102 } 1103 1104 return val; 1105 } 1106 createParseError(ThreadContext context, String message)1107 private static RaiseException createParseError(ThreadContext context, String message) { 1108 if (parseErrorClass == null) { 1109 parseErrorClass = 1110 (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"); 1111 } 1112 return RaiseException.from(context.runtime, parseErrorClass, message); 1113 } 1114 wrapField( ThreadContext context, FieldDescriptor fieldDescriptor, Object value)1115 private IRubyObject wrapField( 1116 ThreadContext context, FieldDescriptor fieldDescriptor, Object value) { 1117 return wrapField(context, fieldDescriptor, value, false); 1118 } 1119 wrapField( ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes)1120 private IRubyObject wrapField( 1121 ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes) { 1122 if (value == null) { 1123 return context.runtime.getNil(); 1124 } 1125 Ruby runtime = context.runtime; 1126 1127 switch (fieldDescriptor.getType()) { 1128 case INT32: 1129 case INT64: 1130 case FIXED32: 1131 case SINT32: 1132 case FIXED64: 1133 case SINT64: 1134 case SFIXED64: 1135 case SFIXED32: 1136 case UINT32: 1137 case UINT64: 1138 case FLOAT: 1139 case DOUBLE: 1140 case BOOL: 1141 case BYTES: 1142 case STRING: 1143 return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value, encodeBytes); 1144 case MESSAGE: 1145 RubyClass typeClass = 1146 (RubyClass) 1147 ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)) 1148 .msgclass(context); 1149 RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK); 1150 return msg.buildFrom(context, (DynamicMessage) value); 1151 case ENUM: 1152 EnumValueDescriptor enumValueDescriptor = (EnumValueDescriptor) value; 1153 if (enumValueDescriptor.getIndex() == -1) { // UNKNOWN ENUM VALUE 1154 return runtime.newFixnum(enumValueDescriptor.getNumber()); 1155 } 1156 return runtime.newSymbol(enumValueDescriptor.getName()); 1157 default: 1158 return runtime.newString(value.toString()); 1159 } 1160 } 1161 repeatedFieldForFieldDescriptor( ThreadContext context, FieldDescriptor fieldDescriptor)1162 private RubyRepeatedField repeatedFieldForFieldDescriptor( 1163 ThreadContext context, FieldDescriptor fieldDescriptor) { 1164 IRubyObject typeClass = context.runtime.getNilClass(); 1165 IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor); 1166 FieldDescriptor.Type type = fieldDescriptor.getType(); 1167 1168 if (type == FieldDescriptor.Type.MESSAGE) { 1169 typeClass = ((RubyDescriptor) descriptor).msgclass(context); 1170 1171 } else if (type == FieldDescriptor.Type.ENUM) { 1172 typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context); 1173 } 1174 1175 RubyRepeatedField field = 1176 new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass); 1177 field.setName(fieldDescriptor.getName()); 1178 1179 return field; 1180 } 1181 getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor)1182 private IRubyObject getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor) { 1183 return getFieldInternal(context, fieldDescriptor, true); 1184 } 1185 getFieldInternal( ThreadContext context, FieldDescriptor fieldDescriptor, boolean returnDefaults)1186 private IRubyObject getFieldInternal( 1187 ThreadContext context, FieldDescriptor fieldDescriptor, boolean returnDefaults) { 1188 OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof(); 1189 if (oneofDescriptor != null) { 1190 if (oneofCases.get(oneofDescriptor) == fieldDescriptor) { 1191 IRubyObject value = fields.get(fieldDescriptor); 1192 if (value == null) { 1193 FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor); 1194 if (oneofCase != null) { 1195 Object builderValue = builder.getField(oneofCase); 1196 if (builderValue != null) { 1197 boolean encodeBytes = 1198 oneofCase.hasDefaultValue() && builderValue.equals(oneofCase.getDefaultValue()); 1199 value = wrapField(context, oneofCase, builderValue, encodeBytes); 1200 } 1201 } 1202 if (value == null) { 1203 return context.nil; 1204 } else { 1205 return value; 1206 } 1207 } else { 1208 return value; 1209 } 1210 } else { 1211 FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor); 1212 if (oneofCase != fieldDescriptor) { 1213 if (fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE || !returnDefaults) { 1214 return context.nil; 1215 } else { 1216 return wrapField(context, fieldDescriptor, fieldDescriptor.getDefaultValue(), true); 1217 } 1218 } 1219 if (returnDefaults || builder.hasField(fieldDescriptor)) { 1220 Object rawValue = builder.getField(oneofCase); 1221 boolean encodeBytes = 1222 oneofCase.hasDefaultValue() && rawValue.equals(oneofCase.getDefaultValue()); 1223 IRubyObject value = wrapField(context, oneofCase, rawValue, encodeBytes); 1224 fields.put(fieldDescriptor, value); 1225 return value; 1226 } else { 1227 return context.nil; 1228 } 1229 } 1230 } 1231 1232 if (fieldDescriptor.isMapField()) { 1233 RubyMap map = (RubyMap) fields.get(fieldDescriptor); 1234 if (map == null) { 1235 map = newMapForField(context, fieldDescriptor); 1236 int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor); 1237 FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1); 1238 FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2); 1239 RubyDescriptor kvDescriptor = 1240 (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 1241 RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context); 1242 for (int i = 0; i < mapSize; i++) { 1243 RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK); 1244 DynamicMessage message = 1245 (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i); 1246 kvMessage.buildFrom(context, message); 1247 map.indexSet( 1248 context, 1249 kvMessage.getField(context, keyField), 1250 kvMessage.getField(context, valueField)); 1251 } 1252 fields.put(fieldDescriptor, map); 1253 } 1254 return map; 1255 } 1256 1257 if (fieldDescriptor.isRepeated()) { 1258 return getRepeatedField(context, fieldDescriptor); 1259 } 1260 1261 if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE 1262 || builder.hasField(fieldDescriptor) 1263 || fields.containsKey(fieldDescriptor)) { 1264 if (fields.containsKey(fieldDescriptor)) { 1265 return fields.get(fieldDescriptor); 1266 } else if (returnDefaults || builder.hasField(fieldDescriptor)) { 1267 Object rawValue = builder.getField(fieldDescriptor); 1268 boolean encodeBytes = 1269 fieldDescriptor.hasDefaultValue() && rawValue.equals(fieldDescriptor.getDefaultValue()); 1270 IRubyObject value = wrapField(context, fieldDescriptor, rawValue, encodeBytes); 1271 if (builder.hasField(fieldDescriptor)) { 1272 fields.put(fieldDescriptor, value); 1273 } 1274 return value; 1275 } 1276 } 1277 return context.nil; 1278 } 1279 setFieldInternal( ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value)1280 private IRubyObject setFieldInternal( 1281 ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { 1282 return setFieldInternal(context, fieldDescriptor, null, value); 1283 } 1284 setFieldInternal( ThreadContext context, FieldDescriptor fieldDescriptor, RubyFieldDescriptor rubyFieldDescriptor, IRubyObject value)1285 private IRubyObject setFieldInternal( 1286 ThreadContext context, 1287 FieldDescriptor fieldDescriptor, 1288 RubyFieldDescriptor rubyFieldDescriptor, 1289 IRubyObject value) { 1290 testFrozen("can't modify frozen " + getMetaClass()); 1291 1292 if (fieldDescriptor.isMapField()) { 1293 if (!(value instanceof RubyMap)) { 1294 throw Utils.createTypeError(context, "Expected Map instance"); 1295 } 1296 RubyMap thisMap = (RubyMap) getFieldInternal(context, fieldDescriptor); 1297 thisMap.mergeIntoSelf(context, value); 1298 1299 } else if (fieldDescriptor.isRepeated()) { 1300 if (value instanceof RubyRepeatedField) { 1301 fields.put(fieldDescriptor, value); 1302 } else { 1303 throw Utils.createTypeError(context, "Expected repeated field array"); 1304 } 1305 1306 } else { 1307 boolean addValue = true; 1308 FieldDescriptor.Type fieldType = fieldDescriptor.getType(); 1309 OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof(); 1310 1311 // Determine the typeclass, if any 1312 IRubyObject typeClass = context.runtime.getObject(); 1313 if (fieldType == FieldDescriptor.Type.MESSAGE) { 1314 if (rubyFieldDescriptor != null) { 1315 typeClass = ((RubyDescriptor) rubyFieldDescriptor.getSubtype(context)).msgclass(context); 1316 } else { 1317 typeClass = 1318 ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); 1319 } 1320 if (value.isNil()) { 1321 addValue = false; 1322 } 1323 } else if (fieldType == FieldDescriptor.Type.ENUM) { 1324 typeClass = 1325 ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)) 1326 .enummodule(context); 1327 value = enumToSymbol(context, fieldDescriptor.getEnumType(), value); 1328 } 1329 1330 if (oneofDescriptor != null) { 1331 FieldDescriptor oneofCase = oneofCases.get(oneofDescriptor); 1332 1333 // Remove the existing field if we are setting a different field in the Oneof 1334 if (oneofCase != null && oneofCase != fieldDescriptor) { 1335 fields.remove(oneofCase); 1336 } 1337 1338 // Keep track of what Oneofs are set 1339 if (value.isNil()) { 1340 oneofCases.remove(oneofDescriptor); 1341 if (fieldDescriptor.getRealContainingOneof() != null) { 1342 addValue = false; 1343 } 1344 } else { 1345 oneofCases.put(oneofDescriptor, fieldDescriptor); 1346 } 1347 } 1348 1349 if (addValue) { 1350 value = 1351 Utils.checkType( 1352 context, fieldType, fieldDescriptor.getName(), value, (RubyModule) typeClass); 1353 fields.put(fieldDescriptor, value); 1354 } else { 1355 fields.remove(fieldDescriptor); 1356 } 1357 } 1358 return context.nil; 1359 } 1360 getDescriptorForField( ThreadContext context, FieldDescriptor fieldDescriptor)1361 private IRubyObject getDescriptorForField( 1362 ThreadContext context, FieldDescriptor fieldDescriptor) { 1363 RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); 1364 RubyFieldDescriptor fd = 1365 (RubyFieldDescriptor) 1366 thisRbDescriptor.lookup(context, context.runtime.newString(fieldDescriptor.getName())); 1367 return fd.getSubtype(context); 1368 } 1369 enumToSymbol( ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value)1370 private IRubyObject enumToSymbol( 1371 ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value) { 1372 if (value instanceof RubySymbol) { 1373 return (RubySymbol) value; 1374 } else if (Utils.isRubyNum(value)) { 1375 EnumValueDescriptor enumValue = 1376 enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); 1377 if (enumValue.getIndex() != -1) { 1378 return context.runtime.newSymbol(enumValue.getName()); 1379 } else { 1380 return value; 1381 } 1382 } else if (value instanceof RubyString) { 1383 return ((RubyString) value).intern(); 1384 } 1385 1386 return context.runtime.newSymbol("UNKNOWN"); 1387 } 1388 rubyToRepeatedField( ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value)1389 private RubyRepeatedField rubyToRepeatedField( 1390 ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { 1391 RubyArray arr = value.convertToArray(); 1392 RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor); 1393 IRubyObject[] values = new IRubyObject[arr.size()]; 1394 FieldDescriptor.Type fieldType = fieldDescriptor.getType(); 1395 1396 RubyModule typeClass = null; 1397 if (fieldType == FieldDescriptor.Type.MESSAGE) { 1398 RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 1399 typeClass = (RubyModule) descriptor.msgclass(context); 1400 } else if (fieldType == FieldDescriptor.Type.ENUM) { 1401 RubyEnumDescriptor enumDescriptor = 1402 (RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor); 1403 typeClass = (RubyModule) enumDescriptor.enummodule(context); 1404 } 1405 1406 for (int i = 0; i < arr.size(); i++) { 1407 IRubyObject item = arr.eltInternal(i); 1408 if (item.isNil()) { 1409 throw Utils.createTypeError(context, "nil message not allowed here."); 1410 } 1411 if (item instanceof RubyHash && typeClass != null) { 1412 values[i] = ((RubyClass) typeClass).newInstance(context, item, Block.NULL_BLOCK); 1413 } else { 1414 if (fieldType == FieldDescriptor.Type.ENUM) { 1415 item = enumToSymbol(context, fieldDescriptor.getEnumType(), item); 1416 } 1417 1418 values[i] = item; 1419 } 1420 } 1421 repeatedField.push(context, values); 1422 1423 return repeatedField; 1424 } 1425 newMapForField(ThreadContext context, FieldDescriptor fieldDescriptor)1426 private RubyMap newMapForField(ThreadContext context, FieldDescriptor fieldDescriptor) { 1427 RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); 1428 FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1); 1429 FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2); 1430 IRubyObject keyType = RubySymbol.newSymbol(context.runtime, keyField.getType().name()); 1431 IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name()); 1432 1433 if (valueField.getType() == FieldDescriptor.Type.MESSAGE) { 1434 RubyFieldDescriptor rubyFieldDescriptor = 1435 (RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value")); 1436 RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubtype(context); 1437 return (RubyMap) 1438 cMap.newInstance( 1439 context, keyType, valueType, rubyDescriptor.msgclass(context), Block.NULL_BLOCK); 1440 1441 } else if (valueField.getType() == FieldDescriptor.Type.ENUM) { 1442 RubyFieldDescriptor rubyFieldDescriptor = 1443 (RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value")); 1444 RubyEnumDescriptor rubyEnumDescriptor = 1445 (RubyEnumDescriptor) rubyFieldDescriptor.getSubtype(context); 1446 return (RubyMap) 1447 cMap.newInstance( 1448 context, 1449 keyType, 1450 valueType, 1451 rubyEnumDescriptor.enummodule(context), 1452 Block.NULL_BLOCK); 1453 1454 } else { 1455 return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK); 1456 } 1457 } 1458 isWrappable(FieldDescriptor fieldDescriptor)1459 private boolean isWrappable(FieldDescriptor fieldDescriptor) { 1460 if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE) return false; 1461 1462 return isWrapper(fieldDescriptor.getMessageType()); 1463 } 1464 isWrapper(Descriptor messageDescriptor)1465 private static boolean isWrapper(Descriptor messageDescriptor) { 1466 switch (messageDescriptor.getFullName()) { 1467 case "google.protobuf.DoubleValue": 1468 case "google.protobuf.FloatValue": 1469 case "google.protobuf.Int64Value": 1470 case "google.protobuf.UInt64Value": 1471 case "google.protobuf.Int32Value": 1472 case "google.protobuf.UInt32Value": 1473 case "google.protobuf.BoolValue": 1474 case "google.protobuf.StringValue": 1475 case "google.protobuf.BytesValue": 1476 return true; 1477 default: 1478 return false; 1479 } 1480 } 1481 validateMessageType( ThreadContext context, FieldDescriptor fieldDescriptor, String methodName)1482 private void validateMessageType( 1483 ThreadContext context, FieldDescriptor fieldDescriptor, String methodName) { 1484 if (descriptor != fieldDescriptor.getContainingType()) { 1485 throw Utils.createTypeError(context, methodName + " method called on wrong message type"); 1486 } 1487 } 1488 1489 private static RubyClass parseErrorClass; 1490 1491 private static final String AS_VALUE_SUFFIX = "_as_value"; 1492 private static final String CLEAR_PREFIX = "clear_"; 1493 private static final String CONST_SUFFIX = "_const"; 1494 private static final String HAS_PREFIX = "has_"; 1495 private static final String QUESTION_MARK = "?"; 1496 private static final int SINK_MAXIMUM_NESTING = 64; 1497 1498 private Descriptor descriptor; 1499 private DynamicMessage.Builder builder; 1500 private Map<FieldDescriptor, IRubyObject> fields; 1501 private Map<OneofDescriptor, FieldDescriptor> oneofCases; 1502 private RubyClass cRepeatedField; 1503 private RubyClass cMap; 1504 private boolean ignoreUnknownFieldsOnInit = false; 1505 } 1506