Skip to content

Commit

Permalink
implement PerformanceObserver and friends (#8011)
Browse files Browse the repository at this point in the history
* implement PerformanceObserver, PerformanceMeasure, PerformanceMark and friends

* add PerformanceObserver test, clean up other performance tests

* [autofix.ci] apply automated fixes

* Small cleanup

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 6, 2024
1 parent 2fd85ef commit ecdde88
Show file tree
Hide file tree
Showing 52 changed files with 6,459 additions and 174 deletions.
5 changes: 5 additions & 0 deletions src/bun.js/bindings/ScriptExecutionContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ static void registerHTTPContextForWebSocket(ScriptExecutionContext* script, us_s
}
}

JSGlobalObject* ScriptExecutionContext::globalObject()
{
return m_globalObject;
}

us_socket_context_t* ScriptExecutionContext::webSocketContextSSL()
{
if (!m_ssl_client_websockets_ctx) {
Expand Down
151 changes: 27 additions & 124 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@
#include "JSStringDecoder.h"
#include "JSReadableState.h"
#include "JSReadableHelper.h"
#include "JSPerformance.h"
#include "Performance.h"
#include "JSPerformanceObserver.h"
#include "JSPerformanceObserverEntryList.h"
#include "JSPerformanceEntry.h"
#include "JSPerformanceMeasure.h"
#include "JSPerformanceMark.h"
#include "BunProcess.h"
#include "AsyncContextFrame.h"

Expand Down Expand Up @@ -1085,6 +1092,12 @@ WEBCORE_GENERATED_CONSTRUCTOR_GETTER(FetchHeaders);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(MessageChannel);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(MessageEvent);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(MessagePort);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(Performance);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(PerformanceEntry);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(PerformanceMark);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(PerformanceMeasure);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(PerformanceObserver);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(PerformanceObserverEntryList);
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableByteStreamController)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStream)
WEBCORE_GENERATED_CONSTRUCTOR_GETTER(ReadableStreamBYOBReader)
Expand Down Expand Up @@ -2292,126 +2305,6 @@ extern "C" JSC__JSValue ZigGlobalObject__createNativeReadableStream(Zig::GlobalO
return JSC::JSValue::encode(call(globalObject, function, callData, JSC::jsUndefined(), arguments));
}

extern "C" uint64_t Bun__readOriginTimer(void*);
extern "C" double Bun__readOriginTimerStart(void*);

static inline JSC::EncodedJSValue functionPerformanceNowBody(JSGlobalObject* globalObject)
{
auto* global = reinterpret_cast<GlobalObject*>(globalObject);
// nanoseconds to seconds
double time = static_cast<double>(Bun__readOriginTimer(global->bunVM()));
double result = time / 1000000.0;

// https://github.com/oven-sh/bun/issues/5604
return JSValue::encode(jsDoubleNumber(result));
}

static inline EncodedJSValue functionPerformanceGetEntriesByNameBody(JSGlobalObject* globalObject)
{
auto& vm = globalObject->vm();
auto* global = reinterpret_cast<GlobalObject*>(globalObject);
auto* array = JSC::constructEmptyArray(globalObject, nullptr);
return JSValue::encode(array);
}

extern "C" {
class JSPerformanceObject;
static JSC_DECLARE_HOST_FUNCTION(functionPerformanceNow);
static JSC_DECLARE_HOST_FUNCTION(functionPerformanceGetEntriesByName);
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(functionPerformanceNowWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject*, JSPerformanceObject*));
}

class JSPerformanceObject final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
static JSPerformanceObject* create(JSC::VM& vm, JSDOMGlobalObject* globalObject, JSC::Structure* structure)
{
JSPerformanceObject* ptr = new (NotNull, JSC::allocateCell<JSPerformanceObject>(vm)) JSPerformanceObject(vm, globalObject, structure);
ptr->finishCreation(vm);
return ptr;
}

DECLARE_INFO;
template<typename CellType, JSC::SubspaceAccess>
static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
STATIC_ASSERT_ISO_SUBSPACE_SHARABLE(JSPerformanceObject, Base);
return &vm.plainObjectSpace();
}
static JSC::Structure* createStructure(JSC::VM& vm, JSC::JSGlobalObject* globalObject, JSC::JSValue prototype)
{
return JSC::Structure::create(vm, globalObject, prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags), info());
}

private:
JSPerformanceObject(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure)
: JSC::JSNonFinalObject(vm, structure)
{
}

void finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
static const JSC::DOMJIT::Signature DOMJITSignatureForPerformanceNow(
functionPerformanceNowWithoutTypeCheck,
JSPerformanceObject::info(),
JSC::DOMJIT::Effect::forWriteKinds(DFG::AbstractHeapKind::SideState),
SpecDoubleReal);

JSFunction* now = JSFunction::create(
vm,
globalObject(),
0,
String("now"_s),
functionPerformanceNow, ImplementationVisibility::Public, NoIntrinsic, functionPerformanceNow,
&DOMJITSignatureForPerformanceNow);
this->putDirect(vm, JSC::Identifier::fromString(vm, "now"_s), now, 0);

