Coverage Report

Created: 2019-03-22 08:08

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
Line
Count
Source (jump to first uncovered line)
1
//===- CoreEngine.h - Path-Sensitive Dataflow Engine ------------*- C++ -*-===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
//
9
//  This file defines a generic engine for intraprocedural, path-sensitive,
10
//  dataflow analysis via graph reachability.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H
15
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H
16
17
#include "clang/AST/Stmt.h"
18
#include "clang/Analysis/AnalysisDeclContext.h"
19
#include "clang/Analysis/CFG.h"
20
#include "clang/Analysis/ProgramPoint.h"
21
#include "clang/Basic/LLVM.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
24
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
25
#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
26
#include "llvm/ADT/SmallVector.h"
27
#include "llvm/Support/Casting.h"
28
#include <cassert>
29
#include <memory>
30
#include <utility>
31
#include <vector>
32
33
namespace clang {
34
35
class AnalyzerOptions;
36
class CXXBindTemporaryExpr;
37
class Expr;
38
class LabelDecl;
39
40
namespace ento {
41
42
class FunctionSummariesTy;
43
class SubEngine;
44
45
//===----------------------------------------------------------------------===//
46
/// CoreEngine - Implements the core logic of the graph-reachability
47
///   analysis. It traverses the CFG and generates the ExplodedGraph.
48
///   Program "states" are treated as opaque void pointers.
49
///   The template class CoreEngine (which subclasses CoreEngine)
50
///   provides the matching component to the engine that knows the actual types
51
///   for states.  Note that this engine only dispatches to transfer functions
52
///   at the statement and block-level.  The analyses themselves must implement
53
///   any transfer function logic and the sub-expression level (if any).
54
class CoreEngine {
55
  friend class CommonNodeBuilder;
56
  friend class EndOfFunctionNodeBuilder;
57
  friend class ExprEngine;
58
  friend class IndirectGotoNodeBuilder;
59
  friend class NodeBuilder;
60
  friend struct NodeBuilderContext;
61
  friend class SwitchNodeBuilder;
62
63
public:
64
  using BlocksExhausted =
65
      std::vector<std::pair<BlockEdge, const ExplodedNode *>>;
66
67
  using BlocksAborted =
68
      std::vector<std::pair<const CFGBlock *, const ExplodedNode *>>;
69
70
private:
71
  SubEngine &SubEng;
72
73
  /// G - The simulation graph.  Each node is a (location,state) pair.
74
  mutable ExplodedGraph G;
75
76
  /// WList - A set of queued nodes that need to be processed by the
77
  ///  worklist algorithm.  It is up to the implementation of WList to decide
78
  ///  the order that nodes are processed.
79
  std::unique_ptr<WorkList> WList;
80
81
  /// BCounterFactory - A factory object for created BlockCounter objects.
82
  ///   These are used to record for key nodes in the ExplodedGraph the
83
  ///   number of times different CFGBlocks have been visited along a path.
84
  BlockCounter::Factory BCounterFactory;
85
86
  /// The locations where we stopped doing work because we visited a location
87
  ///  too many times.
88
  BlocksExhausted blocksExhausted;
89
90
  /// The locations where we stopped because the engine aborted analysis,
91
  /// usually because it could not reason about something.
92
  BlocksAborted blocksAborted;
93
94
  /// The information about functions shared by the whole translation unit.
95
  /// (This data is owned by AnalysisConsumer.)
96
  FunctionSummariesTy *FunctionSummaries;
97
98
  void generateNode(const ProgramPoint &Loc,
99
                    ProgramStateRef State,
100
                    ExplodedNode *Pred);
101
102
  void HandleBlockEdge(const BlockEdge &E, ExplodedNode *Pred);
103
  void HandleBlockEntrance(const BlockEntrance &E, ExplodedNode *Pred);
104
  void HandleBlockExit(const CFGBlock *B, ExplodedNode *Pred);
105
106
  void HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred);
107
108
  void HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, ExplodedNode *Pred);
109
110
  void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
111
                    ExplodedNode *Pred);
112
  void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
113
                                    const CFGBlock *B, ExplodedNode *Pred);
114
115
  /// Handle conditional logic for running static initializers.
116
  void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
117
                        ExplodedNode *Pred);
118
119
private:
120
  ExplodedNode *generateCallExitBeginNode(ExplodedNode *N,
121
                                          const ReturnStmt *RS);
122
123
public:
124
  /// Construct a CoreEngine object to analyze the provided CFG.
125
  CoreEngine(SubEngine &subengine,
126
             FunctionSummariesTy *FS,
127
             AnalyzerOptions &Opts);
