<commit_before>//===--- ArgumentList.cpp - Function and subscript argument lists -*- C++ -*==//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the logic for the Argument and ArgumentList classes.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/ArgumentList.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ParameterList.h"

using namespace swift;

Type swift::__Expr_getType(Expr *E) { return E->getType(); }

SourceRange Argument::getSourceRange() const {
  auto labelLoc = getLabelLoc();
  if (labelLoc.isInvalid())
    return getExpr()->getSourceRange();

  auto exprEndLoc = getExpr()->getEndLoc();
  if (exprEndLoc.isInvalid())
    return labelLoc;

  return SourceRange(labelLoc, exprEndLoc);
}

bool Argument::isInOut() const {
  return ArgExpr->isSemanticallyInOutExpr();
}

ArgumentList *ArgumentList::create(ASTContext &ctx, SourceLoc lParenLoc,
                                   ArrayRef<Argument> args, SourceLoc rParenLoc,
                                   Optional<unsigned> firstTrailingClosureIndex,
                                   bool isImplicit, ArgumentList *originalArgs,
                                   AllocationArena arena) {
  SmallVector<Expr *, 4> exprs;
  SmallVector<Identifier, 4> labels;
  SmallVector<SourceLoc, 4> labelLocs;

  bool hasLabels = false;
  bool hasLabelLocs = false;
  for (auto &arg : args) {
    exprs.push_back(arg.getExpr());

    hasLabels |= !arg.getLabel().empty();
    labels.push_back(arg.getLabel());

    hasLabelLocs |= arg.getLabelLoc().isValid();
    labelLocs.push_back(arg.getLabelLoc());
  }
  if (!hasLabels)
    labels.clear();
  if (!hasLabelLocs)
    labelLocs.clear();

  auto numBytes =
      totalSizeToAlloc<Expr *, Identifier, SourceLoc, ArgumentList *>(
          exprs.size(), labels.size(), labelLocs.size(), originalArgs ? 1 : 0);
  auto *mem = ctx.Allocate(numBytes, alignof(ArgumentList), arena);
  auto *argList = new (mem)
      ArgumentList(lParenLoc, rParenLoc, args.size(), firstTrailingClosureIndex,
                   originalArgs, isImplicit, hasLabels, hasLabelLocs);

  std::uninitialized_copy(exprs.begin(), exprs.end(),
                          argList->getExprsBuffer().begin());
  if (hasLabels) {
    std::uninitialized_copy(labels.begin(), labels.end(),
                            argList->getLabelsBuffer().begin());
  }
  if (hasLabelLocs) {
    std::uninitialized_copy(labelLocs.begin(), labelLocs.end(),
                            argList->getLabelLocsBuffer().begin());
  }
  if (originalArgs) {
    *argList->getTrailingObjects<ArgumentList *>() = originalArgs;
  }
  return argList;
}

ArgumentList *
ArgumentList::createParsed(ASTContext &ctx, SourceLoc lParenLoc,
                           ArrayRef<Argument> args, SourceLoc rParenLoc,
                           Optional<unsigned> firstTrailingClosureIndex) {
  return create(ctx, lParenLoc, args, rParenLoc, firstTrailingClosureIndex,
                /*implicit*/ false);
}

ArgumentList *ArgumentList::createTypeChecked(ASTContext &ctx,
                                              ArgumentList *originalArgs,
                                              ArrayRef<Argument> newArgs) {
  return create(ctx, originalArgs->getLParenLoc(), newArgs,
                originalArgs->getRParenLoc(), /*trailingClosureIdx*/ None,
                originalArgs->isImplicit(), originalArgs);
}

ArgumentList *ArgumentList::createImplicit(ASTContext &ctx, SourceLoc lParenLoc,
                                           ArrayRef<Argument> args,
                                           SourceLoc rParenLoc,
                                           AllocationArena arena) {
  return create(ctx, lParenLoc, args, rParenLoc,
                /*firstTrailingClosureIdx*/ None, /*implicit*/ true,
                /*originalArgs*/ nullptr, arena);
}

ArgumentList *ArgumentList::createImplicit(ASTContext &ctx,
                                           ArrayRef<Argument> args,
                                           AllocationArena arena) {
  return createImplicit(ctx, SourceLoc(), args, SourceLoc());
}

ArgumentList *ArgumentList::forImplicitSingle(ASTContext &ctx, Identifier label,
                                              Expr *arg) {
  return createImplicit(ctx, {Argument(SourceLoc(), label, arg)});
}

ArgumentList *ArgumentList::forImplicitUnlabeled(ASTContext &ctx,
                                                 ArrayRef<Expr *> argExprs) {
  SmallVector<Argument, 4> args;
  for (auto *argExpr : argExprs)
    args.push_back(Argument::unlabeled(argExpr));
  return createImplicit(ctx, args);
}

ArgumentList *ArgumentList::forImplicitCallTo(DeclNameRef fnNameRef,
                                              ArrayRef<Expr *> argExprs,
                                              ASTContext &ctx) {
  auto labels = fnNameRef.getArgumentNames();
  assert(labels.size() == argExprs.size());

  SmallVector<Argument, 8> args;
  for (auto idx : indices(argExprs))
    args.emplace_back(SourceLoc(), labels[idx], argExprs[idx]);

  return createImplicit(ctx, args);
}

ArgumentList *ArgumentList::forImplicitCallTo(ParameterList *params,
                                              ArrayRef<Expr *> argExprs,
                                              ASTContext &ctx) {
  assert(params->size() == argExprs.size());
  SmallVector<Argument, 8> args;
  for (auto idx : indices(argExprs)) {
    auto *param = params->get(idx);
    assert(param->isInOut() == argExprs[idx]->isSemanticallyInOutExpr());
    args.emplace_back(SourceLoc(), param->getArgumentName(), argExprs[idx]);
  }
  return createImplicit(ctx, args);
}

SourceLoc ArgumentList::getLoc() const {
  // If we have an unlabeled unary arg, return the start loc of the expr. This
  // preserves the behavior of when such argument lists were represented by
  // ParenExprs.
  if (auto *unary = getUnlabeledUnaryExpr())
    return unary->getStartLoc();
  return getStartLoc();
}

SourceRange ArgumentList::getSourceRange() const {
  auto start = LParenLoc;
  if (start.isInvalid()) {
    // Scan forward for the first valid source loc.
    for (auto arg : *this) {
      start = arg.getStartLoc();
      if (start.isValid())
        break;
    }
  }
  auto end = RParenLoc;
  if (hasAnyTrailingClosures() || RParenLoc.isInvalid()) {
    // Scan backward for the first valid source loc. We use getOriginalArgs to
    // filter out default arguments and get accurate trailing closure info.
    for (auto arg : llvm::reverse(*getOriginalArgs())) {
      end = arg.getEndLoc();
      if (end.isValid())
        break;
    }
  }
  if (start.isInvalid() || end.isInvalid())
    return SourceRange();

  return SourceRange(start, end);
}

ArrayRef<Identifier>
ArgumentList::getArgumentLabels(SmallVectorImpl<Identifier> &scratch) const {
  assert(scratch.empty());
  if (HasLabels)
    return getLabelsBuffer();
  scratch.append(size(), Identifier());
  return scratch;
}

Optional<unsigned> ArgumentList::findArgumentExpr(Expr *expr,
                                                  bool allowSemantic) const {
  if (allowSemantic)
    expr = expr->getSemanticsProvidingExpr();
  for (auto idx : indices(*this)) {
    auto *argExpr = getExpr(idx);
    if (allowSemantic)
      argExpr = argExpr->getSemanticsProvidingExpr();

    if (expr == argExpr)
      return idx;
  }
  return None;
}

Expr *ArgumentList::packIntoImplicitTupleOrParen(
    ASTContext &ctx, llvm::function_ref<Type(Expr *)> getType) const {
  assert(!hasAnyInOutArgs() && "Cannot construct bare tuple/paren with inout");
  if (auto *unary = getUnlabeledUnaryExpr()) {
    auto *paren = new (ctx) ParenExpr(SourceLoc(), unary, SourceLoc());
    if (auto ty = getType(unary))
      paren->setType(ParenType::get(ctx, ty));
    paren->setImplicit();
    return paren;
  }

  SmallVector<Expr *, 8> argExprs;
  SmallVector<Identifier, 8> argLabels;
  SmallVector<TupleTypeElt, 8> tupleEltTypes;

  for (auto arg : *this) {
    auto *argExpr = arg.getExpr();
    argExprs.push_back(argExpr);
    argLabels.push_back(arg.getLabel());
    if (auto ty = getType(argExpr))
      tupleEltTypes.emplace_back(ty, arg.getLabel());
  }
  assert(tupleEltTypes.empty() || tupleEltTypes.size() == argExprs.size());

  auto *tuple = TupleExpr::createImplicit(ctx, argExprs, argLabels);
  if (empty() || !tupleEltTypes.empty())
    tuple->setType(TupleType::get(tupleEltTypes, ctx));

  return tuple;
}

Type ArgumentList::composeTupleOrParenType(
    ASTContext &ctx, llvm::function_ref<Type(Expr *)> getType) const {
  if (auto *unary = getUnlabeledUnaryExpr()) {
    auto ty = getType(unary);
    assert(ty);
    ParameterTypeFlags flags;
    if (get(0).isInOut()) {
      ty = ty->getInOutObjectType();
      flags = flags.withInOut(true);
    }
    return ParenType::get(ctx, ty, flags);
  }
  SmallVector<TupleTypeElt, 4> elts;
  for (auto arg : *this) {
    auto ty = getType(arg.getExpr());
    assert(ty);
    ParameterTypeFlags flags;
    if (arg.isInOut()) {
      ty = ty->getInOutObjectType();
      flags = flags.withInOut(true);
    }
    elts.emplace_back(ty, arg.getLabel(), flags);
  }
  return TupleType::get(elts, ctx);
}

bool ArgumentList::matches(ArrayRef<AnyFunctionType::Param> params,
                           llvm::function_ref<Type(Expr *)> getType) const {
  if (size() != params.size())
    return false;

  for (auto i : indices(*this)) {
    auto arg = get(i);
    auto &param = params[i];

    if (arg.getLabel() != param.getLabel())
      return false;

    if (arg.isInOut() != param.isInOut())
      return false;

    auto argTy = getType(arg.getExpr());
    assert(argTy && "Expected type for argument");
    auto paramTy = param.getParameterType();
    assert(paramTy && "Expected a type for param");

    if (arg.isInOut())
      argTy = argTy->getInOutObjectType();

    if (!argTy->isEqual(paramTy))
      return false;
  }
  return true;
}
<commit_msg>[AST] Fix ArgumentList constructor to pass arena<commit_after>//===--- ArgumentList.cpp - Function and subscript argument lists -*- C++ -*==//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the logic for the Argument and ArgumentList classes.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/ArgumentList.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Expr.h"
#include "swift/AST/ParameterList.h"

using namespace swift;

Type swift::__Expr_getType(Expr *E) { return E->getType(); }

SourceRange Argument::getSourceRange() const {
  auto labelLoc = getLabelLoc();
  if (labelLoc.isInvalid())
    return getExpr()->getSourceRange();

  auto exprEndLoc = getExpr()->getEndLoc();
  if (exprEndLoc.isInvalid())
    return labelLoc;

  return SourceRange(labelLoc, exprEndLoc);
}

