private function generateFromJSONMethod()

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(),
      );
  }