Skip to content

Commit 81b528e

Browse files
Andrii Korotkovfacebook-github-bot
authored andcommitted
Call clearTerseFields during the Thrift object construction
Summary: Call the method after the object is constructed using `withDefaultValues` before deserialization. This should populate terse fields with intrinsic defaults. Reviewed By: rmakheja, thedavekwon Differential Revision: D39779291 fbshipit-source-id: cd92a614c1b47fb757ddaa59371e754199226dda
1 parent d718369 commit 81b528e

38 files changed

+171
-26
lines changed

hphp/runtime/ext/thrift/binary.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ Object binary_deserialize_struct(const String& clsName,
440440
SpecHolder specHolder;
441441
auto const& spec = specHolder.getSpec(cls);
442442
Object dest = spec.newObject(cls);
443+
spec.clearTerseFields(cls, dest);
443444

444445
auto const& fields = spec.fields;
445446
const size_t numFields = fields.size();

hphp/runtime/ext/thrift/compact.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,7 @@ struct CompactReader {
704704
SpecHolder specHolder;
705705
auto const& spec = specHolder.getSpec(cls);
706706
Object dest = spec.newObject(cls);
707+
spec.clearTerseFields(cls, dest);
707708

708709
readStructBegin();
709710
int16_t fieldNum;

hphp/runtime/ext/thrift/spec-holder.cpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,25 @@ const Func* lookupWithDefaultValuesFunc(const Class* cls) {
147147
return func;
148148
}
149149

150+
const StaticString s_clearTerseFields("clearTerseFields");
151+
152+
const Func* lookupClearTerseFieldsFunc(const Class* cls) {
153+
auto const func = cls->lookupMethod(s_clearTerseFields.get());
154+
if (func == nullptr) {
155+
thrift_error(
156+
folly::sformat("Method {}::clearTerseFields() not found", cls->name()),
157+
ERR_INVALID_DATA
158+
);
159+
}
160+
if (func->isStatic()) {
161+
thrift_error(
162+
folly::sformat("Method {}::clearTerseFields() is static", cls->name()),
163+
ERR_INVALID_DATA
164+
);
165+
}
166+
return func;
167+
}
168+
150169
StructSpec compileSpec(const Array& spec, const Class* cls) {
151170
// A union field also writes to a property named __type. If one exists, we
152171
// need to also verify that it accepts integer values. We only need to do
@@ -197,10 +216,13 @@ StructSpec compileSpec(const Array& spec, const Class* cls) {
197216

198217
// We can precompute the cls::withDefaultValues() func pointer only if the
199218
// underlying class cannot change.
200-
auto const func = cls != nullptr && cls->isPersistent()
219+
auto const withDefaultValuesFunc = cls != nullptr && cls->isPersistent()
201220
? lookupWithDefaultValuesFunc(cls) : nullptr;
202221

203-
return StructSpec{HPHP::FixedVector(std::move(temp)), func};
222+
auto const clearTerseFieldsFunc = cls != nullptr && cls->isPersistent()
223+
? lookupClearTerseFieldsFunc(cls) : nullptr;
224+
225+
return StructSpec{HPHP::FixedVector(std::move(temp)), withDefaultValuesFunc, clearTerseFieldsFunc};
204226
}
205227

206228
} // namespace
@@ -303,7 +325,9 @@ Object StructSpec::newObject(Class* cls) const {
303325

304326
auto obj = g_context->invokeFuncFew(
305327
func, cls, 0, nullptr, RuntimeCoeffects::pure(), false /* dynamic */);
306-
if (tvIsObject(obj)) return Object::attach(obj.m_data.pobj);
328+
if (tvIsObject(obj)) {
329+
return Object::attach(obj.m_data.pobj);
330+
}
307331

308332
SCOPE_EXIT { tvDecRefGen(obj); };
309333
thrift_error(
@@ -315,4 +339,11 @@ Object StructSpec::newObject(Class* cls) const {
315339
);
316340
}
317341

342+
void StructSpec::clearTerseFields(Class* cls, const Object& obj) const {
343+
auto const func = clearTerseFieldsFunc != nullptr
344+
? clearTerseFieldsFunc : lookupClearTerseFieldsFunc(cls);
345+
g_context->invokeFuncFew(
346+
func, obj.get(), 0, nullptr, RuntimeCoeffects::write_props());
347+
}
348+
318349
}

hphp/runtime/ext/thrift/spec-holder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,10 @@ struct FieldSpec {
6262
struct StructSpec {
6363
FixedVector<FieldSpec> fields;
6464
const Func* withDefaultValuesFunc;
65+
const Func* clearTerseFieldsFunc;
6566

6667
Object newObject(Class* cls) const;
68+
void clearTerseFields(Class* cls, const Object& obj) const;
6769
};
6870

6971
// Provides safe access to specifications.

hphp/runtime/vm/coeffects.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ RuntimeCoeffects RuntimeCoeffects::globals_leak_safe() {
6262
X(zoned_with) \
6363
X(zoned) \
6464
X(leak_safe_shallow) \
65-
X(write_this_props)
65+
X(write_this_props) \
66+
X(write_props)
6667

6768
#define X(x) \
6869
RuntimeCoeffects RuntimeCoeffects::x() { \

hphp/runtime/vm/coeffects.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ struct RuntimeCoeffects {
4444
static RuntimeCoeffects zoned_with();
4545
static RuntimeCoeffects zoned();
4646
static RuntimeCoeffects write_this_props();
47+
static RuntimeCoeffects write_props();
4748
static RuntimeCoeffects globals_leak_safe();
4849
static RuntimeCoeffects leak_safe_shallow();
4950

hphp/test/slow/class-ptr/lazyclass-suppress.inc

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,27 +40,31 @@ class ThriftStruct {
4040
public static function withDefaultValues()[]: this {
4141
return new static("");
4242
}
43+
44+
public function clearTerseFields()[write_props]: void {}
4345
}
44-
46+
4547
class NestedStruct {
4648
public string $str;
47-
49+
4850
public function __construct(string $str) {
4951
$this->str = $str;
5052
}
51-
53+
5254
public function withDefaultValues()[]: this {
5355
return new static("");
5456
}
57+
58+
public function clearTerseFields()[write_props]: void {}
5559
};
5660

5761
class NestedStructAdapter {
5862
const type THackType = NestedStruct;
59-
63+
6064
public static function toThrift(NestedStruct $hack_value): ThriftStruct {
6165
return new ThriftStruct(HH\Lib\Str\reverse($hack_value->str));
6266
}
63-
67+
6468
public static function fromThrift(ThriftStruct $thrift_struct): NestedStruct {
6569
return new NestedStruct(HH\Lib\Str\reverse($thrift_struct->str));
6670
}
@@ -77,10 +81,12 @@ class Foo {
7781
];
7882

7983
public ?NestedStructAdapter::THackType $nested = null;
80-
84+
8185
public static function withDefaultValues()[]: this {
8286
return new static();
8387
}
88+
89+
public function clearTerseFields()[write_props]: void {}
8490
};
8591

8692
class DummyProtocol {

hphp/test/slow/hack_arr_compat/thrift_list.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ class Listish {
6262
public static function withDefaultValues()[]: this {
6363
return new static();
6464
}
65+
66+
public function clearTerseFields()[write_props]: void {}
6567
}
6668

6769
function test() {

hphp/test/slow/hack_arr_compat/thrift_map.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class Mappish {
6666
public static function withDefaultValues()[]: this {
6767
return new static();
6868
}
69+
70+
public function clearTerseFields()[write_props]: void {}
6971
}
7072

7173
class TestStruct {
@@ -82,6 +84,8 @@ class TestStruct {
8284
public static function withDefaultValues()[]: this {
8385
return new static();
8486
}
87+
88+
public function clearTerseFields()[write_props]: void {}
8589
}
8690

8791
function test() {

hphp/test/slow/hack_arr_compat/thrift_map_2.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class Mappish {
6666
public static function withDefaultValues()[]: this {
6767
return new static();
6868
}
69+
70+
public function clearTerseFields()[write_props]: void {}
6971
}
7072

7173
class Settish {
@@ -84,6 +86,8 @@ class Settish {
8486
public static function withDefaultValues()[]: this {
8587
return new static();
8688
}
89+
90+
public function clearTerseFields()[write_props]: void {}
8791
}
8892

8993
class TestStruct {
@@ -100,6 +104,8 @@ class TestStruct {
100104
public static function withDefaultValues()[]: this {
101105
return new static();
102106
}
107+
108+
public function clearTerseFields()[write_props]: void {}
103109
}
104110

105111
function test() {

0 commit comments

Comments
 (0)