128
129
  CoreEngine(const CoreEngine &) = delete;
130
  CoreEngine &operator=(const CoreEngine &) = delete;
131
132
  /// getGraph - Returns the exploded graph.
133
10.5k
  ExplodedGraph &getGraph() { return G; }
134
135
  /// ExecuteWorkList - Run the worklist algorithm for a maximum number of
136
  ///  steps.  Returns true if there is still simulation state on the worklist.
137
  bool ExecuteWorkList(const LocationContext *L, unsigned Steps,
138
                       ProgramStateRef InitState);
139
140
  /// Returns true if there is still simulation state on the worklist.
141
  bool ExecuteWorkListWithInitialState(const LocationContext *L,
142
                                       unsigned Steps,
143
                                       ProgramStateRef InitState,
144
                                       ExplodedNodeSet &Dst);
145
146
  /// Dispatch the work list item based on the given location information.
147
  /// Use Pred parameter as the predecessor state.
148
  void dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
149
                        const WorkListUnit& WU);
150
151
  // Functions for external checking of whether we have unfinished work
152
326
  bool wasBlockAborted() const { return !blocksAborted.empty(); }
153
335
  bool wasBlocksExhausted() const { return !blocksExhausted.empty(); }
154
331
  bool hasWorkRemaining() const { return wasBlocksExhausted() ||
155
331
                                         
WList->hasWork()326
||
156
331
                                         
wasBlockAborted()326
; }
157
158
  /// Inform the CoreEngine that a basic block was aborted because
159
  /// it could not be completely analyzed.
160
2
  void addAbortedBlock(const ExplodedNode *node, const CFGBlock *block) {
161
2
    blocksAborted.push_back(std::make_pair(block, node));
162
2
  }
163
164
60.2k
  WorkList *getWorkList() const { return WList.get(); }
165
166
4
  BlocksExhausted::const_iterator blocks_exhausted_begin() const {
167
4
    return blocksExhausted.begin();
168
4
  }
169
170
4
  BlocksExhausted::const_iterator blocks_exhausted_end() const {
171
4
    return blocksExhausted.end();
172
4
  }
173
174
0
  BlocksAborted::const_iterator blocks_aborted_begin() const {
175
0
    return blocksAborted.begin();
176
0
  }
177
178
0
  BlocksAborted::const_iterator blocks_aborted_end() const {
179
0
    return blocksAborted.end();
180
0
  }
181
182
  /// Enqueue the given set of nodes onto the work list.
183
  void enqueue(ExplodedNodeSet &Set);
184
185
  /// Enqueue nodes that were created as a result of processing
186
  /// a statement onto the work list.
187
  void enqueue(ExplodedNodeSet &Set, const CFGBlock *Block, unsigned Idx);
188
189
  /// enqueue the nodes corresponding to the end of function onto the
190
  /// end of path / work list.
191
  void enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS);
192
193
  /// Enqueue a single node created as a result of statement processing.
194
  void enqueueStmtNode(ExplodedNode *N, const CFGBlock *Block, unsigned Idx);
195
};
196
197
// TODO: Turn into a class.
198
struct NodeBuilderContext {
199
  const CoreEngine &Eng;
200
  const CFGBlock *Block;
201
  const LocationContext *LC;
202
203
  NodeBuilderContext(const CoreEngine &E, const CFGBlock *B, ExplodedNode *N)
204
1.17M
      : Eng(E), Block(B), LC(N->getLocationContext()) { assert(B); }
205
206
  /// Return the CFGBlock associated with this builder.
207
976k
  const CFGBlock *getBlock() const { return Block; }
208
209
  /// Returns the number of times the current basic block has been
210
  /// visited on the exploded graph path.
211
198k
  unsigned blockCount() const {
212
198k
    return Eng.WList->getBlockCounter().getNumVisited(
213
198k
                    LC->getStackFrame(),
214
198k
                    Block->getBlockID());
215
198k
  }
216
};
217
218
/// \class NodeBuilder
219
/// This is the simplest builder which generates nodes in the
220
/// ExplodedGraph.
221
///
222
/// The main benefit of the builder is that it automatically tracks the
223
/// frontier nodes (or destination set). This is the set of nodes which should
224
/// be propagated to the next step / builder. They are the nodes which have been
225
/// added to the builder (either as the input node set or as the newly
226
/// constructed nodes) but did not have any outgoing transitions added.
227
class NodeBuilder {
228
  virtual void anchor();
229
230
protected:
231
  const NodeBuilderContext &C;
232
233
  /// Specifies if the builder results have been finalized. For example, if it
234
  /// is set to false, autotransitions are yet to be generated.
235
  bool Finalized;
236
237
  bool HasGeneratedNodes = false;
238
239
  /// The frontier set - a set of nodes which need to be propagated after
240
  /// the builder dies.
241
  ExplodedNodeSet &Frontier;
242
243
  /// Checks if the results are ready.
244
0
  virtual bool checkResults() {
245
0
    return Finalized;
246
0
  }
247
248
0
  bool hasNoSinksInFrontier() {
249
0
    for (const auto  I : Frontier)
250
0
      if (I->isSink())
251
0
        return false;
252
0
    return true;
253
0
  }
254
255
  /// Allow subclasses to finalize results before result_begin() is executed.
256
33.6k
  virtual void finalizeResults() {}
257
258
  ExplodedNode *generateNodeImpl(const ProgramPoint &PP,
259
                                 ProgramStateRef State,
260
                                 ExplodedNode *Pred,
261
                                 bool MarkAsSink = false);
262
263
public:
264
  NodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet,
265
              const NodeBuilderContext &Ctx, bool F = true)