JSFunction* noopNotImplemented = JSFunction::create(
vm,
globalObject(),
0,
String("noopNotImplemented"_s),
functionNoop, ImplementationVisibility::Public, NoIntrinsic, functionNoop,
nullptr);

this->putDirect(vm, JSC::Identifier::fromString(vm, "mark"_s), noopNotImplemented, 0);
this->putDirect(vm, JSC::Identifier::fromString(vm, "markResourceTiming"_s), noopNotImplemented, 0);
this->putDirect(vm, JSC::Identifier::fromString(vm, "measure"_s), noopNotImplemented, 0);

// this is a stub impl for getEntriesByName(https://github.com/oven-sh/bun/issues/6537)
JSFunction* getEntriesByName = JSFunction::create(
vm, globalObject(), 0, String("getEntriesByName"_s), functionPerformanceGetEntriesByName, ImplementationVisibility::Public, NoIntrinsic, functionPerformanceGetEntriesByName, nullptr);
this->putDirect(vm, JSC::Identifier::fromString(vm, "getEntriesByName"_s), getEntriesByName, 0);
this->putDirect(
vm,
JSC::Identifier::fromString(vm, "timeOrigin"_s),
jsNumber(Bun__readOriginTimerStart(reinterpret_cast<Zig::GlobalObject*>(this->globalObject())->bunVM())),
PropertyAttribute::ReadOnly | 0);
}
};
const ClassInfo JSPerformanceObject::s_info = { "Performance"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSPerformanceObject) };

JSC_DEFINE_HOST_FUNCTION(functionPerformanceNow, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
return functionPerformanceNowBody(globalObject);
}

JSC_DEFINE_HOST_FUNCTION(functionPerformanceGetEntriesByName, (JSGlobalObject * globalObject, JSC::CallFrame* callFrame))
{
return functionPerformanceGetEntriesByNameBody(globalObject);
}

JSC_DEFINE_JIT_OPERATION(functionPerformanceNowWithoutTypeCheck, JSC::EncodedJSValue, (JSC::JSGlobalObject * lexicalGlobalObject, JSPerformanceObject* castedThis))
{
VM& vm = JSC::getVM(lexicalGlobalObject);
IGNORE_WARNINGS_BEGIN("frame-address")
CallFrame* callFrame = DECLARE_CALL_FRAME(vm);
IGNORE_WARNINGS_END
JSC::JITOperationPrologueCallFrameTracer tracer(vm, callFrame);
return functionPerformanceNowBody(lexicalGlobalObject);
}

extern "C" JSC__JSValue Bun__Jest__createTestModuleObject(JSC::JSGlobalObject*);
extern "C" JSC__JSValue Bun__Jest__createTestPreloadObject(JSC::JSGlobalObject*);
extern "C" JSC__JSValue Bun__Jest__testPreloadObject(Zig::GlobalObject* globalObject)
Expand Down Expand Up @@ -3189,10 +3082,8 @@ void GlobalObject::finishCreation(VM& vm)

m_performanceObject.initLater(
[](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSObject>::Initializer& init) {
JSPerformanceObject* object = JSPerformanceObject::create(init.vm, reinterpret_cast<Zig::GlobalObject*>(init.owner),
JSPerformanceObject::createStructure(init.vm, init.owner, init.owner->objectPrototype()));

init.set(object);
auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(init.owner);
init.set(toJS(init.owner, globalObject, globalObject->performance().get()).getObject());
});

