Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AllocatedValue and make it more dynamic. #153

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

marsupial
Copy link
Contributor

ValueExtractionSynthesizer::SynthesizeSVRInit is currently transforming into code that is not standards compliant. The issue is mostly that either a constructor or cling::printValue can throw which leaves AllocatedValue thinking the memory it allocated is a valid object and running it's destructor when it shouldn't.

I solved this by reserving the byte before the payload memory for ValueExtractionSynthesizer::SynthesizeSVRInit to use and mark that construction has finished properly.

This PR has the relevant changes for AllocatedValue and also uses the reserved byte to allow it to only allocate the space required for caching the destructor and array-size fields for the type it is building.

The last commit is the disabling of the fix, as the changes to ValueExtractionSynthesizer::SynthesizeSVRInit are not included, and I probably won't back port what I have in that arena.

I can submit the changes I have to ValueExtractionSynthesizer::SynthesizeSVRInit which were done to avoid the copying that occurs when value-printing as well as provide an implementation that is not dependent on C++/overloading, but the transformation should basically become:

char* Ptr = cling_ValueExtraction() // My less C++ version of your `setValueWithAlloc`
new (Ptr)  ObjectType ...
((char*)Ptr)[-1] |= 1;

Relevant code for ValueExtractionSynthesizer::SynthesizeSVRInit:

        // Here Call is the expression holding the function that will trigger
        // cling::Value to allocate memory in a AllocatedValue

        // Reuse the void* function parameter.
        // vpValue = cling_ValueExtraction()
        ExprResult Alloc =
            m_Sema->CreateBuiltinBinOp(Loc, BO_Assign, ValueP, Call.get());

        // call new(vpValue) Object(Args)
        Call = m_Sema->BuildCXXNew(E->getSourceRange(),
                                   /*useGlobal ::*/ true,
                                   /*placementLParen*/ Loc,
                                   /*placementArgs*/MultiExprArg(ValueP),
                                   /*placementRParen*/ Loc,
                                   /*TypeIdParens*/ SourceRange(),
                                   /*allocType*/ ETSI->getType(),
                                   /*allocTypeInfo*/ ETSI,
                                   /*arraySize*/ ArraySize,
                                   /*directInitRange*/ InitRange,
                                   /*initializer*/Init);

        // Mark the constructor as succeeding. This is tied to cling::Value
        // AllocatedValue that delivers a pointer to a char buffer with the
        // element -1 as part of the object.
        //
        // ((char*)vpValue)[-1] = -1;

        Expr* Payload = utils::Synthesize::CStyleCastPtrExpr(
            m_Sema, AST.getPointerType(AST.CharTy), ValueP);

        IntegerLiteral* NegOne =
            new (AST) IntegerLiteral(AST, llvm::APInt(8, -1), AST.CharTy, Loc);

        ArraySubscriptExpr* Subscript =
            new (AST) ArraySubscriptExpr(Payload, NegOne, AST.CharTy,
                                         VK_LValue, OK_Ordinary, Loc);

        ExprResult Done = m_Sema->CreateBuiltinBinOp(
            Loc, BO_OrAssign, Subscript,
            new (AST) IntegerLiteral(AST, llvm::APInt(8, 1), AST.CharTy, Loc));

        llvm::SmallVector<Stmt*, 4> Mark;
        Mark.push_back(Alloc.get());
        Mark.push_back(Call.get());
        Mark.push_back(Done.get());

        // Push the pointer to the type allocated as the last statement or
        // clang becomes unhappy
        if (!DRefEnd) {
          Mark.push_back(utils::Synthesize::CStyleCastPtrExpr(
            m_Sema, AST.getPointerType(ElemTy), ValueP));
        } else
          Mark.push_back(DRefEnd);

        // Finish it off and deliver the expression into Call
        m_Sema->ActOnStartOfCompoundStmt();
        Stmt* Stmt = m_Sema->ActOnCompoundStmt(Loc, Loc, Mark, false).get();
        m_Sema->ActOnFinishOfCompoundStmt();
        m_Sema->ActOnStartStmtExpr();
        Call  = m_Sema->ActOnStmtExpr(Loc, Stmt, Loc);

      }
      if (Call.isInvalid()) {
        m_Sema->Diag(E->getLocStart(), diag::err_unsupported_unknown_any_expr);
        return Call.get();
      }

      // Handle possible cleanups:
      Call = m_Sema->ActOnFinishFullExpr(Call.get());
      if (VDecl) {
        VDecl->setInit(Call.get());
        return E;
      }
 

…s thrown/failed.

Previous implementation was both non-conforming and could easily lead to a crash.
By delivering a memory as a char buffer, and allowing the client to write into the -1 slot,
ValuePrinterSynthesizer can mark that construction has succeeded and the destructor can be called.

Additionally this change hides most implementation details of AllocatedValue from cling::Value.
Currently the bit is not being set in ValueExtractionSynthesizer which would mean no destructor would ever run!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant