All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.fraunhofer.aisec.cpg.frontends.cpp.DeclaratorHandler Maven / Gradle / Ivy

Go to download

A simple library to extract a code property graph out of source code. It has support for multiple passes that can extend the analysis after the graph is constructed.

There is a newer version: 8.3.0
Show newest version
/*
 * Copyright (c) 2019, Fraunhofer AISEC. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *                    $$$$$$\  $$$$$$$\   $$$$$$\
 *                   $$  __$$\ $$  __$$\ $$  __$$\
 *                   $$ /  \__|$$ |  $$ |$$ /  \__|
 *                   $$ |      $$$$$$$  |$$ |$$$$\
 *                   $$ |      $$  ____/ $$ |\_$$ |
 *                   $$ |  $$\ $$ |      $$ |  $$ |
 *                   \$$$$$   |$$ |      \$$$$$   |
 *                    \______/ \__|       \______/
 *
 */

package de.fraunhofer.aisec.cpg.frontends.cpp;

import de.fraunhofer.aisec.cpg.frontends.Handler;
import de.fraunhofer.aisec.cpg.graph.ConstructorDeclaration;
import de.fraunhofer.aisec.cpg.graph.Declaration;
import de.fraunhofer.aisec.cpg.graph.Expression;
import de.fraunhofer.aisec.cpg.graph.FieldDeclaration;
import de.fraunhofer.aisec.cpg.graph.FunctionDeclaration;
import de.fraunhofer.aisec.cpg.graph.MethodDeclaration;
import de.fraunhofer.aisec.cpg.graph.NodeBuilder;
import de.fraunhofer.aisec.cpg.graph.ParamVariableDeclaration;
import de.fraunhofer.aisec.cpg.graph.RecordDeclaration;
import de.fraunhofer.aisec.cpg.graph.Type;
import de.fraunhofer.aisec.cpg.graph.ValueDeclaration;
import de.fraunhofer.aisec.cpg.graph.VariableDeclaration;
import de.fraunhofer.aisec.cpg.passes.scopes.RecordScope;
import de.fraunhofer.aisec.cpg.passes.scopes.Scope;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTExpression;
import org.eclipse.cdt.core.dom.ast.IASTInitializer;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTParameterDeclaration;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTArrayDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTFunctionDeclarator;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTVisibilityLabel;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;

class DeclaratorHandler extends Handler {

  DeclaratorHandler(CXXLanguageFrontend lang) {
    super(Declaration::new, lang);

    map.put(CPPASTDeclarator.class, ctx -> handleDeclarator((CPPASTDeclarator) ctx));
    map.put(CPPASTArrayDeclarator.class, ctx -> handleDeclarator((CPPASTDeclarator) ctx));
    map.put(
        CPPASTFunctionDeclarator.class,
        ctx -> handleFunctionDeclarator((CPPASTFunctionDeclarator) ctx));
    map.put(
        CPPASTCompositeTypeSpecifier.class,
        ctx -> handleCompositeTypeSpecifier((CPPASTCompositeTypeSpecifier) ctx));
  }

  private int getEvaluatedIntegerValue(IASTExpression exp) {
    try {
      Method method = exp.getClass().getMethod("getEvaluation");
      ICPPEvaluation evaluation = (ICPPEvaluation) method.invoke(exp);
      return evaluation.getValue().numberValue().intValue();
    } catch (Exception e) {
      return -1;
    }
  }

