Guide for adding new JSI functionality to the JavaScript Interface (JSI) layer. Use when the user asks to add, create, or implement new methods or features in the JSI Runtime interface. Covers all required files across JSI core, Hermes implementation, and SynthTrace replay support.
When adding new functionality (methods) to the JSI Runtime interface, you must modify a specific set of files across multiple layers. This skill describes each file, the patterns to follow, and important conventions.
JSI (JavaScript Interface) is an abstraction layer that allows C++ code to interact with JavaScript runtimes. The architecture consists of:
xplat/jsi/jsi/) — The abstract interface definitionsxplat/static_h/API/hermes/) — Hermes-specific implementationxplat/static_h/API/hermes/) — Recording and replay infrastructure for debuggingxplat/jsi/jsi/jsi.h — Add pure virtual method declaration to interface AND override declaration in classIRuntimeRuntimexplat/jsi/jsi/jsi.cpp — Add default implementation (if providing one)xplat/jsi/jsi/jsi-inl.h — Add inline helper methods (if needed)xplat/jsi/jsi/decorator.h — Add method overrides to RuntimeDecorator and WithRuntimeDecoratorxplat/jsi/jsi/test/testlib.cpp — Add tests for the JSI APIxplat/static_h/API/hermes/hermes.cpp — Add Hermes-specific implementation in HermesRuntimeImplxplat/static_h/unittests/API/APITest.cpp — Add Hermes-specific testsxplat/static_h/API/hermes/SynthTrace.h — Add new Record typesxplat/static_h/API/hermes/SynthTrace.cpp — Implement Record serializationxplat/static_h/API/hermes/TracingRuntime.h — Declare tracing method overridesxplat/static_h/API/hermes/TracingRuntime.cpp — Implement tracing logicxplat/static_h/API/hermes/SynthTraceParser.cpp — Add parsing for new recordsxplat/static_h/API/hermes/TraceInterpreter.cpp — Add replay logicxplat/static_h/unittests/API/SynthTraceTest.cpp — Add replay testsxplat/static_h/unittests/API/SynthTraceSerializationTest.cpp — Add serialization testsxplat/static_h/unittests/API/SynthTraceParserTest.cpp — Add parser testsWhen adding new features (methods) to the JSI Runtime, you have two options:
Provide a default implementation in jsi::Runtime that works via JavaScript
calls. This allows all runtimes (JSC, V8, etc.) to work without modification.
// jsi.h - Add virtual method with default implementation
virtual void myNewMethod(const Object& obj, const Value& val);
// jsi.cpp - Implement default using JavaScript
void Runtime::myNewMethod(const Object& obj, const Value& val) {
auto myFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "myMethod");
myFn.call(*this, obj, val);
}
Make the method pure virtual, requiring all runtimes (JSCRuntime, V8Runtime, HermesRuntime) to implement it. Only use this when a JavaScript-based default is not possible.
jsi.h (JSI Core Interface)Add the pure virtual method declaration to the IRuntime interface, and the override declaration to the Runtime class:
// In IRuntime interface (around line 580)
class JSI_EXPORT IRuntime : public ICast {
// ... existing methods ...
/// Brief description of what the method does.
/// \param obj Description of the object parameter.
/// \param val Description of the value parameter.
/// \return Description of return value (if any).
virtual void myNewMethod(const Object& obj, const Value& val) = 0;
// For methods returning Value, use this pattern:
virtual Value myNewGetter(const Object& obj) = 0;
};
// In Runtime class (around line 730) - add override declarations
class JSI_EXPORT Runtime : public IRuntime {
// ... existing methods ...
void myNewMethod(const Object& obj, const Value& val) override;
Value myNewGetter(const Object& obj) override;
};
Important: The IRuntime interface contains the pure virtual declarations (= 0),
while the Runtime class provides the default implementation with override keyword.
This separation allows other runtimes to implement the interface differently while
Runtime provides a JavaScript-based fallback.
If adding convenience methods to JSI types (like `Object`), add them too:
```cpp
class JSI_EXPORT Object : public Pointer {
// ... existing methods ...
/// Sets something on the object.
void setMyThing(IRuntime& runtime, const Value& val) const {
return runtime.myNewMethod(*this, val);
}
/// Gets something from the object.
inline Value getMyThing(Runtime& runtime) const;
};
jsi.cppImplement the default using JavaScript APIs when possible:
void Runtime::myNewMethod(const Object& obj, const Value& val) {
// Get the JavaScript function
auto myMethodFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "myMethod");
// Call it with the appropriate arguments
myMethodFn.call(*this, obj, val);
}
Value Runtime::myNewGetter(const Object& obj) {
auto myGetterFn = global()
.getPropertyAsObject(*this, "Object")
.getPropertyAsFunction(*this, "myGetter");
return myGetterFn.call(*this, obj);
}
jsi-inl.h (If Needed)For methods that return incomplete types at declaration time:
Value Object::getMyThing(Runtime& runtime) const {
return runtime.myNewGetter(*this);
}
decorator.hAdd method overrides to both RuntimeDecorator and WithRuntimeDecorator:
// In RuntimeDecorator class (around line 282)
void myNewMethod(const Object& obj, const Value& val) override {
plain_.myNewMethod(obj, val);
}
Value myNewGetter(const Object& obj) override {
return plain_.myNewGetter(obj);
}
// In WithRuntimeDecorator class (around line 760)
void myNewMethod(const Object& obj, const Value& val) override {
Around around{with_};
RD::myNewMethod(obj, val);
}
Value myNewGetter(const Object& obj) override {
Around around{with_};
return RD::myNewGetter(obj);
}
test/testlib.cppTest the default implementation using a RuntimeDecorator:
TEST_P(JSITest, MyNewFeature) {
// Use a RuntimeDecorator to test the default implementation
class RD : public RuntimeDecorator<Runtime, Runtime> {
public:
explicit RD(Runtime& rt) : RuntimeDecorator(rt) {}
void myNewMethod(const Object& obj, const Value& val) override {
return Runtime::myNewMethod(obj, val);
}
Value myNewGetter(const Object& obj) override {
return Runtime::myNewGetter(obj);
}
};
RD rd = RD(rt);
Object obj(rd);
// Test the functionality
obj.setMyThing(rd, Value(123));
EXPECT_EQ(obj.getMyThing(rd).getNumber(), 123);
}
hermes.cppImplement the optimized version using Hermes VM APIs inside HermesRuntimeImpl:
// In HermesRuntimeImpl class declaration (around line 673)
void myNewMethod(const jsi::Object& obj, const jsi::Value& val) override;