feat: support native ES classes with lazy registration#1983
feat: support native ES classes with lazy registration#1983NathanWalker wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (9)
🚧 Files skipped from review as they are similar to previous changes (8)
📝 WalkthroughWalkthroughNative ES classes extending native types now lazily generate Java proxy classes, support Java dispatch and interface implementation, marshal to ChangesNative ES class proxy support
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant ESConstructor
participant MetadataNode
participant CallbackHandlers
participant JavaResolver
ESConstructor->>MetadataNode: Resolve native class type
MetadataNode->>MetadataNode: EnsureExtendedESClass
MetadataNode->>CallbackHandlers: ResolveClass with overrides and interfaces
CallbackHandlers->>JavaResolver: resolveClass
JavaResolver-->>CallbackHandlers: Return generated proxy class
CallbackHandlers-->>MetadataNode: Return cached class
MetadataNode-->>ESConstructor: Attach TypeMetadata
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test-app/runtime/src/main/cpp/JsArgConverter.cpp`:
- Around line 155-173: Update the failure message construction in
JsArgConverter’s function-conversion branch to use a bounded write matching
buff’s 1024-byte capacity, replacing the unbounded sprintf call while preserving
the existing message and index values.
In `@test-app/runtime/src/main/cpp/JsArgToArrayConverter.cpp`:
- Around line 137-154: Update the arg->IsFunction() handling in
JsArgToArrayConverter to permit native constructor marshalling only when the
target component type is java.lang.Class or java.lang.Object, matching the
scalar converter’s target-type check. Reject constructors for String,
interfaces, and other incompatible component types before SetConvertedObject,
while preserving successful conversion for Class[] and Object[] and the existing
error reporting.
In `@test-app/runtime/src/main/cpp/MetadataNode.cpp`:
- Around line 1304-1313: Update the deterministic name generation in the
ResolveClass path around HashESClassId to include the generated proxy shape,
specifically overridden methods and static interfaces, in the cache key
alongside scriptName, baseClassName, and className. Ensure equivalent shapes
remain stable while changed shapes produce distinct fullClassName values, and
add a regression covering cache reuse with the same class identity but a changed
override/interface set.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: e8e4729c-31fa-43dd-af9f-83c5e85f7cb5
📒 Files selected for processing (9)
test-app/app/src/main/assets/app/mainpage.jstest-app/app/src/main/assets/app/tests/testNativeESClasses.jstest-app/app/src/main/assets/internal/ts_helpers.jstest-app/runtime/src/main/cpp/CallbackHandlers.cpptest-app/runtime/src/main/cpp/CallbackHandlers.htest-app/runtime/src/main/cpp/JsArgConverter.cpptest-app/runtime/src/main/cpp/JsArgToArrayConverter.cpptest-app/runtime/src/main/cpp/MetadataNode.cpptest-app/runtime/src/main/cpp/MetadataNode.h
| } else if (arg->IsFunction() && | ||
| (typeSignature == "Ljava/lang/Class;" || typeSignature == "Ljava/lang/Object;")) { | ||
| // a native type ctor (or a plain ES class extending one - registered lazily here) | ||
| // passed where Java expects a java.lang.Class | ||
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(m_isolate, arg.As<Function>()); | ||
|
|
||
| if (!typeName.empty()) { | ||
| JEnv env; | ||
| jclass clazz = env.FindClass(typeName); | ||
| success = clazz != nullptr; | ||
| if (success) { | ||
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | ||
| SetConvertedObject(index, clazz, true /* isGlobal */); | ||
| } | ||
| } | ||
|
|
||
| if (!success) { | ||
| sprintf(buff, "Cannot convert function to %s at index %d", typeSignature.c_str(), index); | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Bound the function-conversion error message write.
Line 171 copies the unbounded typeSignature into the fixed 1024-byte buff, allowing stack corruption. Use snprintf, consistent with the buffer size.
Proposed fix
- sprintf(buff, "Cannot convert function to %s at index %d", typeSignature.c_str(), index);
+ snprintf(buff, sizeof(buff), "Cannot convert function to %s at index %d",
+ typeSignature.c_str(), index);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (arg->IsFunction() && | |
| (typeSignature == "Ljava/lang/Class;" || typeSignature == "Ljava/lang/Object;")) { | |
| // a native type ctor (or a plain ES class extending one - registered lazily here) | |
| // passed where Java expects a java.lang.Class | |
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(m_isolate, arg.As<Function>()); | |
| if (!typeName.empty()) { | |
| JEnv env; | |
| jclass clazz = env.FindClass(typeName); | |
| success = clazz != nullptr; | |
| if (success) { | |
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | |
| SetConvertedObject(index, clazz, true /* isGlobal */); | |
| } | |
| } | |
| if (!success) { | |
| sprintf(buff, "Cannot convert function to %s at index %d", typeSignature.c_str(), index); | |
| } | |
| } else if (arg->IsFunction() && | |
| (typeSignature == "Ljava/lang/Class;" || typeSignature == "Ljava/lang/Object;")) { | |
| // a native type ctor (or a plain ES class extending one - registered lazily here) | |
| // passed where Java expects a java.lang.Class | |
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(m_isolate, arg.As<Function>()); | |
| if (!typeName.empty()) { | |
| JEnv env; | |
| jclass clazz = env.FindClass(typeName); | |
| success = clazz != nullptr; | |
| if (success) { | |
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | |
| SetConvertedObject(index, clazz, true /* isGlobal */); | |
| } | |
| } | |
| if (!success) { | |
| snprintf(buff, sizeof(buff), "Cannot convert function to %s at index %d", | |
| typeSignature.c_str(), index); | |
| } |
🧰 Tools
🪛 ast-grep (0.44.1)
[error] 171-171: Use of an unbounded buffer function that can overflow the destination; use a size-bounded equivalent (fgets, strncpy/strlcpy, strncat/strlcat, snprintf).
Context: sprintf(buff, "Cannot convert function to %s at index %d", typeSignature.c_str(), index)
Note: [CWE-120] Buffer Copy without Checking Size of Input ('Classic Buffer Overflow').
(dangerous-buffer-functions-cpp)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test-app/runtime/src/main/cpp/JsArgConverter.cpp` around lines 155 - 173,
Update the failure message construction in JsArgConverter’s function-conversion
branch to use a bounded write matching buff’s 1024-byte capacity, replacing the
unbounded sprintf call while preserving the existing message and index values.
Source: Linters/SAST tools
| } else if (arg->IsFunction()) { | ||
| // a native type ctor (or a plain ES class extending one - registered lazily here) | ||
| // marshals to its java.lang.Class | ||
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(isolate, arg.As<Function>()); | ||
|
|
||
| if (!typeName.empty()) { | ||
| jclass clazz = env.FindClass(typeName); | ||
| if (clazz != nullptr) { | ||
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | ||
| SetConvertedObject(env, index, clazz, true /* isGlobal */); | ||
| success = true; | ||
| } | ||
| } | ||
|
|
||
| if (!success) { | ||
| s << "Cannot marshal JavaScript function at index " << index | ||
| << " to Java type. Only native type constructors can be marshalled (to java.lang.Class)."; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Restrict constructor marshalling to Class[] and Object[].
This branch converts every native constructor to a jclass, even when the target component is String, an interface, or another incompatible type. That reports conversion success and defers failure to JNI array storage. Mirror the scalar converter’s target-type check.
Proposed fix
- } else if (arg->IsFunction()) {
+ } else if (arg->IsFunction() &&
+ (m_return_type == "Ljava/lang/Class;" ||
+ m_return_type == "Ljava/lang/Object;")) {Please cover Class[], Object[], and rejection for an incompatible component type.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } else if (arg->IsFunction()) { | |
| // a native type ctor (or a plain ES class extending one - registered lazily here) | |
| // marshals to its java.lang.Class | |
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(isolate, arg.As<Function>()); | |
| if (!typeName.empty()) { | |
| jclass clazz = env.FindClass(typeName); | |
| if (clazz != nullptr) { | |
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | |
| SetConvertedObject(env, index, clazz, true /* isGlobal */); | |
| success = true; | |
| } | |
| } | |
| if (!success) { | |
| s << "Cannot marshal JavaScript function at index " << index | |
| << " to Java type. Only native type constructors can be marshalled (to java.lang.Class)."; | |
| } | |
| } else if (arg->IsFunction() && | |
| (m_return_type == "Ljava/lang/Class;" || | |
| m_return_type == "Ljava/lang/Object;")) { | |
| // a native type ctor (or a plain ES class extending one - registered lazily here) | |
| // marshals to its java.lang.Class | |
| auto typeName = MetadataNode::TryResolveClassCtorTypeName(isolate, arg.As<Function>()); | |
| if (!typeName.empty()) { | |
| jclass clazz = env.FindClass(typeName); | |
| if (clazz != nullptr) { | |
| // JEnv caches classes as global refs - mark as global so the dtor doesn't delete it | |
| SetConvertedObject(env, index, clazz, true /* isGlobal */); | |
| success = true; | |
| } | |
| } | |
| if (!success) { | |
| s << "Cannot marshal JavaScript function at index " << index | |
| << " to Java type. Only native type constructors can be marshalled (to java.lang.Class)."; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@test-app/runtime/src/main/cpp/JsArgToArrayConverter.cpp` around lines 137 -
154, Update the arg->IsFunction() handling in JsArgToArrayConverter to permit
native constructor marshalling only when the target component type is
java.lang.Class or java.lang.Object, matching the scalar converter’s target-type
check. Reject constructors for String, interfaces, and other incompatible
component types before SetConvertedObject, while preserving successful
conversion for Class[] and Object[] and the existing error reporting.
3b966c1 to
2b209a5
Compare
Parity with NativeScript/ios#403
Summary by CodeRabbit
class extendssyntax, including overridden getters/methods, constructor forwarding, static members, and interface dispatch.java.lang.Class/Objectcorrectly.NativeClassdecorator for compatibility with transpiled/annotated code.