@nocobase/plugin-workflow-javascript 2.1.0-beta.11 → 2.1.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/externalVersion.js +4 -4
- package/dist/node_modules/isolated-vm/.clang-tidy +13 -0
- package/dist/node_modules/isolated-vm/.dockerignore +9 -0
- package/dist/node_modules/isolated-vm/Dockerfile.alpine +9 -0
- package/dist/node_modules/isolated-vm/Dockerfile.debian +12 -0
- package/dist/node_modules/isolated-vm/LICENSE +13 -0
- package/dist/node_modules/isolated-vm/binding.gyp +120 -0
- package/dist/node_modules/isolated-vm/include.js +3 -0
- package/dist/node_modules/isolated-vm/inspector-example.js +59 -0
- package/dist/node_modules/isolated-vm/isolated-vm.d.ts +820 -0
- package/dist/node_modules/isolated-vm/isolated-vm.js +1 -0
- package/dist/node_modules/isolated-vm/native-example/binding.gyp +23 -0
- package/dist/node_modules/isolated-vm/native-example/example.cc +61 -0
- package/dist/node_modules/isolated-vm/native-example/package.json +13 -0
- package/dist/node_modules/isolated-vm/native-example/usage.js +35 -0
- package/dist/node_modules/isolated-vm/out/isolated_vm.node +0 -0
- package/dist/node_modules/isolated-vm/package.json +1 -0
- package/dist/node_modules/isolated-vm/src/external_copy/error.h +33 -0
- package/dist/node_modules/isolated-vm/src/external_copy/external_copy.cc +509 -0
- package/dist/node_modules/isolated-vm/src/external_copy/external_copy.h +117 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer.cc +85 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer.h +136 -0
- package/dist/node_modules/isolated-vm/src/external_copy/serializer_nortti.cc +73 -0
- package/dist/node_modules/isolated-vm/src/external_copy/string.cc +124 -0
- package/dist/node_modules/isolated-vm/src/external_copy/string.h +28 -0
- package/dist/node_modules/isolated-vm/src/isolate/allocator.h +32 -0
- package/dist/node_modules/isolated-vm/src/isolate/allocator_nortti.cc +142 -0
- package/dist/node_modules/isolated-vm/src/isolate/class_handle.h +334 -0
- package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.cc +220 -0
- package/dist/node_modules/isolated-vm/src/isolate/cpu_profile_manager.h +100 -0
- package/dist/node_modules/isolated-vm/src/isolate/environment.cc +626 -0
- package/dist/node_modules/isolated-vm/src/isolate/environment.h +381 -0
- package/dist/node_modules/isolated-vm/src/isolate/executor.cc +198 -0
- package/dist/node_modules/isolated-vm/src/isolate/executor.h +183 -0
- package/dist/node_modules/isolated-vm/src/isolate/external.h +64 -0
- package/dist/node_modules/isolated-vm/src/isolate/functor_runners.h +97 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/array.h +145 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/callbacks.h +272 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/error.h +140 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/extract_params.h +145 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/handle_cast.h +257 -0
- package/dist/node_modules/isolated-vm/src/isolate/generic/read_option.h +47 -0
- package/dist/node_modules/isolated-vm/src/isolate/holder.cc +88 -0
- package/dist/node_modules/isolated-vm/src/isolate/holder.h +63 -0
- package/dist/node_modules/isolated-vm/src/isolate/inspector.cc +200 -0
- package/dist/node_modules/isolated-vm/src/isolate/inspector.h +70 -0
- package/dist/node_modules/isolated-vm/src/isolate/node_wrapper.h +15 -0
- package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.cc +22 -0
- package/dist/node_modules/isolated-vm/src/isolate/platform_delegate.h +46 -0
- package/dist/node_modules/isolated-vm/src/isolate/remote_handle.h +164 -0
- package/dist/node_modules/isolated-vm/src/isolate/run_with_timeout.h +171 -0
- package/dist/node_modules/isolated-vm/src/isolate/runnable.h +29 -0
- package/dist/node_modules/isolated-vm/src/isolate/scheduler.cc +191 -0
- package/dist/node_modules/isolated-vm/src/isolate/scheduler.h +165 -0
- package/dist/node_modules/isolated-vm/src/isolate/specific.h +35 -0
- package/dist/node_modules/isolated-vm/src/isolate/stack_trace.cc +219 -0
- package/dist/node_modules/isolated-vm/src/isolate/stack_trace.h +24 -0
- package/dist/node_modules/isolated-vm/src/isolate/strings.h +127 -0
- package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.cc +385 -0
- package/dist/node_modules/isolated-vm/src/isolate/three_phase_task.h +136 -0
- package/dist/node_modules/isolated-vm/src/isolate/transferable.h +15 -0
- package/dist/node_modules/isolated-vm/src/isolate/util.h +45 -0
- package/dist/node_modules/isolated-vm/src/isolate/v8_inspector_wrapper.h +12 -0
- package/dist/node_modules/isolated-vm/src/isolate/v8_version.h +12 -0
- package/dist/node_modules/isolated-vm/src/isolated_vm.h +71 -0
- package/dist/node_modules/isolated-vm/src/lib/covariant.h +50 -0
- package/dist/node_modules/isolated-vm/src/lib/lockable.h +178 -0
- package/dist/node_modules/isolated-vm/src/lib/suspend.h +106 -0
- package/dist/node_modules/isolated-vm/src/lib/thread_pool.cc +98 -0
- package/dist/node_modules/isolated-vm/src/lib/thread_pool.h +45 -0
- package/dist/node_modules/isolated-vm/src/lib/timer.cc +233 -0
- package/dist/node_modules/isolated-vm/src/lib/timer.h +36 -0
- package/dist/node_modules/isolated-vm/src/module/callback.cc +151 -0
- package/dist/node_modules/isolated-vm/src/module/callback.h +64 -0
- package/dist/node_modules/isolated-vm/src/module/context_handle.cc +241 -0
- package/dist/node_modules/isolated-vm/src/module/context_handle.h +35 -0
- package/dist/node_modules/isolated-vm/src/module/evaluation.cc +109 -0
- package/dist/node_modules/isolated-vm/src/module/evaluation.h +99 -0
- package/dist/node_modules/isolated-vm/src/module/external_copy_handle.cc +119 -0
- package/dist/node_modules/isolated-vm/src/module/external_copy_handle.h +64 -0
- package/dist/node_modules/isolated-vm/src/module/isolate.cc +136 -0
- package/dist/node_modules/isolated-vm/src/module/isolate_handle.cc +611 -0
- package/dist/node_modules/isolated-vm/src/module/isolate_handle.h +47 -0
- package/dist/node_modules/isolated-vm/src/module/lib_handle.cc +77 -0
- package/dist/node_modules/isolated-vm/src/module/lib_handle.h +28 -0
- package/dist/node_modules/isolated-vm/src/module/module_handle.cc +475 -0
- package/dist/node_modules/isolated-vm/src/module/module_handle.h +68 -0
- package/dist/node_modules/isolated-vm/src/module/native_module_handle.cc +104 -0
- package/dist/node_modules/isolated-vm/src/module/native_module_handle.h +49 -0
- package/dist/node_modules/isolated-vm/src/module/reference_handle.cc +636 -0
- package/dist/node_modules/isolated-vm/src/module/reference_handle.h +106 -0
- package/dist/node_modules/isolated-vm/src/module/script_handle.cc +107 -0
- package/dist/node_modules/isolated-vm/src/module/script_handle.h +37 -0
- package/dist/node_modules/isolated-vm/src/module/session_handle.cc +173 -0
- package/dist/node_modules/isolated-vm/src/module/session_handle.h +31 -0
- package/dist/node_modules/isolated-vm/src/module/transferable.cc +268 -0
- package/dist/node_modules/isolated-vm/src/module/transferable.h +42 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.0.0.h +360 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v18.3.0.h +376 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v20.0.0.h +397 -0
- package/dist/node_modules/isolated-vm/vendor/v8_inspector/nodejs_v22.0.0.h +419 -0
- package/dist/node_modules/winston-transport/package.json +1 -1
- package/dist/server/IsolatedVm.js +75 -0
- package/dist/server/ScriptInstruction.d.ts +6 -0
- package/dist/server/ScriptInstruction.js +11 -1
- package/dist/server/Vm.js +42 -27
- package/package.json +3 -2
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#include "allocator.h"
|
|
2
|
+
#include "environment.h"
|
|
3
|
+
#include <cstdlib>
|
|
4
|
+
|
|
5
|
+
using namespace v8;
|
|
6
|
+
|
|
7
|
+
namespace ivm {
|
|
8
|
+
namespace {
|
|
9
|
+
|
|
10
|
+
class ExternalMemoryHandle {
|
|
11
|
+
public:
|
|
12
|
+
ExternalMemoryHandle(Local<Object> local_handle, size_t size) :
|
|
13
|
+
handle{Isolate::GetCurrent(), local_handle}, size{size} {
|
|
14
|
+
handle.SetWeak(reinterpret_cast<void*>(this), &WeakCallbackV8, WeakCallbackType::kParameter);
|
|
15
|
+
IsolateEnvironment::GetCurrent().AddWeakCallback(&handle, WeakCallback, this);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
ExternalMemoryHandle(const ExternalMemoryHandle&) = delete;
|
|
19
|
+
auto operator=(const ExternalMemoryHandle&) = delete;
|
|
20
|
+
|
|
21
|
+
~ExternalMemoryHandle() {
|
|
22
|
+
auto* allocator = IsolateEnvironment::GetCurrent().GetLimitedAllocator();
|
|
23
|
+
if (allocator != nullptr) {
|
|
24
|
+
allocator->AdjustAllocatedSize(-static_cast<ptrdiff_t>(size));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
private:
|
|
29
|
+
static void WeakCallbackV8(const WeakCallbackInfo<void>& info) {
|
|
30
|
+
WeakCallback(info.GetParameter());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static void WeakCallback(void* param) {
|
|
34
|
+
auto* that = reinterpret_cast<ExternalMemoryHandle*>(param);
|
|
35
|
+
IsolateEnvironment::GetCurrent().RemoveWeakCallback(&that->handle);
|
|
36
|
+
that->handle.Reset();
|
|
37
|
+
delete that;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
v8::Persistent<v8::Value> handle;
|
|
41
|
+
size_t size;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
} // anonymous namespace
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* ArrayBuffer::Allocator that enforces memory limits. The v8 documentation specifically says
|
|
48
|
+
* that it's unsafe to call back into v8 from this class but I took a look at
|
|
49
|
+
* GetHeapStatistics() and I think it'll be ok.
|
|
50
|
+
*/
|
|
51
|
+
auto LimitedAllocator::Check(const size_t length) -> bool {
|
|
52
|
+
if (v8_heap + env.extra_allocated_memory + length > next_check) {
|
|
53
|
+
HeapStatistics heap_statistics;
|
|
54
|
+
Isolate* isolate = Isolate::GetCurrent();
|
|
55
|
+
isolate->GetHeapStatistics(&heap_statistics);
|
|
56
|
+
v8_heap = heap_statistics.used_heap_size();
|
|
57
|
+
if (v8_heap + env.extra_allocated_memory + length > limit + env.misc_memory_size) {
|
|
58
|
+
// This is might be dangerous but the tests pass soooo..
|
|
59
|
+
isolate->LowMemoryNotification();
|
|
60
|
+
isolate->GetHeapStatistics(&heap_statistics);
|
|
61
|
+
v8_heap = heap_statistics.used_heap_size();
|
|
62
|
+
if (v8_heap + env.extra_allocated_memory + length > limit + env.misc_memory_size) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
next_check = v8_heap + env.extra_allocated_memory + length + 1024 * 1024;
|
|
67
|
+
}
|
|
68
|
+
return v8_heap + env.extra_allocated_memory + length <= limit + env.misc_memory_size;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
LimitedAllocator::LimitedAllocator(IsolateEnvironment& env, size_t limit) : env(env), limit(limit), v8_heap(1024 * 1024 * 4), next_check(1024 * 1024) {}
|
|
72
|
+
|
|
73
|
+
auto LimitedAllocator::Allocate(size_t length) -> void* {
|
|
74
|
+
if (Check(length)) {
|
|
75
|
+
env.extra_allocated_memory += length;
|
|
76
|
+
return std::calloc(length, 1);
|
|
77
|
+
} else {
|
|
78
|
+
++failures;
|
|
79
|
+
if (length <= 64) { // kMinAddedElementsCapacity * sizeof(uint32_t)
|
|
80
|
+
// When a tiny TypedArray is created v8 will avoid calling the allocator and instead just use
|
|
81
|
+
// the internal heap. This is all fine until someone wants a pointer to the underlying buffer,
|
|
82
|
+
// in that case v8 will "materialize" an ArrayBuffer which does invoke this allocator. If the
|
|
83
|
+
// allocator refuses to return a valid pointer it will result in a hard crash so we have no
|
|
84
|
+
// choice but to let this allocation succeed. Luckily the amount of memory allocated is tiny
|
|
85
|
+
// and will soon be freed because at the same time we terminate the isolate.
|
|
86
|
+
env.extra_allocated_memory += length;
|
|
87
|
+
env.Terminate();
|
|
88
|
+
return std::calloc(length, 1);
|
|
89
|
+
} else {
|
|
90
|
+
// The places end up here are more graceful and will throw a RangeError
|
|
91
|
+
return nullptr;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
auto LimitedAllocator::AllocateUninitialized(size_t length) -> void* {
|
|
97
|
+
if (Check(length)) {
|
|
98
|
+
env.extra_allocated_memory += length;
|
|
99
|
+
return std::malloc(length);
|
|
100
|
+
} else {
|
|
101
|
+
++failures;
|
|
102
|
+
if (length <= 64) {
|
|
103
|
+
env.extra_allocated_memory += length;
|
|
104
|
+
env.Terminate();
|
|
105
|
+
return std::malloc(length);
|
|
106
|
+
} else {
|
|
107
|
+
return nullptr;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
void LimitedAllocator::Free(void* data, size_t length) {
|
|
113
|
+
env.extra_allocated_memory -= length;
|
|
114
|
+
next_check -= length;
|
|
115
|
+
std::free(data);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
auto LimitedAllocator::Reallocate(void* data, size_t old_length, size_t new_length) -> void* {
|
|
119
|
+
auto delta = static_cast<ssize_t>(new_length) - static_cast<ssize_t>(old_length);
|
|
120
|
+
if (delta > 0) {
|
|
121
|
+
if (!Check(delta)) {
|
|
122
|
+
return nullptr;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
env.extra_allocated_memory += delta;
|
|
126
|
+
return ArrayBuffer::Allocator::Reallocate(data, old_length, new_length);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void LimitedAllocator::AdjustAllocatedSize(ptrdiff_t length) {
|
|
130
|
+
env.extra_allocated_memory += length;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
auto LimitedAllocator::GetFailureCount() const -> int {
|
|
134
|
+
return failures;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
void LimitedAllocator::Track(Local<Object> handle, size_t size) {
|
|
138
|
+
new ExternalMemoryHandle{handle, size};
|
|
139
|
+
AdjustAllocatedSize(size);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
} // namespace ivm
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#include <v8.h>
|
|
3
|
+
#include "environment.h"
|
|
4
|
+
#include "functor_runners.h"
|
|
5
|
+
#include "util.h"
|
|
6
|
+
#include "generic/callbacks.h"
|
|
7
|
+
#include "generic/extract_params.h"
|
|
8
|
+
#include "generic/handle_cast.h"
|
|
9
|
+
#include "generic/read_option.h"
|
|
10
|
+
#include "v8-function-callback.h"
|
|
11
|
+
|
|
12
|
+
#include <cassert>
|
|
13
|
+
#include <cstddef>
|
|
14
|
+
#include <type_traits>
|
|
15
|
+
#include <stdexcept>
|
|
16
|
+
|
|
17
|
+
namespace ivm {
|
|
18
|
+
|
|
19
|
+
namespace detail {
|
|
20
|
+
struct ConstructorFunctionHolder : detail::FreeFunctionHolder {
|
|
21
|
+
using FreeFunctionHolder::FreeFunctionHolder;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
template <class Signature>
|
|
25
|
+
struct ConstructorFunctionImpl;
|
|
26
|
+
|
|
27
|
+
template <class Type>
|
|
28
|
+
struct TemplateHolder {
|
|
29
|
+
static IsolateSpecific<v8::FunctionTemplate> specific;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
template <class Type>
|
|
33
|
+
IsolateSpecific<v8::FunctionTemplate> TemplateHolder<Type>::specific;
|
|
34
|
+
|
|
35
|
+
template <class Type, class = void>
|
|
36
|
+
struct DontFreezePrototype : std::false_type{};
|
|
37
|
+
|
|
38
|
+
template <class Type>
|
|
39
|
+
struct DontFreezePrototype<Type, typename Type::DontFreezePrototype> : std::true_type {};
|
|
40
|
+
|
|
41
|
+
template <class Type, class = void>
|
|
42
|
+
struct DontFreezeInstance : std::false_type{};
|
|
43
|
+
|
|
44
|
+
template <class Type>
|
|
45
|
+
struct DontFreezeInstance<Type, typename Type::DontFreezeInstance> : std::true_type {};
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Analogous to node::ObjectWrap but Isolate-aware. Also has a bunch of unnecessary template stuff.
|
|
51
|
+
*/
|
|
52
|
+
class ClassHandle {
|
|
53
|
+
template <class Signature>
|
|
54
|
+
friend struct detail::ConstructorFunctionImpl;
|
|
55
|
+
private:
|
|
56
|
+
v8::Persistent<v8::Value> handle;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Utility methods to set up object prototype
|
|
60
|
+
*/
|
|
61
|
+
struct TemplateDefinition {
|
|
62
|
+
v8::Isolate* isolate;
|
|
63
|
+
v8::Local<v8::FunctionTemplate>& tmpl;
|
|
64
|
+
v8::Local<v8::ObjectTemplate>& proto;
|
|
65
|
+
v8::Local<v8::Signature>& sig;
|
|
66
|
+
|
|
67
|
+
// This add regular methods
|
|
68
|
+
template <typename... Args>
|
|
69
|
+
void Add(const char* name, detail::MemberFunctionHolder impl, Args... args) {
|
|
70
|
+
v8::Local<v8::String> name_handle = v8_symbol(name);
|
|
71
|
+
proto->Set(name_handle, v8::FunctionTemplate::New(isolate, impl.callback, {}, sig, impl.length));
|
|
72
|
+
Add(args...);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// This adds function templates to the prototype
|
|
76
|
+
template <typename... Args>
|
|
77
|
+
void Add(const char* name, v8::Local<v8::FunctionTemplate>& value, Args... args) {
|
|
78
|
+
v8::Local<v8::String> name_handle = v8_symbol(name);
|
|
79
|
+
proto->Set(name_handle, value);
|
|
80
|
+
Add(args...);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// This adds static functions on the object constructor
|
|
84
|
+
template <typename... Args>
|
|
85
|
+
void Add(const char* name, detail::FreeFunctionHolder impl, Args... args) {
|
|
86
|
+
v8::Local<v8::String> name_handle = v8_symbol(name);
|
|
87
|
+
tmpl->Set(name_handle, v8::FunctionTemplate::New(isolate, impl.callback, {}, v8::Local<v8::Signature>(), impl.length));
|
|
88
|
+
Add(args...);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// This adds accessors
|
|
92
|
+
template <typename... Args>
|
|
93
|
+
void Add(const char* name, detail::MemberAccessorHolder impl, Args... args) {
|
|
94
|
+
v8::Local<v8::String> name_handle = v8_symbol(name);
|
|
95
|
+
proto->SetAccessor(name_handle, impl.getter.callback, impl.setter.callback);
|
|
96
|
+
Add(args...);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// This adds static accessors
|
|
100
|
+
template <typename... Args>
|
|
101
|
+
void Add(const char* name, detail::StaticAccessorHolder impl, Args... args) {
|
|
102
|
+
v8::Local<v8::String> name_handle = v8_symbol(name);
|
|
103
|
+
v8::Local<v8::FunctionTemplate> setter;
|
|
104
|
+
if (impl.setter.callback != nullptr) {
|
|
105
|
+
setter = v8::FunctionTemplate::New(isolate, impl.setter.callback, name_handle);
|
|
106
|
+
}
|
|
107
|
+
tmpl->SetAccessorProperty(name_handle, v8::FunctionTemplate::New(isolate, impl.getter.callback, {}), setter);
|
|
108
|
+
Add(args...);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
void Add() {} // NOLINT
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Convenience wrapper for the obtuse SetWeak function signature. When the callback is called
|
|
116
|
+
* the handle will already be gone.
|
|
117
|
+
*/
|
|
118
|
+
template <typename P, void (*F)(P*)>
|
|
119
|
+
void SetWeak(P* param) {
|
|
120
|
+
auto& isolate = IsolateEnvironment::GetCurrent();
|
|
121
|
+
isolate.AddWeakCallback(&this->handle, (void(*)(void*))F, param);
|
|
122
|
+
handle.SetWeak(param, WeakCallback<P, F>, v8::WeakCallbackType::kParameter);
|
|
123
|
+
}
|
|
124
|
+
template <typename P, void (*F)(P*)>
|
|
125
|
+
static void WeakCallback(const v8::WeakCallbackInfo<P>& info) {
|
|
126
|
+
F(info.GetParameter());
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Invoked once JS loses all references to this object
|
|
131
|
+
*/
|
|
132
|
+
static void WeakCallback(void* param) {
|
|
133
|
+
auto& isolate = IsolateEnvironment::GetCurrent();
|
|
134
|
+
auto* that = reinterpret_cast<ClassHandle*>(param);
|
|
135
|
+
isolate.RemoveWeakCallback(&that->handle);
|
|
136
|
+
delete that; // NOLINT
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Transfer ownership of this C++ pointer to the v8 handle lifetime.
|
|
141
|
+
*/
|
|
142
|
+
static void Wrap(std::unique_ptr<ClassHandle> ptr, v8::Local<v8::Object> handle) {
|
|
143
|
+
handle->SetAlignedPointerInInternalField(0, ptr.get());
|
|
144
|
+
ptr->handle.Reset(v8::Isolate::GetCurrent(), handle);
|
|
145
|
+
ClassHandle* ptr_raw = ptr.release();
|
|
146
|
+
ptr_raw->SetWeak<void, WeakCallback>(ptr_raw);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* It just throws when you call it; used when `nullptr` is passed as constructor
|
|
151
|
+
*/
|
|
152
|
+
static void PrivateConstructor(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
153
|
+
throw RuntimeTypeError(detail::CalleeName(info)+ " constructor is private");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected:
|
|
157
|
+
/**
|
|
158
|
+
* Sets up this object's FunctionTemplate inside the current isolate
|
|
159
|
+
*/
|
|
160
|
+
template <typename... Args>
|
|
161
|
+
static auto MakeClass(const char* class_name, detail::ConstructorFunctionHolder New, Args... args) -> v8::Local<v8::FunctionTemplate> {
|
|
162
|
+
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
163
|
+
v8::Local<v8::String> name_handle = v8_symbol(class_name);
|
|
164
|
+
v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
|
|
165
|
+
isolate, New.callback == nullptr ? PrivateConstructor : New.callback,
|
|
166
|
+
name_handle, {}, New.length
|
|
167
|
+
);
|
|
168
|
+
tmpl->SetClassName(name_handle);
|
|
169
|
+
|
|
170
|
+
auto instance_tmpl = tmpl->InstanceTemplate();
|
|
171
|
+
instance_tmpl->SetImmutableProto();
|
|
172
|
+
instance_tmpl->SetInternalFieldCount(1);
|
|
173
|
+
|
|
174
|
+
auto proto = tmpl->PrototypeTemplate();
|
|
175
|
+
proto->SetImmutableProto();
|
|
176
|
+
v8::Local<v8::Signature> sig = v8::Signature::New(isolate, tmpl);
|
|
177
|
+
TemplateDefinition def{isolate, tmpl, proto, sig};
|
|
178
|
+
def.Add(args...);
|
|
179
|
+
|
|
180
|
+
return tmpl;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Inherit from another class's FunctionTemplate
|
|
185
|
+
*/
|
|
186
|
+
template <typename T>
|
|
187
|
+
static auto Inherit(v8::Local<v8::FunctionTemplate> definition) -> v8::Local<v8::FunctionTemplate> {
|
|
188
|
+
v8::Local<v8::FunctionTemplate> parent = GetFunctionTemplate<T>();
|
|
189
|
+
definition->Inherit(parent);
|
|
190
|
+
return definition;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public:
|
|
194
|
+
ClassHandle() = default;
|
|
195
|
+
ClassHandle(const ClassHandle&) = delete;
|
|
196
|
+
auto operator= (const ClassHandle&) -> ClassHandle& = delete;
|
|
197
|
+
virtual ~ClassHandle() {
|
|
198
|
+
if (!handle.IsEmpty()) {
|
|
199
|
+
handle.ClearWeak();
|
|
200
|
+
handle.Reset();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Returns instance of this class for this context.
|
|
206
|
+
*/
|
|
207
|
+
template <typename T>
|
|
208
|
+
static auto Init() -> v8::Local<v8::Function> {
|
|
209
|
+
return GetFunctionTemplate<T>()->GetFunction();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Returns the FunctionTemplate for this isolate, generating it if needed.
|
|
214
|
+
*/
|
|
215
|
+
template <class Type>
|
|
216
|
+
static auto GetFunctionTemplate() -> v8::Local<v8::FunctionTemplate> {
|
|
217
|
+
return detail::TemplateHolder<Type>::specific.Deref([&]() {
|
|
218
|
+
return Type::Definition();
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Freeze the handle and prototype unless `DontFreezeInstance` is set on the class handle
|
|
224
|
+
*/
|
|
225
|
+
template <class Type>
|
|
226
|
+
static auto MaybeFreeze(v8::Local<v8::Object> handle) {
|
|
227
|
+
auto context = handle->GetIsolate()->GetCurrentContext();
|
|
228
|
+
if (!detail::DontFreezePrototype<Type>::value) {
|
|
229
|
+
handle->GetPrototype().As<v8::Object>()->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
|
|
230
|
+
}
|
|
231
|
+
if (!detail::DontFreezeInstance<Type>::value) {
|
|
232
|
+
handle->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Builds a new instance of T from scratch, used in factory functions.
|
|
238
|
+
*/
|
|
239
|
+
template <typename T, typename ...Args>
|
|
240
|
+
static auto NewInstance(Args&&... args) -> v8::Local<v8::Object> {
|
|
241
|
+
auto context = v8::Isolate::GetCurrent()->GetCurrentContext();
|
|
242
|
+
v8::Local<v8::Object> instance = Unmaybe(GetFunctionTemplate<T>()->InstanceTemplate()->NewInstance(context));
|
|
243
|
+
MaybeFreeze<T>(instance);
|
|
244
|
+
Wrap(std::make_unique<T>(std::forward<Args>(args)...), instance);
|
|
245
|
+
return instance;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Pull out native pointer from v8 handle
|
|
250
|
+
*/
|
|
251
|
+
template <typename T>
|
|
252
|
+
static auto Unwrap(v8::Local<v8::Object> handle) -> T* {
|
|
253
|
+
assert(!handle.IsEmpty());
|
|
254
|
+
if (!ClassHandle::GetFunctionTemplate<T>()->HasInstance(handle)) {
|
|
255
|
+
return nullptr;
|
|
256
|
+
}
|
|
257
|
+
assert(handle->InternalFieldCount() > 0);
|
|
258
|
+
return dynamic_cast<T*>(static_cast<ClassHandle*>(handle->GetAlignedPointerFromInternalField(0)));
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Returns the JS value that this ClassHandle points to
|
|
263
|
+
*/
|
|
264
|
+
auto This() -> v8::Local<v8::Object> {
|
|
265
|
+
return Deref(handle).As<v8::Object>();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Conversions from v8::Value -> Type&
|
|
270
|
+
template <class Type>
|
|
271
|
+
inline auto HandleCastImpl(v8::Local<v8::Value> value, const HandleCastArguments& /*arguments*/, HandleCastTag<Type&> /*tag*/) -> Type& {
|
|
272
|
+
if (!value->IsObject()) {
|
|
273
|
+
throw ParamIncorrect("an object");
|
|
274
|
+
}
|
|
275
|
+
v8::Local<v8::Object> handle = value.As<v8::Object>();
|
|
276
|
+
if (handle->InternalFieldCount() != 1) {
|
|
277
|
+
throw ParamIncorrect("something else");
|
|
278
|
+
}
|
|
279
|
+
auto* ptr = ClassHandle::Unwrap<Type>(handle);
|
|
280
|
+
if (ptr == nullptr) {
|
|
281
|
+
throw ParamIncorrect("something else");
|
|
282
|
+
} else {
|
|
283
|
+
return *ptr;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
namespace detail {
|
|
288
|
+
|
|
289
|
+
template <class Signature>
|
|
290
|
+
struct ConstructorFunctionImpl;
|
|
291
|
+
|
|
292
|
+
template <class Signature>
|
|
293
|
+
struct ConstructorFunctionImpl<Signature*> : ConstructorFunctionImpl<Signature> {};
|
|
294
|
+
|
|
295
|
+
template <class Return, class ...Args>
|
|
296
|
+
struct ConstructorFunctionImpl<Return(Args...)> {
|
|
297
|
+
using Type = v8::Local<v8::Value>(*)(v8::Local<v8::Value> This, Args... args);
|
|
298
|
+
|
|
299
|
+
template <Return(*Function)(Args...)>
|
|
300
|
+
static inline auto Invoke(v8::Local<v8::Value> This, Args... args) -> v8::Local<v8::Value> {
|
|
301
|
+
auto instance = Function(args...);
|
|
302
|
+
if (instance) {
|
|
303
|
+
v8::Local<v8::Object> handle = This.As<v8::Object>();
|
|
304
|
+
ClassHandle::MaybeFreeze<typename decltype(instance)::element_type>(handle);
|
|
305
|
+
ClassHandle::Wrap(std::move(instance), handle);
|
|
306
|
+
return handle;
|
|
307
|
+
} else {
|
|
308
|
+
return Undefined(v8::Isolate::GetCurrent());
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
} // namespace detail
|
|
314
|
+
|
|
315
|
+
template <class Signature, Signature Function>
|
|
316
|
+
struct ConstructorFunction : detail::ConstructorFunctionHolder {
|
|
317
|
+
using ConstructorFunctionHolder::ConstructorFunctionHolder;
|
|
318
|
+
ConstructorFunction() : ConstructorFunctionHolder{
|
|
319
|
+
Entry,
|
|
320
|
+
std::tuple_size<typename detail::extract_arguments<Signature>::arguments>::value} {}
|
|
321
|
+
|
|
322
|
+
static void Entry(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
|
323
|
+
detail::RunBarrier([&]() {
|
|
324
|
+
if (!info.IsConstructCall()) {
|
|
325
|
+
throw RuntimeTypeError(detail::CalleeName(info)+ " must be called with `new`");
|
|
326
|
+
}
|
|
327
|
+
ToCallback<-1, typename Impl::Type, &Impl::template Invoke<Function>>()(info);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
using Impl = detail::ConstructorFunctionImpl<Signature>;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
} // namespace ivm
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#include "cpu_profile_manager.h"
|
|
2
|
+
#include "environment.h"
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
#include <climits>
|
|
5
|
+
#include <cmath>
|
|
6
|
+
#include <cstdio>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <memory>
|
|
9
|
+
#include <mutex>
|
|
10
|
+
#include <thread>
|
|
11
|
+
#include <vector>
|
|
12
|
+
#include "isolate/strings.h"
|
|
13
|
+
#include "v8-platform.h"
|
|
14
|
+
#include "v8.h"
|
|
15
|
+
|
|
16
|
+
using namespace v8;
|
|
17
|
+
|
|
18
|
+
namespace ivm {
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* CpuProfileManagerImpl
|
|
22
|
+
*/
|
|
23
|
+
IVMCpuProfile::IVMCpuProfile(const std::shared_ptr<v8::CpuProfile>& cpuProfile):
|
|
24
|
+
profile_thread(std::this_thread::get_id()),
|
|
25
|
+
start_time(cpuProfile->GetStartTime()),
|
|
26
|
+
end_time(cpuProfile->GetEndTime()),
|
|
27
|
+
samples_count(cpuProfile->GetSamplesCount()) {
|
|
28
|
+
|
|
29
|
+
samples.reserve(samples_count);
|
|
30
|
+
timestamps.reserve(samples_count);
|
|
31
|
+
for (int64_t i = 0; i < samples_count; i++) {
|
|
32
|
+
const int idx = static_cast<int>(i);
|
|
33
|
+
samples.push_back(cpuProfile->GetSample(idx)->GetNodeId());
|
|
34
|
+
timestamps.push_back(cpuProfile->GetSampleTimestamp(idx));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// handle nodes
|
|
38
|
+
FlatNodes(cpuProfile->GetTopDownRoot(), &profileNodes);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
auto IVMCpuProfile::GetTidValue(Isolate *iso) -> Local<Value>{
|
|
42
|
+
const auto tid = std::hash<std::thread::id>{}(profile_thread);
|
|
43
|
+
return Number::New(iso, static_cast<double>(tid));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
auto IVMCpuProfile::BuildCpuProfile(Isolate *iso) -> Local<Value> {
|
|
47
|
+
auto& strings = StringTable::Get();
|
|
48
|
+
auto context = iso->GetCurrentContext();
|
|
49
|
+
auto profileObject = Object::New(iso);
|
|
50
|
+
|
|
51
|
+
Unmaybe(profileObject->Set(context, strings.startTime, Number::New(iso, static_cast<double>(start_time))));
|
|
52
|
+
Unmaybe(profileObject->Set(context, strings.endTime, Number::New(iso, static_cast<double>(end_time))));
|
|
53
|
+
|
|
54
|
+
const Local<Array> samplesArr = Array::New(iso);
|
|
55
|
+
const Local<Array> timeDeltasArr = Array::New(iso);
|
|
56
|
+
|
|
57
|
+
for (int64_t i = 0; i < samples_count; i++) {
|
|
58
|
+
Unmaybe(samplesArr->Set(context, i, Number::New(iso, static_cast<double>(samples[i]))));
|
|
59
|
+
Unmaybe(timeDeltasArr->Set(context, i, Number::New(iso, static_cast<double>(timestamps[i] - start_time))));
|
|
60
|
+
}
|
|
61
|
+
Unmaybe(profileObject->Set(context, strings.samples, samplesArr));
|
|
62
|
+
Unmaybe(profileObject->Set(context, strings.timeDeltas, timeDeltasArr));
|
|
63
|
+
|
|
64
|
+
const Local<Array> nodeArr = Array::New(iso, static_cast<int>(profileNodes.size()));
|
|
65
|
+
Unmaybe(profileObject->Set(context, strings.nodes, nodeArr));
|
|
66
|
+
|
|
67
|
+
const size_t count = profileNodes.size();
|
|
68
|
+
for (size_t i = 0; i < count; i++) {
|
|
69
|
+
ProfileNode node = profileNodes[i];
|
|
70
|
+
Unmaybe(nodeArr->Set(context, i, node.ToJSObject(iso)));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return profileObject;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
auto IVMCpuProfile::ToJSObject(Isolate *iso) -> Local<Value> {
|
|
77
|
+
auto& strings = StringTable::Get();
|
|
78
|
+
auto context = iso->GetCurrentContext();
|
|
79
|
+
auto result = Object::New(iso);
|
|
80
|
+
|
|
81
|
+
Unmaybe(result->Set(context, (v8::Local<v8::Value>)strings.threadId, GetTidValue(iso)));
|
|
82
|
+
Unmaybe(result->Set(context, strings.profile, BuildCpuProfile(iso)));
|
|
83
|
+
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
IVMCpuProfile::CallFrame::CallFrame(const v8::CpuProfileNode* node):
|
|
88
|
+
function_name(node->GetFunctionNameStr()),
|
|
89
|
+
url(node->GetScriptResourceNameStr()),
|
|
90
|
+
script_id(node->GetScriptId()),
|
|
91
|
+
line_number(node->GetLineNumber()),
|
|
92
|
+
column_number(node->GetColumnNumber()) {}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
auto IVMCpuProfile::CallFrame::ToJSObject(v8::Isolate *iso) -> Local<Value> {
|
|
96
|
+
auto& strings = StringTable::Get();
|
|
97
|
+
Local<Object> callFrame = Object::New(iso);
|
|
98
|
+
const Local<Context> context = iso->GetCurrentContext();
|
|
99
|
+
Unmaybe(callFrame->Set(context, strings.functionName, String::NewFromUtf8(iso, function_name).ToLocalChecked()));
|
|
100
|
+
Unmaybe(callFrame->Set(context, strings.url, String::NewFromUtf8(iso, url).ToLocalChecked()));
|
|
101
|
+
Unmaybe(callFrame->Set(context, strings.scriptId, Number::New(iso, script_id)));
|
|
102
|
+
Unmaybe(callFrame->Set(context, strings.lineNumber, Number::New(iso, line_number)));
|
|
103
|
+
Unmaybe(callFrame->Set(context, strings.columnNumber, Number::New(iso, column_number)));
|
|
104
|
+
return callFrame;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
IVMCpuProfile::ProfileNode::ProfileNode(const v8::CpuProfileNode* node):
|
|
108
|
+
hit_count(node->GetHitCount()),
|
|
109
|
+
node_id(node->GetNodeId()),
|
|
110
|
+
bailout_reason(node->GetBailoutReason()),
|
|
111
|
+
call_frame(node) {
|
|
112
|
+
const int childrenCount = node->GetChildrenCount();
|
|
113
|
+
children.reserve(childrenCount);
|
|
114
|
+
for (int i = 0; i < childrenCount; i++) {
|
|
115
|
+
children.push_back(node->GetChild(i)->GetNodeId());
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
auto IVMCpuProfile::ProfileNode::ToJSObject(Isolate *iso) -> Local<Value> {
|
|
120
|
+
auto& strings = StringTable::Get();
|
|
121
|
+
auto context = iso->GetCurrentContext();
|
|
122
|
+
|
|
123
|
+
Local<Object> nodeObj = Object::New(iso);
|
|
124
|
+
|
|
125
|
+
Unmaybe(nodeObj->Set(context, strings.hitCount, Number::New(iso, hit_count)));
|
|
126
|
+
Unmaybe(nodeObj->Set(context, strings.id, Number::New(iso, node_id)));
|
|
127
|
+
|
|
128
|
+
if (strlen(bailout_reason) > 0) {
|
|
129
|
+
Unmaybe(nodeObj->Set(context, strings.bailoutReason, String::NewFromUtf8(iso, bailout_reason).ToLocalChecked()));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const int children_count = static_cast<int>(children.size());
|
|
133
|
+
const Local<Array> childrenArr = Array::New(iso, children_count);
|
|
134
|
+
Unmaybe(nodeObj->Set(context, strings.children, childrenArr));
|
|
135
|
+
|
|
136
|
+
for (int j = 0; j < children_count; j++) {
|
|
137
|
+
Unmaybe(childrenArr->Set(context, j, Number::New(iso, static_cast<double>(children[j]))));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const Local<Value> callFrame = call_frame.ToJSObject(iso);
|
|
141
|
+
Unmaybe(nodeObj->Set(context, strings.callFrame, callFrame));
|
|
142
|
+
|
|
143
|
+
return nodeObj;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
CpuProfileManager::CpuProfileManager() = default;
|
|
147
|
+
|
|
148
|
+
void CpuProfileManager::StartProfiling(const char* title) {
|
|
149
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
150
|
+
std::string str(title);
|
|
151
|
+
profile_titles.insert(str);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
auto CpuProfileManager::StopProfiling(const char* title) -> std::vector<IVMCpuProfile> {
|
|
155
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
156
|
+
const std::string str_title(title);
|
|
157
|
+
|
|
158
|
+
std::vector<IVMCpuProfile> currently_collected;
|
|
159
|
+
|
|
160
|
+
if (profile_begin_ptrs.find(str_title) == profile_begin_ptrs.end()) {
|
|
161
|
+
return currently_collected;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
auto iter = profile_begin_ptrs[str_title];
|
|
165
|
+
|
|
166
|
+
// Iterate using an iterator
|
|
167
|
+
for (auto it = iter; it != profile_items.end(); ++it) {
|
|
168
|
+
currently_collected.push_back(*it);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// remove the titles
|
|
172
|
+
profile_titles.erase(str_title);
|
|
173
|
+
// remove the profile ptr from map
|
|
174
|
+
profile_begin_ptrs.erase(str_title);
|
|
175
|
+
|
|
176
|
+
// TODO clean up old profiles, extract private method
|
|
177
|
+
CleanProfiles();
|
|
178
|
+
|
|
179
|
+
return currently_collected;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
auto CpuProfileManager::IsProfiling() -> bool {
|
|
183
|
+
return !profile_titles.empty();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
auto CpuProfileManager::InjestCpuProfile(const std::shared_ptr<v8::CpuProfile>& cpuProfile) -> void {
|
|
187
|
+
const std::lock_guard<std::mutex> lock(mutex);
|
|
188
|
+
profile_items.emplace_back(cpuProfile);
|
|
189
|
+
|
|
190
|
+
for(const std::string &title : profile_titles) {
|
|
191
|
+
if (profile_begin_ptrs.find(title) == profile_begin_ptrs.end()) {
|
|
192
|
+
profile_begin_ptrs[title] = std::prev(profile_items.end());
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
void CpuProfileManager::CleanProfiles() {
|
|
198
|
+
if (profile_titles.empty()) {
|
|
199
|
+
profile_items.clear();
|
|
200
|
+
profile_begin_ptrs.clear();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
int64_t min_start_time = INT_MAX;
|
|
204
|
+
|
|
205
|
+
for (const auto& profile_item: profile_begin_ptrs) {
|
|
206
|
+
if (profile_item.second->GetStartTime() < min_start_time) {
|
|
207
|
+
min_start_time = profile_item.second->GetStartTime();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
for (auto it = profile_items.begin(); it != profile_items.end(); ) {
|
|
212
|
+
if (it->GetStartTime() < min_start_time) {
|
|
213
|
+
it = profile_items.erase(it);
|
|
214
|
+
} else {
|
|
215
|
+
++it;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
}
|