  private VariableDeclaration handleDeclarator(CPPASTDeclarator ctx) {
    // type will be filled out later
    VariableDeclaration declaration =
        NodeBuilder.newVariableDeclaration(
            ctx.getName().toString(), Type.getUnknown(), ctx.getRawSignature());

    IASTInitializer init = ctx.getInitializer();

    if (init != null) {
      declaration.setInitializer(lang.getInitializerHandler().handle(init));
    }

    String typeAdjustment =
        List.of(ctx.getPointerOperators()).stream()
            .map(IASTNode::getRawSignature)
            .collect(Collectors.joining());
    if (ctx instanceof CPPASTArrayDeclarator) {
      /* TODO This is responsible for adding array size to the type adjustment. To be determined
          whether this is necessary and in which form it should be adopted
      CPPASTArrayDeclarator arrayDecl = (CPPASTArrayDeclarator) ctx;
      List dimensions = new ArrayList<>();
      int elementMultiplier = 1;
      for (IASTArrayModifier mod : arrayDecl.getArrayModifiers()) {
        dimensions.add(mod.getRawSignature());
        if (mod.getRawSignature().length() > 2) {
          int dimension = getEvaluatedIntegerValue(mod.getConstantExpression());
          if (dimension == -1) {
            elementMultiplier = -1;
            break;
          }
          dimensions.set(dimensions.size() - 1, "[" + dimension + "]");
          elementMultiplier *= dimension;
        }
      }
      if (declaration.getInitializer() instanceof InitializerListExpression) {
        InitializerListExpression initList =
            (InitializerListExpression) declaration.getInitializer();
        // narrow down array type to size of initializer list expression

        // Here one could compute the statically determinable dimension size from the initializer.
        // The initializer list may
        // be flattened and the computation is far from trivial if the dimension expression is
        // constant but has to be evaluated
        // In newer standards dynamic length arrays are possible.
        if (dimensions.get(0).length() <= 2)
          if (initList.getInitializers().stream()
              .noneMatch(elmt -> elmt instanceof InitializerListExpression)) {
            if (elementMultiplier > 0)
              dimensions.set(
                  0,
                  "["
                      + (-Math.floorDiv(-initList.getInitializers().size(), elementMultiplier))
                      + "]");
          } else {
            dimensions.set(0, "[" + initList.getInitializers().size() + "]");
          }

        typeAdjustment += String.join("", dimensions);

      } else if (declaration.getInitializer() instanceof Literal
          && ((Literal) declaration.getInitializer()).getValue() instanceof String) {
        // narrow down array type to length of string literal
        typeAdjustment +=
            "["
                + (((String) ((Literal) declaration.getInitializer()).getValue()).length() + 1)
                + "]";
      } else {
        typeAdjustment += String.join("", dimensions);
      }
      */
      typeAdjustment += "[]";
    }

    // forward type adjustments
    declaration.getType().setTypeAdjustment(typeAdjustment);
    lang.getScopeManager().addValueDeclaration(declaration);
    return declaration;
  }

  private ValueDeclaration handleFunctionDeclarator(CPPASTFunctionDeclarator ctx) {
    // Attention! If this declarator has no name, this is not actually a new function but
    // rather a function pointer
    if (ctx.getName().toString().isEmpty()) {
      return handleFunctionPointer(ctx);
    }
    String name = ctx.getName().toString();

    FunctionDeclaration declaration;

    // check for function definitions that are really methods and constructors
    if (name.contains("::")) {
      String[] rr = name.split("::");

      String recordName = rr[0];
      String methodName = rr[1];

      declaration =
          NodeBuilder.newMethodDeclaration(
              methodName,
              ctx.getRawSignature(),
              false,
              this.lang.getRecordForName(recordName).orElse(null));
    } else {
      declaration = NodeBuilder.newFunctionDeclaration(name, ctx.getRawSignature());
    }
    lang.getScopeManager().enterScope(declaration);

    int i = 0;
    for (ICPPASTParameterDeclaration param : ctx.getParameters()) {
      ParamVariableDeclaration arg = lang.getParameterDeclarationHandler().handle(param);

      IBinding binding = ctx.getParameters()[i].getDeclarator().getName().resolveBinding();

      if (binding != null) {
        lang.cacheDeclaration(binding, arg);
      }

      arg.setArgumentIndex(i);
      // Note that this .addValueDeclaration call already adds arg to the function's parameters.
      // This is why the following line has been commented out by @KW
      lang.getScopeManager().addValueDeclaration(arg);
      // declaration.getParameters().add(arg);
      i++;
    }

    // Check for varargs. Note the difference to Java: here, we don't have a named array
    // containing the varargs, but they are rather treated as kind of an invisible arg list that is
    // appended to the original ones. For coherent graph behaviour, we introduce a dummy that
    // wraps this list
    if (ctx.takesVarArgs()) {
      ParamVariableDeclaration varargs =
          NodeBuilder.newMethodParameterIn("va_args", Type.getUnknown(), true, "");
      varargs.setArgumentIndex(i);
      lang.getScopeManager().addValueDeclaration(varargs);
    }

    // forward type adjustments
    declaration
        .getType()
        .setTypeAdjustment(
            List.of(ctx.getPointerOperators()).stream()
                .map(IASTNode::getRawSignature)
                .collect(Collectors.joining()));

    //    lang.addFunctionDeclaration(declaration);
    lang.getScopeManager().leaveScope(declaration);
    return declaration;
  }