266
1.73M
      : C(Ctx), Finalized(F), Frontier(DstSet) {
267
1.73M
    Frontier.Add(SrcNode);
268
1.73M
  }
269
270
  NodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet,
271
              const NodeBuilderContext &Ctx, bool F = true)
272
3.80M
      : C(Ctx), Finalized(F), Frontier(DstSet) {
273
3.80M
    Frontier.insert(SrcSet);
274
3.80M
    assert(hasNoSinksInFrontier());
275
3.80M
  }
276
277
5.54M
  virtual ~NodeBuilder() = default;
278
279
  /// Generates a node in the ExplodedGraph.
280
  ExplodedNode *generateNode(const ProgramPoint &PP,
281
                             ProgramStateRef State,
282
1.30M
                             ExplodedNode *Pred) {
283
1.30M
    return generateNodeImpl(PP, State, Pred, false);
284
1.30M
  }
285
286
  /// Generates a sink in the ExplodedGraph.
287
  ///
288
  /// When a node is marked as sink, the exploration from the node is stopped -
289
  /// the node becomes the last node on the path and certain kinds of bugs are
290
  /// suppressed.
291
  ExplodedNode *generateSink(const ProgramPoint &PP,
292
                             ProgramStateRef State,
293
7.29k
                             ExplodedNode *Pred) {
294
7.29k
    return generateNodeImpl(PP, State, Pred, true);
295
7.29k
  }
296
297
26.0k
  const ExplodedNodeSet &getResults() {
298
26.0k
    finalizeResults();
299
26.0k
    assert(checkResults());
300
26.0k
    return Frontier;
301
26.0k
  }
302
303
  using iterator = ExplodedNodeSet::iterator;
304
305
  /// Iterators through the results frontier.
306
7.54k
  iterator begin() {
307
7.54k
    finalizeResults();
308
7.54k
    assert(checkResults());
309
7.54k
    return Frontier.begin();
310
7.54k
  }
311
312
0
  iterator end() {
313
0
    finalizeResults();
314
0
    return Frontier.end();
315
0
  }
316
317
129k
  const NodeBuilderContext &getContext() { return C; }
318
123k
  bool hasGeneratedNodes() { return HasGeneratedNodes; }
319
320
40.5k
  void takeNodes(const ExplodedNodeSet &S) {
321
40.5k
    for (const auto I : S)
322
40.5k
      Frontier.erase(I);
323
40.5k
  }
324
325
1.08M
  void takeNodes(ExplodedNode *N) { Frontier.erase(N); }
326
1.05M
  void addNodes(const ExplodedNodeSet &S) { Frontier.insert(S); }
327
0
  void addNodes(ExplodedNode *N) { Frontier.Add(N); }
328
};
329
330
/// \class NodeBuilderWithSinks
331
/// This node builder keeps track of the generated sink nodes.
332
class NodeBuilderWithSinks: public NodeBuilder {
333
  void anchor() override;
334
335
protected:
336
  SmallVector<ExplodedNode*, 2> sinksGenerated;
337
  ProgramPoint &Location;
338
339
public:
340
  NodeBuilderWithSinks(ExplodedNode *Pred, ExplodedNodeSet &DstSet,
341
                       const NodeBuilderContext &Ctx, ProgramPoint &L)
342
123k
      : NodeBuilder(Pred, DstSet, Ctx), Location(L) {}
343
344
  ExplodedNode *generateNode(ProgramStateRef State,
345
                             ExplodedNode *Pred,
346
122k
                             const ProgramPointTag *Tag = nullptr) {
347
122k
    const ProgramPoint &LocalLoc = (Tag ? 
Location.withTag(Tag)0
: Location);
348
122k
    return NodeBuilder::generateNode(LocalLoc, State, Pred);
349
122k
  }
350
351
  ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred,
352
1.04k
                             const ProgramPointTag *Tag = nullptr) {
353
1.04k
    const ProgramPoint &LocalLoc = (Tag ? Location.withTag(Tag) : 
Location0
);
354
1.04k
    ExplodedNode *N = NodeBuilder::generateSink(LocalLoc, State, Pred);
355
1.04k
    if (N && N->isSink())
356
1.04k
      sinksGenerated.push_back(N);
357
1.04k
    return N;
358
1.04k
  }
