Commit d099a886 authored by Brian Duff's avatar Brian Duff

Add clone() method support for nano.

Upstreamed from Another Place (cr/57247854).

Change-Id: I2aaf59544c0f5ae21a51891d8a5eeda1dc722c90
parent fb96026b
...@@ -97,19 +97,19 @@ ...@@ -97,19 +97,19 @@
<arg value="src/test/java/com/google/protobuf/nano/map_test.proto" /> <arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" /> <arg value="--javanano_out=store_unknown_fields=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" /> <arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" /> <arg value="--javanano_out=store_unknown_fields=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" /> <arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" />
</exec> </exec>
<exec executable="../src/protoc"> <exec executable="../src/protoc">
<arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true:target/generated-test-sources" /> <arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true,generate_clone=true:target/generated-test-sources" />
<arg value="--proto_path=src/test/java/com" /> <arg value="--proto_path=src/test/java/com" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" /> <arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" />
</exec> </exec>
......
...@@ -184,4 +184,11 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>> ...@@ -184,4 +184,11 @@ public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>>
return (unknownFieldData == null || unknownFieldData.isEmpty() return (unknownFieldData == null || unknownFieldData.isEmpty()
? 0 : unknownFieldData.hashCode()); ? 0 : unknownFieldData.hashCode());
} }
@Override
public M clone() throws CloneNotSupportedException {
M cloned = (M) super.clone();
InternalNano.cloneUnknownFieldData(this, cloned);
return cloned;
}
} }
...@@ -37,7 +37,7 @@ package com.google.protobuf.nano; ...@@ -37,7 +37,7 @@ package com.google.protobuf.nano;
* *
* Based on {@link android.support.v4.util.SpareArrayCompat}. * Based on {@link android.support.v4.util.SpareArrayCompat}.
*/ */
class FieldArray { class FieldArray implements Cloneable {
private static final FieldData DELETED = new FieldData(); private static final FieldData DELETED = new FieldData();
private boolean mGarbage = false; private boolean mGarbage = false;
...@@ -270,4 +270,19 @@ class FieldArray { ...@@ -270,4 +270,19 @@ class FieldArray {
} }
return true; return true;
} }
@Override
public final FieldArray clone() {
// Trigger GC so we compact and don't copy DELETED elements.
int size = size();
FieldArray clone = new FieldArray(size);
System.arraycopy(mFieldNumbers, 0, clone.mFieldNumbers, 0, size);
for (int i = 0; i < size; i++) {
if (mData[i] != null) {
clone.mData[i] = mData[i].clone();
}
}
clone.mSize = size;
return clone;
}
} }
...@@ -39,7 +39,7 @@ import java.util.List; ...@@ -39,7 +39,7 @@ import java.util.List;
* Stores unknown fields. These might be extensions or fields that the generated API doesn't * Stores unknown fields. These might be extensions or fields that the generated API doesn't
* know about yet. * know about yet.
*/ */
class FieldData { class FieldData implements Cloneable {
private Extension<?, ?> cachedExtension; private Extension<?, ?> cachedExtension;
private Object value; private Object value;
/** The serialised values for this object. Will be cleared if getValue is called */ /** The serialised values for this object. Will be cleared if getValue is called */
...@@ -187,4 +187,54 @@ class FieldData { ...@@ -187,4 +187,54 @@ class FieldData {
return result; return result;
} }
@Override
public final FieldData clone() {
FieldData clone = new FieldData();
try {
clone.cachedExtension = cachedExtension;
if (unknownFieldData == null) {
clone.unknownFieldData = null;
} else {
clone.unknownFieldData.addAll(unknownFieldData);
}
// Whether we need to deep clone value depends on its type. Primitive reference types
// (e.g. Integer, Long etc.) are ok, since they're immutable. We need to clone arrays
// and messages.
if (value == null) {
// No cloning required.
} else if (value instanceof MessageNano) {
clone.value = ((MessageNano) value).clone();
} else if (value instanceof byte[]) {
clone.value = ((byte[]) value).clone();
} else if (value instanceof byte[][]) {
byte[][] valueArray = (byte[][]) value;
byte[][] cloneArray = new byte[valueArray.length][];
clone.value = cloneArray;
for (int i = 0; i < valueArray.length; i++) {
cloneArray[i] = valueArray[i].clone();
}
} else if (value instanceof boolean[]) {
clone.value = ((boolean[]) value).clone();
} else if (value instanceof int[]) {
clone.value = ((int[]) value).clone();
} else if (value instanceof long[]) {
clone.value = ((long[]) value).clone();
} else if (value instanceof float[]) {
clone.value = ((float[]) value).clone();
} else if (value instanceof double[]) {
clone.value = ((double[]) value).clone();
} else if (value instanceof MessageNano[]) {
MessageNano[] valueArray = (MessageNano[]) value;
MessageNano[] cloneArray = new MessageNano[valueArray.length];
clone.value = cloneArray;
for (int i = 0; i < valueArray.length; i++) {
cloneArray[i] = valueArray[i].clone();
}
}
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
} }
...@@ -536,4 +536,12 @@ public final class InternalNano { ...@@ -536,4 +536,12 @@ public final class InternalNano {
} }
return o.hashCode(); return o.hashCode();
} }
// This avoids having to make FieldArray public.
public static void cloneUnknownFieldData(ExtendableMessageNano original,
ExtendableMessageNano cloned) {
if (original.unknownFieldData != null) {
cloned.unknownFieldData = (FieldArray) original.unknownFieldData.clone();
}
}
} }
...@@ -187,4 +187,12 @@ public abstract class MessageNano { ...@@ -187,4 +187,12 @@ public abstract class MessageNano {
public String toString() { public String toString() {
return MessageNanoPrinter.print(this); return MessageNanoPrinter.print(this);
} }
/**
* Provides support for cloning. This only works if you specify the generate_clone method.
*/
@Override
public MessageNano clone() throws CloneNotSupportedException {
return (MessageNano) super.clone();
}
} }
...@@ -42,6 +42,10 @@ import java.util.Arrays; ...@@ -42,6 +42,10 @@ import java.util.Arrays;
final class UnknownFieldData { final class UnknownFieldData {
final int tag; final int tag;
/**
* Important: this should be treated as immutable, even though it's possible
* to change the array values.
*/
final byte[] bytes; final byte[] bytes;
UnknownFieldData(int tag, byte[] bytes) { UnknownFieldData(int tag, byte[] bytes) {
......
...@@ -3023,6 +3023,10 @@ public class NanoTest extends TestCase { ...@@ -3023,6 +3023,10 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat)));
assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble)));
assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum)));
// Clone the message and ensure it's still equal.
Extensions.ExtendableMessage clone = message.clone();
assertEquals(clone, message);
} }
public void testNullExtensions() throws Exception { public void testNullExtensions() throws Exception {
...@@ -4406,6 +4410,22 @@ public class NanoTest extends TestCase { ...@@ -4406,6 +4410,22 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(nonPacked.enums, packed.enums)); assertTrue(Arrays.equals(nonPacked.enums, packed.enums));
} }
public void testClone() throws Exception {
// A simple message.
AnotherMessage anotherMessage = new AnotherMessage();
anotherMessage.string = "Hello";
anotherMessage.value = true;
anotherMessage.integers = new int[] { 1, 2, 3 };
AnotherMessage clone = anotherMessage.clone();
assertEquals(clone, anotherMessage);
// Verify it was a deep clone - changes to the clone shouldn't affect the
// original.
clone.integers[1] = 100;
assertFalse(clone.equals(anotherMessage));
}
private void assertHasWireData(MessageNano message, boolean expected) { private void assertHasWireData(MessageNano message, boolean expected) {
byte[] bytes = MessageNano.toByteArray(message); byte[] bytes = MessageNano.toByteArray(message);
int wireLength = bytes.length; int wireLength = bytes.length;
......
...@@ -16,6 +16,7 @@ enum AnEnum { ...@@ -16,6 +16,7 @@ enum AnEnum {
message AnotherMessage { message AnotherMessage {
optional string string = 1; optional string string = 1;
optional bool value = 2; optional bool value = 2;
repeated int32 integers = 3;
} }
message ContainerMessage { message ContainerMessage {
......
...@@ -498,6 +498,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -498,6 +498,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\n");
} }
void RepeatedEnumFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void RepeatedEnumFieldGenerator:: void RepeatedEnumFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
......
...@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { ...@@ -106,6 +106,7 @@ class RepeatedEnumFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const; void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
......
...@@ -83,6 +83,7 @@ class FieldGenerator { ...@@ -83,6 +83,7 @@ class FieldGenerator {
virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0;
virtual void GenerateEqualsCode(io::Printer* printer) const = 0; virtual void GenerateEqualsCode(io::Printer* printer) const = 0;
virtual void GenerateHashCodeCode(io::Printer* printer) const = 0; virtual void GenerateHashCodeCode(io::Printer* printer) const = 0;
virtual void GenerateFixClonedCode(io::Printer* printer) const {}
protected: protected:
const Params& params_; const Params& params_;
......
...@@ -152,6 +152,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file, ...@@ -152,6 +152,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
params.set_ignore_services(option_value == "true"); params.set_ignore_services(option_value == "true");
} else if (option_name == "parcelable_messages") { } else if (option_name == "parcelable_messages") {
params.set_parcelable_messages(option_value == "true"); params.set_parcelable_messages(option_value == "true");
} else if (option_name == "generate_clone") {
params.set_generate_clone(option_value == "true");
} else { } else {
*error = "Ignore unknown javanano generator option: " + option_name; *error = "Ignore unknown javanano generator option: " + option_name;
} }
......
...@@ -136,18 +136,23 @@ void MessageGenerator::Generate(io::Printer* printer) { ...@@ -136,18 +136,23 @@ void MessageGenerator::Generate(io::Printer* printer) {
} }
if (params_.store_unknown_fields() && params_.parcelable_messages()) { if (params_.store_unknown_fields() && params_.parcelable_messages()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$> {\n", " com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$>",
"classname", descriptor_->name()); "classname", descriptor_->name());
} else if (params_.store_unknown_fields()) { } else if (params_.store_unknown_fields()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n", " com.google.protobuf.nano.ExtendableMessageNano<$classname$>",
"classname", descriptor_->name()); "classname", descriptor_->name());
} else if (params_.parcelable_messages()) { } else if (params_.parcelable_messages()) {
printer->Print( printer->Print(
" com.google.protobuf.nano.android.ParcelableMessageNano {\n"); " com.google.protobuf.nano.android.ParcelableMessageNano");
} else { } else {
printer->Print( printer->Print(
" com.google.protobuf.nano.MessageNano {\n"); " com.google.protobuf.nano.MessageNano");
}
if (params_.generate_clone()) {
printer->Print(" implements java.lang.Cloneable {\n");
} else {
printer->Print(" {\n");
} }
printer->Indent(); printer->Indent();
...@@ -306,6 +311,10 @@ void MessageGenerator::Generate(io::Printer* printer) { ...@@ -306,6 +311,10 @@ void MessageGenerator::Generate(io::Printer* printer) {
GenerateClear(printer); GenerateClear(printer);
if (params_.generate_clone()) {
GenerateClone(printer);
}
if (params_.generate_equals()) { if (params_.generate_equals()) {
GenerateEquals(printer); GenerateEquals(printer);
GenerateHashCode(printer); GenerateHashCode(printer);
...@@ -536,6 +545,33 @@ void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) { ...@@ -536,6 +545,33 @@ void MessageGenerator::GenerateFieldInitializers(io::Printer* printer) {
printer->Print("cachedSize = -1;\n"); printer->Print("cachedSize = -1;\n");
} }
void MessageGenerator::GenerateClone(io::Printer* printer) {
printer->Print(
"@Override\n"
"public $classname$ clone() {\n",
"classname", descriptor_->name());
printer->Indent();
printer->Print(
"$classname$ cloned;\n"
"try {\n"
" cloned = ($classname$) super.clone();\n"
"} catch (java.lang.CloneNotSupportedException e) {\n"
" throw new java.lang.AssertionError(e);\n"
"}\n",
"classname", descriptor_->name());
for (int i = 0; i < descriptor_->field_count(); i++) {
field_generators_.get(descriptor_->field(i)).GenerateFixClonedCode(printer);
}
printer->Outdent();
printer->Print(
" return cloned;\n"
"}\n"
"\n");
}
void MessageGenerator::GenerateEquals(io::Printer* printer) { void MessageGenerator::GenerateEquals(io::Printer* printer) {
// Don't override if there are no fields. We could generate an // Don't override if there are no fields. We could generate an
// equals method that compares types, but often empty messages // equals method that compares types, but often empty messages
......
...@@ -80,6 +80,7 @@ class MessageGenerator { ...@@ -80,6 +80,7 @@ class MessageGenerator {
void GenerateFieldInitializers(io::Printer* printer); void GenerateFieldInitializers(io::Printer* printer);
void GenerateEquals(io::Printer* printer); void GenerateEquals(io::Printer* printer);
void GenerateHashCode(io::Printer* printer); void GenerateHashCode(io::Printer* printer);
void GenerateClone(io::Printer* printer);
const Params& params_; const Params& params_;
const Descriptor* descriptor_; const Descriptor* descriptor_;
......
...@@ -126,6 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -126,6 +126,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\n");
} }
void MessageFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void MessageFieldGenerator:: void MessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
...@@ -212,6 +220,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -212,6 +220,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\n");
} }
void MessageOneofFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$oneof_name$ != null) {\n"
" cloned.$oneof_name$ = this.$oneof_name$.clone();\n"
"}\n");
}
void MessageOneofFieldGenerator:: void MessageOneofFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
GenerateOneofFieldEquals(descriptor_, variables_, printer); GenerateOneofFieldEquals(descriptor_, variables_, printer);
...@@ -312,6 +328,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -312,6 +328,19 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
"}\n"); "}\n");
} }
void RepeatedMessageFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = new $type$[this.$name$.length];\n"
" for (int i = 0; i < this.$name$.length; i++) {\n"
" if (this.$name$[i] != null) {\n"
" cloned.$name$[i] = this.$name$[i].clone();\n"
" }\n"
" }\n"
"}\n");
}
void RepeatedMessageFieldGenerator:: void RepeatedMessageFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_, printer->Print(variables_,
......
...@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator { ...@@ -58,6 +58,7 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
...@@ -80,6 +81,7 @@ class MessageOneofFieldGenerator : public FieldGenerator { ...@@ -80,6 +81,7 @@ class MessageOneofFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
...@@ -102,6 +104,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { ...@@ -102,6 +104,7 @@ class RepeatedMessageFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
const FieldDescriptor* descriptor_; const FieldDescriptor* descriptor_;
......
...@@ -66,6 +66,7 @@ class Params { ...@@ -66,6 +66,7 @@ class Params {
bool parcelable_messages_; bool parcelable_messages_;
bool reftypes_primitive_enums_; bool reftypes_primitive_enums_;
bool generate_clear_; bool generate_clear_;
bool generate_clone_;
public: public:
Params(const string & base_name) : Params(const string & base_name) :
...@@ -81,7 +82,8 @@ class Params { ...@@ -81,7 +82,8 @@ class Params {
ignore_services_(false), ignore_services_(false),
parcelable_messages_(false), parcelable_messages_(false),
reftypes_primitive_enums_(false), reftypes_primitive_enums_(false),
generate_clear_(true) { generate_clear_(true),
generate_clone_(false) {
} }
const string& base_name() const { const string& base_name() const {
...@@ -231,6 +233,13 @@ class Params { ...@@ -231,6 +233,13 @@ class Params {
bool generate_clear() const { bool generate_clear() const {
return generate_clear_; return generate_clear_;
} }
void set_generate_clone(bool value) {
generate_clone_ = value;
}
bool generate_clone() const {
return generate_clone_;
}
}; };
} // namespace javanano } // namespace javanano
......
...@@ -364,6 +364,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const { ...@@ -364,6 +364,14 @@ GenerateSerializedSizeCode(io::Printer* printer) const {
} }
} }
void RepeatedPrimitiveFieldGenerator::
GenerateFixClonedCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null && this.$name$.length > 0) {\n"
" cloned.$name$ = this.$name$.clone();\n"
"}\n");
}
void PrimitiveFieldGenerator:: void PrimitiveFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const { GenerateEqualsCode(io::Printer* printer) const {
// We define equality as serialized form equality. If generate_has(), // We define equality as serialized form equality. If generate_has(),
......
...@@ -131,6 +131,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { ...@@ -131,6 +131,7 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator {
void GenerateSerializedSizeCode(io::Printer* printer) const; void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const; void GenerateHashCodeCode(io::Printer* printer) const;
void GenerateFixClonedCode(io::Printer* printer) const;
private: private:
void GenerateRepeatedDataSizeCode(io::Printer* printer) const; void GenerateRepeatedDataSizeCode(io::Printer* printer) const;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment