in src/__Private/codegen/CodegenSyntax.hack [363:452]
private function generateFromJSONMethod(Schema\TAST $syntax): CodegenMethod {
$cg = $this->getCodegenFactory();
$body = $cg->codegenHackBuilder()
->addAssignment(
'$offset',
'$initial_offset',
HackBuilderValues::literal(),
);
foreach ($syntax['fields'] as $field) {
$spec = $this->getTypeSpecForField($syntax, $field['field_name']);
$body
->addf('$%s = ', $field['field_name'])
->addMultilineCall(
'Node::fromJSON',
vec[
Str\format(
'($json[\'%s_%s\']%s',
$syntax['prefix'],
$field['field_name'],
// Normally the JSON field always exists, even for nullable
// fields (it would just have "kind": "missing"), so we shouldn't
// need the following fallback. However, there is a special case:
// If the field is removed from the schema, then it will obviously
// not be present here. But we want to be able to treat the new
// schema as compatible with the previous version, which this
// fallback takes care of.
$spec['nullable']
? " ?? dict['kind' => 'missing']) as dict<_, _>"
: ') as dict<_, _>',
),
'$file',
'$offset',
'$source',
\var_export($spec['class'], true),
],
);
if ($spec['nullable']) {
$body->addLinef(
'$offset += $%s?->getWidth() ?? 0;',
$field['field_name'],
);
continue;
}
$body
->addLinef(
'$%s = $%s as nonnull;',
$field['field_name'],
$field['field_name'],
)
->addLinef('$offset += $%s->getWidth();', $field['field_name']);
}
return $cg
->codegenMethod('fromJSON')
->setIsOverride()
->setIsStatic()
->addParameter('dict<arraykey, mixed> $json')
->addParameter('string $file')
->addParameter('int $initial_offset')
->addParameter('string $source')
->addParameter('string $_type_hint')
->setReturnType('this')
->setBody(
$body
->addAssignment(
'$source_ref',
shape(
'file' => '$file',
'source' => '$source',
'offset' => '$initial_offset',
'width' => '$offset - $initial_offset',
),
HackBuilderValues::shapeWithUniformRendering(
HackBuilderValues::literal(),
),
)
->addMultilineCall(
'return new static',
Vec\concat(
Vec\map(
$syntax['fields'],
$field ==> '/* HH_IGNORE_ERROR[4110] */ $'.$field['field_name'],
),
vec['$source_ref'],
),
)
->getCode(),
);
}