m_processEnvObject.initLater(
Expand Down Expand Up @@ -4055,6 +3946,18 @@ extern "C" void Bun__performTask(Zig::GlobalObject* globalObject, WebCore::Event
task->performTask(*globalObject->scriptExecutionContext());
}

RefPtr<Performance> GlobalObject::performance()
{
if (!m_performance) {
auto* context = this->scriptExecutionContext();
double nanoTimeOrigin = Bun__readOriginTimerStart(this->bunVM());
auto timeOrigin = MonotonicTime::fromRawSeconds(nanoTimeOrigin / 1000.0);
m_performance = Performance::create(context, timeOrigin);
}

return m_performance;
}

void GlobalObject::queueTask(WebCore::EventLoopTask* task)
{
Bun__queueTask(this, task);
Expand Down
6 changes: 5 additions & 1 deletion src/bun.js/bindings/ZigGlobalObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class DOMWrapperWorld;
class GlobalScope;
class SubtleCrypto;
class EventTarget;
class Performance;
} // namespace WebCore

namespace Bun {
Expand Down Expand Up @@ -266,6 +267,8 @@ class GlobalObject : public JSC::JSGlobalObject {

bool hasProcessObject() const { return m_processObject.isInitialized(); }

RefPtr<WebCore::Performance> performance();

JSC::JSObject* processObject() { return m_processObject.getInitializedOnMainThread(this); }
JSC::JSObject* processEnvObject() { return m_processEnvObject.getInitializedOnMainThread(this); }
JSC::JSObject* bunObject() { return m_bunObject.getInitializedOnMainThread(this); }
Expand All @@ -278,7 +281,7 @@ class GlobalObject : public JSC::JSGlobalObject {
template<typename Visitor>
void visitGeneratedLazyClasses(GlobalObject*, Visitor&);

ALWAYS_INLINE void* bunVM() { return m_bunVM; }
ALWAYS_INLINE void* bunVM() const { return m_bunVM; }
bool isThreadLocalDefaultGlobalObject = false;

JSObject* subtleCrypto() { return m_subtleCryptoObject.getInitializedOnMainThread(this); }
Expand Down Expand Up @@ -448,6 +451,7 @@ class GlobalObject : public JSC::JSGlobalObject {
Lock m_gcLock;
Ref<WebCore::DOMWrapperWorld> m_world;
Bun::CommonStrings m_commonStrings;
RefPtr<WebCore::Performance> m_performance { nullptr };

// JSC's hashtable code-generator tries to access these properties, so we make them public.
// However, we'd like it better if they could be protected.
Expand Down
8 changes: 7 additions & 1 deletion src/bun.js/bindings/ZigGlobalObject.lut.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
MessageChannel MessageChannelConstructorCallback PropertyCallback
MessageEvent MessageEventConstructorCallback PropertyCallback
MessagePort MessagePortConstructorCallback PropertyCallback
Performance PerformanceConstructorCallback PropertyCallback
PerformanceEntry PerformanceEntryConstructorCallback PropertyCallback
PerformanceMark PerformanceMarkConstructorCallback PropertyCallback
PerformanceMeasure PerformanceMeasureConstructorCallback PropertyCallback
PerformanceObserver PerformanceObserverConstructorCallback PropertyCallback
PerformanceObserverEntryList PerformanceObserverEntryListConstructorCallback PropertyCallback
ReadableByteStreamController ReadableByteStreamControllerConstructorCallback PropertyCallback
ReadableStream ReadableStreamConstructorCallback PropertyCallback
ReadableStreamBYOBReader ReadableStreamBYOBReaderConstructorCallback PropertyCallback
Expand All @@ -70,13 +76,13 @@
SubtleCrypto SubtleCryptoConstructorCallback PropertyCallback
TextEncoder TextEncoderConstructorCallback PropertyCallback
TransformStream TransformStreamConstructorCallback PropertyCallback
TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback
URL DOMURLConstructorCallback PropertyCallback
URLSearchParams URLSearchParamsConstructorCallback PropertyCallback
WebSocket WebSocketConstructorCallback PropertyCallback
Worker WorkerConstructorCallback PropertyCallback
WritableStream WritableStreamConstructorCallback PropertyCallback
WritableStreamDefaultController WritableStreamDefaultControllerConstructorCallback PropertyCallback
WritableStreamDefaultWriter WritableStreamDefaultWriterConstructorCallback PropertyCallback
TransformStreamDefaultController TransformStreamDefaultControllerConstructorCallback PropertyCallback
@end
*/
14 changes: 7 additions & 7 deletions src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -678,18 +678,18 @@ class DOMClientIsoSubspaces {
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForIntersectionObserverEntry;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForLocation;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForNavigator;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformance;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceEntry;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceMark;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceMeasure;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformance;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceEntry;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceMark;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceMeasure;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceNavigation;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceNavigationTiming;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceObserver;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceObserverEntryList;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceObserver;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceObserverEntryList;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformancePaintTiming;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceResourceTiming;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceServerTiming;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceTiming;
std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForPerformanceTiming;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForRemoteDOMWindow;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForResizeObserver;
// std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForResizeObserverEntry;
Expand Down
14 changes: 7 additions & 7 deletions src/bun.js/bindings/webcore/DOMIsoSubspaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -671,18 +671,18 @@ class DOMIsoSubspaces {
// std::unique_ptr<IsoSubspace> m_subspaceForIntersectionObserverEntry;
// std::unique_ptr<IsoSubspace> m_subspaceForLocation;
// std::unique_ptr<IsoSubspace> m_subspaceForNavigator;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformance;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceEntry;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceMark;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceMeasure;
std::unique_ptr<IsoSubspace> m_subspaceForPerformance;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceEntry;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceMark;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceMeasure;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceNavigation;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceNavigationTiming;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceObserver;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceObserverEntryList;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceObserver;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceObserverEntryList;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformancePaintTiming;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceResourceTiming;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceServerTiming;
// std::unique_ptr<IsoSubspace> m_subspaceForPerformanceTiming;
std::unique_ptr<IsoSubspace> m_subspaceForPerformanceTiming;
// std::unique_ptr<IsoSubspace> m_subspaceForRemoteDOMWindow;
// std::unique_ptr<IsoSubspace> m_subspaceForResizeObserver;
// std::unique_ptr<IsoSubspace> m_subspaceForResizeObserverEntry;
Expand Down
Loading

0 comments on commit ecdde88

Please sign in to comment.