bool Argument::isInOut() const {
  return ArgExpr->isSemanticallyInOutExpr();
}

ArgumentList *ArgumentList::create(ASTContext &ctx, SourceLoc lParenLoc,
                                   ArrayRef<Argument> args, SourceLoc rParenLoc,
                                   Optional<unsigned> firstTrailingClosureIndex,
                                   bool isImplicit, ArgumentList *originalArgs,
                                   AllocationArena arena) {
  SmallVector<Expr *, 4> exprs;
  SmallVector<Identifier, 4> labels;
  SmallVector<SourceLoc, 4> labelLocs;

  bool hasLabels = false;
  bool hasLabelLocs = false;
  for (auto &arg : args) {
    exprs.push_back(arg.getExpr());

    hasLabels |= !arg.getLabel().empty();
    labels.push_back(arg.getLabel());

    hasLabelLocs |= arg.getLabelLoc().isValid();
    labelLocs.push_back(arg.getLabelLoc());
  }
  if (!hasLabels)
    labels.clear();
  if (!hasLabelLocs)
    labelLocs.clear();

  auto numBytes =
      totalSizeToAlloc<Expr *, Identifier, SourceLoc, ArgumentList *>(
          exprs.size(), labels.size(), labelLocs.size(), originalArgs ? 1 : 0);
  auto *mem = ctx.Allocate(numBytes, alignof(ArgumentList), arena);
  auto *argList = new (mem)
      ArgumentList(lParenLoc, rParenLoc, args.size(), firstTrailingClosureIndex,
                   originalArgs, isImplicit, hasLabels, hasLabelLocs);

  std::uninitialized_copy(exprs.begin(), exprs.end(),
                          argList->getExprsBuffer().begin());
  if (hasLabels) {
    std::uninitialized_copy(labels.begin(), labels.end(),
                            argList->getLabelsBuffer().begin());
  }
  if (hasLabelLocs) {
    std::uninitialized_copy(labelLocs.begin(), labelLocs.end(),
                            argList->getLabelLocsBuffer().begin());
  }
  if (originalArgs) {
    *argList->getTrailingObjects<ArgumentList *>() = originalArgs;
  }
  return argList;
}

ArgumentList *
ArgumentList::createParsed(ASTContext &ctx, SourceLoc lParenLoc,
                           ArrayRef<Argument> args, SourceLoc rParenLoc,
                           Optional<unsigned> firstTrailingClosureIndex) {
  return create(ctx, lParenLoc, args, rParenLoc, firstTrailingClosureIndex,
                /*implicit*/ false);
}

ArgumentList *ArgumentList::createTypeChecked(ASTContext &ctx,
                                              ArgumentList *originalArgs,
                                              ArrayRef<Argument> newArgs) {
  return create(ctx, originalArgs->getLParenLoc(), newArgs,
                originalArgs->getRParenLoc(), /*trailingClosureIdx*/ None,
                originalArgs->isImplicit(), originalArgs);
}

ArgumentList *ArgumentList::createImplicit(ASTContext &ctx, SourceLoc lParenLoc,
                                           ArrayRef<Argument> args,
                                           SourceLoc rParenLoc,
                                           AllocationArena arena) {
  return create(ctx, lParenLoc, args, rParenLoc,
                /*firstTrailingClosureIdx*/ None, /*implicit*/ true,
                /*originalArgs*/ nullptr, arena);
}

ArgumentList *ArgumentList::createImplicit(ASTContext &ctx,
                                           ArrayRef<Argument> args,
                                           AllocationArena arena) {
  return createImplicit(ctx, SourceLoc(), args, SourceLoc(), arena);
}

ArgumentList *ArgumentList::forImplicitSingle(ASTContext &ctx, Identifier label,
                                              Expr *arg) {
  return createImplicit(ctx, {Argument(SourceLoc(), label, arg)});
}

ArgumentList *ArgumentList::forImplicitUnlabeled(ASTContext &ctx,
                                                 ArrayRef<Expr *> argExprs) {
  SmallVector<Argument, 4> args;
  for (auto *argExpr : argExprs)
    args.push_back(Argument::unlabeled(argExpr));
  return createImplicit(ctx, args);
}

ArgumentList *ArgumentList::forImplicitCallTo(DeclNameRef fnNameRef,
                                              ArrayRef<Expr *> argExprs,
                                              ASTContext &ctx) {
  auto labels = fnNameRef.getArgumentNames();
  assert(labels.size() == argExprs.size());

  SmallVector<Argument, 8> args;
  for (auto idx : indices(argExprs))
    args.emplace_back(SourceLoc(), labels[idx], argExprs[idx]);

  return createImplicit(ctx, args);
}

ArgumentList *ArgumentList::forImplicitCallTo(ParameterList *params,
                                              ArrayRef<Expr *> argExprs,
                                              ASTContext &ctx) {
  assert(params->size() == argExprs.size());
  SmallVector<Argument, 8> args;
  for (auto idx : indices(argExprs)) {
    auto *param = params->get(idx);
    assert(param->isInOut() == argExprs[idx]->isSemanticallyInOutExpr());
    args.emplace_back(SourceLoc(), param->getArgumentName(), argExprs[idx]);
  }
  return createImplicit(ctx, args);
}

SourceLoc ArgumentList::getLoc() const {
  // If we have an unlabeled unary arg, return the start loc of the expr. This
  // preserves the behavior of when such argument lists were represented by
  // ParenExprs.
  if (auto *unary = getUnlabeledUnaryExpr())
    return unary->getStartLoc();
  return getStartLoc();
}

SourceRange ArgumentList::getSourceRange() const {
  auto start = LParenLoc;
  if (start.isInvalid()) {
    // Scan forward for the first valid source loc.
    for (auto arg : *this) {
      start = arg.getStartLoc();
      if (start.isValid())
        break;
    }
  }
  auto end = RParenLoc;
  if (hasAnyTrailingClosures() || RParenLoc.isInvalid()) {
    // Scan backward for the first valid source loc. We use getOriginalArgs to
    // filter out default arguments and get accurate trailing closure info.
    for (auto arg : llvm::reverse(*getOriginalArgs())) {
      end = arg.getEndLoc();
      if (end.isValid())
        break;
    }
  }
  if (start.isInvalid() || end.isInvalid())
    return SourceRange();

  return SourceRange(start, end);
}

ArrayRef<Identifier>
ArgumentList::getArgumentLabels(SmallVectorImpl<Identifier> &scratch) const {
  assert(scratch.empty());
  if (HasLabels)
    return getLabelsBuffer();
  scratch.append(size(), Identifier());
  return scratch;
}

Optional<unsigned> ArgumentList::findArgumentExpr(Expr *expr,
                                                  bool allowSemantic) const {
  if (allowSemantic)
    expr = expr->getSemanticsProvidingExpr();
  for (auto idx : indices(*this)) {
    auto *argExpr = getExpr(idx);
    if (allowSemantic)
      argExpr = argExpr->getSemanticsProvidingExpr();

    if (expr == argExpr)
      return idx;
  }
  return None;
}

Expr *ArgumentList::packIntoImplicitTupleOrParen(
    ASTContext &ctx, llvm::function_ref<Type(Expr *)> getType) const {
  assert(!hasAnyInOutArgs() && "Cannot construct bare tuple/paren with inout");
  if (auto *unary = getUnlabeledUnaryExpr()) {
    auto *paren = new (ctx) ParenExpr(SourceLoc(), unary, SourceLoc());
    if (auto ty = getType(unary))
      paren->setType(ParenType::get(ctx, ty));
    paren->setImplicit();
    return paren;
  }

  SmallVector<Expr *, 8> argExprs;
  SmallVector<Identifier, 8> argLabels;
  SmallVector<TupleTypeElt, 8> tupleEltTypes;

  for (auto arg : *this) {
    auto *argExpr = arg.getExpr();
    argExprs.push_back(argExpr);
    argLabels.push_back(arg.getLabel());
    if (auto ty = getType(argExpr))
      tupleEltTypes.emplace_back(ty, arg.getLabel());
  }
  assert(tupleEltTypes.empty() || tupleEltTypes.size() == argExprs.size());

  auto *tuple = TupleExpr::createImplicit(ctx, argExprs, argLabels);
  if (empty() || !tupleEltTypes.empty())
    tuple->setType(TupleType::get(tupleEltTypes, ctx));

  return tuple;
}

Type ArgumentList::composeTupleOrParenType(
    ASTContext &ctx, llvm::function_ref<Type(Expr *)> getType) const {
  if (auto *unary = getUnlabeledUnaryExpr()) {
    auto ty = getType(unary);
    assert(ty);
    ParameterTypeFlags flags;
    if (get(0).isInOut()) {
      ty = ty->getInOutObjectType();
      flags = flags.withInOut(true);
    }
    return ParenType::get(ctx, ty, flags);
  }
  SmallVector<TupleTypeElt, 4> elts;
  for (auto arg : *this) {
    auto ty = getType(arg.getExpr());
    assert(ty);
    ParameterTypeFlags flags;
    if (arg.isInOut()) {
      ty = ty->getInOutObjectType();
      flags = flags.withInOut(true);
    }
    elts.emplace_back(ty, arg.getLabel(), flags);
  }
  return TupleType::get(elts, ctx);
}

bool ArgumentList::matches(ArrayRef<AnyFunctionType::Param> params,
                           llvm::function_ref<Type(Expr *)> getType) const {
  if (size() != params.size())
    return false;

  for (auto i : indices(*this)) {
    auto arg = get(i);
    auto &param = params[i];

    if (arg.getLabel() != param.getLabel())
      return false;

    if (arg.isInOut() != param.isInOut())
      return false;

    auto argTy = getType(arg.getExpr());
    assert(argTy && "Expected type for argument");
    auto paramTy = param.getParameterType();
    assert(paramTy && "Expected a type for param");

    if (arg.isInOut())
      argTy = argTy->getInOutObjectType();

    if (!argTy->isEqual(paramTy))
      return false;
  }
  return true;
}
<|endoftext|>


<commit_before>#include "CppUTest/TestHarness.h"
#include "CppUTest/TestMemoryAllocator.h"
#include "CppUTest/MemoryLeakDetector.h"
#include "CppUTest/TestOutput.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTest/PlatformSpecificFunctions.h"
#include "CppUTest/TestTestingFixture.h"
#include "AllocationInCppFile.h"

#include "CppUTest/TestHarness_c.h"
#include "AllocationInCFile.h"

TEST_GROUP(BasicBehavior)
{
};

TEST(BasicBehavior, CanDeleteNullPointers)
{
    delete (char*) NULL;
    delete [] (char*) NULL;
}

#ifndef CPPUTEST_MEM_LEAK_DETECTION_DISABLED

TEST(BasicBehavior, deleteArrayInvalidatesMemory)
{
    unsigned char* memory = new unsigned char[10];
    PlatformSpecificMemset(memory, 0xAB, 10);
    delete [] memory;
    CHECK(memory[5] != 0xCB);
}

TEST(BasicBehavior, deleteInvalidatesMemory)
{
    unsigned char* memory = new unsigned char;
    *memory = 0xAD;
    delete memory;
    CHECK(*memory != 0xAD);
}