  private ValueDeclaration handleFunctionPointer(CPPASTFunctionDeclarator ctx) {
    Expression initializer =
        ctx.getInitializer() == null
            ? null
            : lang.getInitializerHandler().handle(ctx.getInitializer());
    // unfortunately we are not told whether this is a field or not, so we have to find it out
    // ourselves
    ValueDeclaration result;
    FunctionDeclaration currFunction = lang.getScopeManager().getCurrentFunction();
    if (currFunction != null) {
      // variable
      result =
          NodeBuilder.newVariableDeclaration(
              ctx.getNestedDeclarator().getName().toString(),
              Type.getUnknown(),
              ctx.getRawSignature());
      ((VariableDeclaration) result).setInitializer(initializer);
      result.setLocation(lang.getLocationFromRawNode(ctx));
      result.getType().setFunctionPtr(true);
      result.refreshType();
    } else {
      RecordScope recordScope =
          (RecordScope) lang.getScopeManager().getFirstScopeThat(RecordScope.class::isInstance);
      // if (recordScope != null) {
      // field
      String code = ctx.getRawSignature();
      Pattern namePattern = Pattern.compile("\\((\\*|.+\\*)(?[^)]*)");
      Matcher matcher = namePattern.matcher(code);
      String name = "";
      if (matcher.find()) {
        name = matcher.group("name").strip();
      }
      result =
          NodeBuilder.newFieldDeclaration(
              name,
              Type.getUnknown(),
              Collections.emptyList(),
              code,
              lang.getLocationFromRawNode(ctx),
              initializer);
      result.setLocation(lang.getLocationFromRawNode(ctx));
      result.getType().setFunctionPtr(true);
      result.refreshType();
      /*} else {
        // not in a record and not in a field, strange. This should not happen
        log.error(
            "Function pointer declaration that is neither in a function nor in a record. "
                + "This should not happen!");
        return null;
      }*/
    }
    return result;
  }

  private RecordDeclaration handleCompositeTypeSpecifier(CPPASTCompositeTypeSpecifier ctx) {
    String kind;
    switch (ctx.getKey()) {
      default:
      case IASTCompositeTypeSpecifier.k_struct:
        kind = "struct";
        break;
      case IASTCompositeTypeSpecifier.k_union:
        kind = "union";
        break;
      case ICPPASTCompositeTypeSpecifier.k_class:
        kind = "class";
        break;
    }
    RecordDeclaration recordDeclaration =
        NodeBuilder.newRecordDeclaration(
            ctx.getName().toString(), new ArrayList<>(), kind, ctx.getRawSignature());

    this.lang.addRecord(recordDeclaration);

    lang.getScopeManager().enterScope(recordDeclaration);

    if (kind.equals("class")) {
      de.fraunhofer.aisec.cpg.graph.FieldDeclaration thisDeclaration =
          NodeBuilder.newFieldDeclaration(
              "this",
              new de.fraunhofer.aisec.cpg.graph.Type(ctx.getName().toString()),
              new ArrayList<>(),
              "this",
              null,
              null);
      recordDeclaration.getFields().add(thisDeclaration);
      lang.getScopeManager().addValueDeclaration(thisDeclaration);
    }

    for (IASTDeclaration member : ctx.getMembers()) {
      if (member instanceof CPPASTVisibilityLabel) {
        // TODO: parse visibility
        continue;
      }
      Declaration declaration = lang.getDeclarationHandler().handle(member);
      Scope declarationScope = lang.getScopeManager().getScopeOfStatment(declaration);

      if (declaration instanceof FunctionDeclaration) {
        MethodDeclaration method =
            MethodDeclaration.from((FunctionDeclaration) declaration, recordDeclaration);
        declaration.disconnectFromGraph();

        // check, if its a constructor
        if (declaration.getName().equals(recordDeclaration.getName())) {
          ConstructorDeclaration constructor = ConstructorDeclaration.from(method);
          if (declarationScope != null) {
            declarationScope.setAstNode(
                constructor); // Adjust cpg Node by which scopes are identified
          }
          recordDeclaration.getConstructors().add(constructor);
        } else {
          recordDeclaration.getMethods().add(method);
        }

        if (declarationScope != null) {
          declarationScope.setAstNode(method); // Adjust cpg Node by which scopes are identified
        }
      } else if (declaration instanceof VariableDeclaration) {
        recordDeclaration.getFields().add(FieldDeclaration.from((VariableDeclaration) declaration));
      } else if (declaration instanceof FieldDeclaration) {
        recordDeclaration.getFields().add((FieldDeclaration) declaration);
      }
    }

    if (recordDeclaration.getConstructors().isEmpty()) {
      de.fraunhofer.aisec.cpg.graph.ConstructorDeclaration constructorDeclaration =
          NodeBuilder.newConstructorDeclaration(
              recordDeclaration.getName(), recordDeclaration.getName(), recordDeclaration);
      recordDeclaration.getConstructors().add(constructorDeclaration);
      lang.getScopeManager().addValueDeclaration(constructorDeclaration);
    }

    lang.getScopeManager().leaveScope(recordDeclaration);
    return recordDeclaration;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy