|
|
|
@ -32,19 +32,25 @@ |
|
|
|
|
|
|
|
|
|
package com.google.protobuf.jruby; |
|
|
|
|
|
|
|
|
|
import com.google.protobuf.ByteString; |
|
|
|
|
import com.google.protobuf.CodedInputStream; |
|
|
|
|
import com.google.protobuf.Descriptors.Descriptor; |
|
|
|
|
import com.google.protobuf.Descriptors.EnumDescriptor; |
|
|
|
|
import com.google.protobuf.Descriptors.EnumValueDescriptor; |
|
|
|
|
import com.google.protobuf.Descriptors.FieldDescriptor; |
|
|
|
|
import com.google.protobuf.Descriptors.FileDescriptor; |
|
|
|
|
import com.google.protobuf.Descriptors.OneofDescriptor; |
|
|
|
|
import com.google.protobuf.ByteString; |
|
|
|
|
import com.google.protobuf.CodedInputStream; |
|
|
|
|
import com.google.protobuf.DynamicMessage; |
|
|
|
|
import com.google.protobuf.InvalidProtocolBufferException; |
|
|
|
|
import com.google.protobuf.Message; |
|
|
|
|
import com.google.protobuf.UnknownFieldSet; |
|
|
|
|
import com.google.protobuf.util.JsonFormat; |
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
import java.security.MessageDigest; |
|
|
|
|
import java.security.NoSuchAlgorithmException; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import org.jruby.*; |
|
|
|
|
import org.jruby.anno.JRubyMethod; |
|
|
|
|
import org.jruby.exceptions.RaiseException; |
|
|
|
@ -54,13 +60,6 @@ import org.jruby.runtime.ThreadContext; |
|
|
|
|
import org.jruby.runtime.builtin.IRubyObject; |
|
|
|
|
import org.jruby.util.ByteList; |
|
|
|
|
|
|
|
|
|
import java.nio.ByteBuffer; |
|
|
|
|
import java.security.MessageDigest; |
|
|
|
|
import java.security.NoSuchAlgorithmException; |
|
|
|
|
import java.util.HashMap; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
|
|
|
|
|
public class RubyMessage extends RubyObject { |
|
|
|
|
private final String DEFAULT_VALUE = "google.protobuf.FieldDescriptorProto.default_value"; |
|
|
|
|
private final String TYPE = "type"; |
|
|
|
@ -97,27 +96,40 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
throw runtime.newArgumentError("expected Hash arguments."); |
|
|
|
|
} |
|
|
|
|
RubyHash hash = args[0].convertToHash(); |
|
|
|
|
hash.visitAll(context, new RubyHash.Visitor() { |
|
|
|
|
hash.visitAll( |
|
|
|
|
context, |
|
|
|
|
new RubyHash.Visitor() { |
|
|
|
|
@Override |
|
|
|
|
public void visit(IRubyObject key, IRubyObject value) { |
|
|
|
|
if (!(key instanceof RubySymbol) && !(key instanceof RubyString)) { |
|
|
|
|
throw Utils.createTypeError(context, |
|
|
|
|
"Expected string or symbols as hash keys in initialization map."); |
|
|
|
|
throw Utils.createTypeError( |
|
|
|
|
context, "Expected string or symbols as hash keys in initialization map."); |
|
|
|
|
} |
|
|
|
|
final FieldDescriptor fieldDescriptor = findField(context, key, ignoreUnknownFieldsOnInit); |
|
|
|
|
final FieldDescriptor fieldDescriptor = |
|
|
|
|
findField(context, key, ignoreUnknownFieldsOnInit); |
|
|
|
|
|
|
|
|
|
if (value == null || value.isNil()) return; |
|
|
|
|
|
|
|
|
|
if (fieldDescriptor.isMapField()) { |
|
|
|
|
if (!(value instanceof RubyHash)) |
|
|
|
|
throw runtime.newArgumentError("Expected Hash object as initializer value for map field '" + key.asJavaString() + "' (given " + value.getMetaClass() + ")."); |
|
|
|
|
throw runtime.newArgumentError( |
|
|
|
|
"Expected Hash object as initializer value for map field '" |
|
|
|
|
+ key.asJavaString() |
|
|
|
|
+ "' (given " |
|
|
|
|
+ value.getMetaClass() |
|
|
|
|
+ ")."); |
|
|
|
|
|
|
|
|
|
final RubyMap map = newMapForField(context, fieldDescriptor); |
|
|
|
|
map.mergeIntoSelf(context, value); |
|
|
|
|
fields.put(fieldDescriptor, map); |
|
|
|
|
} else if (fieldDescriptor.isRepeated()) { |
|
|
|
|
if (!(value instanceof RubyArray)) |
|
|
|
|
throw runtime.newArgumentError("Expected array as initializer value for repeated field '" + key.asJavaString() + "' (given " + value.getMetaClass() + ")."); |
|
|
|
|
throw runtime.newArgumentError( |
|
|
|
|
"Expected array as initializer value for repeated field '" |
|
|
|
|
+ key.asJavaString() |
|
|
|
|
+ "' (given " |
|
|
|
|
+ value.getMetaClass() |
|
|
|
|
+ ")."); |
|
|
|
|
fields.put(fieldDescriptor, rubyToRepeatedField(context, fieldDescriptor, value)); |
|
|
|
|
} else { |
|
|
|
|
OneofDescriptor oneof = fieldDescriptor.getContainingOneof(); |
|
|
|
@ -125,18 +137,20 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
oneofCases.put(oneof, fieldDescriptor); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (value instanceof RubyHash && fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
if (value instanceof RubyHash |
|
|
|
|
&& fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
RubyDescriptor descriptor = |
|
|
|
|
(RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
RubyClass typeClass = (RubyClass) descriptor.msgclass(context); |
|
|
|
|
value = (IRubyObject) typeClass.newInstance(context, value, Block.NULL_BLOCK); |
|
|
|
|
fields.put(fieldDescriptor, value); |
|
|
|
|
} else { |
|
|
|
|
indexSet(context, key, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, null); |
|
|
|
|
}, |
|
|
|
|
null); |
|
|
|
|
} |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
@ -242,8 +256,7 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
@JRubyMethod(name = {"==", "eql?"}) |
|
|
|
|
public IRubyObject eq(ThreadContext context, IRubyObject other) { |
|
|
|
|
Ruby runtime = context.runtime; |
|
|
|
|
if (!(other instanceof RubyMessage)) |
|
|
|
|
return runtime.getFalse(); |
|
|
|
|
if (!(other instanceof RubyMessage)) return runtime.getFalse(); |
|
|
|
|
RubyMessage message = (RubyMessage) other; |
|
|
|
|
if (descriptor != message.descriptor) { |
|
|
|
|
return runtime.getFalse(); |
|
|
|
@ -280,7 +293,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
if (methodName.startsWith(CLEAR_PREFIX)) { |
|
|
|
|
String strippedMethodName = methodName.substring(6); |
|
|
|
|
oneofDescriptor = rubyDescriptor.lookupOneof(context, context.runtime.newSymbol(strippedMethodName)); |
|
|
|
|
oneofDescriptor = |
|
|
|
|
rubyDescriptor.lookupOneof(context, context.runtime.newSymbol(strippedMethodName)); |
|
|
|
|
if (!oneofDescriptor.isNil()) { |
|
|
|
|
return context.runtime.getTrue(); |
|
|
|
|
} |
|
|
|
@ -292,27 +306,30 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) { |
|
|
|
|
String strippedMethodName = methodName.substring(4, methodName.length() - 1); |
|
|
|
|
FieldDescriptor fieldDescriptor = descriptor.findFieldByName(strippedMethodName); |
|
|
|
|
if (fieldDescriptor != null && |
|
|
|
|
(!proto3 || fieldDescriptor.getContainingOneof() == null || fieldDescriptor |
|
|
|
|
.getContainingOneof().isSynthetic()) && |
|
|
|
|
fieldDescriptor.hasPresence()) { |
|
|
|
|
if (fieldDescriptor != null |
|
|
|
|
&& (!proto3 |
|
|
|
|
|| fieldDescriptor.getContainingOneof() == null |
|
|
|
|
|| fieldDescriptor.getContainingOneof().isSynthetic()) |
|
|
|
|
&& fieldDescriptor.hasPresence()) { |
|
|
|
|
return context.runtime.getTrue(); |
|
|
|
|
} |
|
|
|
|
oneofDescriptor = rubyDescriptor.lookupOneof(context, RubyString.newString(context.runtime, strippedMethodName)); |
|
|
|
|
oneofDescriptor = |
|
|
|
|
rubyDescriptor.lookupOneof( |
|
|
|
|
context, RubyString.newString(context.runtime, strippedMethodName)); |
|
|
|
|
if (!oneofDescriptor.isNil()) { |
|
|
|
|
return context.runtime.getTrue(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (methodName.endsWith(AS_VALUE_SUFFIX)) { |
|
|
|
|
FieldDescriptor fieldDescriptor = descriptor.findFieldByName( |
|
|
|
|
methodName.substring(0, methodName.length() - 9)); |
|
|
|
|
FieldDescriptor fieldDescriptor = |
|
|
|
|
descriptor.findFieldByName(methodName.substring(0, methodName.length() - 9)); |
|
|
|
|
if (fieldDescriptor != null && isWrappable(fieldDescriptor)) { |
|
|
|
|
return context.runtime.getTrue(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (methodName.endsWith(CONST_SUFFIX)) { |
|
|
|
|
FieldDescriptor fieldDescriptor = descriptor.findFieldByName( |
|
|
|
|
methodName.substring(0, methodName.length() - 6)); |
|
|
|
|
FieldDescriptor fieldDescriptor = |
|
|
|
|
descriptor.findFieldByName(methodName.substring(0, methodName.length() - 6)); |
|
|
|
|
if (fieldDescriptor != null) { |
|
|
|
|
if (fieldDescriptor.getType() == FieldDescriptor.Type.ENUM) { |
|
|
|
|
return context.runtime.getTrue(); |
|
|
|
@ -337,7 +354,9 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
if (args.length == 2) { |
|
|
|
|
includePrivate = context.runtime.getTrue().equals(args[1]); |
|
|
|
|
} |
|
|
|
|
return metaClass.respondsToMethod(methodName, includePrivate) ? context.runtime.getTrue() : context.runtime.getFalse(); |
|
|
|
|
return metaClass.respondsToMethod(methodName, includePrivate) |
|
|
|
|
? context.runtime.getTrue() |
|
|
|
|
: context.runtime.getFalse(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
@ -427,21 +446,25 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if (methodName.startsWith(HAS_PREFIX) && methodName.endsWith(QUESTION_MARK)) { |
|
|
|
|
methodName = methodName.substring(4, methodName.length() - 1); // Trim "has_" and "?" off the field name
|
|
|
|
|
methodName = |
|
|
|
|
methodName.substring( |
|
|
|
|
4, methodName.length() - 1); // Trim "has_" and "?" off the field name
|
|
|
|
|
oneofDescriptor = rubyDescriptor.lookupOneof(context, runtime.newSymbol(methodName)); |
|
|
|
|
if (!oneofDescriptor.isNil()) { |
|
|
|
|
RubyOneofDescriptor rubyOneofDescriptor = (RubyOneofDescriptor) oneofDescriptor; |
|
|
|
|
return oneofCases.containsKey(rubyOneofDescriptor.getDescriptor()) ? runtime.getTrue() : runtime.getFalse(); |
|
|
|
|
return oneofCases.containsKey(rubyOneofDescriptor.getDescriptor()) |
|
|
|
|
? runtime.getTrue() |
|
|
|
|
: runtime.getFalse(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fieldDescriptor = descriptor.findFieldByName(methodName); |
|
|
|
|
|
|
|
|
|
if (fieldDescriptor != null && |
|
|
|
|
(!proto3 || fieldDescriptor.getContainingOneof() == null || fieldDescriptor |
|
|
|
|
.getContainingOneof().isSynthetic()) && |
|
|
|
|
fieldDescriptor.hasPresence()) { |
|
|
|
|
return fields.containsKey(fieldDescriptor) ? runtime.getTrue() |
|
|
|
|
: runtime.getFalse(); |
|
|
|
|
if (fieldDescriptor != null |
|
|
|
|
&& (!proto3 |
|
|
|
|
|| fieldDescriptor.getContainingOneof() == null |
|
|
|
|
|| fieldDescriptor.getContainingOneof().isSynthetic()) |
|
|
|
|
&& fieldDescriptor.hasPresence()) { |
|
|
|
|
return fields.containsKey(fieldDescriptor) ? runtime.getTrue() : runtime.getFalse(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if (methodName.endsWith(AS_VALUE_SUFFIX)) { |
|
|
|
@ -471,12 +494,14 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
RubyArray retValues = runtime.newArray(values.getLength()); |
|
|
|
|
for (int i = 0; i < values.getLength(); i++) { |
|
|
|
|
String val = values.eltInternal(i).toString(); |
|
|
|
|
retValues.store((long) i, runtime.newFixnum(enumDescriptor.findValueByName(val).getNumber())); |
|
|
|
|
retValues.store( |
|
|
|
|
(long) i, runtime.newFixnum(enumDescriptor.findValueByName(val).getNumber())); |
|
|
|
|
} |
|
|
|
|
return retValues; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return runtime.newFixnum(enumDescriptor.findValueByName(enumValue.asJavaString()).getNumber()); |
|
|
|
|
return runtime.newFixnum( |
|
|
|
|
enumDescriptor.findValueByName(enumValue.asJavaString()).getNumber()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -489,7 +514,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return setFieldInternal(context, fieldDescriptor, args[1]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IRubyObject oneofDescriptor = rubyDescriptor.lookupOneof(context, RubyString.newString(context.runtime, methodName)); |
|
|
|
|
IRubyObject oneofDescriptor = |
|
|
|
|
rubyDescriptor.lookupOneof(context, RubyString.newString(context.runtime, methodName)); |
|
|
|
|
if (!oneofDescriptor.isNil()) { |
|
|
|
|
throw runtime.newRuntimeError("Oneof accessors are read-only."); |
|
|
|
|
} |
|
|
|
@ -504,22 +530,23 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return setFieldInternal(context, fieldDescriptor, args[1]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); |
|
|
|
|
RubyClass typeClass = |
|
|
|
|
(RubyClass) |
|
|
|
|
((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)) |
|
|
|
|
.msgclass(context); |
|
|
|
|
RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK); |
|
|
|
|
msg.indexSet(context, runtime.newString("value"), args[1]); |
|
|
|
|
return setFieldInternal(context, fieldDescriptor, msg); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Helpers.invokeSuper(context, this, metaClass, "method_missing", args, Block.NULL_BLOCK); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* call-seq: |
|
|
|
|
* Message.dup => new_message |
|
|
|
|
* Performs a shallow copy of this message and returns the new copy. |
|
|
|
|
* call-seq: Message.dup => new_message Performs a shallow copy of this message and returns the |
|
|
|
|
* new copy. |
|
|
|
|
*/ |
|
|
|
|
@JRubyMethod |
|
|
|
|
public IRubyObject dup(ThreadContext context) { |
|
|
|
@ -530,7 +557,9 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} else if (fields.containsKey(fieldDescriptor)) { |
|
|
|
|
dup.setFieldInternal(context, fieldDescriptor, fields.get(fieldDescriptor)); |
|
|
|
|
} else if (this.builder.hasField(fieldDescriptor)) { |
|
|
|
|
dup.fields.put(fieldDescriptor, wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor))); |
|
|
|
|
dup.fields.put( |
|
|
|
|
fieldDescriptor, |
|
|
|
|
wrapField(context, fieldDescriptor, this.builder.getField(fieldDescriptor))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return dup; |
|
|
|
@ -560,7 +589,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
@JRubyMethod(required = 1, optional = 1, meta = true) |
|
|
|
|
public static IRubyObject encode(ThreadContext context, IRubyObject recv, IRubyObject[] args) { |
|
|
|
|
if (recv != args[0].getMetaClass()) { |
|
|
|
|
throw context.runtime.newArgumentError("Tried to encode a " + args[0].getMetaClass() + " message with " + recv); |
|
|
|
|
throw context.runtime.newArgumentError( |
|
|
|
|
"Tried to encode a " + args[0].getMetaClass() + " message with " + recv); |
|
|
|
|
} |
|
|
|
|
RubyMessage message = (RubyMessage) args[0]; |
|
|
|
|
int recursionLimitInt = SINK_MAXIMUM_NESTING; |
|
|
|
@ -573,7 +603,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
recursionLimitInt = ((RubyNumeric) recursionLimit).getIntValue(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return context.runtime.newString(new ByteList(message.build(context, 0, recursionLimitInt).toByteArray())); |
|
|
|
|
return context.runtime.newString( |
|
|
|
|
new ByteList(message.build(context, 0, recursionLimitInt).toByteArray())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
@ -598,7 +629,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
throw context.runtime.newArgumentError("Expected hash arguments."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IRubyObject recursionLimit = ((RubyHash) args[1]).fastARef(context.runtime.newSymbol("recursion_limit")); |
|
|
|
|
IRubyObject recursionLimit = |
|
|
|
|
((RubyHash) args[1]).fastARef(context.runtime.newSymbol("recursion_limit")); |
|
|
|
|
if (recursionLimit != null) { |
|
|
|
|
input.setRecursionLimit(((RubyNumeric) recursionLimit).getIntValue()); |
|
|
|
|
} |
|
|
|
@ -607,17 +639,28 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
try { |
|
|
|
|
ret.builder.mergeFrom(input); |
|
|
|
|
} catch (Exception e) { |
|
|
|
|
throw RaiseException.from(context.runtime, (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"), e.getMessage()); |
|
|
|
|
throw RaiseException.from( |
|
|
|
|
context.runtime, |
|
|
|
|
(RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"), |
|
|
|
|
e.getMessage()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret.proto3) { |
|
|
|
|
// Need to reset unknown values in repeated enum fields
|
|
|
|
|
ret.builder.getUnknownFields().asMap().forEach((i, values) -> { |
|
|
|
|
ret.builder |
|
|
|
|
.getUnknownFields() |
|
|
|
|
.asMap() |
|
|
|
|
.forEach( |
|
|
|
|
(i, values) -> { |
|
|
|
|
FieldDescriptor fd = ret.builder.getDescriptorForType().findFieldByNumber(i); |
|
|
|
|
if (fd != null && fd.isRepeated() && fd.getType() == FieldDescriptor.Type.ENUM) { |
|
|
|
|
EnumDescriptor ed = fd.getEnumType(); |
|
|
|
|
values.getVarintList().forEach(value -> { |
|
|
|
|
ret.builder.addRepeatedField(fd, ed.findValueByNumberCreatingIfUnknown(value.intValue())); |
|
|
|
|
values |
|
|
|
|
.getVarintList() |
|
|
|
|
.forEach( |
|
|
|
|
value -> { |
|
|
|
|
ret.builder.addRepeatedField( |
|
|
|
|
fd, ed.findValueByNumberCreatingIfUnknown(value.intValue())); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
@ -636,7 +679,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
* emit_defaults: set true to emit 0/false values (default is to omit them) |
|
|
|
|
*/ |
|
|
|
|
@JRubyMethod(name = "encode_json", required = 1, optional = 1, meta = true) |
|
|
|
|
public static IRubyObject encodeJson(ThreadContext context, IRubyObject recv, IRubyObject[] args) { |
|
|
|
|
public static IRubyObject encodeJson( |
|
|
|
|
ThreadContext context, IRubyObject recv, IRubyObject[] args) { |
|
|
|
|
Ruby runtime = context.runtime; |
|
|
|
|
RubyMessage message = (RubyMessage) args[0]; |
|
|
|
|
JsonFormat.Printer printer = JsonFormat.printer().omittingInsignificantWhitespace(); |
|
|
|
@ -663,7 +707,9 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
printer = printer.preservingProtoFieldNames(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
printer = printer.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(message.descriptor).build()); |
|
|
|
|
printer = |
|
|
|
|
printer.usingTypeRegistry( |
|
|
|
|
JsonFormat.TypeRegistry.newBuilder().add(message.descriptor).build()); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
result = printer.print(message.build(context, 0, SINK_MAXIMUM_NESTING)); |
|
|
|
@ -689,7 +735,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
* raise an error) |
|
|
|
|
*/ |
|
|
|
|
@JRubyMethod(name = "decode_json", required = 1, optional = 1, meta = true) |
|
|
|
|
public static IRubyObject decodeJson(ThreadContext context, IRubyObject recv, IRubyObject[] args) { |
|
|
|
|
public static IRubyObject decodeJson( |
|
|
|
|
ThreadContext context, IRubyObject recv, IRubyObject[] args) { |
|
|
|
|
Ruby runtime = context.runtime; |
|
|
|
|
boolean ignoreUnknownFields = false; |
|
|
|
|
IRubyObject data = args[0]; |
|
|
|
@ -700,7 +747,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
throw runtime.newArgumentError("Expected hash arguments."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IRubyObject ignoreSetting = ((RubyHash) args[1]).fastARef(runtime.newSymbol("ignore_unknown_fields")); |
|
|
|
|
IRubyObject ignoreSetting = |
|
|
|
|
((RubyHash) args[1]).fastARef(runtime.newSymbol("ignore_unknown_fields")); |
|
|
|
|
if (ignoreSetting != null && ignoreSetting.isTrue()) { |
|
|
|
|
parser = parser.ignoringUnknownFields(); |
|
|
|
|
} |
|
|
|
@ -711,7 +759,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RubyMessage ret = (RubyMessage) ((RubyClass) recv).newInstance(context, Block.NULL_BLOCK); |
|
|
|
|
parser = parser.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(ret.descriptor).build()); |
|
|
|
|
parser = |
|
|
|
|
parser.usingTypeRegistry(JsonFormat.TypeRegistry.newBuilder().add(ret.descriptor).build()); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
parser.merge(data.asJavaString(), ret.builder); |
|
|
|
@ -720,7 +769,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (isWrapper(ret.descriptor)) { |
|
|
|
|
throw runtime.newRuntimeError("Parsing a wrapper type from JSON at the top level does not work."); |
|
|
|
|
throw runtime.newRuntimeError( |
|
|
|
|
"Parsing a wrapper type from JSON at the top level does not work."); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
@ -735,7 +785,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
|
|
|
|
|
if (!value.isNil()) { |
|
|
|
|
if (fdef.isRepeated() && !fdef.isMapField()) { |
|
|
|
|
if (!proto3 && ((RubyRepeatedField) value).size() == 0) continue; // Don't output empty repeated fields for proto2
|
|
|
|
|
if (!proto3 && ((RubyRepeatedField) value).size() == 0) |
|
|
|
|
continue; // Don't output empty repeated fields for proto2
|
|
|
|
|
if (fdef.getType() != FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
value = Helpers.invoke(context, value, "to_a"); |
|
|
|
|
} else { |
|
|
|
@ -773,8 +824,10 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
|
|
|
|
|
if (value instanceof RubyMap) { |
|
|
|
|
builder.clearField(fieldDescriptor); |
|
|
|
|
RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
for (DynamicMessage kv : ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { |
|
|
|
|
RubyDescriptor mapDescriptor = |
|
|
|
|
(RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
for (DynamicMessage kv : |
|
|
|
|
((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { |
|
|
|
|
builder.addRepeatedField(fieldDescriptor, kv); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -783,18 +836,23 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
|
|
|
|
|
builder.clearField(fieldDescriptor); |
|
|
|
|
for (int i = 0; i < repeatedField.size(); i++) { |
|
|
|
|
Object item = convert(context, fieldDescriptor, repeatedField.get(i), depth, recursionLimit, |
|
|
|
|
Object item = |
|
|
|
|
convert( |
|
|
|
|
context, |
|
|
|
|
fieldDescriptor, |
|
|
|
|
repeatedField.get(i), |
|
|
|
|
depth, |
|
|
|
|
recursionLimit, |
|
|
|
|
/*isDefaultValueForBytes*/ false); |
|
|
|
|
builder.addRepeatedField(fieldDescriptor, item); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} else if (!value.isNil()) { |
|
|
|
|
/** |
|
|
|
|
* Detect the special case where default_value strings are provided for byte fields. |
|
|
|
|
* If so, disable normal string encoding behavior within convert. |
|
|
|
|
* For a more detailed explanation of other possible workarounds, see the comments |
|
|
|
|
* above {@code com.google.protobuf.Internal#stringDefaultValue() |
|
|
|
|
* stringDefaultValue}. |
|
|
|
|
* Detect the special case where default_value strings are provided for byte fields. If so, |
|
|
|
|
* disable normal string encoding behavior within convert. For a more detailed explanation |
|
|
|
|
* of other possible workarounds, see the comments above {@code |
|
|
|
|
* com.google.protobuf.Internal#stringDefaultValue() stringDefaultValue}. |
|
|
|
|
*/ |
|
|
|
|
boolean isDefaultStringForBytes = false; |
|
|
|
|
if (DEFAULT_VALUE.equals(fieldDescriptor.getFullName())) { |
|
|
|
@ -804,7 +862,10 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
isDefaultStringForBytes = true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
builder.setField(fieldDescriptor, convert(context, fieldDescriptor, value, depth, recursionLimit, isDefaultStringForBytes)); |
|
|
|
|
builder.setField( |
|
|
|
|
fieldDescriptor, |
|
|
|
|
convert( |
|
|
|
|
context, fieldDescriptor, value, depth, recursionLimit, isDefaultStringForBytes)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -822,9 +883,10 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
value = getFieldInternal(context, fieldDescriptor); |
|
|
|
|
if (value instanceof RubyMap) { |
|
|
|
|
builder.clearField(fieldDescriptor); |
|
|
|
|
RubyDescriptor mapDescriptor = (RubyDescriptor) getDescriptorForField(context, |
|
|
|
|
fieldDescriptor); |
|
|
|
|
for (DynamicMessage kv : ((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { |
|
|
|
|
RubyDescriptor mapDescriptor = |
|
|
|
|
(RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
for (DynamicMessage kv : |
|
|
|
|
((RubyMap) value).build(context, mapDescriptor, depth, recursionLimit)) { |
|
|
|
|
builder.addRepeatedField(fieldDescriptor, kv); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -866,22 +928,30 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
if (!fieldDescriptor.hasPresence()) { |
|
|
|
|
throw context.runtime.newArgumentError("does not track presence"); |
|
|
|
|
} |
|
|
|
|
return fields.containsKey(fieldDescriptor) ? context.runtime.getTrue() : context.runtime.getFalse(); |
|
|
|
|
return fields.containsKey(fieldDescriptor) |
|
|
|
|
? context.runtime.getTrue() |
|
|
|
|
: context.runtime.getFalse(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
protected IRubyObject setField(ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
protected IRubyObject setField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
validateMessageType(context, fieldDescriptor, "set"); |
|
|
|
|
return setFieldInternal(context, fieldDescriptor, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private RubyRepeatedField getRepeatedField(ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
private RubyRepeatedField getRepeatedField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
if (fields.containsKey(fieldDescriptor)) { |
|
|
|
|
return (RubyRepeatedField) fields.get(fieldDescriptor); |
|
|
|
|
} |
|
|
|
|
int count = this.builder.getRepeatedFieldCount(fieldDescriptor); |
|
|
|
|
RubyRepeatedField ret = repeatedFieldForFieldDescriptor(context, fieldDescriptor); |
|
|
|
|
for (int i = 0; i < count; i++) { |
|
|
|
|
ret.push(context, new IRubyObject[] {wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i))}); |
|
|
|
|
ret.push( |
|
|
|
|
context, |
|
|
|
|
new IRubyObject[] { |
|
|
|
|
wrapField(context, fieldDescriptor, this.builder.getRepeatedField(fieldDescriptor, i)) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
fields.put(fieldDescriptor, ret); |
|
|
|
|
return ret; |
|
|
|
@ -902,11 +972,16 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
|
|
|
|
|
private void discardUnknownFields(ThreadContext context, Message.Builder messageBuilder) { |
|
|
|
|
messageBuilder.setUnknownFields(UnknownFieldSet.getDefaultInstance()); |
|
|
|
|
messageBuilder.getAllFields().forEach((fd, value) -> { |
|
|
|
|
messageBuilder |
|
|
|
|
.getAllFields() |
|
|
|
|
.forEach( |
|
|
|
|
(fd, value) -> { |
|
|
|
|
if (fd.getType() == FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
if (fd.isRepeated()) { |
|
|
|
|
messageBuilder.clearField(fd); |
|
|
|
|
((List) value).forEach((val) -> { |
|
|
|
|
((List) value) |
|
|
|
|
.forEach( |
|
|
|
|
(val) -> { |
|
|
|
|
Message.Builder submessageBuilder = ((DynamicMessage) val).toBuilder(); |
|
|
|
|
discardUnknownFields(context, submessageBuilder); |
|
|
|
|
messageBuilder.addRepeatedField(fd, submessageBuilder.build()); |
|
|
|
@ -924,7 +999,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return findField(context, fieldName, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private FieldDescriptor findField(ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField) { |
|
|
|
|
private FieldDescriptor findField( |
|
|
|
|
ThreadContext context, IRubyObject fieldName, boolean ignoreUnknownField) { |
|
|
|
|
String nameStr = fieldName.asJavaString(); |
|
|
|
|
FieldDescriptor ret = this.descriptor.findFieldByName(nameStr); |
|
|
|
|
if (ret == null && !ignoreUnknownField) { |
|
|
|
@ -934,9 +1010,12 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// convert a ruby object to protobuf type, skip type check since it is checked on the way in
|
|
|
|
|
private Object convert(ThreadContext context, |
|
|
|
|
private Object convert( |
|
|
|
|
ThreadContext context, |
|
|
|
|
FieldDescriptor fieldDescriptor, |
|
|
|
|
IRubyObject value, int depth, int recursionLimit, |
|
|
|
|
IRubyObject value, |
|
|
|
|
int depth, |
|
|
|
|
int recursionLimit, |
|
|
|
|
boolean isDefaultStringForBytes) { |
|
|
|
|
Object val = null; |
|
|
|
|
switch (fieldDescriptor.getType()) { |
|
|
|
@ -997,16 +1076,19 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
|
|
|
|
|
private static RaiseException createParseError(ThreadContext context, String message) { |
|
|
|
|
if (parseErrorClass == null) { |
|
|
|
|
parseErrorClass = (RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"); |
|
|
|
|
parseErrorClass = |
|
|
|
|
(RubyClass) context.runtime.getClassFromPath("Google::Protobuf::ParseError"); |
|
|
|
|
} |
|
|
|
|
return RaiseException.from(context.runtime, parseErrorClass, message); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject wrapField(ThreadContext context, FieldDescriptor fieldDescriptor, Object value) { |
|
|
|
|
private IRubyObject wrapField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, Object value) { |
|
|
|
|
return wrapField(context, fieldDescriptor, value, false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject wrapField(ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes) { |
|
|
|
|
private IRubyObject wrapField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, Object value, boolean encodeBytes) { |
|
|
|
|
if (value == null) { |
|
|
|
|
return context.runtime.getNil(); |
|
|
|
|
} |
|
|
|
@ -1030,7 +1112,10 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
case STRING: |
|
|
|
|
return Utils.wrapPrimaryValue(context, fieldDescriptor.getType(), value, encodeBytes); |
|
|
|
|
case MESSAGE: |
|
|
|
|
RubyClass typeClass = (RubyClass) ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); |
|
|
|
|
RubyClass typeClass = |
|
|
|
|
(RubyClass) |
|
|
|
|
((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)) |
|
|
|
|
.msgclass(context); |
|
|
|
|
RubyMessage msg = (RubyMessage) typeClass.newInstance(context, Block.NULL_BLOCK); |
|
|
|
|
return msg.buildFrom(context, (DynamicMessage) value); |
|
|
|
|
case ENUM: |
|
|
|
@ -1044,7 +1129,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private RubyRepeatedField repeatedFieldForFieldDescriptor(ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
private RubyRepeatedField repeatedFieldForFieldDescriptor( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
IRubyObject typeClass = context.runtime.getNilClass(); |
|
|
|
|
IRubyObject descriptor = getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
FieldDescriptor.Type type = fieldDescriptor.getType(); |
|
|
|
@ -1056,7 +1142,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
typeClass = ((RubyEnumDescriptor) descriptor).enummodule(context); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
RubyRepeatedField field = new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass); |
|
|
|
|
RubyRepeatedField field = |
|
|
|
|
new RubyRepeatedField(context.runtime, cRepeatedField, type, typeClass); |
|
|
|
|
field.setName(fieldDescriptor.getName()); |
|
|
|
|
|
|
|
|
|
return field; |
|
|
|
@ -1066,8 +1153,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return getFieldInternal(context, fieldDescriptor, true); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject getFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor, |
|
|
|
|
boolean returnDefaults) { |
|
|
|
|
private IRubyObject getFieldInternal( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, boolean returnDefaults) { |
|
|
|
|
OneofDescriptor oneofDescriptor = fieldDescriptor.getContainingOneof(); |
|
|
|
|
if (oneofDescriptor != null) { |
|
|
|
|
if (oneofCases.get(oneofDescriptor) == fieldDescriptor) { |
|
|
|
@ -1077,7 +1164,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
if (oneofCase != null) { |
|
|
|
|
Object builderValue = builder.getField(oneofCase); |
|
|
|
|
if (builderValue != null) { |
|
|
|
|
boolean encodeBytes = oneofCase.hasDefaultValue() && builderValue.equals(oneofCase.getDefaultValue()); |
|
|
|
|
boolean encodeBytes = |
|
|
|
|
oneofCase.hasDefaultValue() && builderValue.equals(oneofCase.getDefaultValue()); |
|
|
|
|
value = wrapField(context, oneofCase, builderValue, encodeBytes); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -1092,17 +1180,16 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} else { |
|
|
|
|
FieldDescriptor oneofCase = builder.getOneofFieldDescriptor(oneofDescriptor); |
|
|
|
|
if (oneofCase != fieldDescriptor) { |
|
|
|
|
if (fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE |
|
|
|
|
|| !returnDefaults) { |
|
|
|
|
if (fieldDescriptor.getType() == FieldDescriptor.Type.MESSAGE || !returnDefaults) { |
|
|
|
|
return context.nil; |
|
|
|
|
} else { |
|
|
|
|
return wrapField(context, fieldDescriptor, |
|
|
|
|
fieldDescriptor.getDefaultValue(), true); |
|
|
|
|
return wrapField(context, fieldDescriptor, fieldDescriptor.getDefaultValue(), true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (returnDefaults || builder.hasField(fieldDescriptor)) { |
|
|
|
|
Object rawValue = builder.getField(oneofCase); |
|
|
|
|
boolean encodeBytes = oneofCase.hasDefaultValue() && rawValue.equals(oneofCase.getDefaultValue()); |
|
|
|
|
boolean encodeBytes = |
|
|
|
|
oneofCase.hasDefaultValue() && rawValue.equals(oneofCase.getDefaultValue()); |
|
|
|
|
IRubyObject value = wrapField(context, oneofCase, rawValue, encodeBytes); |
|
|
|
|
fields.put(fieldDescriptor, value); |
|
|
|
|
return value; |
|
|
|
@ -1119,13 +1206,18 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
int mapSize = this.builder.getRepeatedFieldCount(fieldDescriptor); |
|
|
|
|
FieldDescriptor keyField = fieldDescriptor.getMessageType().findFieldByNumber(1); |
|
|
|
|
FieldDescriptor valueField = fieldDescriptor.getMessageType().findFieldByNumber(2); |
|
|
|
|
RubyDescriptor kvDescriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
RubyDescriptor kvDescriptor = |
|
|
|
|
(RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
RubyClass kvClass = (RubyClass) kvDescriptor.msgclass(context); |
|
|
|
|
for (int i = 0; i < mapSize; i++) { |
|
|
|
|
RubyMessage kvMessage = (RubyMessage) kvClass.newInstance(context, Block.NULL_BLOCK); |
|
|
|
|
DynamicMessage message = (DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i); |
|
|
|
|
DynamicMessage message = |
|
|
|
|
(DynamicMessage) this.builder.getRepeatedField(fieldDescriptor, i); |
|
|
|
|
kvMessage.buildFrom(context, message); |
|
|
|
|
map.indexSet(context, kvMessage.getField(context, keyField), kvMessage.getField(context, valueField)); |
|
|
|
|
map.indexSet( |
|
|
|
|
context, |
|
|
|
|
kvMessage.getField(context, keyField), |
|
|
|
|
kvMessage.getField(context, valueField)); |
|
|
|
|
} |
|
|
|
|
fields.put(fieldDescriptor, map); |
|
|
|
|
} |
|
|
|
@ -1136,13 +1228,15 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return getRepeatedField(context, fieldDescriptor); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE || |
|
|
|
|
builder.hasField(fieldDescriptor) || fields.containsKey(fieldDescriptor)) { |
|
|
|
|
if (fieldDescriptor.getType() != FieldDescriptor.Type.MESSAGE |
|
|
|
|
|| builder.hasField(fieldDescriptor) |
|
|
|
|
|| fields.containsKey(fieldDescriptor)) { |
|
|
|
|
if (fields.containsKey(fieldDescriptor)) { |
|
|
|
|
return fields.get(fieldDescriptor); |
|
|
|
|
} else if (returnDefaults || builder.hasField(fieldDescriptor)) { |
|
|
|
|
Object rawValue = builder.getField(fieldDescriptor); |
|
|
|
|
boolean encodeBytes = fieldDescriptor.hasDefaultValue() && rawValue.equals(fieldDescriptor.getDefaultValue()); |
|
|
|
|
boolean encodeBytes = |
|
|
|
|
fieldDescriptor.hasDefaultValue() && rawValue.equals(fieldDescriptor.getDefaultValue()); |
|
|
|
|
IRubyObject value = wrapField(context, fieldDescriptor, rawValue, encodeBytes); |
|
|
|
|
if (builder.hasField(fieldDescriptor)) { |
|
|
|
|
fields.put(fieldDescriptor, value); |
|
|
|
@ -1153,7 +1247,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return context.nil; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject setFieldInternal(ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
private IRubyObject setFieldInternal( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
testFrozen("can't modify frozen " + getMetaClass()); |
|
|
|
|
|
|
|
|
|
if (fieldDescriptor.isMapField()) { |
|
|
|
@ -1178,12 +1273,15 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
// Determine the typeclass, if any
|
|
|
|
|
IRubyObject typeClass = context.runtime.getObject(); |
|
|
|
|
if (fieldType == FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
typeClass = ((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); |
|
|
|
|
typeClass = |
|
|
|
|
((RubyDescriptor) getDescriptorForField(context, fieldDescriptor)).msgclass(context); |
|
|
|
|
if (value.isNil()) { |
|
|
|
|
addValue = false; |
|
|
|
|
} |
|
|
|
|
} else if (fieldType == FieldDescriptor.Type.ENUM) { |
|
|
|
|
typeClass = ((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)).enummodule(context); |
|
|
|
|
typeClass = |
|
|
|
|
((RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor)) |
|
|
|
|
.enummodule(context); |
|
|
|
|
value = enumToSymbol(context, fieldDescriptor.getEnumType(), value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1207,7 +1305,9 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (addValue) { |
|
|
|
|
value = Utils.checkType(context, fieldType, fieldDescriptor.getName(), value, (RubyModule) typeClass); |
|
|
|
|
value = |
|
|
|
|
Utils.checkType( |
|
|
|
|
context, fieldType, fieldDescriptor.getName(), value, (RubyModule) typeClass); |
|
|
|
|
fields.put(fieldDescriptor, value); |
|
|
|
|
} else { |
|
|
|
|
fields.remove(fieldDescriptor); |
|
|
|
@ -1216,17 +1316,22 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return context.nil; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject getDescriptorForField(ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
private IRubyObject getDescriptorForField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor) { |
|
|
|
|
RubyDescriptor thisRbDescriptor = (RubyDescriptor) getDescriptor(context, metaClass); |
|
|
|
|
RubyFieldDescriptor fd = (RubyFieldDescriptor) thisRbDescriptor.lookup(context, context.runtime.newString(fieldDescriptor.getName())); |
|
|
|
|
RubyFieldDescriptor fd = |
|
|
|
|
(RubyFieldDescriptor) |
|
|
|
|
thisRbDescriptor.lookup(context, context.runtime.newString(fieldDescriptor.getName())); |
|
|
|
|
return fd.getSubtype(context); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private IRubyObject enumToSymbol(ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value) { |
|
|
|
|
private IRubyObject enumToSymbol( |
|
|
|
|
ThreadContext context, EnumDescriptor enumDescriptor, IRubyObject value) { |
|
|
|
|
if (value instanceof RubySymbol) { |
|
|
|
|
return (RubySymbol) value; |
|
|
|
|
} else if (Utils.isRubyNum(value)) { |
|
|
|
|
EnumValueDescriptor enumValue = enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); |
|
|
|
|
EnumValueDescriptor enumValue = |
|
|
|
|
enumDescriptor.findValueByNumberCreatingIfUnknown(RubyNumeric.num2int(value)); |
|
|
|
|
if (enumValue.getIndex() != -1) { |
|
|
|
|
return context.runtime.newSymbol(enumValue.getName()); |
|
|
|
|
} else { |
|
|
|
@ -1239,8 +1344,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
return context.runtime.newSymbol("UNKNOWN"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private RubyRepeatedField rubyToRepeatedField(ThreadContext context, |
|
|
|
|
FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
private RubyRepeatedField rubyToRepeatedField( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, IRubyObject value) { |
|
|
|
|
RubyArray arr = value.convertToArray(); |
|
|
|
|
RubyRepeatedField repeatedField = repeatedFieldForFieldDescriptor(context, fieldDescriptor); |
|
|
|
|
IRubyObject[] values = new IRubyObject[arr.size()]; |
|
|
|
@ -1251,7 +1356,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
RubyDescriptor descriptor = (RubyDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
typeClass = (RubyModule) descriptor.msgclass(context); |
|
|
|
|
} else if (fieldType == FieldDescriptor.Type.ENUM) { |
|
|
|
|
RubyEnumDescriptor enumDescriptor = (RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
RubyEnumDescriptor enumDescriptor = |
|
|
|
|
(RubyEnumDescriptor) getDescriptorForField(context, fieldDescriptor); |
|
|
|
|
typeClass = (RubyModule) enumDescriptor.enummodule(context); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1283,18 +1389,25 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
IRubyObject valueType = RubySymbol.newSymbol(context.runtime, valueField.getType().name()); |
|
|
|
|
|
|
|
|
|
if (valueField.getType() == FieldDescriptor.Type.MESSAGE) { |
|
|
|
|
RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context, |
|
|
|
|
context.runtime.newString("value")); |
|
|
|
|
RubyFieldDescriptor rubyFieldDescriptor = |
|
|
|
|
(RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value")); |
|
|
|
|
RubyDescriptor rubyDescriptor = (RubyDescriptor) rubyFieldDescriptor.getSubtype(context); |
|
|
|
|
return (RubyMap) cMap.newInstance(context, keyType, valueType, |
|
|
|
|
rubyDescriptor.msgclass(context), Block.NULL_BLOCK); |
|
|
|
|
return (RubyMap) |
|
|
|
|
cMap.newInstance( |
|
|
|
|
context, keyType, valueType, rubyDescriptor.msgclass(context), Block.NULL_BLOCK); |
|
|
|
|
|
|
|
|
|
} else if (valueField.getType() == FieldDescriptor.Type.ENUM) { |
|
|
|
|
RubyFieldDescriptor rubyFieldDescriptor = (RubyFieldDescriptor) mapDescriptor.lookup(context, |
|
|
|
|
context.runtime.newString("value")); |
|
|
|
|
RubyEnumDescriptor rubyEnumDescriptor = (RubyEnumDescriptor) rubyFieldDescriptor.getSubtype(context); |
|
|
|
|
return (RubyMap) cMap.newInstance(context, keyType, valueType, |
|
|
|
|
rubyEnumDescriptor.enummodule(context), Block.NULL_BLOCK); |
|
|
|
|
RubyFieldDescriptor rubyFieldDescriptor = |
|
|
|
|
(RubyFieldDescriptor) mapDescriptor.lookup(context, context.runtime.newString("value")); |
|
|
|
|
RubyEnumDescriptor rubyEnumDescriptor = |
|
|
|
|
(RubyEnumDescriptor) rubyFieldDescriptor.getSubtype(context); |
|
|
|
|
return (RubyMap) |
|
|
|
|
cMap.newInstance( |
|
|
|
|
context, |
|
|
|
|
keyType, |
|
|
|
|
valueType, |
|
|
|
|
rubyEnumDescriptor.enummodule(context), |
|
|
|
|
Block.NULL_BLOCK); |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
return (RubyMap) cMap.newInstance(context, keyType, valueType, Block.NULL_BLOCK); |
|
|
|
@ -1324,7 +1437,8 @@ public class RubyMessage extends RubyObject { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void validateMessageType(ThreadContext context, FieldDescriptor fieldDescriptor, String methodName) { |
|
|
|
|
private void validateMessageType( |
|
|
|
|
ThreadContext context, FieldDescriptor fieldDescriptor, String methodName) { |
|
|
|
|
if (descriptor != fieldDescriptor.getContainingType()) { |
|
|
|
|
throw Utils.createTypeError(context, methodName + " method called on wrong message type"); |
|
|
|
|
} |
|
|
|
|