static void deleteUnallocatedMemory()
{
    delete (char*) 0x1234678;
    FAIL("Should never come here"); // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

TEST(BasicBehavior, deleteWillNotThrowAnExceptionWhenDeletingUnallocatedMemoryButCanStillCauseTestFailures)
{
    /*
     * Test failure might cause an exception. But according to C++ standard, you aren't allowed
     * to throw exceptions in the delete function. If you do that, it will call std::terminate.
     * Therefore, the delete will need to fail without exceptions.
     */
    MemoryLeakFailure* defaultReporter = MemoryLeakWarningPlugin::getGlobalFailureReporter();
    TestTestingFixture fixture;
    fixture.setTestFunction(deleteUnallocatedMemory);
    fixture.runAllTests();
    LONGS_EQUAL(1, fixture.getFailureCount());
    POINTERS_EQUAL(defaultReporter, MemoryLeakWarningPlugin::getGlobalFailureReporter());
}

#endif

#ifdef CPPUTEST_USE_MALLOC_MACROS

/* This include is added because *sometimes* the cstdlib does an #undef. This should have been prevented */
#if CPPUTEST_USE_STD_CPP_LIB
#include <cstdlib>
#endif

TEST(BasicBehavior, bothMallocAndFreeAreOverloaded)
{
    void* memory = cpputest_malloc_location(sizeof(char), "file", 10);
    free(memory);

    memory = malloc(sizeof(unsigned char));
    cpputest_free_location(memory, "file", 10);
}

#endif

#if CPPUTEST_USE_MEM_LEAK_DETECTION

TEST(BasicBehavior, freeInvalidatesMemory)
{
    unsigned char* memory = (unsigned char*) cpputest_malloc(sizeof(unsigned char));
    *memory = 0xAD;
    cpputest_free(memory);
    CHECK(*memory != 0xAD);
}
#endif

TEST_GROUP(MemoryLeakOverridesToBeUsedInProductionCode)
{
    MemoryLeakDetector* memLeakDetector;
    void setup()
    {
        memLeakDetector = MemoryLeakWarningPlugin::getGlobalDetector();
    }

};

#ifdef CPPUTEST_USE_MALLOC_MACROS

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideIsUsed)
{
    int memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
    void* memory = malloc(10);
    LONGS_EQUAL(memLeaks+1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    free (memory);
}

#endif

TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeMallocByTemporarlySwitchingOffMalloc)
{
    int memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
#ifdef CPPUTEST_USE_MALLOC_MACROS
    #undef malloc
    #undef free
#endif
    void* memory = malloc(10);
    LONGS_EQUAL(memLeaks, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    free (memory);

#ifdef CPPUTEST_USE_MALLOC_MACROS
#include "CppUTest/MemoryLeakDetectorMallocMacros.h"
#endif
}

/* TEST... allowing for a new overload in a class */
class NewDummyClass
{
public:
    static bool overloaded_new_called;

#ifdef CPPUTEST_USE_NEW_MACROS
    #undef new
#endif
    void* operator new (size_t size)
#ifdef CPPUTEST_USE_NEW_MACROS
    #include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
    {
        overloaded_new_called = true;
        return malloc(size);
    }
    void dummyFunction()
    {
        char* memory = new char;
        delete memory;
    }
};
bool NewDummyClass::overloaded_new_called = false;

TEST(MemoryLeakOverridesToBeUsedInProductionCode, NoSideEffectsFromTurningOffNewMacros)
{
    /*
     * Interesting effect of wrapping the operator new around the macro is
     * that the actual new that is called is a different one than expected.
     *
     * The overloaded operator new doesn't actually ever get called.
     *
     * This might come as a surprise, so it is important to realize!
     */
    NewDummyClass dummy;
    dummy.dummyFunction();
    // CHECK(dummy.overloaded_new_called);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeNewByTemporarlySwitchingOffNew)
{
#ifdef CPPUTEST_USE_NEW_MACROS
    #undef new
    #undef delete
#endif
    char* memory = new char[10];
    delete [] memory;
#ifdef CPPUTEST_USE_NEW_MACROS
    #include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
}

#if CPPUTEST_USE_MEM_LEAK_DETECTION

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewMacroOverloadViaIncludeFileWorks)
{
    char* leak = newAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayMacroOverloadViaIncludeFileWorks)
{
    char* leak = newArrayAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
    delete[] leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideWorks)
{
    char* leak = mallocAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCFile.c", memLeakDetector->report(mem_leak_period_checking));
    freeAllocation(leak);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocWithButFreeWithoutLeakDetectionDoesntCrash)
{
    char* leak = mallocAllocation();
    freeAllocationWithoutMacro(leak);
    LONGS_EQUAL(2, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    memLeakDetector->removeMemoryLeakInformationWithoutCheckingOrDeallocatingTheMemoryButDeallocatingTheAccountInformation(getCurrentMallocAllocator(), leak, true);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewOverloadingWithoutMacroWorks)
{
    char* leak = newAllocationWithoutMacro();
    STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayOverloadingWithoutMacroWorks)
{
    char* leak = newArrayAllocationWithoutMacro();
    STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
    delete[] leak;
}

#else

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MemoryOverridesAreDisabled)
{
    char* leak = newAllocation();
    STRCMP_EQUAL("No memory leaks were detected.", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

#endif

TEST_GROUP(OutOfMemoryTestsForOperatorNew)
{
    TestMemoryAllocator* no_memory_allocator;
    void setup()
    {
        no_memory_allocator = new NullUnknownAllocator;
        setCurrentNewAllocator(no_memory_allocator);
        setCurrentNewArrayAllocator(no_memory_allocator);
    }

    void teardown()
    {
        setCurrentNewAllocatorToDefault();
        setCurrentNewArrayAllocatorToDefault();
        delete no_memory_allocator;
    }
};

#if CPPUTEST_USE_MEM_LEAK_DETECTION

#if CPPUTEST_USE_STD_CPP_LIB

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNew)
{
    CHECK_THROWS(std::bad_alloc, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNew)
{
    CHECK_THROWS(std::bad_alloc, new char[10]);
}

TEST_GROUP(TestForExceptionsInConstructor)
{
};

TEST(TestForExceptionsInConstructor,ConstructorThrowsAnException)
{
    CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor);
}

TEST(TestForExceptionsInConstructor,ConstructorThrowsAnExceptionAllocatedAsArray)
{
    CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor[10]);
}

#else

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNull)
{
    POINTERS_EQUAL(NULL, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNull)
{
    POINTERS_EQUAL(NULL, new char[10]);
}

#endif

#undef new

#if CPPUTEST_USE_STD_CPP_LIB

/*
 * CLang 4.2 and memory allocation.
 *
 * Clang 4.2 has done some optimizations to their memory management that actually causes slightly different behavior than what the C++ Standard defines.
 * Usually this is not a problem... but in this case, it is a problem.
 *
 * More information about the optimization can be found at: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3433.html
 * We've done a bug-report to clang to fix some of this non-standard behavior, which is open at: http://llvm.org/bugs/show_bug.cgi?id=15541
 *
 * I very much hope that nobody would actually ever hit this bug/optimization as it is hard to figure out what is going on.
 *
 * The original test simply did "new char". Because the memory wasn't assigned to anything and is local in context, the optimization *doesn't* call
 * the operator new overload. Because it doesn't call the operator new (optimizing away a call to operator new), therefore the method wouldn't throw an exception
 * and therefore this test failed.
 *
 * The first attempt to fix this is to create a local variable and assigned the memory to that. Also this doesn't work as it still detects the allocation is
 * local and optimizes away the memory call.
 *
 * Now, we assign the memory on some static global which fools the optimizer to believe that it isn't local and it stops optimizing the operator new call.
 *
 * We (Bas Vodde and Terry Yin) suspect that in a real product, you wouldn't be able to detect the optimization and it's breaking of Standard C++. Therefore,
 * for now, we keep this hack in the test to fool the optimizer and hope nobody will ever notice this 'optimizer behavior' in a real product.
 *
 */

static char* some_memory;

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
{
    CHECK_THROWS(std::bad_alloc, some_memory = new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
{
    CHECK_THROWS(std::bad_alloc, some_memory = new char[10]);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new (std::nothrow) char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new (std::nothrow) char[10]);
}

#else

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new char[10]);
}

#endif

#endif
<commit_msg>Added tests for the operator delete with sizeof as additional parameter<commit_after>#include "CppUTest/TestHarness.h"
#include "CppUTest/TestMemoryAllocator.h"
#include "CppUTest/MemoryLeakDetector.h"
#include "CppUTest/TestOutput.h"
#include "CppUTest/TestRegistry.h"
#include "CppUTest/PlatformSpecificFunctions.h"
#include "CppUTest/TestTestingFixture.h"
#include "AllocationInCppFile.h"

#include "CppUTest/TestHarness_c.h"
#include "AllocationInCFile.h"

TEST_GROUP(BasicBehavior)
{
};

TEST(BasicBehavior, CanDeleteNullPointers)
{
    delete (char*) NULL;
    delete [] (char*) NULL;
}

#ifndef CPPUTEST_MEM_LEAK_DETECTION_DISABLED

TEST(BasicBehavior, deleteArrayInvalidatesMemory)
{
    unsigned char* memory = new unsigned char[10];
    PlatformSpecificMemset(memory, 0xAB, 10);
    delete [] memory;
    CHECK(memory[5] != 0xCB);
}

TEST(BasicBehavior, deleteInvalidatesMemory)
{
    unsigned char* memory = new unsigned char;
    *memory = 0xAD;
    delete memory;
    CHECK(*memory != 0xAD);
}

TEST(BasicBehavior, DeleteWithSizeParameterWorks)
{
    char* charMemory = new char;
    char* charArrayMemory = new char[10];
    ::operator delete(charMemory, sizeof(char));
    ::operator delete[](charArrayMemory, sizeof(char)* 10);
}

static void deleteUnallocatedMemory()
{
    delete (char*) 0x1234678;
    FAIL("Should never come here"); // LCOV_EXCL_LINE
} // LCOV_EXCL_LINE

TEST(BasicBehavior, deleteWillNotThrowAnExceptionWhenDeletingUnallocatedMemoryButCanStillCauseTestFailures)
{
    /*
     * Test failure might cause an exception. But according to C++ standard, you aren't allowed
     * to throw exceptions in the delete function. If you do that, it will call std::terminate.
     * Therefore, the delete will need to fail without exceptions.
     */
    MemoryLeakFailure* defaultReporter = MemoryLeakWarningPlugin::getGlobalFailureReporter();
    TestTestingFixture fixture;
    fixture.setTestFunction(deleteUnallocatedMemory);
    fixture.runAllTests();
    LONGS_EQUAL(1, fixture.getFailureCount());
    POINTERS_EQUAL(defaultReporter, MemoryLeakWarningPlugin::getGlobalFailureReporter());
}

#endif

#ifdef CPPUTEST_USE_MALLOC_MACROS

/* This include is added because *sometimes* the cstdlib does an #undef. This should have been prevented */
#if CPPUTEST_USE_STD_CPP_LIB
#include <cstdlib>
#endif

TEST(BasicBehavior, bothMallocAndFreeAreOverloaded)
{
    void* memory = cpputest_malloc_location(sizeof(char), "file", 10);
    free(memory);

    memory = malloc(sizeof(unsigned char));
    cpputest_free_location(memory, "file", 10);
}

