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

95.23% Statements 120/126
88.15% Branches 67/76
100% Functions 2/2
95.23% Lines 120/126

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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 1261x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 50x 50x 50x 50x 50x 50x 14x 14x 14x 14x 11x 11x 1x 1x 14x   3x 3x 3x 14x 46x 46x 50x     46x 46x 46x 46x 46x 42x 42x 3x 42x 5x 39x 34x 34x 34x   34x 2x 34x 24x 24x 1x 24x 23x 23x 1x 23x 16x 16x 24x 1x 1x 24x 42x 46x 32x 50x 5x 5x 32x 32x 32x 1x 1x 32x 8x 8x 1x 1x 8x 8x 24x 24x 32x 32x 32x 32x 32x 32x 11x 11x     11x 11x 11x 11x 11x 11x 11x 11x 11x 11x 3x 11x 1x 1x 7x 7x 7x 20x 32x 1x 1x 19x 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, DataType, GenericObjectReferenceType, ObjectReferenceType, UnknownType, VoidType} from "../../types/basic";
import {ClassDefinition, InterfaceDefinition} from "../../types";
import {StatementSyntax} from "../_statement_syntax";
import {IClassDefinition} from "../../types/_class_definition";
import {ObjectOriented} from "../_object_oriented";
import {TypeUtils} from "../_type_utils";
 
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.findDirectExpressions(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 UnknownType) {
          throw new Error("Target type unknown, " + t.concatTokens());
        } else if (!(found instanceof ObjectReferenceType)
            && !(found instanceof AnyType)
            && !(found instanceof DataType)
            && !(found instanceof GenericObjectReferenceType)) {
          throw new Error("Target must be an object reference, " + t.concatTokens());
        } 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 InterfaceDefinition && type === undefined) {
            throw new Error("Interface reference, cannot be instantiated");
          } else if (found instanceof ObjectReferenceType
              && type === undefined
              && scope.findInterfaceDefinition(found.getQualifiedName())) {
            throw new Error("Interface reference, cannot be instantiated");
          } else 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, filename);
  }
 
  private validateParameters(cdef: IClassDefinition | undefined, node: StatementNode, scope: CurrentScope, filename: string) {
    if (cdef === undefined) {
      const sources = node.findDirectExpression(Expressions.ParameterListS)?.findAllExpressions(Expressions.Source);
      for (const s of sources || []) {
        new Source().runSyntax(s, scope, filename);
      }
      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()));
 
    for (const p of node.findDirectExpression(Expressions.ParameterListS)?.findAllExpressions(Expressions.ParameterS) || []) {
      const name = p.findDirectExpression(Expressions.ParameterName)?.concatTokens().toUpperCase();
      if (name === undefined) {
        continue;
      }
 
      const source = p.findDirectExpression(Expressions.Source);
      const sourceType = new Source().runSyntax(source, scope, filename);
 
      const calculated = source?.findFirstExpression(Expressions.MethodCallChain) !== undefined
        || source?.findFirstExpression(Expressions.StringTemplate) !== undefined
        || source?.findFirstExpression(Expressions.ArithOperator) !== undefined;
 
      const found = allImporting?.find(p => p.getName().toUpperCase() === name);
      if (found === undefined) {
        throw new Error(`constructor parameter "${name}" does not exist`);
      } else if (new TypeUtils(scope).isAssignableStrict(sourceType, found.getType(), calculated) === false) {
        throw new Error(`constructor parameter "${name}" type not compatible`);
      }
 
      requiredImporting.delete(name);
    }
 
    for (const r of requiredImporting.values()) {
      throw new Error(`constructor parameter "${r}" must be supplied`);
    }
  }
}