359
360
0
  const SmallVectorImpl<ExplodedNode*> &getSinks() const {
361
0
    return sinksGenerated;
362
0
  }
363
};
364
365
/// \class StmtNodeBuilder
366
/// This builder class is useful for generating nodes that resulted from
367
/// visiting a statement. The main difference from its parent NodeBuilder is
368
/// that it creates a statement specific ProgramPoint.
369
class StmtNodeBuilder: public NodeBuilder {
370
  NodeBuilder *EnclosingBldr;
371
372
public:
373
  /// Constructs a StmtNodeBuilder. If the builder is going to process
374
  /// nodes currently owned by another builder(with larger scope), use
375
  /// Enclosing builder to transfer ownership.
376
  StmtNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet,
377
                  const NodeBuilderContext &Ctx,
378
                  NodeBuilder *Enclosing = nullptr)
379
1.46M
      : NodeBuilder(SrcNode, DstSet, Ctx), EnclosingBldr(Enclosing) {
380
1.46M
    if (EnclosingBldr)
381
0
      EnclosingBldr->takeNodes(SrcNode);
382
1.46M
  }
383
384
  StmtNodeBuilder(ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet,
385
                  const NodeBuilderContext &Ctx,
386
                  NodeBuilder *Enclosing = nullptr)
387
858k
      : NodeBuilder(SrcSet, DstSet, Ctx), EnclosingBldr(Enclosing) {
388
858k
    if (EnclosingBldr)
389
0
      for (const auto I : SrcSet)
390
0
        EnclosingBldr->takeNodes(I);
391
858k
  }
392
393
  ~StmtNodeBuilder() override;
394
395
  using NodeBuilder::generateNode;
396
  using NodeBuilder::generateSink;
397
398
  ExplodedNode *generateNode(const Stmt *S,
399
                             ExplodedNode *Pred,
400
                             ProgramStateRef St,
401
                             const ProgramPointTag *tag = nullptr,
402
1.02M
                             ProgramPoint::Kind K = ProgramPoint::PostStmtKind){
403
1.02M
    const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
404
1.02M
                                  Pred->getLocationContext(), tag);
405
1.02M
    return NodeBuilder::generateNode(L, St, Pred);
406
1.02M
  }
407
408
  ExplodedNode *generateSink(const Stmt *S,
409
                             ExplodedNode *Pred,
410
                             ProgramStateRef St,
411
                             const ProgramPointTag *tag = nullptr,
412
24
                             ProgramPoint::Kind K = ProgramPoint::PostStmtKind){
413
24
    const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
414
24
                                  Pred->getLocationContext(), tag);
415
24
    return NodeBuilder::generateSink(L, St, Pred);
416
24
  }
417
};
418
419
/// BranchNodeBuilder is responsible for constructing the nodes
420
/// corresponding to the two branches of the if statement - true and false.
421
class BranchNodeBuilder: public NodeBuilder {
422
  const CFGBlock *DstT;
423
  const CFGBlock *DstF;
424
425
  bool InFeasibleTrue;
426
  bool InFeasibleFalse;
427
428
  void anchor() override;
429
430
public:
431
  BranchNodeBuilder(ExplodedNode *SrcNode, ExplodedNodeSet &DstSet,
432
                    const NodeBuilderContext &C,
433
                    const CFGBlock *dstT, const CFGBlock *dstF)
434
      : NodeBuilder(SrcNode, DstSet, C), DstT(dstT), DstF(dstF),
435
696
        InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) {
436
696
    // The branch node builder does not generate autotransitions.
437
696
    // If there are no successors it means that both branches are infeasible.
438
696
    takeNodes(SrcNode);
439
696
  }
440
441
  BranchNodeBuilder(const ExplodedNodeSet &SrcSet, ExplodedNodeSet &DstSet,
442
                    const NodeBuilderContext &C,
443
                    const CFGBlock *dstT, const CFGBlock *dstF)
444
      : NodeBuilder(SrcSet, DstSet, C), DstT(dstT), DstF(dstF),