#endif

#if CPPUTEST_USE_MEM_LEAK_DETECTION

TEST(BasicBehavior, freeInvalidatesMemory)
{
    unsigned char* memory = (unsigned char*) cpputest_malloc(sizeof(unsigned char));
    *memory = 0xAD;
    cpputest_free(memory);
    CHECK(*memory != 0xAD);
}
#endif

TEST_GROUP(MemoryLeakOverridesToBeUsedInProductionCode)
{
    MemoryLeakDetector* memLeakDetector;
    void setup()
    {
        memLeakDetector = MemoryLeakWarningPlugin::getGlobalDetector();
    }

};

#ifdef CPPUTEST_USE_MALLOC_MACROS

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideIsUsed)
{
    int memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
    void* memory = malloc(10);
    LONGS_EQUAL(memLeaks+1, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    free (memory);
}

#endif

TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeMallocByTemporarlySwitchingOffMalloc)
{
    int memLeaks = memLeakDetector->totalMemoryLeaks(mem_leak_period_checking);
#ifdef CPPUTEST_USE_MALLOC_MACROS
    #undef malloc
    #undef free
#endif
    void* memory = malloc(10);
    LONGS_EQUAL(memLeaks, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    free (memory);

#ifdef CPPUTEST_USE_MALLOC_MACROS
#include "CppUTest/MemoryLeakDetectorMallocMacros.h"
#endif
}

/* TEST... allowing for a new overload in a class */
class NewDummyClass
{
public:
    static bool overloaded_new_called;

#ifdef CPPUTEST_USE_NEW_MACROS
    #undef new
#endif
    void* operator new (size_t size)
#ifdef CPPUTEST_USE_NEW_MACROS
    #include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
    {
        overloaded_new_called = true;
        return malloc(size);
    }
    void dummyFunction()
    {
        char* memory = new char;
        delete memory;
    }
};
bool NewDummyClass::overloaded_new_called = false;

TEST(MemoryLeakOverridesToBeUsedInProductionCode, NoSideEffectsFromTurningOffNewMacros)
{
    /*
     * Interesting effect of wrapping the operator new around the macro is
     * that the actual new that is called is a different one than expected.
     *
     * The overloaded operator new doesn't actually ever get called.
     *
     * This might come as a surprise, so it is important to realize!
     */
    NewDummyClass dummy;
    dummy.dummyFunction();
    // CHECK(dummy.overloaded_new_called);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, UseNativeNewByTemporarlySwitchingOffNew)
{
#ifdef CPPUTEST_USE_NEW_MACROS
    #undef new
    #undef delete
#endif
    char* memory = new char[10];
    delete [] memory;
#ifdef CPPUTEST_USE_NEW_MACROS
    #include "CppUTest/MemoryLeakDetectorNewMacros.h"
#endif
}

#if CPPUTEST_USE_MEM_LEAK_DETECTION

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewMacroOverloadViaIncludeFileWorks)
{
    char* leak = newAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayMacroOverloadViaIncludeFileWorks)
{
    char* leak = newArrayAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCppFile.cpp", memLeakDetector->report(mem_leak_period_checking));
    delete[] leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocOverrideWorks)
{
    char* leak = mallocAllocation();
    STRCMP_NOCASE_CONTAINS("AllocationInCFile.c", memLeakDetector->report(mem_leak_period_checking));
    freeAllocation(leak);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MallocWithButFreeWithoutLeakDetectionDoesntCrash)
{
    char* leak = mallocAllocation();
    freeAllocationWithoutMacro(leak);
    LONGS_EQUAL(2, memLeakDetector->totalMemoryLeaks(mem_leak_period_checking));
    memLeakDetector->removeMemoryLeakInformationWithoutCheckingOrDeallocatingTheMemoryButDeallocatingTheAccountInformation(getCurrentMallocAllocator(), leak, true);
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewOverloadingWithoutMacroWorks)
{
    char* leak = newAllocationWithoutMacro();
    STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

TEST(MemoryLeakOverridesToBeUsedInProductionCode, OperatorNewArrayOverloadingWithoutMacroWorks)
{
    char* leak = newArrayAllocationWithoutMacro();
    STRCMP_CONTAINS("unknown", memLeakDetector->report(mem_leak_period_checking));
    delete[] leak;
}

#else

TEST(MemoryLeakOverridesToBeUsedInProductionCode, MemoryOverridesAreDisabled)
{
    char* leak = newAllocation();
    STRCMP_EQUAL("No memory leaks were detected.", memLeakDetector->report(mem_leak_period_checking));
    delete leak;
}

#endif

TEST_GROUP(OutOfMemoryTestsForOperatorNew)
{
    TestMemoryAllocator* no_memory_allocator;
    void setup()
    {
        no_memory_allocator = new NullUnknownAllocator;
        setCurrentNewAllocator(no_memory_allocator);
        setCurrentNewArrayAllocator(no_memory_allocator);
    }

    void teardown()
    {
        setCurrentNewAllocatorToDefault();
        setCurrentNewArrayAllocatorToDefault();
        delete no_memory_allocator;
    }
};

#if CPPUTEST_USE_MEM_LEAK_DETECTION

#if CPPUTEST_USE_STD_CPP_LIB

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNew)
{
    CHECK_THROWS(std::bad_alloc, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNew)
{
    CHECK_THROWS(std::bad_alloc, new char[10]);
}

TEST_GROUP(TestForExceptionsInConstructor)
{
};

TEST(TestForExceptionsInConstructor,ConstructorThrowsAnException)
{
    CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor);
}

TEST(TestForExceptionsInConstructor,ConstructorThrowsAnExceptionAllocatedAsArray)
{
    CHECK_THROWS(int, new ClassThatThrowsAnExceptionInTheConstructor[10]);
}

#else

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNull)
{
    POINTERS_EQUAL(NULL, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNull)
{
    POINTERS_EQUAL(NULL, new char[10]);
}

#endif

#undef new

#if CPPUTEST_USE_STD_CPP_LIB

/*
 * CLang 4.2 and memory allocation.
 *
 * Clang 4.2 has done some optimizations to their memory management that actually causes slightly different behavior than what the C++ Standard defines.
 * Usually this is not a problem... but in this case, it is a problem.
 *
 * More information about the optimization can be found at: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3433.html
 * We've done a bug-report to clang to fix some of this non-standard behavior, which is open at: http://llvm.org/bugs/show_bug.cgi?id=15541
 *
 * I very much hope that nobody would actually ever hit this bug/optimization as it is hard to figure out what is going on.
 *
 * The original test simply did "new char". Because the memory wasn't assigned to anything and is local in context, the optimization *doesn't* call
 * the operator new overload. Because it doesn't call the operator new (optimizing away a call to operator new), therefore the method wouldn't throw an exception
 * and therefore this test failed.
 *
 * The first attempt to fix this is to create a local variable and assigned the memory to that. Also this doesn't work as it still detects the allocation is
 * local and optimizes away the memory call.
 *
 * Now, we assign the memory on some static global which fools the optimizer to believe that it isn't local and it stops optimizing the operator new call.
 *
 * We (Bas Vodde and Terry Yin) suspect that in a real product, you wouldn't be able to detect the optimization and it's breaking of Standard C++. Therefore,
 * for now, we keep this hack in the test to fool the optimizer and hope nobody will ever notice this 'optimizer behavior' in a real product.
 *
 */

static char* some_memory;

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
{
    CHECK_THROWS(std::bad_alloc, some_memory = new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorThrowsAnExceptionWhenUsingStdCppNewWithoutOverride)
{
    CHECK_THROWS(std::bad_alloc, some_memory = new char[10]);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new (std::nothrow) char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new (std::nothrow) char[10]);
}

#else

TEST(OutOfMemoryTestsForOperatorNew, FailingNewOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new char);
}

TEST(OutOfMemoryTestsForOperatorNew, FailingNewArrayOperatorReturnsNullWithoutOverride)
{
    POINTERS_EQUAL(NULL, new char[10]);
}

#endif

#endif
<|endoftext|>


<commit_before>
#include <QtTest>
#include <QtDebug>

#include <Control.h>
#include <Parser.h>
#include <AST.h>

CPLUSPLUS_USE_NAMESPACE

class tst_AST: public QObject
{
    Q_OBJECT

    Control control;

public:
    TranslationUnit *parse(const QByteArray &source,
                           TranslationUnit::ParseMode mode)
    {
        StringLiteral *fileId = control.findOrInsertFileName("<stdin>");
        TranslationUnit *unit = new TranslationUnit(&control, fileId);
        unit->setSource(source.constData(), source.length());
        unit->parse(mode);
        return unit;
    }

    TranslationUnit *parseStatement(const QByteArray &source)
    { return parse(source, TranslationUnit::ParseStatement); }

private slots:
    void if_statement();
    void if_else_statement();
    void cpp_initializer();
};

void tst_AST::if_statement()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("if (a) b;"));

    AST *ast = unit->ast();
    QVERIFY(ast != 0);

    IfStatementAST *stmt = ast->asIfStatement();
    QVERIFY(stmt != 0);
    QCOMPARE(stmt->if_token, 1U);
    QCOMPARE(stmt->lparen_token, 2U);
    QVERIFY(stmt->condition != 0);
    QCOMPARE(stmt->rparen_token, 4U);
    QVERIFY(stmt->statement != 0);
    QCOMPARE(stmt->else_token, 0U);
    QVERIFY(stmt->else_statement == 0);

    // check the `then' statement
    ExpressionStatementAST *then_stmt = stmt->statement->asExpressionStatement();
    QVERIFY(then_stmt != 0);
    QVERIFY(then_stmt->expression != 0);
    QCOMPARE(then_stmt->semicolon_token, 6U);

    SimpleNameAST *id_expr = then_stmt->expression->asSimpleName();
    QVERIFY(id_expr != 0);
    QCOMPARE(id_expr->identifier_token, 5U);
}

void tst_AST::if_else_statement()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("if (a) b; else c;"));

    AST *ast = unit->ast();
    QVERIFY(ast != 0);

    IfStatementAST *stmt = ast->asIfStatement();
    QVERIFY(stmt != 0);
    QCOMPARE(stmt->if_token, 1U);
    QCOMPARE(stmt->lparen_token, 2U);
    QVERIFY(stmt->condition != 0);
    QCOMPARE(stmt->rparen_token, 4U);
    QVERIFY(stmt->statement != 0);
    QCOMPARE(stmt->else_token, 7U);
    QVERIFY(stmt->else_statement != 0);
}

void tst_AST::cpp_initializer()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("QFileInfo fileInfo(foo);"));
    AST *ast = unit->ast();
    QVERIFY(ast != 0);
}

QTEST_APPLESS_MAIN(tst_AST)
#include "tst_ast.moc"
<commit_msg>Testing C++ initializer of function declarations.<commit_after>
#include <QtTest>
#include <QtDebug>

#include <Control.h>
#include <Parser.h>
#include <AST.h>

CPLUSPLUS_USE_NAMESPACE

class tst_AST: public QObject
{
    Q_OBJECT

    Control control;

public:
    TranslationUnit *parse(const QByteArray &source,
                           TranslationUnit::ParseMode mode)
    {
        StringLiteral *fileId = control.findOrInsertFileName("<stdin>");
        TranslationUnit *unit = new TranslationUnit(&control, fileId);
        unit->setSource(source.constData(), source.length());
        unit->parse(mode);
        return unit;
    }

