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

96.04% Statements 97/101
90.57% Branches 48/53
100% Functions 2/2
96.04% Lines 97/101

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 1011x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 30x 30x 30x 30x 30x 30x 11x 11x 11x 11x 9x 9x 1x 1x 11x   2x 2x 2x 11x 27x 27x 30x 9x 9x 26x 26x 26x 26x 26x 25x 25x 2x 25x 23x   23x 2x 23x 15x 15x 11x 11x 15x 1x 1x 15x 25x 26x 22x 30x 2x 2x 22x 22x 22x 1x 1x 22x 4x 4x 18x 18x 22x 22x 22x 22x 22x 22x 22x 8x 8x     8x 8x 3x 3x 5x 5x 15x 22x 1x 1x 14x 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 {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 GenericObjectReferenceType)) {
          throw new Error("Target must be a 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`);
    }
  }
}