445
40.5k
        InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) {
446
40.5k
    takeNodes(SrcSet);
447
40.5k
  }
448
449
  ExplodedNode *generateNode(ProgramStateRef State, bool branch,
450
                             ExplodedNode *Pred);
451
452
0
  const CFGBlock *getTargetBlock(bool branch) const {
453
0
    return branch ? DstT : DstF;
454
0
  }
455
456
14.6k
  void markInfeasible(bool branch) {
457
14.6k
    if (branch)
458
6.14k
      InFeasibleTrue = true;
459
8.47k
    else
460
8.47k
      InFeasibleFalse = true;
461
14.6k
  }
462
463
147k
  bool isFeasible(bool branch) {
464
147k
    return branch ? 
!InFeasibleTrue75.2k
:
!InFeasibleFalse72.4k
;
465
147k
  }
466
};
467
468
class IndirectGotoNodeBuilder {
469
  CoreEngine& Eng;
470
  const CFGBlock *Src;
471
  const CFGBlock &DispatchBlock;
472
  const Expr *E;
473
  ExplodedNode *Pred;
474
475
public:
476
  IndirectGotoNodeBuilder(ExplodedNode *pred, const CFGBlock *src,
477
                    const Expr *e, const CFGBlock *dispatch, CoreEngine* eng)
478
8
      : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {}
479
480
  class iterator {
481
    friend class IndirectGotoNodeBuilder;
482
483
    CFGBlock::const_succ_iterator I;
484
485
16
    iterator(CFGBlock::const_succ_iterator i) : I(i) {}
486
487
  public:
488
8
    iterator &operator++() { ++I; return *this; }
489
16
    bool operator!=(const iterator &X) const { return I != X.I; }
490
491
0
    const LabelDecl *getLabel() const {
492
0
      return cast<LabelStmt>((*I)->getLabel())->getDecl();
493
0
    }
494
495
8
    const CFGBlock *getBlock() const {
496
8
      return *I;
497
8
    }
498
  };
499
500
8
  iterator begin() { return iterator(DispatchBlock.succ_begin()); }
501
8
  iterator end() { return iterator(DispatchBlock.succ_end()); }
502
503
  ExplodedNode *generateNode(const iterator &I,
504
                             ProgramStateRef State,
505
                             bool isSink = false);
506
507
8
  const Expr *getTarget() const { return E; }
508
509
8
  ProgramStateRef getState() const { return Pred->State; }
510
511
8
  const LocationContext *getLocationContext() const {
512
8
    return Pred->getLocationContext();
513
8
  }
514
};
515
516
class SwitchNodeBuilder {
517
  CoreEngine& Eng;
518
  const CFGBlock *Src;
519
  const Expr *Condition;
520
  ExplodedNode *Pred;
521
522
public:
523
  SwitchNodeBuilder(ExplodedNode *pred, const CFGBlock *src,
524
                    const Expr *condition, CoreEngine* eng)
525
216
      : Eng(*eng), Src(src), Condition(condition), Pred(pred) {}
526
527
  class iterator {
528
    friend class SwitchNodeBuilder;
529
530
    CFGBlock::const_succ_reverse_iterator I;
531
532
432
    iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {}
533
534
  public:
535
409
    iterator &operator++() { ++I; return *this; }
536
625
    bool operator!=(const iterator &X) const { return I != X.I; }
537
216
    bool operator==(const iterator &X) const { return I == X.I; }
538
539
485
    const CaseStmt *getCase() const {
540
485
      return cast<CaseStmt>((*I)->getLabel());
541
485
    }
542
543
855
    const CFGBlock *getBlock() const {
544
855
      return *I;
545
855
    }
546
  };
547
548
216
  iterator begin() { return iterator(Src->succ_rbegin()+1); }
549
216
  iterator end() { return iterator(Src->succ_rend()); }
550
551
138
  const SwitchStmt *getSwitch() const {
552
138
    return cast<SwitchStmt>(Src->getTerminator());
553
138
  }
554
555
  ExplodedNode *generateCaseStmtNode(const iterator &I,
556
                                     ProgramStateRef State);
557
558
  ExplodedNode *generateDefaultCaseNode(ProgramStateRef State,
559
                                        bool isSink = false);
560
561
216
  const Expr *getCondition() const { return Condition; }
562
563
216
  ProgramStateRef getState() const { return Pred->State; }
564
565
216
  const LocationContext *getLocationContext() const {
566
216
    return Pred->getLocationContext();
567
216
  }
568
};
569
570
} // namespace ento
571
572
} // namespace clang
573
574
#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_COREENGINE_H