    TranslationUnit *parseStatement(const QByteArray &source)
    { return parse(source, TranslationUnit::ParseStatement); }

private slots:
    void if_statement();
    void if_else_statement();
    void cpp_initializer_or_function_declaration();
};

void tst_AST::if_statement()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("if (a) b;"));

    AST *ast = unit->ast();
    QVERIFY(ast != 0);

    IfStatementAST *stmt = ast->asIfStatement();
    QVERIFY(stmt != 0);
    QCOMPARE(stmt->if_token, 1U);
    QCOMPARE(stmt->lparen_token, 2U);
    QVERIFY(stmt->condition != 0);
    QCOMPARE(stmt->rparen_token, 4U);
    QVERIFY(stmt->statement != 0);
    QCOMPARE(stmt->else_token, 0U);
    QVERIFY(stmt->else_statement == 0);

    // check the `then' statement
    ExpressionStatementAST *then_stmt = stmt->statement->asExpressionStatement();
    QVERIFY(then_stmt != 0);
    QVERIFY(then_stmt->expression != 0);
    QCOMPARE(then_stmt->semicolon_token, 6U);

    SimpleNameAST *id_expr = then_stmt->expression->asSimpleName();
    QVERIFY(id_expr != 0);
    QCOMPARE(id_expr->identifier_token, 5U);
}

void tst_AST::if_else_statement()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("if (a) b; else c;"));

    AST *ast = unit->ast();
    QVERIFY(ast != 0);

    IfStatementAST *stmt = ast->asIfStatement();
    QVERIFY(stmt != 0);
    QCOMPARE(stmt->if_token, 1U);
    QCOMPARE(stmt->lparen_token, 2U);
    QVERIFY(stmt->condition != 0);
    QCOMPARE(stmt->rparen_token, 4U);
    QVERIFY(stmt->statement != 0);
    QCOMPARE(stmt->else_token, 7U);
    QVERIFY(stmt->else_statement != 0);

    // check the `then' statement
    ExpressionStatementAST *then_stmt = stmt->statement->asExpressionStatement();
    QVERIFY(then_stmt != 0);
    QVERIFY(then_stmt->expression != 0);
    QCOMPARE(then_stmt->semicolon_token, 6U);

    SimpleNameAST *a_id_expr = then_stmt->expression->asSimpleName();
    QVERIFY(a_id_expr != 0);
    QCOMPARE(a_id_expr->identifier_token, 5U);

    // check the `then' statement
    ExpressionStatementAST *else_stmt = stmt->else_statement->asExpressionStatement();
    QVERIFY(else_stmt != 0);
    QVERIFY(else_stmt->expression != 0);
    QCOMPARE(else_stmt->semicolon_token, 9U);

    SimpleNameAST *b_id_expr = else_stmt->expression->asSimpleName();
    QVERIFY(b_id_expr != 0);
    QCOMPARE(b_id_expr->identifier_token, 8U);
}

void tst_AST::cpp_initializer_or_function_declaration()
{
    QSharedPointer<TranslationUnit> unit(parseStatement("QFileInfo fileInfo(foo);"));
    AST *ast = unit->ast();
    QVERIFY(ast != 0);

    DeclarationStatementAST *stmt = ast->asDeclarationStatement();
    QVERIFY(stmt != 0);

    QVERIFY(stmt->declaration != 0);

    SimpleDeclarationAST *simple_decl = stmt->declaration->asSimpleDeclaration();
    QVERIFY(simple_decl != 0);

    QVERIFY(simple_decl->decl_specifier_seq != 0);
    QVERIFY(simple_decl->decl_specifier_seq->next == 0);
    QVERIFY(simple_decl->declarators != 0);
    QVERIFY(simple_decl->declarators->next == 0);
    QCOMPARE(simple_decl->semicolon_token, 6U);

    NamedTypeSpecifierAST *named_ty = simple_decl->decl_specifier_seq->asNamedTypeSpecifier();
    QVERIFY(named_ty != 0);
    QVERIFY(named_ty->name != 0);

    SimpleNameAST *simple_named_ty = named_ty->name->asSimpleName();
    QVERIFY(simple_named_ty != 0);
    QCOMPARE(simple_named_ty->identifier_token, 1U);

    DeclaratorAST *declarator = simple_decl->declarators->declarator;
    QVERIFY(declarator != 0);
    QVERIFY(declarator->core_declarator != 0);
    QVERIFY(declarator->postfix_declarators != 0);
    QVERIFY(declarator->postfix_declarators->next == 0);
    QVERIFY(declarator->initializer == 0);

    DeclaratorIdAST *decl_id = declarator->core_declarator->asDeclaratorId();
    QVERIFY(decl_id != 0);
    QVERIFY(decl_id->name != 0);
    QVERIFY(decl_id->name->asSimpleName() != 0);
    QCOMPARE(decl_id->name->asSimpleName()->identifier_token, 2U);

    FunctionDeclaratorAST *fun_declarator = declarator->postfix_declarators->asFunctionDeclarator();
    QVERIFY(fun_declarator != 0);
    QCOMPARE(fun_declarator->lparen_token, 3U);
    QVERIFY(fun_declarator->parameters != 0);
    QCOMPARE(fun_declarator->rparen_token, 5U);

    // check the formal arguments
    ParameterDeclarationClauseAST *param_clause = fun_declarator->parameters;
    QVERIFY(param_clause->parameter_declarations != 0);
    QVERIFY(param_clause->parameter_declarations->next == 0);
    QCOMPARE(param_clause->dot_dot_dot_token, 0U);

    // check the parameter
    ParameterDeclarationAST *param = param_clause->parameter_declarations->asParameterDeclaration();
    QVERIFY(param->type_specifier != 0);
    QVERIFY(param->type_specifier->next == 0);
    QVERIFY(param->type_specifier->asNamedTypeSpecifier() != 0);
    QVERIFY(param->type_specifier->asNamedTypeSpecifier()->name != 0);
    QVERIFY(param->type_specifier->asNamedTypeSpecifier()->name->asSimpleName() != 0);
    QCOMPARE(param->type_specifier->asNamedTypeSpecifier()->name->asSimpleName()->identifier_token, 4U);
}

QTEST_APPLESS_MAIN(tst_AST)
#include "tst_ast.moc"
<|endoftext|>


<commit_before>//
//  AKSequencerDSPKernel.hpp
//  AudioKit
//
//  Created by Jeff Cooper on 1/25/19.
//  Copyright © 2019 AudioKit. All rights reserved.
//

#include <stdio.h>
#pragma once
#include <vector>
using std::vector;

#define NOTEON 0x90
#define NOTEOFF 0x80

struct MIDIEvent {
    uint8_t status;
    uint8_t data1;
    uint8_t data2;
    double beat;
};

struct MIDINote {
    struct MIDIEvent noteOn;
    struct MIDIEvent noteOff;
};

enum {
    startPointAddress = 0,
};

class AKSequencerEngineDSPKernel : public AKDSPKernel, public AKOutputBuffered {
public:

    AKSequencerEngineDSPKernel() {}

    void init(int channelCount, double sampleRate) override {

    }

    void setTargetAU(AudioUnit target) {
        targetAU = target;
    }

    void start() {
        started = true;
        isPlaying = true;
    }

    void stop() {
        started = false;
        isPlaying = false;
    }

    void seekTo(double position) {
        positionInSamples = beatToSamples(position);
    }

    void setTempo(double newValue) {
        double lastPosition = currentPositionInBeats(); // 1) save where we are before we manipulate time
        tempo = newValue;                               // 2) manipulate time
        seekTo(lastPosition);                           // 3) go back to where we were before time manipulation
    }

    void reset() {
        resetted = true;
        startPointRamper.reset();
    }

    void destroy() {

    }

    void setStartPoint(float value) {
        startPoint = value;
    }

    void setParameter(AUParameterAddress address, AUValue value) {
        switch (address) {
            case startPointAddress:
                startPointRamper.setUIValue(clamp(value, 0.0f, 10.0f));
                break;
        }
    }

    AUValue getParameter(AUParameterAddress address) {
        switch (address) {
            case startPointAddress:
                return startPointRamper.getUIValue();

            default: return 0.0f;
        }
    }

    void startRamp(AUParameterAddress address, AUValue value, AUAudioFrameCount duration) override {
        switch (address) {
            case startPointAddress:
                startPointRamper.startRamp(clamp(value, 0.0f, 10.0f), duration);
                break;
        }
    }

    void addPlayingNote(MIDINote note, int offset) {
        sendMidiData(note.noteOn.status, note.noteOn.data1, note.noteOn.data2, offset, note.noteOn.beat);
        playingNotes.push_back(note);
    }

    void stopPlayingNote(MIDINote note, int offset, int index) {
        sendMidiData(note.noteOff.status, note.noteOff.data1, note.noteOff.data2, offset, note.noteOff.beat);
        playingNotes.erase(playingNotes.begin() + index);
    }

    void process(AUAudioFrameCount frameCount, AUAudioFrameCount bufferOffset) override {
        if (isPlaying) {
            if (positionInSamples >= lengthInSamples()){
                if (!loopEnabled) { //stop if played enough
                    stop();
                    return;
                }
            }
            long currentStartSample = positionModulo();
            long currentEndSample = currentStartSample + frameCount;
            for (int i = 0; i < events.size(); i++) {
                // go through every event
                int triggerTime = beatToSamples(events[i].beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    // this event is supposed to trigger between currentStartSample and currentEndSample
                    int offset = (int)(triggerTime - currentStartSample);
                    sendMidiData(events[i].status, events[i].data1, events[i].data2,
                                 offset, events[i].beat);
                } else if (currentEndSample > lengthInSamples() && loopEnabled) {
                    // this buffer extends beyond the length of the loop and looping is on
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        // this event would trigger early enough in the next loop that it should happen in this buffer
                        // ie. this buffer contains events from the previous loop, and the next loop
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        sendMidiData(events[i].status, events[i].data1, events[i].data2,
                                     offset, events[i].beat);
                    }
                }
            }

            // Check scheduled notes for note ons
            for (int i = 0; i < notes.size(); i++) {
                int triggerTime = beatToSamples(notes[i].noteOn.beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    int offset = (int)(triggerTime - currentStartSample);
                    addPlayingNote(notes[i], offset);
                } else if (currentEndSample > lengthInSamples() && loopEnabled) {
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        addPlayingNote(notes[i], offset);
                    }
                }
            }

