All files / src/abap/5_syntax/statements create_object.ts

96.07% Statements 98/102
90.56% Branches 48/53
100% Functions 2/2
96.07% Lines 98/102

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 1021x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 25x 25x 25x 25x 25x 25x 9x 9x 9x 9x 7x 7x 1x 1x 9x   2x 2x 2x 9x 22x 22x 25x 6x 6x 22x 22x 22x 22x 22x 22x 22x 2x 22x 20x 20x   20x 1x 20x 12x 12x 10x 10x 12x 1x 1x 12x 22x 22x 20x 25x 3x 3x 20x 20x 20x 1x 1x 20x 5x 5x 15x 15x 20x 20x 20x 20x 20x 20x 20x 6x 6x     6x 6x 3x 3x 3x 3x 12x 20x 1x 1x 11x 1x
import * as Expressions from "../../2_statements/expressions";
import {StatementNode} from "../../nodes";
import {CurrentScope} from "../_current_scope";
import {Source} from "../expressions/source";
import {Target} from "../expressions/target";
import {Dynamic} from "../expressions/dynamic";
import {ReferenceType} from "../_reference";
import {AnyType, GenericObjectReferenceType, ObjectReferenceType, VoidType} from "../../types/basic";
import {ClassDefinition} from "../../types";
import {StatementSyntax} from "../_statement_syntax";
import {IClassDefinition} from "../../types/_class_definition";
import {ObjectOriented} from "../_object_oriented";
 
export class CreateObject implements StatementSyntax {
  public runSyntax(node: StatementNode, scope: CurrentScope, filename: string): void {
 
    let cdef: IClassDefinition | undefined = undefined;
 
    // CREATE OBJECT, TYPE
    const type = node.findExpressionAfterToken("TYPE");
    if (type && type.get() instanceof Expressions.ClassName) {
      const token = type.getFirstToken();
      const name = token.getStr();
      cdef = scope.findClassDefinition(name);
      if (cdef) {
        scope.addReference(token, cdef, ReferenceType.ObjectOrientedReference, filename);
        if (cdef.isAbstract() === true) {
          throw new Error(cdef.getName() + " is abstract, cannot be instantiated");
        }
      } else if (scope.getDDIC().inErrorNamespace(name) === false) {
        scope.addReference(token, undefined, ReferenceType.ObjectOrientedVoidReference, filename, {ooName: name, ooType: "CLAS"});
      } else {
        throw new Error("TYPE \"" + name + "\" not found");
      }
    }
 
    // just recurse
    for (const s of node.findAllExpressions(Expressions.Source)) {
      new Source().runSyntax(s, scope, filename);
    }
 
    let first = true;
    for (const t of node.findAllExpressions(Expressions.Target)) {
      const found = new Target().runSyntax(t, scope, filename);
      if (first === true) {
        first = false;
        if (found instanceof VoidType) {
          continue;
        } else if (!(found instanceof ObjectReferenceType)
            && !(found instanceof AnyType)
            && !(found instanceof GenericObjectReferenceType)) {
          throw new Error("Target must be an object reference");
        } else if (found instanceof GenericObjectReferenceType && type === undefined) {
          throw new Error("Generic type, cannot be instantiated");
        } else if (found instanceof ObjectReferenceType) {
          const id = found.getIdentifier();
          if (id instanceof ClassDefinition && cdef === undefined) {
            cdef = id;
          }
          if (type === undefined && id instanceof ClassDefinition && id.isAbstract() === true) {
            throw new Error(id.getName() + " is abstract, cannot be instantiated");
          }
        }
      }
    }
 
    for (const t of node.findDirectExpressions(Expressions.Dynamic)) {
      new Dynamic().runSyntax(t, scope, filename);
    }
 
    this.validateParameters(cdef, node, scope);
  }
 
  private validateParameters(cdef: IClassDefinition | undefined, node: StatementNode, scope: CurrentScope) {
    if (cdef === undefined) {
      return;
    }
 
    const methodDef = new ObjectOriented(scope).searchMethodName(cdef, "CONSTRUCTOR");
    const methodParameters = methodDef.method?.getParameters();
 
    const allImporting = methodParameters?.getImporting() || [];
    const requiredImporting = new Set(methodParameters?.getRequiredParameters().map(i => i.getName().toUpperCase()));
 
// todo, validate types
    for (const p of node.findDirectExpression(Expressions.ParameterListS)?.findAllExpressions(Expressions.ParameterS) || []) {
      const name = p.findDirectExpression(Expressions.ParameterName)?.concatTokens().toUpperCase();
      if (name === undefined) {
        continue;
      }
 
      if (allImporting?.some(p => p.getName().toUpperCase() === name) === false) {
        throw new Error(`constructor parameter "${name}" does not exist`);
      }
      requiredImporting.delete(name);
    }
 
    for (const r of requiredImporting.values()) {
      throw new Error(`constructor parameter "${r}" must be supplied`);
    }
  }
}