}],
],
},
- 'cflags': [ '-O3' ],
+ 'cflags': [ '-Os' ],
'conditions': [
['OS=="linux"', {
'conditions': [
# pass the leftover positional arguments to GYP
gyp_args += args
# @lwnode
-gyp_args += get_lwnode_gyp_options()
+if options.without_bundled_v8:
+ gyp_args += get_lwnode_gyp_options()
+else:
+ gyp_args += ['-Dlwnode='+ 'false']
# @end of lwnode
if warn.warned and not options.verbose:
'build_asan%': '0',
},
'target_defaults': {
- 'defines': [],
+ 'defines': [ 'LWNODE=1' ],
'cflags!': [ '-Wno-error' ],
'cflags': [
- '-Wall', '-Wextra', '-Werror',
+ '-Wall', '-Wextra', '-Werror', '-ggdb',
'-Wno-unused-variable',
'-Wno-unused-function',
'-Wno-unused-but-set-variable',
},
'configurations': {
'Debug': {
- 'cflags': [ '-g', '-O0', '-Werror' ],
+ 'cflags': [ '-O0' ],
},
'Release': {
'defines': ['NDEBUG'],
- 'cflags': [ '-Wfatal-errors' ],
+ 'cflags': [ '-Wfatal-errors', '-Os' ],
},
},
'conditions': [
'-Wl,-z,relro,-z,now',
],
'cflags': [
- '-g', '-O0',
+ '-ggdb', '-Os',
'-fPIC', '-fPIE',
'-fstack-protector-strong',
'-D_FORTIFY_SOURCE=2',
return toEvaluatorResultRef(result);
}
+COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::Init == (int)VMInstance::PromiseHookType::Init, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::Resolve == (int)VMInstance::PromiseHookType::Resolve, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::Before == (int)VMInstance::PromiseHookType::Before, "");
+COMPILE_ASSERT((int)VMInstanceRef::PromiseHookType::After == (int)VMInstance::PromiseHookType::After, "");
+
PersistentRefHolder<VMInstanceRef> VMInstanceRef::create(PlatformRef* platform, const char* locale, const char* timezone, const char* baseCacheDir)
{
return PersistentRefHolder<VMInstanceRef>(toRef(new VMInstance(new PlatformBridge(platform), locale, timezone, baseCacheDir)));
PromiseObjectRef* PromiseObjectRef::create(ExecutionStateRef* state)
{
- return toRef(new PromiseObject(*toImpl(state)));
+ auto promise = new PromiseObject(*toImpl(state));
+ if (UNLIKELY(toImpl(state)->context()->vmInstance()->isPromiseHookRegistered())) {
+ toImpl(state)->context()->vmInstance()->triggerPromiseHook(*toImpl(state), VMInstance::PromiseHookType::Init, promise);
+ }
+ return toRef(promise);
}
COMPILE_ASSERT((int)PromiseObjectRef::PromiseState::Pending == (int)PromiseObject::PromiseState::Pending, "");
void PromiseObjectRef::fulfill(ExecutionStateRef* state, ValueRef* value)
{
+ if (UNLIKELY(toImpl(state)->context()->vmInstance()->isPromiseHookRegistered())) {
+ toImpl(state)->context()->vmInstance()->triggerPromiseHook(*toImpl(state), VMInstance::PromiseHookType::Resolve, toImpl(this));
+ }
toImpl(this)->fulfill(*toImpl(state), toImpl(value));
}
void PromiseObjectRef::reject(ExecutionStateRef* state, ValueRef* reason)
{
+ if (UNLIKELY(toImpl(state)->context()->vmInstance()->isPromiseHookRegistered())) {
+ toImpl(state)->context()->vmInstance()->triggerPromiseHook(*toImpl(state), VMInstance::PromiseHookType::Resolve, toImpl(this));
+ }
toImpl(this)->reject(*toImpl(state), toImpl(reason));
}
PromiseObject* promise = promiseValue.asObject()->asPromiseObject();
if (UNLIKELY(state.context()->vmInstance()->isPromiseHookRegistered())) {
- state.context()->vmInstance()->triggerPromiseHook(state, VMInstance::PromiseHookType::Resolve, promise, Value());
+ state.context()->vmInstance()->triggerPromiseHook(state, VMInstance::PromiseHookType::Resolve, promise);
}
// Let alreadyResolved be F.[[AlreadyResolved]].
PromiseObject* promise = promiseValue.asObject()->asPromiseObject();
if (UNLIKELY(state.context()->vmInstance()->isPromiseHookRegistered())) {
- state.context()->vmInstance()->triggerPromiseHook(state, VMInstance::PromiseHookType::Resolve, promise, Value());
+ state.context()->vmInstance()->triggerPromiseHook(state, VMInstance::PromiseHookType::Resolve, promise);
}
// Let alreadyResolved be F.[[AlreadyResolved]].
m_promiseHookPublic = nullptr;
}
- void triggerPromiseHook(ExecutionState& state, PromiseHookType type, PromiseObject* promise, const Value& parent)
+ void triggerPromiseHook(ExecutionState& state, PromiseHookType type, PromiseObject* promise, const Value& parent = Value())
{
ASSERT(!!m_promiseHook);
if (m_promiseHookPublic) {
CodeEventHandler();
CodeEventHandler(const CodeEventHandler&);
CodeEventHandler& operator=(const CodeEventHandler&);
- void* internal_listener_;
+ void* internal_listener_ = nullptr;
};
} // namespace v8
void operator delete(void*, size_t);
void operator delete[](void*, size_t);
- internal::Isolate* isolate_;
- internal::Address* prev_next_;
- internal::Address* prev_limit_;
+ internal::Isolate* isolate_ = nullptr;
+ internal::Address* prev_next_ = nullptr;
+ internal::Address* prev_limit_ = nullptr;
// Local::New uses CreateHandle with an Isolate* parameter.
template<class F> friend class Local;
void operator delete[](void*, size_t);
internal::Address* Escape(internal::Address* escape_value);
- internal::Address* escape_slot_;
+ internal::Address* escape_slot_ = nullptr;
};
/**
void operator delete(void*, size_t);
void operator delete[](void*, size_t);
- internal::Isolate* const isolate_;
- internal::Address* prev_limit_;
- int prev_sealed_level_;
+ internal::Isolate* const isolate_ = nullptr;
+ internal::Address* prev_limit_ = nullptr;
+ int prev_sealed_level_ = 0;
};
private:
struct PrivateData;
- PrivateData* private_;
+ PrivateData* private_ = nullptr;
};
/**
private:
struct PrivateData;
- PrivateData* private_;
+ PrivateData* private_ = nullptr;
};
private:
const char* name_;
size_t source_length_; // expected to initialize before source_
- String::ExternalOneByteStringResource* source_;
+ String::ExternalOneByteStringResource* source_ = nullptr;
int dep_count_;
const char** deps_;
bool auto_enable_;
bool does_zap_garbage_;
size_t number_of_native_contexts_;
size_t number_of_detached_contexts_;
- size_t total_global_handles_size_;
- size_t used_global_handles_size_;
+ size_t total_global_handles_size_ = 0;
+ size_t used_global_handles_size_ = 0;
friend class V8;
friend class Isolate;
private:
OnFailure on_failure_;
- void* internal_;
+ void* internal_ = nullptr;
};
const AllowJavascriptExecutionScope&) = delete;
private:
- void* internal_throws_;
- void* internal_assert_;
- void* internal_dump_;
+ void* internal_throws_ = nullptr;
+ void* internal_assert_ = nullptr;
+ void* internal_dump_ = nullptr;
};
/**
const SuppressMicrotaskExecutionScope&) = delete;
private:
- internal::Isolate* const isolate_;
- internal::MicrotaskQueue* const microtask_queue_;
- internal::Address previous_stack_height_;
+ internal::Isolate* const isolate_ = nullptr;
+ internal::MicrotaskQueue* const microtask_queue_ = nullptr;
+ internal::Address previous_stack_height_ = 0;
friend class internal::ThreadLocalTop;
};
SafeForTerminationScope& operator=(const SafeForTerminationScope&) = delete;
private:
- internal::Isolate* isolate_;
+ internal::Isolate* isolate_ = nullptr;
bool prev_value_;
};
size_t AddData(Local<Context> context, internal::Address object);
size_t AddData(internal::Address object);
- void* data_;
+ void* data_ = nullptr;
};
/**
TryCatch* next_;
void* exception_;
void* message_obj_;
- void* js_stack_comparable_address_;
+ void* js_stack_comparable_address_ = nullptr;
bool is_verbose_ : 1;
bool can_continue_ : 1;
bool capture_message_ : 1;
LWNODE_RETURN_LOCAL(Object);
}
+static inline std::string toStdStringWithoutException(ContextRef* context,
+ ValueRef* esValue) {
+ return esValue->toStringWithoutException(context)->toStdUTF8String();
+}
+
MaybeLocal<v8::Value> Function::Call(Local<Context> context,
v8::Local<v8::Value> recv,
int argc,
API_ENTER_WITH_CONTEXT(context, MaybeLocal<Value>());
LWNODE_CHECK(CVAL(this)->value()->isCallable());
- auto lwContext = VAL(*context)->context();
+ auto esContext = VAL(*context)->context()->get();
GCVector<ValueRef*> arguments;
for (int i = 0; i < argc; i++) {
}
auto r = Evaluator::execute(
- lwContext->get(),
+ esContext,
[](ExecutionStateRef* state,
ValueRef* self,
ValueRef* receiver,
arguments.size(),
arguments.data());
- API_HANDLE_EXCEPTION(r, lwIsolate, MaybeLocal<Value>());
+ if (!r.isSuccessful()) {
+ LWNODE_DLOG_ERROR("Evaluate");
+ LWNODE_DLOG_RAW("Internal:\n this: %p (es: %p)\n recv: %p (es: %p)",
+ this,
+ CVAL(this)->value(),
+ *recv,
+ CVAL(*recv)->value());
+
+ LWNODE_DLOG_RAW(" arguments (%d):", argc);
+ for (int i = 0; i < argc; i++) {
+ auto esValue = VAL(*argv[i])->value();
+ LWNODE_DLOG_RAW(" [%d] %p (es: %p) %s",
+ i,
+ *argv[i],
+ esValue,
+ toStdStringWithoutException(esContext, esValue).c_str());
+ }
+
+ LWNODE_DLOG_RAW("Execute:\n %s (%s:%d)\nResource:\n %s\n%s",
+ TRACE_ARGS2,
+ "N/A",
+ EvalResultHelper::getErrorString(
+ lwIsolate->GetCurrentContext()->get(), r)
+ .c_str());
+
+ lwIsolate->SetPendingExceptionAndMessage(r.error.get(), r.stackTraceData);
+ lwIsolate->ReportPendingMessages();
+ return MaybeLocal<Value>();
+ }
return Utils::NewLocal<Value>(lwIsolate->toV8(), r.result);
}
void* External::Value() const {
auto esObject = CVAL(this)->value()->asObject();
+ LWNODE_CHECK(ObjectRefHelper::getExtraData(esObject));
auto externalObjectData =
ObjectRefHelper::getExtraData(esObject)->asExternalObjectData();
[](ExecutionStateRef* state,
EscargotShim::IsolateWrap* lwIsolate) -> ValueRef* {
auto promise = PromiseObjectRef::create(state);
-
- // NOTE: Update RunPromiseHook to be called from Escargot after Escargot
- // supports this feature
- lwIsolate->RunPromiseHook(
- PromiseHookType::kInit, promise, ValueRef::createUndefined());
-
return promise;
},
lwIsolate);
PromiseObjectRef* promise,
ValueRef* esValue,
EscargotShim::IsolateWrap* lwIsolate) -> ValueRef* {
- // NOTE: Update RunPromiseHook to be called from Escargot after Escargot
- // supports this feature
- lwIsolate->RunPromiseHook(PromiseHookType::kResolve, promise, esValue);
-
promise->fulfill(state, esValue);
return ValueRef::createUndefined();
},
PromiseObjectRef* promise,
ValueRef* esValue,
EscargotShim::IsolateWrap* lwIsolate) -> ValueRef* {
- // NOTE: Update RunPromiseHook to be called from Escargot after Escargot
- // supports this feature
- lwIsolate->RunPromiseHook(PromiseHookType::kResolve, promise, esValue);
-
promise->reject(state, esValue);
return ValueRef::createUndefined();
},
}
Local<Symbol> v8::Symbol::ForApi(Isolate* isolate, Local<String> name) {
- LWNODE_RETURN_LOCAL(Symbol);
+ API_ENTER_NO_EXCEPTION(isolate);
+
+ SymbolRef* esSymbol =
+ lwIsolate->getApiSymbol(VAL(*name)->value()->asString());
+
+ return Utils::NewLocal<Symbol>(isolate, esSymbol);
}
#define WELL_KNOWN_SYMBOLS(V) \
// @todo For now, we ignore the private attribute and use a normal Symbol
// instead.
- SymbolRef* esSymbol = lwIsolate->getPrivateSymbol(esName);
+ SymbolRef* esSymbol = lwIsolate->createApiPrivateSymbol(esName);
return Utils::NewLocal<Private>(isolate, esSymbol);
}
API_ENTER_NO_EXCEPTION(isolate);
SymbolRef* esSymbol =
- lwIsolate->getPrivateSymbol(VAL(*name)->value()->asString());
+ lwIsolate->getApiPrivateSymbol(VAL(*name)->value()->asString());
return Utils::NewLocal<Private>(isolate, esSymbol);
}
}
void Isolate::ClearKeptObjects() {
-#if !defined(GC_HEAP_TRACE_ONLY)
- MemoryUtil::gc();
- MemoryUtil::gcInvokeFinalizers();
-#endif
LWNODE_RETURN_VOID;
}
}
void Isolate::RequestGarbageCollectionForTesting(GarbageCollectionType type) {
- LWNODE_RETURN_VOID;
+ IsolateWrap::GetCurrent()->CollectGarbage();
}
Isolate* Isolate::GetCurrent() {
}
Isolate* Isolate::New(const Isolate::CreateParams& params) {
+ LWNODE_CALL_TRACE();
Isolate* isolate = Allocate();
Initialize(isolate, params);
return isolate;
}
void Isolate::Enter() {
+ LWNODE_CALL_TRACE();
IsolateWrap::fromV8(this)->Enter();
}
void Isolate::Exit() {
+ LWNODE_CALL_TRACE();
IsolateWrap::fromV8(this)->Exit();
}
auto lwIsolate = IsolateWrap::fromV8(v8_isolate);
auto vmInstance = lwIsolate->vmInstance();
- if (vmInstance->hasPendingJob() == false) {
- return;
- }
+ GCHeap::ProcessingHoldScope scope;
while (vmInstance->hasPendingJob()) {
auto r = vmInstance->executePendingJob();
HandleScope::~HandleScope() {
LWNODE_CALL_TRACE_UNINDENT();
- LWNODE_CALL_TRACE();
+ LWNODE_CALL_TRACE("%p", this);
IsolateWrap::fromV8(isolate_)->popHandleScope(this);
}
LWNODE_CHECK(handle->isValid());
- lwIsolate->addHandleToCurrentScope(handle);
+ switch (handle->location()) {
+ case HandleWrap::Location::Local:
+ lwIsolate->addHandleToCurrentScope(handle);
+ return reinterpret_cast<i::Address*>(handle);
+
+ case HandleWrap::Location::Strong:
+ case HandleWrap::Location::Weak: {
+ auto cloned = handle->clone(HandleWrap::Location::Local);
+ lwIsolate->addHandleToCurrentScope(cloned);
+ return reinterpret_cast<i::Address*>(cloned);
+ }
+
+ default:
+ break;
+ }
+
+ LWNODE_CHECK_NOT_REACH_HERE();
return reinterpret_cast<i::Address*>(value);
}
}
void Context::Enter() {
+ LWNODE_CALL_TRACE("%s", VAL(this)->getHandleInfoString().c_str());
VAL(this)->context()->Enter();
}
void Context::Exit() {
+ LWNODE_CALL_TRACE();
VAL(this)->context()->Exit();
}
if (!fnData->checkSignature(state, thisValue)) {
IsolateWrap::GetCurrent()->ScheduleThrow(TypeErrorObjectRef::create(
state, StringRef::createFromASCII("Illegal invocation")));
+ LWNODE_DLOG_ERROR("Signature mismatch!");
+ return ValueRef::createUndefined();
}
Local<Value> result;
i::Address* V8::GlobalizeReference(i::Isolate* isolate, i::Address* obj) {
LWNODE_CALL_TRACE();
LWNODE_CHECK(isolate);
+#if defined(GC_HEAP_TRACE_ONLY)
+ auto persistent = PersistentWrap::GlobalizeReference(
+ reinterpret_cast<Isolate*>(isolate), obj);
+ return reinterpret_cast<i::Address*>(persistent);
+#endif
+
IsolateWrap::fromV8(isolate)->globalHandles()->Create(VAL(obj));
- Engine::current()->gcHeap()->GlobalizeReference(obj, isolate);
return obj;
}
WeakCallbackType type) {
LWNODE_CALL_TRACE();
- Engine::current()->gcHeap()->MakeWeak(
- location, parameter, weak_callback, type);
+#if defined(GC_HEAP_TRACE_ONLY)
+ PersistentWrap::MakeWeak(location, parameter, weak_callback);
+ return;
+#endif
#if defined(LWNODE_ENABLE_EXPERIMENTAL)
if (type != WeakCallbackType::kParameter) {
LWNODE_RETURN_VOID; // TODO
}
-
GlobalHandles::MakeWeak(VAL(location), parameter, weak_callback);
-#else
- LWNODE_RETURN_VOID;
#endif
}
void V8::MakeWeak(i::Address** location_addr) {
+#if defined(LWNODE_ENABLE_EXPERIMENTAL)
+ GlobalHandles::MakeWeak(
+ VAL(*location_addr), reinterpret_cast<void*>(location_addr), nullptr);
+#endif
LWNODE_RETURN_VOID;
}
void* V8::ClearWeak(i::Address* location) {
LWNODE_CALL_TRACE();
- Engine::current()->gcHeap()->ClearWeak(location);
+#if defined(GC_HEAP_TRACE_ONLY)
+ return PersistentWrap::ClearWeak(location);
+#endif
+
+#if defined(LWNODE_ENABLE_EXPERIMENTAL)
+ return GlobalHandles::ClearWeakness(VAL(location));
+#else
LWNODE_RETURN_NULLPTR;
+#endif
}
void V8::AnnotateStrongRetainer(i::Address* location, const char* label) {
void V8::DisposeGlobal(i::Address* location) {
LWNODE_CALL_TRACE();
+#if defined(GC_HEAP_TRACE_ONLY)
+ PersistentWrap::DisposeGlobal(location);
+ return;
+#endif
+
GlobalHandles::Destroy(VAL(location));
- Engine::current()->gcHeap()->DisposeGlobal(location);
}
void V8::DisposeTracedGlobal(internal::Address* location) {
ScriptParserRef::InitializeScriptResult scriptResult =
context->scriptParser()->initializeScript(
StringRef::createExternalFromASCII(buffer, bufferSize),
- StringRef::createFromASCII(name, sizeof(name)));
+ StringRef::createFromASCII(name,
+ strnlen(name, v8::String::kMaxLength)));
LWNODE_CHECK_MSG(scriptResult.isSuccessful(),
"Cannot parser %s: %s",
name,
#include "engine.h"
#include <iomanip>
#include <sstream>
+#include "handle.h"
#include "utils/logger.h"
#include "utils/misc.h"
#include "utils/string.h"
}
// --- G C H e a p ---
-#if !defined(GC_HEAP_TRACE_ONLY)
+
#define GC_WRAP_PERSISTENT_POINTER(p) (GC_heap_pointer)(p)
-#define GC_UNWRAP_POINTER(p) ((void*)p)
-#else
-#define GC_WRAP_PERSISTENT_POINTER(p) GC_HIDE_POINTER(p)
-#define GC_UNWRAP_POINTER(p) ((void*)GC_HIDE_POINTER(p))
-#endif
+#define GC_UNWRAP_PERSISTENT_POINTER(p) ((void*)p)
+#define GC_WRAP_WEAK_POINTER(p) (GC_heap_pointer)(p)
+#define GC_UNWRAP_WEAK_POINTER(p) ((void*)p)
/*
- @note gc heap tracing lifetime
-
- a) The types of heap tracing are strong, weak and phantom weak.
- b) Every persitent is created as strong type at the registration.
- Following flow is considered:
-
- i) strong <-> weak <-> phantom weak -> (finalizer) -> nullptr
- ii) strong -> nullptr
- iii) phantom weak -> strong
-
- If weak counter is 1, then the pointer will be phantom weak which is
- collectable for GC.
+ state diagram:
+ FREE -> STRONG <-> WEAK -> (Post GC Processing) -> { STRONG, WEAK, FREE }
*/
-void GCHeap::GlobalizeReference(void* address, void* data) {
- LWNODE_CALL_TRACE("address %p, data %p", address, data);
+void GCHeap::acquire(void* address, Kind kind, void* data) {
+ LWNODE_CALL_TRACE_ID(
+ GCHEAP,
+ "%s kind %u data %p",
+ PersistentWrap::as(address)->getPersistentInfoString().c_str(),
+ kind,
+ data);
+
auto iter = persistents_.find(GC_WRAP_PERSISTENT_POINTER(address));
if (iter != persistents_.end()) {
- iter->second.strong++;
- iter->second.weak--;
- iter->second.weak = std::max(iter->second.weak, 0);
- // no-progress handling weak phantoms
+ if (kind == STRONG) iter->second.strong++;
+ if (kind == WEAK) iter->second.weak++;
} else {
- auto iter = weakPhantoms_.find(GC_HIDE_POINTER(address));
+ auto iter = weakPhantoms_.find(GC_WRAP_WEAK_POINTER(address));
if (iter != weakPhantoms_.end()) {
- // move phantom weak to strong
AddressInfo info = iter->second;
LWNODE_CHECK(info.strong == 0);
- LWNODE_CHECK(info.weak == 1);
- info.strong++;
- info.weak--;
+ LWNODE_CHECK(info.weak != 0);
+
+ if (kind == STRONG) iter->second.strong++;
+ if (kind == WEAK) iter->second.weak++;
persistents_.emplace(GC_WRAP_PERSISTENT_POINTER(address), info);
weakPhantoms_.erase(iter);
- // no-progress handling weak phantoms
} else {
persistents_.emplace(GC_WRAP_PERSISTENT_POINTER(address),
AddressInfo(1, 0, data));
}
}
- notifyUpdate(address);
+ postUpdate(address);
}
-void GCHeap::DisposeGlobal(void* address) {
- LWNODE_CALL_TRACE("address %p", address);
+void GCHeap::release(void* address, Kind kind) {
+ LWNODE_CALL_TRACE_ID(
+ GCHEAP,
+ "%s kind %u",
+ PersistentWrap::as(address)->getPersistentInfoString().c_str(),
+ kind);
+
auto iter = persistents_.find(GC_WRAP_PERSISTENT_POINTER(address));
if (iter != persistents_.end()) {
- iter->second.strong--;
+ if (kind == STRONG) iter->second.strong--;
+ if (kind == WEAK) iter->second.weak--;
+
iter->second.strong = std::max(iter->second.strong, 0);
+ iter->second.weak = std::max(iter->second.weak, 0);
// progress handling weak phantoms
if (iter->second.strong == 0) {
- if (iter->second.weak == 0) {
- persistents_.erase(iter);
- } else if (iter->second.weak <= 1) {
- weakPhantoms_.emplace(GC_HIDE_POINTER(address), iter->second);
- persistents_.erase(iter);
+ if (iter->second.weak > 0) {
+ weakPhantoms_.emplace(GC_WRAP_WEAK_POINTER(address), iter->second);
}
- }
- }
- notifyUpdate(address);
-}
-
-void GCHeap::MakeWeak(void* address) {
- LWNODE_CALL_TRACE("address %p", address);
- auto iter = persistents_.find(GC_WRAP_PERSISTENT_POINTER(address));
- if (iter != persistents_.end()) {
- iter->second.strong--;
- iter->second.strong = std::max(iter->second.strong, 0);
- iter->second.weak++;
-
- // progress handling weak phantoms
- if (iter->second.strong == 0 && iter->second.weak <= 1) {
- weakPhantoms_.emplace(GC_HIDE_POINTER(address), iter->second);
persistents_.erase(iter);
}
- } else {
- auto iter = weakPhantoms_.find(GC_HIDE_POINTER(address));
- if (iter != weakPhantoms_.end()) {
- // move phantom weak to weak
- AddressInfo info = iter->second;
-
- LWNODE_CHECK(info.strong == 0);
- LWNODE_CHECK(info.weak == 1);
- info.weak++;
-
- persistents_.emplace(GC_WRAP_PERSISTENT_POINTER(address), info);
- weakPhantoms_.erase(iter);
- } else {
- LWNODE_CHECK(false); // assumes this doesn't happen. let's see.
- }
}
-
- notifyUpdate(address);
+ postUpdate(address);
}
-void GCHeap::ClearWeak(void* address) {
- LWNODE_CALL_TRACE("address %p", address);
- // 1. handle persistents_ and weakPhantoms_
- auto iter = persistents_.find(GC_WRAP_PERSISTENT_POINTER(address));
- if (iter != persistents_.end()) {
- // guard: ClearWeak can be called even if no weak reference exist.
- if (iter->second.weak > 0) {
- iter->second.weak--;
- }
-
- // progress handling weak phantoms
- if (iter->second.strong <= 0 && iter->second.weak <= 1) {
- weakPhantoms_.emplace(GC_HIDE_POINTER(address), iter->second);
- persistents_.erase(iter);
- }
+void GCHeap::postGarbageCollectionProcessing() {
+ if (isOnPostGarbageCollectionProcessing_ ||
+ ProcessingHoldScope::isSkipProcessing()) {
+ return;
}
- // 2. ensure clearing finalizer bound to this address
- MemoryUtil::gcRegisterFinalizer(address, nullptr, nullptr);
+ LWNODE_CALL_TRACE_ID(
+ GCHEAP, "last: %zu, current: %zu", stat_.weak, weakPhantoms_.size());
+ if (stat_.weak == weakPhantoms_.size()) {
+ return;
+ }
+ isOnPostGarbageCollectionProcessing_ = true;
- notifyUpdate(address);
-}
+ // 1. move weaks to process post task.
+ GCVector<HeapSegment> weaks;
+ weaks.reserve(weakPhantoms_.size());
+ for (const HeapSegment& it : weakPhantoms_) {
+ weaks.push_back(it);
+ }
+ weakPhantoms_.clear();
-void GCHeap::disposePhantomWeak(void* address) {
- LWNODE_CALL_TRACE("address %p", address);
- auto iter = weakPhantoms_.find(GC_HIDE_POINTER(address));
- if (iter != weakPhantoms_.end()) {
- weakPhantoms_.erase(iter);
+ // 2. invoke finalizers
+ for (const auto& iter : weaks) {
+ PersistentWrap* persistent =
+ PersistentWrap::as(GC_UNWRAP_PERSISTENT_POINTER(iter.first));
+ persistent->invokeFinalizer();
}
- notifyUpdate(address);
+
+ stat_.weak = weakPhantoms_.size();
+ isOnPostGarbageCollectionProcessing_ = false;
}
bool GCHeap::isTraced(void* address) {
return true;
}
- if (weakPhantoms_.find(GC_HIDE_POINTER(address)) != weakPhantoms_.end()) {
+ if (weakPhantoms_.find(GC_WRAP_WEAK_POINTER(address)) !=
+ weakPhantoms_.end()) {
return true;
}
+
return false;
}
-void* GCHeap::getPersistentData(void* address) {
- auto iter = persistents_.find(GC_WRAP_PERSISTENT_POINTER(address));
- if (iter != persistents_.end()) {
- return iter->second.data;
- }
-
- auto iterWeak = weakPhantoms_.find(GC_HIDE_POINTER(address));
- if (iterWeak != weakPhantoms_.end()) {
- return iterWeak->second.data;
+void GCHeap::disposePhantomWeak(void* address) {
+ LWNODE_CALL_TRACE_ID(
+ GCHEAP,
+ "%s",
+ PersistentWrap::as(address)->getPersistentInfoString().c_str());
+ stat_.freed++;
+ auto iter = weakPhantoms_.find(GC_WRAP_WEAK_POINTER(address));
+ if (iter != weakPhantoms_.end()) {
+ weakPhantoms_.erase(iter);
}
- return nullptr;
+ postUpdate(address);
}
-typedef void (*formatterFunction)(
- std::stringstream& stream,
- const std::pair<GCHeap::GC_heap_pointer, GCHeap::AddressInfo>& iter);
+typedef void (*formatterFunction)(std::stringstream& stream,
+ const GCHeap::HeapSegment& iter);
static void printAddress(
const GCUnorderedMap<GCHeap::GC_heap_pointer, GCHeap::AddressInfo>& map,
}
}
-void GCHeap::printStatus() {
- if (isStatusPrinted) {
+void GCHeap::printStatus(bool forcePrint) {
+ if (Flags::isTraceCallEnabled("GCHEAP") == false) {
return;
}
- isStatusPrinted = true;
- if (persistents_.size() == 0 && weakPhantoms_.size() == 0) {
+ if (!forcePrint && isStatePrinted_) {
+ return;
+ }
+
+ isStatePrinted_ = true;
+
+ if (!forcePrint || (persistents_.size() == 0 && weakPhantoms_.size() == 0)) {
return;
}
- LWNODE_LOG_INFO(COLOR_GREEN "----- GCHEAP -----" COLOR_RESET);
- LWNODE_LOG_INFO("[HOLD]");
- printAddress(
- persistents_,
- [](std::stringstream& stream,
- const std::pair<GCHeap::GC_heap_pointer, GCHeap::AddressInfo>& iter) {
- stream << std::setw(15) << std::right << GC_UNWRAP_POINTER(iter.first)
- << " ("
- << "S" << std::setw(3) << iter.second.strong << " W"
- << std::setw(3) << iter.second.weak << ") ";
- });
-
- LWNODE_LOG_INFO(COLOR_GREEN "------------------" COLOR_RESET);
- LWNODE_LOG_INFO("[PHANTOM]");
- printAddress(
- weakPhantoms_,
- [](std::stringstream& stream,
- const std::pair<GCHeap::GC_heap_pointer, GCHeap::AddressInfo>& iter) {
- stream << std::setw(15) << std::right << GC_REVEAL_POINTER(iter.first)
- << " ("
- << "S" << std::setw(3) << iter.second.strong << " W"
- << std::setw(3) << iter.second.weak << ") ";
- });
-
- LWNODE_LOG_INFO(COLOR_GREEN "------------------" COLOR_RESET);
+ LWNODE_DLOG_INFO(COLOR_GREEN "----- GCHEAP -----" COLOR_RESET);
+ LWNODE_DLOG_INFO("[STAT]");
+ LWNODE_DLOG_INFO(" freed: %zu", stat_.freed);
+ LWNODE_DLOG_INFO(" strong: %zu", persistents_.size());
+ LWNODE_DLOG_INFO(" weak: %zu", weakPhantoms_.size());
+ LWNODE_DLOG_INFO("[HOLD]");
+ printAddress(persistents_,
+ [](std::stringstream& stream, const HeapSegment& iter) {
+ stream << std::setw(15) << std::right
+ << GC_UNWRAP_PERSISTENT_POINTER(iter.first) << " ("
+ << "S" << std::setw(3) << iter.second.strong << " W"
+ << std::setw(3) << iter.second.weak << ") ";
+ });
+
+ LWNODE_DLOG_INFO(COLOR_GREEN "------------------" COLOR_RESET);
+ LWNODE_DLOG_INFO("[PHANTOM]");
+ printAddress(weakPhantoms_,
+ [](std::stringstream& stream, const HeapSegment& iter) {
+ stream << std::setw(15) << std::right
+ << GC_UNWRAP_WEAK_POINTER(iter.first) << " ("
+ << "S" << std::setw(3) << iter.second.strong << " W"
+ << std::setw(3) << iter.second.weak << ") ";
+ });
+
+ LWNODE_DLOG_INFO(COLOR_GREEN "------------------" COLOR_RESET);
}
-void GCHeap::notifyUpdate(void* address) {
- isStatusPrinted = false;
+void GCHeap::postUpdate(void* address) {
+ isStatePrinted_ = false;
}
-void GCHeap::MakeWeak(void* location,
- void* parameter,
- v8::WeakCallbackInfo<void>::Callback weak_callback,
- v8::WeakCallbackType type) {
- LWNODE_CALL_TRACE("address %p", location);
+void GCHeap::processGCEvent() {
+ auto gcHeap = Engine::current()->gcHeap();
+ gcHeap->printStatus();
+ gcHeap->postGarbageCollectionProcessing();
+}
- if (type != v8::WeakCallbackType::kParameter) {
- LWNODE_CHECK(false);
- }
+std::vector<GCHeap::ProcessingHoldScope*>
+ GCHeap::ProcessingHoldScope::s_processingHoldScopes_;
-#if !defined(GC_HEAP_TRACE_ONLY)
- // 1. register the given finalizer
- struct Params {
- v8::Isolate* isolate;
- void* parameter;
- v8::WeakCallbackInfo<void>::Callback weak_callback;
- };
-
- Params* params = new Params();
-
- LWNODE_CHECK(isTraced(location));
-
- v8::Isolate* v8Isolate =
- reinterpret_cast<v8::Isolate*>(getPersistentData(location));
-
- LWNODE_CHECK_NOT_NULL(v8Isolate);
-
- params->isolate = v8Isolate;
- params->parameter = parameter;
- params->weak_callback = weak_callback;
-
- MemoryUtil::gcRegisterFinalizer(
- location,
- [](void* address, void* data) {
- Engine::current()->gcHeap()->disposePhantomWeak(address);
- Params* params = (Params*)data;
- void* embedderFields[v8::kEmbedderFieldsInWeakCallback] = {};
- v8::WeakCallbackInfo<void> info(
- params->isolate, params->parameter, embedderFields, nullptr);
- params->weak_callback(info);
- delete params;
- },
- params);
-#endif
+GCHeap::ProcessingHoldScope::ProcessingHoldScope() {
+ s_processingHoldScopes_.push_back(this);
+}
- // 2. make this location as weak type
- MakeWeak(location);
+GCHeap::ProcessingHoldScope::~ProcessingHoldScope() {
+ s_processingHoldScopes_.pop_back();
+}
+
+bool GCHeap::ProcessingHoldScope::isSkipProcessing() {
+ if (s_processingHoldScopes_.empty()) {
+ return false;
+ }
+ return true;
}
// --- E n g i n e ---
static Engine* s_engine;
std::unordered_set<v8::String::ExternalStringResourceBase*>
Engine::s_externalStrings;
+static Engine::State s_state = Engine::Freed;
bool Engine::Initialize() {
if (s_engine == nullptr) {
s_engine = new Engine();
s_engine->initialize();
+ s_state = Running;
}
return true;
}
delete s_engine;
s_engine = nullptr;
}
-
+ s_state = Freed;
LWNODE_CALL_TRACE_GC_END();
return true;
}
Globals::initialize();
Memory::setGCFrequency(GC_FREE_SPACE_DIVISOR);
- gcHeap_.reset(new GCHeap());
+ gcHeap_.reset(GCHeap::create());
- if (Flags::isTraceCallEnabled("HEAP")) {
- Memory::setGCEventListener([]() {
- // this is invoked at GC_EVENT_RECLAIM_END phase
- Engine::current()->gcHeap()->printStatus();
- });
- }
+#if defined(GC_HEAP_TRACE_ONLY)
+ // this is invoked at GC_EVENT_RECLAIM_END phase
+ Memory::setGCEventListener(GCHeap::processGCEvent);
+#endif
auto flags = Flags::get();
if (Flags::isTraceGCEnabled()) {
- MemoryUtil::gcStartStatsTrace();
+ LWNODE_DLOG_WARN("temporary blocked for postGarbageCollectionProcessing");
+ // MemoryUtil::gcStartStatsTrace();
}
}
void Engine::dispose() {
LWNODE_CALL_TRACE_GC_START();
+ s_state = OnDestroy;
Memory::setGCEventListener(nullptr);
return s_engine;
}
+Engine::State Engine::getState() {
+ return s_state;
+}
+
void Engine::registerExternalString(
v8::String::ExternalStringResourceBase* v8Str) {
s_externalStrings.emplace(v8Str);
v8::ArrayBuffer::Allocator* allocator_ = nullptr;
};
-#define GC_HEAP_TRACE_ONLY
-
class GCHeap : public gc {
public:
- void GlobalizeReference(void* address, void* data);
- void DisposeGlobal(void* address);
- void MakeWeak(void* address);
- void MakeWeak(void* location,
- void* parameter,
- v8::WeakCallbackInfo<void>::Callback weak_callback,
- v8::WeakCallbackType type);
- void ClearWeak(void* address);
- void disposePhantomWeak(void* address);
- bool isTraced(void* address);
- void* getPersistentData(void* address);
- void printStatus();
+ enum Kind {
+ FREE = 0,
+ STRONG,
+ WEAK,
+ };
- typedef GC_word GC_heap_pointer;
struct AddressInfo {
AddressInfo(int strong_, int weak_, void* data_ = nullptr) {
strong = strong_;
int weak = 0;
void* data = nullptr;
};
+ typedef GC_word GC_heap_pointer;
+ typedef std::pair<GC_heap_pointer, AddressInfo> HeapSegment;
+
+ void acquire(void* address, Kind kind, void* data);
+ void release(void* address, Kind kind);
+ void disposePhantomWeak(void* address);
+ bool isTraced(void* address);
+ void printStatus(bool forcePrint = false);
+
+ class ProcessingHoldScope {
+ public:
+ ProcessingHoldScope();
+ ~ProcessingHoldScope();
+
+ static bool isSkipProcessing();
+
+ private:
+ static std::vector<ProcessingHoldScope*> s_processingHoldScopes_;
+ };
+
+ void postGarbageCollectionProcessing();
+ static void processGCEvent();
+
+ static GCHeap* create() { return new GCHeap(); }
private:
- void notifyUpdate(void* address);
+ void postUpdate(void* address);
GCUnorderedMap<GC_heap_pointer, AddressInfo> persistents_;
GCUnorderedMap<GC_heap_pointer, AddressInfo> weakPhantoms_;
- bool isStatusPrinted = false;
+ bool isStatePrinted_ = false;
+ bool isOnPostGarbageCollectionProcessing_ = false;
+ struct Stat {
+ size_t freed = 0;
+ size_t weak = 0;
+ };
+
+ Stat stat_;
};
class Engine {
GCHeap* gcHeap() { return gcHeap_.get(); }
+ enum State {
+ Freed,
+ Running,
+ OnDestroy,
+ };
+
+ static State getState();
+
private:
Engine() = default;
void initialize();
return nodeBlock;
}
+ size_t clearWeakValue() {
+ auto weakValuesSize = weakValues_.size();
+ if (weakValuesSize == 0) {
+ return 0;
+ }
+ LWNODE_CALL_TRACE_ID(
+ GLOBALHANDLES, "Clear weak values: %ld", weakValuesSize);
+ for (auto& iter : weakValues_) {
+ iter.second->releaseValue();
+ }
+ return weakValuesSize;
+ }
+
+ bool isWeak(ValueWrap* lwValue) {
+ return weakValues_.find(lwValue) != weakValues_.end();
+ }
+
void dispose() { weakValues_.clear(); }
private:
GlobalHandles::Node::Node(void* parameter,
v8::WeakCallbackInfo<void>::Callback callback)
- : parameter_(parameter), callback_(callback) {
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "New Node(%p)", this);
-}
+ : parameter_(parameter), callback_(callback) {}
-GlobalHandles::Node::~Node() {
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Free Node(%p)", this);
-}
+GlobalHandles::Node::~Node() {}
-GlobalHandles::NodeBlock::NodeBlock(v8::Isolate* isolate, uint32_t count)
- : isolate_(isolate), usedNodes_(count) {
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "New NodeBlock(%p)", this);
+GlobalHandles::NodeBlock::NodeBlock(v8::Isolate* isolate,
+ ValueWrap* value,
+ uint32_t count)
+ : isolate_(isolate), value_(value), usedNodes_(count) {
+ holder_.reset(value_);
}
GlobalHandles::NodeBlock::~NodeBlock() {
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Free NodeBlock(%p)", this);
- auto curNode = firstNode_;
- while (curNode) {
- auto nextNode = curNode->nextNode();
- delete curNode;
- curNode = nextNode;
- }
+ delete firstNode_;
+ holder_.release();
}
-bool GlobalHandles::NodeBlock::increaseUsage() {
- return usedNodes_++ == 0;
-}
-
-bool GlobalHandles::NodeBlock::decreaseUsage() {
- LWNODE_DCHECK(usedNodes_ > 0);
- return --usedNodes_ == 0;
-}
-
-GlobalHandles::Node* GlobalHandles::NodeBlock::pushNode(ValueWrap* lwValue,
- Node* node) {
+GlobalHandles::Node* GlobalHandles::NodeBlock::pushNode(Node* node) {
if (firstNode_ == nullptr) {
firstNode_ = node;
- registerWeakCallback(lwValue);
} else {
// TODO
- LWNODE_DLOG_WARN("The weak callback is registered several times.")
+ LWNODE_DLOG_WARN("The weak callback is registered several times.");
+ if (node) {
+ delete node;
+ return nullptr;
+ }
}
return node;
}
-void GlobalHandles::NodeBlock::registerWeakCallback(ValueWrap* lwValue) {
- MemoryUtil::gcRegisterFinalizer(lwValue, [](void* self) {
+void GlobalHandles::NodeBlock::registerWeakCallback() {
+ MemoryUtil::gcRegisterFinalizer(value_, [](void* self) {
LWNODE_CALL_TRACE_GC_START();
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Call weak callback");
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Call weak callback: %p", self);
auto block = g_globalWeakHandler.popBlock(VAL(self));
if (!block) {
v8::WeakCallbackInfo<void> info(
block->isolate(), curNode->parameter(), embedderFields, nullptr);
LWNODE_CHECK_NOT_NULL(block->isolate());
- LWNODE_CALL_TRACE_ID(
- GLOBALHANDLES, "Call v8 callback: parm(%p)", curNode->parameter());
curNode->callback()(info);
}
-
- if (curNode->nextNode()) {
- LWNODE_UNIMPLEMENT; // TODO
- }
+ // TODO: The weak callback is registered several times.(create nextNode)
}
LWNODE_CALL_TRACE_GC_END();
});
}
+void GlobalHandles::NodeBlock::releaseValue() {
+ registerWeakCallback();
+ holder_.release();
+}
+
GlobalHandles::GlobalHandles(v8::Isolate* isolate) : isolate_(isolate) {
g_globalHandlesVector.push_back(this);
}
} else {
++iter->second;
// TODO:
- LWNODE_DLOG_WARN("Persistent value was created multiple times.")
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES,
+ "Persistent value was created multiple times: %p",
+ lwValue);
}
}
return;
}
}
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot destroy: %p", lwValue)
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot destroy: %p", lwValue);
}
bool GlobalHandles::destroy(ValueWrap* lwValue) {
return false;
}
+size_t GlobalHandles::PostGarbageCollectionProcessing(
+ /*const v8::GCCallbackFlags gc_callback_flags*/) {
+#if defined(LWNODE_ENABLE_EXPERIMENTAL)
+ return g_globalWeakHandler.clearWeakValue();
+#else
+ return 0;
+#endif
+}
+
void GlobalHandles::MakeWeak(ValueWrap* lwValue,
void* parameter,
v8::WeakCallbackInfo<void>::Callback callback) {
return;
}
}
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot make weak value: %p", lwValue)
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot make weak value: %p", lwValue);
}
bool GlobalHandles::makeWeak(ValueWrap* lwValue,
return false;
}
- auto block =
- std::make_unique<GlobalHandles::NodeBlock>(isolate_, iter->second);
- block->pushNode(lwValue, new Node(parameter, callback));
+ if (g_globalWeakHandler.isWeak(lwValue)) {
+ return false;
+ }
+
+ auto block = std::make_unique<GlobalHandles::NodeBlock>(
+ isolate_, lwValue, iter->second);
+ block->pushNode(new Node(parameter, callback));
g_globalWeakHandler.pushBlock(lwValue, std::move(block));
+ LWNODE_CALL_TRACE_ID(
+ GLOBALHANDLES, "MakeWeak: %p(%ld)", lwValue, iter->second);
persistentValues_.erase(iter);
- LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "MakeWeak: %p", lwValue)
+ return true;
+}
+
+void* GlobalHandles::ClearWeakness(ValueWrap* lwValue) {
+ for (auto globalHandles : g_globalHandlesVector) {
+ if (globalHandles->clearWeak(lwValue)) {
+ return lwValue;
+ }
+ }
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "Cannot clear weak value: %p", lwValue);
+ return nullptr;
+}
+
+bool GlobalHandles::clearWeak(ValueWrap* lwValue) {
+ auto block = g_globalWeakHandler.popBlock(lwValue);
+ if (!block) {
+ return false;
+ }
+ LWNODE_CHECK(persistentValues_.find(lwValue) == persistentValues_.end());
+ persistentValues_.emplace(lwValue, block->usedNodes());
+ LWNODE_CALL_TRACE_ID(GLOBALHANDLES, "ClearWeak: %p", lwValue);
return true;
}
static void MakeWeak(ValueWrap* lwValue,
void* parameter,
v8::WeakCallbackInfo<void>::Callback callback);
+ static void* ClearWeakness(ValueWrap* lwValue);
+
+ size_t PostGarbageCollectionProcessing(
+ /*const v8::GCCallbackFlags gc_callback_flags*/);
bool destroy(ValueWrap* lwValue);
void* parameter,
v8::WeakCallbackInfo<void>::Callback callback);
+ bool clearWeak(ValueWrap* lwValue);
+
size_t handles_count();
void Dispose();
Node(const Node&) = delete;
- Node* nextNode() { return nextNode_; }
void* parameter() { return parameter_; }
v8::WeakCallbackInfo<void>::Callback callback() { return callback_; }
private:
- Node* nextNode_{nullptr};
void* parameter_{nullptr};
v8::WeakCallbackInfo<void>::Callback callback_;
};
class NodeBlock {
public:
- NodeBlock(v8::Isolate* isolate, uint32_t count);
+ NodeBlock(v8::Isolate* isolate, ValueWrap* value, uint32_t count);
~NodeBlock();
NodeBlock(const NodeBlock&) = delete;
- bool increaseUsage();
-
- bool decreaseUsage();
-
- uint32_t usage() { return usedNodes_; }
+ uint32_t usedNodes() { return usedNodes_; }
Node* firstNode() { return firstNode_; }
void setFirstNode(Node* node) { firstNode_ = node; }
- Node* pushNode(ValueWrap* lwValue, Node* node);
+ Node* pushNode(Node* node);
+
+ void registerWeakCallback();
- void registerWeakCallback(ValueWrap* lwValue);
+ void releaseValue();
v8::Isolate* isolate() { return isolate_; }
private:
v8::Isolate* isolate_{nullptr};
+ ValueWrap* value_{nullptr};
uint32_t usedNodes_{0};
Node* firstNode_{nullptr};
+ Escargot::PersistentRefHolder<ValueWrap> holder_;
};
private:
*/
#include "handle.h"
+#include <sstream>
#include "api.h"
#include "context.h"
+#include "engine.h"
#include "isolate.h"
#include "utils/misc.h"
return type_;
}
+uint8_t HandleWrap::valueType() const {
+ return valueType_;
+}
+
+uint8_t HandleWrap::location() const {
+ return location_;
+}
+
bool HandleWrap::isValid() const {
- return (type_ < HandleWrap::Type::NotPresent);
+ return (type_ > Type::NotPresent && type_ < Type::EndOfType);
+}
+
+bool HandleWrap::isStrongOrWeak() const {
+ return (location_ == Strong || location_ == Weak || location_ == NearDeath);
+}
+
+void HandleWrap::copy(HandleWrap* that, Location location) {
+ val_ = that->val_;
+ type_ = that->type_;
+ valueType_ = that->valueType_;
+ location_ = location;
+}
+
+HandleWrap* HandleWrap::clone(Location location) {
+ auto handle = new HandleWrap();
+ handle->val_ = val_;
+ handle->type_ = type_;
+ handle->valueType_ = valueType_;
+ handle->location_ = location;
+
+ LWNODE_CALL_TRACE_ID(
+ HANDLE, "%p is cloned from %s", handle, getHandleInfoString().c_str());
+ return handle;
+}
+
+HandleWrap* HandleWrap::as(void* address) {
+ auto p = reinterpret_cast<HandleWrap*>(address);
+ LWNODE_CHECK(p->isValid());
+ return p;
+}
+
+std::string HandleWrap::getHandleInfoString() {
+ std::stringstream ss;
+ ss << "(addr: " << this << " es: " << val_
+ << " loc: " << std::to_string(location_)
+ << " type: " << std::to_string(type_) << ")";
+ return ss.str();
}
// --- ValueWrap ---
HandleWrap::ValueType valueType) {
LWNODE_CHECK_NOT_NULL(ptr);
type_ = type;
- holder_ = ptr;
+ val_ = ptr;
valueType_ = valueType;
+ LWNODE_CALL_TRACE_ID(HANDLE, "%s", getHandleInfoString().c_str());
}
bool ValueWrap::isExternalString() const {
}
ValueWrap* ValueWrap::createValue(Escargot::ValueRef* esValue) {
- auto value = new ValueWrap(esValue, Type::JsValue);
- LWNODE_CALL_TRACE_ID(VALUEWRAP, "es:%p | %p", esValue, value);
- return value;
+ return new ValueWrap(esValue, Type::JsValue);
}
ValueRef* ValueWrap::value() const {
LWNODE_CHECK_MSG(type() == Type::JsValue,
- "type should be %d but %d. (this: %p, holder_: %p)",
+ "type should be %d but %d. (this: %p, val_: %p)",
Type::JsValue,
type(),
this,
- holder_);
- return reinterpret_cast<ValueRef*>(holder_);
+ val_);
+ return reinterpret_cast<ValueRef*>(val_);
}
ValueWrap* ValueWrap::createContext(ContextWrap* lwContext) {
- auto value = new ValueWrap(lwContext, Type::Context);
- LWNODE_CALL_TRACE_ID(VALUEWRAP, "es:%p | %p", lwContext, value);
- return value;
+ return new ValueWrap(lwContext, Type::Context);
};
ContextWrap* ValueWrap::context() const {
- LWNODE_CHECK(type() == Type::Context);
- return reinterpret_cast<ContextWrap*>(holder_);
+ LWNODE_CHECK(type_ == Type::Context);
+ return reinterpret_cast<ContextWrap*>(val_);
}
ValueWrap* ValueWrap::createScript(ScriptRef* esScript) {
- auto value = new ValueWrap(esScript, Type::Script);
- LWNODE_CALL_TRACE_ID(VALUEWRAP, "es:%p | %p", esScript, value);
- return value;
+ return new ValueWrap(esScript, Type::Script);
};
ScriptRef* ValueWrap::script() const {
- LWNODE_CHECK(type() == Type::Script);
- return reinterpret_cast<ScriptRef*>(holder_);
+ LWNODE_CHECK(type_ == Type::Script);
+ return reinterpret_cast<ScriptRef*>(val_);
}
Escargot::TemplateRef* ValueWrap::tpl() const {
- LWNODE_CHECK(type() == Type::ObjectTemplate ||
- type() == Type::FunctionTemplate);
- return reinterpret_cast<TemplateRef*>(holder_);
+ LWNODE_CHECK(type_ == Type::ObjectTemplate ||
+ type_ == Type::FunctionTemplate);
+ return reinterpret_cast<TemplateRef*>(val_);
}
ValueWrap* ValueWrap::createFunctionTemplate(
}
Escargot::FunctionTemplateRef* ValueWrap::ftpl() const {
- LWNODE_CHECK(type() == Type::FunctionTemplate);
- return reinterpret_cast<FunctionTemplateRef*>(holder_);
+ LWNODE_CHECK(type_ == Type::FunctionTemplate);
+ return reinterpret_cast<FunctionTemplateRef*>(val_);
}
ValueWrap* ValueWrap::createObjectTemplate(
}
Escargot::ObjectTemplateRef* ValueWrap::otpl() const {
- LWNODE_CHECK(type() == Type::ObjectTemplate);
- return reinterpret_cast<ObjectTemplateRef*>(holder_);
+ LWNODE_CHECK(type_ == Type::ObjectTemplate);
+ return reinterpret_cast<ObjectTemplateRef*>(val_);
}
ValueWrap* ValueWrap::createModule(ModuleWrap* esModule) {
}
ModuleWrap* ValueWrap::module() const {
- LWNODE_CHECK(type() == Type::Module);
- return reinterpret_cast<ModuleWrap*>(holder_);
+ LWNODE_CHECK(type_ == Type::Module);
+ return reinterpret_cast<ModuleWrap*>(val_);
+}
+
+// --- PersistentWrap ---
+
+inline static GCHeap* GCHeap() {
+ return Engine::current()->gcHeap();
+}
+
+inline static void acquireStrong(void* address, void* data = nullptr) {
+ GCHeap()->acquire(address, GCHeap::STRONG, data);
+}
+
+inline static void releaseStrong(void* address) {
+ GCHeap()->release(address, GCHeap::STRONG);
+}
+
+inline static void acquireWeak(void* address, void* data = nullptr) {
+ GCHeap()->acquire(address, GCHeap::WEAK, data);
+}
+
+inline static void releaseWeak(void* address) {
+ GCHeap()->release(address, GCHeap::WEAK);
+}
+
+inline static void makeStrongToWeak(void* address) {
+ acquireWeak(address);
+ releaseStrong(address);
+}
+/*
+ 1. val_ : HandleWrap MUST be compatible with in V8 other apis.
+
+ ex1)
+ v8::Persistent<String> global1;
+ global1.Reset(isolate, v8_str("str"));
+ Local<String> local1 = Local<String>::New(isolate, global1);
+ CHECK(global1 == local1);
+
+ ex2)
+ v8::Context* operator->() {
+ return *reinterpret_cast<v8::Context**>(&context_);
+ }
+ v8::Context* operator*() { return operator->(); }
+ v8::Persistent<v8::Context> context_;
+
+ 2. member values of this instance MUST be hidden to GC.
+*/
+
+PersistentWrap::PersistentWrap(ValueWrap* lwValue) {
+ // copy the given value information and set it as Strong
+ copy(lwValue, Location::Strong);
+
+ holder_ = lwValue;
+
+ LWNODE_CALL_TRACE_ID(GCHEAP,
+ "%s trace: (%p)",
+ getPersistentInfoString().c_str(),
+ getTracingAddress());
+
+ acquireStrong(this, nullptr);
+ LWNODE_DCHECK(GCHeap()->isTraced(this));
+}
+
+void* PersistentWrap::getTracingAddress() {
+ return this;
+}
+
+void* PersistentWrap::GlobalizeReference(v8::Isolate* isolate, void* address) {
+ LWNODE_CALL_TRACE_ID(GCHEAP);
+ ValueWrap* lwValue = reinterpret_cast<ValueWrap*>(address);
+ LWNODE_CHECK(lwValue->isValid());
+
+ PersistentWrap* persistent = new PersistentWrap(lwValue);
+
+ persistent->v8Isolate_ = isolate;
+
+ GCHeap()->printStatus(true);
+
+ return persistent;
+}
+
+void PersistentWrap::DisposeGlobal(void* address) {
+ PersistentWrap* persistent = reinterpret_cast<PersistentWrap*>(address);
+ LWNODE_CALL_TRACE_ID(
+ GCHEAP, "%s", persistent->getPersistentInfoString().c_str());
+
+ LWNODE_DCHECK(persistent->location_ != Local);
+
+ persistent->dispose();
+
+ GCHeap()->printStatus(true);
+}
+
+void PersistentWrap::dispose() {
+ LWNODE_CALL_TRACE_ID(GCHEAP,
+ "%s isFinalizerCalled: %s",
+ getPersistentInfoString().c_str(),
+ strBool(isFinalizerCalled));
+ if (isFinalizerCalled) {
+ return;
+ }
+
+ if (location_ == Location::Strong) {
+ releaseStrong(getTracingAddress());
+ } else if (location_ == Location::Weak) {
+ releaseWeak(getTracingAddress());
+ } else {
+ LWNODE_CHECK_NOT_REACH_HERE();
+ }
+}
+
+void PersistentWrap::MakeWeak(
+ void* address,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback weak_callback) {
+ LWNODE_CALL_TRACE_ID(GCHEAP);
+ PersistentWrap* persitent = reinterpret_cast<PersistentWrap*>(address);
+ LWNODE_CHECK(persitent->location() != Location::Local);
+ persitent->makeWeak(parameter, weak_callback);
+
+ GCHeap()->printStatus(true);
+}
+
+void PersistentWrap::makeWeak(
+ void* parameter, v8::WeakCallbackInfo<void>::Callback weak_callback) {
+ if (location_ == Location::Strong) {
+ // Strong -> Weak
+ makeStrongToWeak(getTracingAddress());
+ location_ = Location::Weak;
+ parameter_ = parameter;
+ weak_callback_ = weak_callback;
+
+ } else if (location_ == Location::Weak) {
+ LWNODE_CHECK(weak_callback_ == weak_callback);
+ LWNODE_CHECK(parameter_ == parameter);
+
+ } else {
+ LWNODE_CHECK_NOT_REACH_HERE();
+ }
+
+ LWNODE_CHECK(GCHeap()->isTraced(this));
+}
+
+void PersistentWrap::invokeFinalizer() {
+ if (Engine::getState() == Engine::Running) {
+ GCHeap()->disposePhantomWeak(this);
+ }
+
+ v8::HandleScope handleScope(v8Isolate_);
+ void* embedderFields[v8::kEmbedderFieldsInWeakCallback] = {};
+ v8::WeakCallbackInfo<void> data(
+ v8Isolate_, parameter_, embedderFields, nullptr);
+
+ weak_callback_(data);
+
+ isFinalizerCalled = true;
+}
+
+void* PersistentWrap::ClearWeak(void* address) {
+ LWNODE_CALL_TRACE_ID(GCHEAP);
+ PersistentWrap* persitent = reinterpret_cast<PersistentWrap*>(address);
+ LWNODE_CHECK(persitent->location() != Location::Local);
+ void* p = persitent->clearWeak();
+ GCHeap()->printStatus(true);
+ return p;
+}
+
+void* PersistentWrap::clearWeak() {
+ if (location_ == Location::Strong) {
+ // Nothing to do
+ LWNODE_CHECK_NULL(weak_callback_);
+ LWNODE_CHECK_NULL(parameter_);
+
+ } else if (location_ == Location::Weak) {
+ // Weak -> Strong
+ acquireStrong(getTracingAddress(), nullptr);
+ releaseWeak(getTracingAddress());
+ location_ = Location::Strong;
+
+ } else {
+ LWNODE_CHECK_NOT_REACH_HERE();
+ }
+
+ void* p = parameter_;
+ parameter_ = nullptr;
+ weak_callback_ = nullptr;
+ return p;
+}
+
+PersistentWrap* PersistentWrap::as(void* address) {
+ auto p = reinterpret_cast<PersistentWrap*>(address);
+ LWNODE_CHECK(p->isStrongOrWeak());
+ LWNODE_CHECK(p->isValid());
+ return p;
+}
+
+std::string PersistentWrap::getPersistentInfoString() {
+ std::stringstream ss;
+ ss << getHandleInfoString() << " holder: " << holder_;
+
+ return ss.str();
}
} // namespace EscargotShim
class IsolateWrap;
class ModuleWrap;
class ExternalStringWrap;
+class GCHeap;
class HandleWrap : public gc {
public:
enum Type : uint8_t {
+ NotPresent = 0,
JsValue = 101,
Context,
ObjectTemplate,
FunctionTemplate,
Script,
Module,
- // NotPresent should be at last
- NotPresent,
+ EndOfType,
+ };
+
+ enum Location : uint8_t {
+ Local = 0,
+ Strong,
+ Weak,
+ NearDeath,
};
enum ValueType : uint8_t {
};
uint8_t type() const;
+ uint8_t valueType() const;
bool isValid() const;
+ bool isStrongOrWeak() const;
+ uint8_t location() const;
+ HandleWrap* clone(Location location = Local);
+ std::string getHandleInfoString();
+ static HandleWrap* as(void* address);
protected:
HandleWrap() = default;
- void* holder_ = nullptr;
+ void copy(HandleWrap* that, Location location);
+
+ void* val_ = nullptr;
uint8_t type_ = Type::NotPresent;
uint8_t valueType_ = ValueType::None; // TODO: remove this variable
+ uint8_t location_ = Location::Local;
};
class ValueWrap : public HandleWrap {
ValueWrap(void* ptr,
HandleWrap::Type type,
HandleWrap::ValueType valueType = HandleWrap::ValueType::None);
+ ValueWrap() = default;
+};
+
+class PersistentWrap : public ValueWrap {
+ public:
+ static void* GlobalizeReference(v8::Isolate* isolate, void* address);
+ static void DisposeGlobal(void* address);
+ static void MakeWeak(void* address,
+ void* parameter,
+ v8::WeakCallbackInfo<void>::Callback weak_callback);
+ static void* ClearWeak(void* address);
+
+ static PersistentWrap* as(void* address);
+ std::string getPersistentInfoString();
+ void invokeFinalizer();
+
+ private:
+ PersistentWrap(ValueWrap* ptr);
+
+ void dispose();
+ void makeWeak(void* parameter,
+ v8::WeakCallbackInfo<void>::Callback weak_callback);
+ void* clearWeak();
+ void* getTracingAddress();
+
+ ValueWrap* holder_{nullptr};
+ v8::Isolate* v8Isolate_{nullptr};
+ v8::WeakCallbackInfo<void>::Callback weak_callback_{nullptr};
+ void* parameter_{nullptr};
+ bool isFinalizerCalled{false};
+ friend class GCHeap;
};
} // namespace EscargotShim
: type_(type), v8scope_(reinterpret_cast<void*>(scope)) {}
void HandleScopeWrap::add(HandleWrap* value) {
- LWNODE_CALL_TRACE_ID(HDLSCOPE, "%p -> %p | %p", value, v8scope_, this);
+ LWNODE_CALL_TRACE_ID(HDLSCOPE,
+ "%s --> %p (lw: %p)",
+ value->getHandleInfoString().c_str(),
+ v8scope_,
+ this);
handles_.push_back(value);
}
}
void IsolateWrap::Dispose() {
+ LWNODE_CALL_TRACE_ID(ISOWRAP);
LWNODE_CALL_TRACE_GC_START();
// NOTE: check unlock_gc_release(); is needed (and where)
// unlock_gc_release();
}
void IsolateWrap::addHandleToCurrentScope(HandleWrap* value) {
+ LWNODE_CALL_TRACE("%p", value);
LWNODE_CHECK(handleScopes_.size() >= 1);
handleScopes_.back()->add(value);
}
}
void IsolateWrap::pushContext(ContextWrap* context) {
- LWNODE_CALL_TRACE_ID(ISOWRAP, "%p", context);
+ LWNODE_CALL_TRACE_ID(ISOWRAP,
+ "%p (%zu -> %zu)",
+ context,
+ contextScopes_.size(),
+ contextScopes_.size() + 1);
if (contextScopes_.size() && (contextScopes_.back() != context)) {
LWNODE_DLOG_WARN(R"(multiple contexts exist:
void IsolateWrap::popContext(ContextWrap* context) {
LWNODE_CHECK(contextScopes_.back() == context);
- LWNODE_CALL_TRACE_ID(ISOWRAP, "%p", context);
+ LWNODE_CALL_TRACE_ID(ISOWRAP,
+ "%p (%zu -> %zu)",
+ context,
+ contextScopes_.size(),
+ contextScopes_.size() - 1);
contextScopes_.pop_back();
}
backingStores_.erase(value);
}
-SymbolRef* IsolateWrap::getPrivateSymbol(StringRef* esString) {
- // @check replace this container if this function is called a lot.
+SymbolRef* IsolateWrap::createApiSymbol(StringRef* name) {
+ // FIXME: Use a set
+ auto newSymbol = SymbolRef::create(name);
+ bool found = false;
+ for (size_t i = 0; i < apiSymbols_.size(); i++) {
+ if (apiSymbols_[i]->description()->equals(name)) {
+ apiSymbols_[i] = newSymbol;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ apiSymbols_.push_back(newSymbol);
+ LWNODE_DLOG_INFO("malc: api symbol: %s", name->toStdUTF8String().c_str());
+ }
+
+ return newSymbol;
+}
+
+SymbolRef* IsolateWrap::getApiSymbol(StringRef* name) {
+ // FIXME: Use a set
LWNODE_CALL_TRACE_ID(ISOWRAP);
- for (size_t i = 0; i < privateSymbols_.size(); i++) {
- if (privateSymbols_[i]->description()->equals(esString)) {
- return privateSymbols_[i];
+ for (size_t i = 0; i < apiSymbols_.size(); i++) {
+ if (apiSymbols_[i]->description()->equals(name)) {
+ return apiSymbols_[i];
}
}
- auto newSymbol = SymbolRef::create(esString);
- privateSymbols_.push_back(newSymbol);
+ return createApiSymbol(name);
+}
- LWNODE_DLOG_INFO("malc: private symbol: %s",
- esString->toStdUTF8String().c_str());
+SymbolRef* IsolateWrap::createApiPrivateSymbol(StringRef* name) {
+ // FIXME: Use a set
+ auto newSymbol = SymbolRef::create(name);
+ bool found = false;
+ for (size_t i = 0; i < apiPrivateSymbols_.size(); i++) {
+ if (apiPrivateSymbols_[i]->description()->equals(name)) {
+ apiPrivateSymbols_[i] = newSymbol;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ apiPrivateSymbols_.push_back(newSymbol);
+ LWNODE_DLOG_INFO("malc: private symbol: %s",
+ name->toStdUTF8String().c_str());
+ }
return newSymbol;
}
+SymbolRef* IsolateWrap::getApiPrivateSymbol(StringRef* name) {
+ // FIXME: Use a set
+ LWNODE_CALL_TRACE_ID(ISOWRAP);
+
+ for (size_t i = 0; i < apiPrivateSymbols_.size(); i++) {
+ if (apiPrivateSymbols_[i]->description()->equals(name)) {
+ return apiPrivateSymbols_[i];
+ }
+ }
+
+ return createApiPrivateSymbol(name);
+}
+
void IsolateWrap::ClearPendingExceptionAndMessage() {
clear_pending_exception();
clear_pending_message_obj();
}
+void IsolateWrap::CollectGarbage() {
+ globalHandles_->PostGarbageCollectionProcessing();
+}
+
void IsolateWrap::SetPendingExceptionAndMessage(
ValueRef* exception,
GCManagedVector<Escargot::Evaluator::StackTraceData>& stackTraceData) {
bool isHole(const ValueWrap* wrap) override;
bool isHole(const Escargot::ValueRef* ref) override;
- SymbolRef* getPrivateSymbol(StringRef* esString);
+ SymbolRef* createApiSymbol(StringRef* name);
+ SymbolRef* getApiSymbol(StringRef* name);
+
+ SymbolRef* createApiPrivateSymbol(StringRef* name);
+ SymbolRef* getApiPrivateSymbol(StringRef* name);
GlobalHandles* globalHandles() { return globalHandles_; }
+ void CollectGarbage();
+
void SetPendingExceptionAndMessage(
ValueRef* exception,
GCManagedVector<Escargot::Evaluator::StackTraceData>& stackTraceData)
GCVector<ContextWrap*> contextScopes_;
PersistentRefHolder<SymbolRef> privateValuesSymbol_;
- GCVector<Escargot::SymbolRef*> privateSymbols_;
+ GCVector<SymbolRef*> apiSymbols_;
+ GCVector<SymbolRef*> apiPrivateSymbols_;
// Isolate Scope
static THREAD_LOCAL IsolateWrap* s_currentIsolate;
};
uint s = 0;
double c = bytes;
- while (c >= 1024 && s < 7) {
+ while (c >= 1024 && s < 7 - 1) {
c /= 1024;
s++;
}
} while (0);
#endif
-#define LWNODE_LOG_INFO(fmt, ...) LWNODE_LOG_RAW("INFO " fmt, ##__VA_ARGS__);
+#define LWNODE_LOG_INFO(fmt, ...) \
+ LWNODE_LOG_RAW("INFO " fmt COLOR_RESET, ##__VA_ARGS__);
#define LWNODE_LOG_WARN(fmt, ...) \
LWNODE_LOG_RAW(COLOR_YELLOW "WARN " fmt COLOR_RESET, ##__VA_ARGS__);
size_t max_chunks_;
std::unique_ptr<TraceWriter> trace_writer_;
std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
- size_t chunk_index_;
+ size_t chunk_index_ = 0;
bool is_empty_ = true;
uint32_t current_chunk_seq_ = 1;
};
args.append('--depth=' + node_root)
+# @lwnode
+ if '-Dnode_core_target_name=lwnode' not in args:
+ global output_dir
+ output_dir = os.path.join(output_dir, 'v8')
+ args.extend(['--generator-output', output_dir])
+ args.extend(['-Goutput_dir=' + output_dir])
+# @end of lwnode
+
# There's a bug with windows which doesn't allow this feature.
if sys.platform != 'win32' and 'ninja' not in args:
# Tell gyp to write the Makefiles into output_dir