            // Check the playing notes for note offs
            int i = 0;
            while (i < playingNotes.size()) {
                int triggerTime = beatToSamples(playingNotes[i].noteOff.beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    int offset = (int)(triggerTime - currentStartSample);
                    stopPlayingNote(playingNotes[i], offset, i);
                    if (i > 0) i--;
                } else if (currentEndSample > lengthInSamples() && loopEnabled) {
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        stopPlayingNote(playingNotes[i], offset, i);
                        if (i > 0) i--;
                    }
                }
                i++;
            }

            positionInSamples += frameCount;
        }
        framesCounted += frameCount;
    }

    void addMIDIEvent(uint8_t status, uint8_t data1, uint8_t data2, double beat) {
        MIDIEvent newEvent;
        newEvent.status = status;
        newEvent.data1 = data1;
        newEvent.data2 = data2;
        newEvent.beat = beat;
        events.push_back(newEvent);
    }

    void addMIDINote(uint8_t number, uint8_t velocity, double beat, double duration) {
        MIDINote newNote;

        newNote.noteOn.status = NOTEON;
        newNote.noteOn.data1 = number;
        newNote.noteOn.data2 = velocity;
        newNote.noteOn.beat = beat;

        newNote.noteOff.status = NOTEOFF;
        newNote.noteOff.data1 = number;
        newNote.noteOff.data2 = velocity;
        newNote.noteOff.beat = beat + duration;

        notes.push_back(newNote);
    }

    void clear() {
        while (playingNotes.size() > 0) {
            stopPlayingNote(playingNotes[0], 0, 0);
        }
        notes.clear();
        events.clear();
    }

    void sendMidiData(UInt8 status, UInt8 data1, UInt8 data2, double offset, double time) {
//        printf("%p: sending: %i %i %i at offset %f (%f beats)\n", &midiEndpoint, status, data1, data2, offset, time);
        if (midiPort == 0 || midiEndpoint == 0) {
            MusicDeviceMIDIEvent(targetAU, status, data1, data2, offset);
        } else {
            MIDIPacketList packetList;
            packetList.numPackets = 1;
            MIDIPacket* firstPacket = &packetList.packet[0];
            firstPacket->length = 3;
            firstPacket->data[0] = status;
            firstPacket->data[1] = data1;
            firstPacket->data[2] = data2;
            firstPacket->timeStamp = offset;
            MIDISend(midiPort, midiEndpoint, &packetList);
        }
    }

    long lengthInSamples() {
        return beatToSamples(length);
    }

    int beatToSamples(double beat) {
        return (int)(beat / tempo * 60 * sampleRate);
    }

    long positionModulo() {
        long length = lengthInSamples();
        if (positionInSamples == 0 || length == 0) {
            return 0;
        } else if (positionInSamples < 0) {
            return positionInSamples;
        } else {
            return positionInSamples % length;
        }
    }

    double currentPositionInBeats() {
        return (double)positionModulo() / sampleRate * (tempo / 60);
    }

    bool validTriggerTime(double beat){
        return true;
    }

private:

    float startPoint = 0;
    AudioUnit targetAU;
    UInt64 framesCounted = 0;
    long positionInSamples = 0;

public:
    bool started = false;
    bool resetted = false;
    ParameterRamper startPointRamper = 1;

    bool isPlaying = false;
    MIDIPortRef midiPort;
    MIDIEndpointRef midiEndpoint;
    AKCCallback loopCallback = nullptr;
    vector<MIDIEvent> events;
    vector<MIDINote> notes;
    vector<MIDINote> playingNotes;
    int maximumPlayCount = 0;
    double length = 4.0;
    double tempo = 120.0;
    bool loopEnabled = true;
    uint numberOfLoops = 0;
};
<commit_msg>Add more note off for velocity=0 noteOn<commit_after>//
//  AKSequencerDSPKernel.hpp
//  AudioKit
//
//  Created by Jeff Cooper on 1/25/19.
//  Copyright © 2019 AudioKit. All rights reserved.
//

#include <stdio.h>
#pragma once
#include <vector>
using std::vector;

#define NOTEON 0x90
#define NOTEOFF 0x80

struct MIDIEvent {
    uint8_t status;
    uint8_t data1;
    uint8_t data2;
    double beat;
};

struct MIDINote {
    struct MIDIEvent noteOn;
    struct MIDIEvent noteOff;
};

enum {
    startPointAddress = 0,
};

class AKSequencerEngineDSPKernel : public AKDSPKernel, public AKOutputBuffered {
public:

    AKSequencerEngineDSPKernel() {}

    void init(int channelCount, double sampleRate) override {

    }

    void setTargetAU(AudioUnit target) {
        targetAU = target;
    }

    void start() {
        started = true;
        isPlaying = true;
    }

    void stop() {
        started = false;
        isPlaying = false;
    }

    void seekTo(double position) {
        positionInSamples = beatToSamples(position);
    }

    void setTempo(double newValue) {
        double lastPosition = currentPositionInBeats(); // 1) save where we are before we manipulate time
        tempo = newValue;                               // 2) manipulate time
        seekTo(lastPosition);                           // 3) go back to where we were before time manipulation
    }

    void reset() {
        resetted = true;
        startPointRamper.reset();
    }

    void destroy() {

    }

    void setStartPoint(float value) {
        startPoint = value;
    }

    void setParameter(AUParameterAddress address, AUValue value) {
        switch (address) {
            case startPointAddress:
                startPointRamper.setUIValue(clamp(value, 0.0f, 10.0f));
                break;
        }
    }

    AUValue getParameter(AUParameterAddress address) {
        switch (address) {
            case startPointAddress:
                return startPointRamper.getUIValue();

            default: return 0.0f;
        }
    }

    void startRamp(AUParameterAddress address, AUValue value, AUAudioFrameCount duration) override {
        switch (address) {
            case startPointAddress:
                startPointRamper.startRamp(clamp(value, 0.0f, 10.0f), duration);
                break;
        }
    }

    void addPlayingNote(MIDINote note, int offset) {
        if (note.noteOn.data2 > 0) {
            sendMidiData(note.noteOn.status, note.noteOn.data1, note.noteOn.data2, offset, note.noteOn.beat);
            playingNotes.push_back(note);
        } else {
            sendMidiData(note.noteOff.status, note.noteOff.data1, note.noteOff.data2, offset, note.noteOn.beat);
        }
    }

    void stopPlayingNote(MIDINote note, int offset, int index) {
        sendMidiData(note.noteOff.status, note.noteOff.data1, note.noteOff.data2, offset, note.noteOff.beat);
        playingNotes.erase(playingNotes.begin() + index);
    }

    void process(AUAudioFrameCount frameCount, AUAudioFrameCount bufferOffset) override {
        if (isPlaying) {
            if (positionInSamples >= lengthInSamples()){
                if (!loopEnabled) { //stop if played enough
                    stop();
                    return;
                }
            }
            long currentStartSample = positionModulo();
            long currentEndSample = currentStartSample + frameCount;
            for (int i = 0; i < events.size(); i++) {
                // go through every event
                int triggerTime = beatToSamples(events[i].beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    // this event is supposed to trigger between currentStartSample and currentEndSample
                    int offset = (int)(triggerTime - currentStartSample);
                    sendMidiData(events[i].status, events[i].data1, events[i].data2,
                                 offset, events[i].beat);
                } else if (currentEndSample > lengthInSamples() && loopEnabled) {
                    // this buffer extends beyond the length of the loop and looping is on
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        // this event would trigger early enough in the next loop that it should happen in this buffer
                        // ie. this buffer contains events from the previous loop, and the next loop
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        sendMidiData(events[i].status, events[i].data1, events[i].data2,
                                     offset, events[i].beat);
                    }
                }
            }

            // Check scheduled notes for note ons
            for (int i = 0; i < notes.size(); i++) {
                int triggerTime = beatToSamples(notes[i].noteOn.beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    int offset = (int)(triggerTime - currentStartSample);
                    addPlayingNote(notes[i], offset);
                } else if (currentEndSample > lengthInSamples() && loopEnabled) {
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        addPlayingNote(notes[i], offset);
                    }
                }
            }

            // Check the playing notes for note offs
            int i = 0;

            while (i < playingNotes.size()) {
                int triggerTime = beatToSamples(playingNotes[i].noteOff.beat);
                if (currentStartSample <= triggerTime && triggerTime < currentEndSample) {
                    int offset = (int)(triggerTime - currentStartSample);
                    stopPlayingNote(playingNotes[i], offset, i);
                    continue;
                }

                if (currentEndSample > lengthInSamples() && loopEnabled) {
                    int loopRestartInBuffer = (int)(lengthInSamples() - currentStartSample);
                    int samplesOfBufferForNewLoop = frameCount - loopRestartInBuffer;
                    if (triggerTime < samplesOfBufferForNewLoop) {
                        int offset = (int)triggerTime + loopRestartInBuffer;
                        stopPlayingNote(playingNotes[i], offset, i);
                        continue;
                    }
                }
                i++;
            }

            positionInSamples += frameCount;
        }
        framesCounted += frameCount;
    }

    void addMIDIEvent(uint8_t status, uint8_t data1, uint8_t data2, double beat) {
        MIDIEvent newEvent;
        newEvent.status = status;
        newEvent.data1 = data1;
        newEvent.data2 = data2;
        newEvent.beat = beat;
        events.push_back(newEvent);
    }

    void addMIDINote(uint8_t number, uint8_t velocity, double beat, double duration) {
        MIDINote newNote;

        newNote.noteOn.status = NOTEON;
        newNote.noteOn.data1 = number;
        newNote.noteOn.data2 = velocity;
        newNote.noteOn.beat = beat;

        newNote.noteOff.status = NOTEOFF;
        newNote.noteOff.data1 = number;
        newNote.noteOff.data2 = velocity;
        newNote.noteOff.beat = beat + duration;

        notes.push_back(newNote);
    }

    void clear() {
        while (playingNotes.size() > 0) {
            stopPlayingNote(playingNotes[0], 0, 0);
        }
        notes.clear();
        events.clear();
    }

    void sendMidiData(UInt8 status, UInt8 data1, UInt8 data2, double offset, double time) {
//        printf("%p: sending: %i %i %i at offset %f (%f beats)\n", &midiEndpoint, status, data1, data2, offset, time);
        if (midiPort == 0 || midiEndpoint == 0) {
            MusicDeviceMIDIEvent(targetAU, status, data1, data2, offset);
        } else {
            MIDIPacketList packetList;
            packetList.numPackets = 1;
            MIDIPacket* firstPacket = &packetList.packet[0];
            firstPacket->length = 3;
            firstPacket->data[0] = status;
            firstPacket->data[1] = data1;
            firstPacket->data[2] = data2;
            firstPacket->timeStamp = offset;
            MIDISend(midiPort, midiEndpoint, &packetList);
        }
    }

    long lengthInSamples() {
        return beatToSamples(length);
    }

    int beatToSamples(double beat) {
        return (int)(beat / tempo * 60 * sampleRate);
    }

    long positionModulo() {
        long length = lengthInSamples();
        if (positionInSamples == 0 || length == 0) {
            return 0;
        } else if (positionInSamples < 0) {
            return positionInSamples;
        } else {
            return positionInSamples % length;
        }
    }

    double currentPositionInBeats() {
        return (double)positionModulo() / sampleRate * (tempo / 60);
    }

    bool validTriggerTime(double beat){
        return true;
    }

private:

    float startPoint = 0;
    AudioUnit targetAU;
    UInt64 framesCounted = 0;
    long positionInSamples = 0;

public:
    bool started = false;
    bool resetted = false;
    ParameterRamper startPointRamper = 1;

    bool isPlaying = false;
    MIDIPortRef midiPort;
    MIDIEndpointRef midiEndpoint;
    AKCCallback loopCallback = nullptr;
    vector<MIDIEvent> events;
    vector<MIDINote> notes;
    vector<MIDINote> playingNotes;
    int maximumPlayCount = 0;
    double length = 4.0;
    double tempo = 120.0;
    bool loopEnabled = true;
    uint numberOfLoops = 0;
};
<|endoftext|>


<commit_before>// Copyright (c) 2020 PaddlePaddle Authors. 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.

#include "paddle/fluid/imperative/amp_auto_cast.h"

#include <memory>
#include <string>

#include "paddle/fluid/imperative/tracer.h"

namespace paddle {
namespace imperative {

class VarBase;

AutoCastGuard::AutoCastGuard(std::shared_ptr<Tracer> tracer, AmpLevel level)
    : tracer_(tracer) {
  pre_amp_level_ = tracer_->GetAmpLevel();

  if (pre_amp_level_ != level) {
    tracer_->SetAmpLevel(level);
  }
}

AutoCastGuard::~AutoCastGuard() { tracer_->SetAmpLevel(pre_amp_level_); }

AmpOperators::AmpOperators()
    : allow_ops_(new std::unordered_set<std::string>()),
      block_ops_(new std::unordered_set<std::string>()),
      unsupported_fp16_ops_(new std::unordered_set<std::string>()) {
  auto& all_kernels = framework::OperatorWithKernel::AllOpKernels();
  auto fp16_dtype = framework::proto::VarType::FP16;
  for (auto it = all_kernels.begin(); it != all_kernels.end(); it++) {
    bool supported = false;
    for (auto& kernel_type : it->second) {
      if ((platform::is_gpu_place(kernel_type.first.place_) ||
           platform::is_xpu_place(kernel_type.first.place_)) &&
          kernel_type.first.data_type_ == fp16_dtype) {
        supported = true;
      }
    }
    if (!supported) {
      unsupported_fp16_ops_->insert(it->first);
    }
  }
}

AmpOperators::~AmpOperators() {}

AmpOperators& AmpOperators::Instance() {
  static AmpOperators instance;
  return instance;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableAllowOps() {
  return allow_ops_;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableBlockOps() {
  return block_ops_;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableUnsupportedFp16Ops() {
  return unsupported_fp16_ops_;
}

std::ostream& operator<<(std::ostream& os, AmpOperators& ops) {
  os << "allow ops: ";
  auto allow_ops = ops.GetMutableAllowOps();
  std::copy((*allow_ops).begin(), (*allow_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  os << "\n";
  os << "block ops: ";
  auto block_ops = ops.GetMutableBlockOps();
  std::copy((*block_ops).begin(), (*block_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  os << "\n";
  os << "unsupported fp16 ops: ";
  auto unsupported_fp16_ops = ops.GetMutableUnsupportedFp16Ops();
  std::copy((*unsupported_fp16_ops).begin(), (*unsupported_fp16_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  return os;
}

inline std::string GetDtypeStr(
    const std::shared_ptr<imperative::VarBase>& var) {
  return framework::DataTypeToString(var->DataType());
}

inline bool NeedCast(const std::shared_ptr<VarBase>& var) {
  if (platform::is_gpu_place(var->Place()) ||
      platform::is_cuda_pinned_place(var->Place()) ||
      platform::is_xpu_place(var->Place())) {
    // CudaPinndePlace is added for varbase created by dataloader
    if (var->DataType() == framework::proto::VarType::FP32 ||
        var->DataType() == framework::proto::VarType::FP16) {
      return true;
    }
  }
  return false;
}

// NOTE: Trace a cast op, so if a var is casted from fp32 to fp16, then the grad
// var will be cast back from fp16 to fp32 during backward phase.
static inline std::shared_ptr<imperative::VarBase> CastToType(
    const std::shared_ptr<VarBase>& var,
    const framework::proto::VarType::Type dst_type) {
  const auto& tracer = imperative::GetCurrentTracer();
  imperative::NameVarBaseMap ins = {{"X", {var}}};
  framework::AttributeMap attrs = {{"in_dtype", var->DataType()},
                                   {"out_dtype", dst_type}};
  auto out = std::shared_ptr<imperative::VarBase>(
      new imperative::VarBase(tracer->GenerateUniqueName()));
  imperative::NameVarBaseMap outs = {{"Out", {out}}};

  {
    AutoCastGuard guard(tracer, AmpLevel::O0);
    tracer->TraceOp("cast", ins, outs, std::move(attrs));
  }

  return out;
}

static inline std::shared_ptr<imperative::VarBase> CastToFP16(
    const std::shared_ptr<VarBase>& var) {
  auto dst_type = framework::proto::VarType::FP16;
  if (NeedCast(var) && (var->DataType() != dst_type)) {
    return CastToType(var, dst_type);
  }
  return var;
}

static inline std::shared_ptr<imperative::VarBase> CastToFP32(
    const std::shared_ptr<VarBase>& var) {
  auto dst_type = framework::proto::VarType::FP32;
  if (NeedCast(var) && (var->DataType() != dst_type)) {
    return CastToType(var, dst_type);
  }
  return var;
}

static inline framework::proto::VarType::Type GetPromoteType(
    const std::string& op_type, const NameVarBaseMap& ins) {
  auto dst_type = framework::proto::VarType::FP16;
  for (const auto& pair : ins) {
    for (const auto& var : pair.second) {
      if (var->DataType() == framework::proto::VarType::FP32) {
        dst_type = var->DataType();
        break;
      }
    }
  }

  // NOTE(juncai): moving_average_abs_max_scale only consider the
  // dtype of input(X)
  if (op_type == "moving_average_abs_max_scale") {
    for (const auto& pair : ins) {
      if (pair.first == "X" &&
          pair.second.front()->DataType() == framework::proto::VarType::FP16) {
        dst_type = framework::proto::VarType::FP16;
      }
    }
  }

  return dst_type;
}

NameVarBaseMap AutoCastInputs(const std::string& op_type,
                              const NameVarBaseMap& ins) {
  NameVarBaseMap new_ins(ins);
  if (AmpOperators::Instance().GetMutableAllowOps()->count(op_type)) {
    for (auto& pair : new_ins) {
      // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16.
      if ((op_type == "batch_norm" || op_type == "layer_norm" ||
           op_type == "sync_batch_norm") &&
          pair.first != "X") {
        continue;
      }

      if ((op_type == "fused_attention" || op_type == "fused_feedforward")) {
        if (pair.first == "LnScale" || pair.first == "LnBias" ||
            pair.first == "Ln2Scale" || pair.first == "Ln2Bias" ||
            pair.first == "Ln1Scale" || pair.first == "Ln1Bias") {
          continue;
        }
      }

      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to float16";
      for (auto& var : pair.second) {
        var = CastToFP16(var);
      }
    }
    return new_ins;
  } else if (AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) {
    for (auto& pair : new_ins) {
      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to float";
      for (auto& var : pair.second) {
        var = CastToFP32(var);
      }
    }
    return new_ins;
  } else {
    auto dst_type = GetPromoteType(op_type, ins);

    // NOTE(zhiqiu): if the op has op fp16 kernel, fall back to fp32.
    if (dst_type == framework::proto::VarType::FP16 &&
        AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count(
            op_type)) {
      dst_type = framework::proto::VarType::FP32;
    }
    for (auto& pair : new_ins) {
      // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16.
      if ((op_type == "batch_norm" || op_type == "layer_norm" ||
           op_type == "sync_batch_norm") &&
          pair.first == "X" && dst_type == framework::proto::VarType::FP32) {
        continue;
      }
      if ((op_type == "fused_attention" || op_type == "fused_feedforwad") &&
          dst_type == framework::proto::VarType::FP32) {
        if (pair.first != "LnScale" && pair.first != "LnBias" &&
            pair.first != "Ln2Scale" && pair.first != "Ln2Bias" &&
            pair.first != "Ln1Scale" && pair.first != "Ln1Bias") {
          continue;
        }
      }
      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to "
              << framework::DataTypeToString(dst_type);
      for (auto& var : pair.second) {
        var = (dst_type == framework::proto::VarType::FP32 ? CastToFP32(var)
                                                           : CastToFP16(var));
      }
    }
    return new_ins;
  }
  return new_ins;
}

NameVarBaseMap CastPureFp16Inputs(const std::string& op_type,
                                  const NameVarBaseMap& ins) {
  NameVarBaseMap new_ins(ins);
  auto dst_type = framework::proto::VarType::FP16;
  if (AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count(op_type) ||
      AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) {
    dst_type = framework::proto::VarType::FP32;
  }
  for (auto& pair : new_ins) {
    if ((op_type == "batch_norm" || op_type == "layer_norm" ||
         op_type == "sync_batch_norm") &&
        pair.first != "X") {
      continue;
    }
    VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
            << GetDtypeStr(*pair.second.cbegin()) << " to "
            << framework::DataTypeToString(dst_type);
    for (auto& var : pair.second) {
      var = (dst_type == framework::proto::VarType::FP32 ? CastToFP32(var)
                                                         : CastToFP16(var));
    }
  }
  return new_ins;
}

}  // namespace imperative
}  // namespace paddle
<commit_msg>for pure fp16 (#37230)<commit_after>// Copyright (c) 2020 PaddlePaddle Authors. 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.

#include "paddle/fluid/imperative/amp_auto_cast.h"

#include <memory>
#include <string>

#include "paddle/fluid/imperative/tracer.h"

namespace paddle {
namespace imperative {

class VarBase;

AutoCastGuard::AutoCastGuard(std::shared_ptr<Tracer> tracer, AmpLevel level)
    : tracer_(tracer) {
  pre_amp_level_ = tracer_->GetAmpLevel();

  if (pre_amp_level_ != level) {
    tracer_->SetAmpLevel(level);
  }
}

AutoCastGuard::~AutoCastGuard() { tracer_->SetAmpLevel(pre_amp_level_); }

AmpOperators::AmpOperators()
    : allow_ops_(new std::unordered_set<std::string>()),
      block_ops_(new std::unordered_set<std::string>()),
      unsupported_fp16_ops_(new std::unordered_set<std::string>()) {
  auto& all_kernels = framework::OperatorWithKernel::AllOpKernels();
  auto fp16_dtype = framework::proto::VarType::FP16;
  for (auto it = all_kernels.begin(); it != all_kernels.end(); it++) {
    bool supported = false;
    for (auto& kernel_type : it->second) {
      if ((platform::is_gpu_place(kernel_type.first.place_) ||
           platform::is_xpu_place(kernel_type.first.place_)) &&
          kernel_type.first.data_type_ == fp16_dtype) {
        supported = true;
      }
    }
    if (!supported) {
      unsupported_fp16_ops_->insert(it->first);
    }
  }
}

AmpOperators::~AmpOperators() {}

AmpOperators& AmpOperators::Instance() {
  static AmpOperators instance;
  return instance;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableAllowOps() {
  return allow_ops_;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableBlockOps() {
  return block_ops_;
}

std::shared_ptr<std::unordered_set<std::string>>
AmpOperators::GetMutableUnsupportedFp16Ops() {
  return unsupported_fp16_ops_;
}

std::ostream& operator<<(std::ostream& os, AmpOperators& ops) {
  os << "allow ops: ";
  auto allow_ops = ops.GetMutableAllowOps();
  std::copy((*allow_ops).begin(), (*allow_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  os << "\n";
  os << "block ops: ";
  auto block_ops = ops.GetMutableBlockOps();
  std::copy((*block_ops).begin(), (*block_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  os << "\n";
  os << "unsupported fp16 ops: ";
  auto unsupported_fp16_ops = ops.GetMutableUnsupportedFp16Ops();
  std::copy((*unsupported_fp16_ops).begin(), (*unsupported_fp16_ops).end(),
            std::ostream_iterator<std::string>(os, " "));
  return os;
}

inline std::string GetDtypeStr(
    const std::shared_ptr<imperative::VarBase>& var) {
  return framework::DataTypeToString(var->DataType());
}

inline bool NeedCast(const std::shared_ptr<VarBase>& var) {
  if (platform::is_gpu_place(var->Place()) ||
      platform::is_cuda_pinned_place(var->Place()) ||
      platform::is_xpu_place(var->Place())) {
    // CudaPinndePlace is added for varbase created by dataloader
    if (var->DataType() == framework::proto::VarType::FP32 ||
        var->DataType() == framework::proto::VarType::FP16) {
      return true;
    }
  }
  return false;
}

// NOTE: Trace a cast op, so if a var is casted from fp32 to fp16, then the grad
// var will be cast back from fp16 to fp32 during backward phase.
static inline std::shared_ptr<imperative::VarBase> CastToType(
    const std::shared_ptr<VarBase>& var,
    const framework::proto::VarType::Type dst_type) {
  const auto& tracer = imperative::GetCurrentTracer();
  imperative::NameVarBaseMap ins = {{"X", {var}}};
  framework::AttributeMap attrs = {{"in_dtype", var->DataType()},
                                   {"out_dtype", dst_type}};
  auto out = std::shared_ptr<imperative::VarBase>(
      new imperative::VarBase(tracer->GenerateUniqueName()));
  imperative::NameVarBaseMap outs = {{"Out", {out}}};

  {
    AutoCastGuard guard(tracer, AmpLevel::O0);
    tracer->TraceOp("cast", ins, outs, std::move(attrs));
  }

  return out;
}

static inline std::shared_ptr<imperative::VarBase> CastToFP16(
    const std::shared_ptr<VarBase>& var) {
  auto dst_type = framework::proto::VarType::FP16;
  if (NeedCast(var) && (var->DataType() != dst_type)) {
    return CastToType(var, dst_type);
  }
  return var;
}

static inline std::shared_ptr<imperative::VarBase> CastToFP32(
    const std::shared_ptr<VarBase>& var) {
  auto dst_type = framework::proto::VarType::FP32;
  if (NeedCast(var) && (var->DataType() != dst_type)) {
    return CastToType(var, dst_type);
  }
  return var;
}

static inline framework::proto::VarType::Type GetPromoteType(
    const std::string& op_type, const NameVarBaseMap& ins) {
  auto dst_type = framework::proto::VarType::FP16;
  for (const auto& pair : ins) {
    for (const auto& var : pair.second) {
      if (var->DataType() == framework::proto::VarType::FP32) {
        dst_type = var->DataType();
        break;
      }
    }
  }

  // NOTE(juncai): moving_average_abs_max_scale only consider the
  // dtype of input(X)
  if (op_type == "moving_average_abs_max_scale") {
    for (const auto& pair : ins) {
      if (pair.first == "X" &&
          pair.second.front()->DataType() == framework::proto::VarType::FP16) {
        dst_type = framework::proto::VarType::FP16;
      }
    }
  }

  return dst_type;
}

NameVarBaseMap AutoCastInputs(const std::string& op_type,
                              const NameVarBaseMap& ins) {
  NameVarBaseMap new_ins(ins);
  if (AmpOperators::Instance().GetMutableAllowOps()->count(op_type)) {
    for (auto& pair : new_ins) {
      // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16.
      if ((op_type == "batch_norm" || op_type == "layer_norm" ||
           op_type == "sync_batch_norm") &&
          pair.first != "X") {
        continue;
      }

      if ((op_type == "fused_attention" || op_type == "fused_feedforward")) {
        if (pair.first == "LnScale" || pair.first == "LnBias" ||
            pair.first == "Ln2Scale" || pair.first == "Ln2Bias" ||
            pair.first == "Ln1Scale" || pair.first == "Ln1Bias") {
          continue;
        }
      }

      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to float16";
      for (auto& var : pair.second) {
        var = CastToFP16(var);
      }
    }
    return new_ins;
  } else if (AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) {
    for (auto& pair : new_ins) {
      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to float";
      for (auto& var : pair.second) {
        var = CastToFP32(var);
      }
    }
    return new_ins;
  } else {
    auto dst_type = GetPromoteType(op_type, ins);

    // NOTE(zhiqiu): if the op has op fp16 kernel, fall back to fp32.
    if (dst_type == framework::proto::VarType::FP16 &&
        AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count(
            op_type)) {
      dst_type = framework::proto::VarType::FP32;
    }
    for (auto& pair : new_ins) {
      // NOTE(zhiqiu): batch_norm and layer_norm support only input x is fp16.
      if ((op_type == "batch_norm" || op_type == "layer_norm" ||
           op_type == "sync_batch_norm") &&
          pair.first == "X" && dst_type == framework::proto::VarType::FP32) {
        continue;
      }
      if ((op_type == "fused_attention" || op_type == "fused_feedforwad") &&
          dst_type == framework::proto::VarType::FP32) {
        if (pair.first != "LnScale" && pair.first != "LnBias" &&
            pair.first != "Ln2Scale" && pair.first != "Ln2Bias" &&
            pair.first != "Ln1Scale" && pair.first != "Ln1Bias") {
          continue;
        }
      }
      VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
              << GetDtypeStr(*pair.second.cbegin()) << " to "
              << framework::DataTypeToString(dst_type);
      for (auto& var : pair.second) {
        var = (dst_type == framework::proto::VarType::FP32 ? CastToFP32(var)
                                                           : CastToFP16(var));
      }
    }
    return new_ins;
  }
  return new_ins;
}

NameVarBaseMap CastPureFp16Inputs(const std::string& op_type,
                                  const NameVarBaseMap& ins) {
  NameVarBaseMap new_ins(ins);
  auto dst_type = framework::proto::VarType::FP16;
  if (AmpOperators::Instance().GetMutableUnsupportedFp16Ops()->count(op_type) ||
      AmpOperators::Instance().GetMutableBlockOps()->count(op_type)) {
    dst_type = framework::proto::VarType::FP32;
  }
  for (auto& pair : new_ins) {
    if ((op_type == "batch_norm" || op_type == "layer_norm" ||
         op_type == "sync_batch_norm") &&
        pair.first != "X") {
      continue;
    }
    if ((op_type == "fused_attention" || op_type == "fused_feedforward")) {
      if (pair.first == "LnScale" || pair.first == "LnBias" ||
          pair.first == "Ln2Scale" || pair.first == "Ln2Bias" ||
          pair.first == "Ln1Scale" || pair.first == "Ln1Bias") {
        continue;
      }
    }
    VLOG(5) << "Op(" << op_type << "): Cast " << pair.first << " from "
            << GetDtypeStr(*pair.second.cbegin()) << " to "
            << framework::DataTypeToString(dst_type);
    for (auto& var : pair.second) {
      var = (dst_type == framework::proto::VarType::FP32 ? CastToFP32(var)
                                                         : CastToFP16(var));
    }
  }
  return new_ins;
}

}  // namespace imperative
}  // namespace paddle
<|endoftext|>


<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

   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. */

#include "paddle/operators/ctc_edit_distance_op.h"

namespace paddle {
namespace operators {

class CTCEditDistanceOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X1"), "Input(X1) shouldn't be null.");
    PADDLE_ENFORCE(ctx->HasInput("X2"), "Input(X2) shouldn't be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null.");
    ctx->SetOutputDim("Out", {1});
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext &ctx) const override {
    return framework::OpKernelType(framework::DataType::FP32,
                                   ctx.device_context());
  }
};

class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  CTCEditDistanceOpMaker(framework::OpProto *proto,
                         framework::OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput("X1",
             "(2-D tensor with shape [M x 1]) The indices for "
             "hypothesis string");
    AddInput("X2",
             "(2-D tensor with shape [N x 1]) The indices "
             "for reference string.");
    AddAttr<bool>("normalized",
                  "(bool, default false) Indicated whether "
                  "normalize the Output(Out) by the length of reference "
                  "string (X2).")
        .SetDefault(false);
    AddOutput("Out",
              "(2-D tensor with shape [1 x 1]) "
              "The output distance of CTCEditDistance operator.");
    AddComment(R"DOC(

CTCEditDistance operator computes the edit distance of two sequences, one named
hypothesis and another named reference.

Edit distance measures how dissimilar two strings, one is hypothesis and another
is reference, are by counting the minimum number of operations to transform
one string into anthor.

)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;

REGISTER_OP_WITHOUT_GRADIENT(ctc_edit_distance, ops::CTCEditDistanceOp,
                             ops::CTCEditDistanceOpMaker);
REGISTER_OP_CPU_KERNEL(
    ctc_edit_distance,
    ops::CTCEditDistanceKernel<paddle::platform::CPUPlace, float>);
<commit_msg>revise the doc in ctc_edit_distance_op<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

   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. */

#include "paddle/operators/ctc_edit_distance_op.h"

namespace paddle {
namespace operators {

class CTCEditDistanceOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext *ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("X1"), "Input(X1) shouldn't be null.");
    PADDLE_ENFORCE(ctx->HasInput("X2"), "Input(X2) shouldn't be null.");
    PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null.");
    ctx->SetOutputDim("Out", {1});
  }

 protected:
  framework::OpKernelType GetKernelType(
      const framework::ExecutionContext &ctx) const override {
    return framework::OpKernelType(framework::DataType::FP32,
                                   ctx.device_context());
  }
};

class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  CTCEditDistanceOpMaker(framework::OpProto *proto,
                         framework::OpAttrChecker *op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput("X1",
             "(2-D tensor with shape [M x 1]) The indices for "
             "hypothesis string");
    AddInput("X2",
             "(2-D tensor with shape [N x 1]) The indices "
             "for reference string.");
    AddAttr<bool>("normalized",
                  "(bool, default false) Indicated whether "
                  "normalize the Output(Out) by the length of reference "
                  "string (X2).")
        .SetDefault(false);
    AddOutput("Out",
              "(2-D tensor with shape [1 x 1]) "
              "The output distance of CTCEditDistance operator.");
    AddComment(R"DOC(

CTCEditDistance operator computes the edit distance of two sequences, one named
hypothesis with length M and another named reference with length N.

Edit distance, also called Levenshtein distance, measures how dissimilar two strings
are by counting the minimum number of operations to transform one string into anthor.
Here the operations include insertion, deletion, and substitution. For example,
given hypothesis string A = "kitten" and reference B = "sitting", the edit distance
is 3 for A will be transformed into B at least after two substitutions and one
insertion:

   "kitten" -> "sitten" -> "sittin" -> "sitting"

If Attr(normalized) is true, the edit distance will be divided by the length of
reference string N.
)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;

REGISTER_OP_WITHOUT_GRADIENT(ctc_edit_distance, ops::CTCEditDistanceOp,
                             ops::CTCEditDistanceOpMaker);
REGISTER_OP_CPU_KERNEL(
    ctc_edit_distance,
    ops::CTCEditDistanceKernel<paddle::platform::CPUPlace, float>);
<|endoftext|>