Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

JS: Backport³ and more additions & fixes #3961

Merged
merged 47 commits into from
Oct 31, 2024

Merge branch 'dev' into js-backport3-and-more

a0b27a9
Select commit
Loading
Failed to load commit list.
Merged

JS: Backport³ and more additions & fixes #3961

Merge branch 'dev' into js-backport3-and-more
a0b27a9
Select commit
Loading
Failed to load commit list.
Task list completed / task-list-completed succeeded Oct 31, 2024 in 0s

3 / 3 tasks completed

All tasks have been completed

Details

Required Tasks

Task Status
backport (here) of backported missing things (there) of great backport & rework (over yonder) xD Incomplete
i separated into individual commits that make sense on their own, might be easier to look there instead of whole diff Incomplete
original authors/contributors credited with co-author where relevant Incomplete
numpad keys support Incomplete
keyboard layout support Incomplete
altPrint() and altPrintln() support Incomplete
quit() function to unlock usb profile (original intent was to alternate badusb profile and mass storage profile, we have this in momentum and other cfw as usbdisk module, but outside scope of this pr, can backport this module later on if you're interested) Incomplete
readAny() to avoid starving read loops with many small reads or hinder usability with few large reads Incomplete
end() to release serial handle allowing new initialization in same script, maybe for gpio usage or different serial port or settings Incomplete
expansion service is automatically disabled to avoid conflict Incomplete
added simple example script we had previously, ported to new remade storage module Incomplete
fixed null ptr crash when user did not pass max len prop. logic of props is from what i can see, always configure view for minimum usable state, props should configure things on top as optional Incomplete
defaultText and defaultTextClear props Incomplete
byte_input Incomplete
there is no file picker based on View object, so this is only gui/* module that is not based on view_factory Incomplete
instead it uses usual dialogs app file picker in sync manner Incomplete
viewDispatcher.currentView to allow users to have extra logic in navigation callbacks for example Incomplete
updated gui example script with showcase of all additions above Incomplete
toString() works correctly on negative numbers, toString(-42) used to give "0" Incomplete
parseInt() Incomplete
toUpperCase() and toLowerCase() Incomplete
example for string functions Incomplete
__filepath and __dirpath to allow relative path contextualization, for example for load() or storage.open() Incomplete
example interactive.js repl script, allows running js in limited capacity without editing files Incomplete
many additions to typedefs that were missing or incomplete, still not everything is documented i believe but getting closer Incomplete
toString(), toUpperCase(), toLowerCase() could all be moved to respective classes i think, for strings there is getprop_builtin_string() in mjs source, can add there, for number there is no such function but may be possible to add another if branch in getprop_builtin() for number type Incomplete
load() accepts a custom scope parameter. with custom scope, assignment in child script is confined to custom scope, lookup is recursive to parent scopes. with no custom scope, code is executed in current scope, so everything is shared like code was inlined Incomplete
for custom scope, we can get custom scope object passed by user and inject __filepath and __dirpath into it, easy Incomplete
for no custom scope, i dont see any good solution: one way is saving current __dirpath and __filepath values from scope, changing them, running load(), then resetting to previous values. might work fine in most cases but if child script runs load() of its own and uses callbacks or things like this, child2 might run a function defined in child1 and then its screwed up. this is a rare edge case of course, if no function callback is passed from child1 to child2 then no code from child1 could run while child2, and second instance of mjs_load() would restore child1's __dirpath and __filepath correctly Incomplete
unit tests Incomplete
example scripts Incomplete
PR has description of feature/bug or link to Confluence/Jira task Completed
Description contains actions to verify feature/bugfix Completed
I've built this code, uploaded it to the device and verified feature/bugfix Completed
We want to maintain a base JS API that is consistent across all firmware flavors. Incomplete
We want JS app developers to be able to leverage additional functionality found in custom firmware editions. Incomplete
We want users not to think about cross-FW compatibility of the scripts that they find online too much. Incomplete
A JS app that works on OFW should also work on a CFW. Incomplete
A JS app that works on a CFW and leverages its extended features may or may not work on OFW. Incomplete
A JS app that does not leverage any additional CFW functionality is guaranteed to work across all firmware editions. Incomplete
A JS app that requires additional CFW functionality will not work on OFW. Incomplete
One can write JS apps that can make use of but do not require additional CFW functionality. Incomplete
CFWs are free to introduce additional JS APIs. Incomplete
CFWs are discouraged from breaking the base JS API found in OFW. Incomplete
CFWs are encouraged to backport any of their extended features into OFW, assuming they are compatible with OFW's vision. Incomplete
checkSdkCompatibility(expectedMajor, expectedMinor): checks if the JS API version is compatible with the one that the script expects (i.e. expectedMajor == actualMajor && expectedMinor >= actualMinor), and if it doesn't, warns the user and asks whether they want to continue executing the script anyways. This is the simplest of the three checks, and it will be automatically inserted in the beginning of the output file unless the developer explicitly asks our JS "toolchain" (SDK) not to do so. By asserting compatibility by default, we guarantee that the user receives a clear warning if their firmware is incompatible with the script instead of cryptic error messages like calling non-callable. Incomplete
isSdkCompatible(expectedMajor, expectedMinor): performs the same check, but just returns the result as a boolean. Incomplete
sdkCompatibilityStatus(expectedMajor, expectedMinor): performs the same check, but returns a more detailed description ("compatible", "firmwareTooOld", "firmwareTooNew") Incomplete
checkSdkVendor(expectedVendor): checks if the JS API vendor matches the one that the script expects, and if it doesn't, warns the user and asks whether they want to continue executing the script anyways. This is the simplest of the two checks for cases where an app requires a feature found in a CFW. This check will not be automatically inserted by our SDK (unlike checkSdkCompatibility). Incomplete
sdkVendor: a string containing the SDK vendor (e.g. "flipperdevices" or "momentum"). A JS app that is able to work without additional features on a best-effort basis may query this global constant to find out whether it can use those features. Incomplete
Both OFW and Momentum are at JS SDK version 1.0. Incomplete
Momentum introduces a feature called "text-input-regex" and bumps its JS SDK version to 1.1. Incomplete
The feature is backported into OFW, bumping its JS SDK version to 1.1 as well. Incomplete
Both OFW and Momentum continue to report true for doesSdkSupport("text-input-regex") until their JS SDK versions are bumped up to 2.0 Incomplete
Starting with JS SDK 2.0, doesSdkSupport("text-input-regex") starts reporting false and checkSdkFeatures(["text-input-regex"]) starts warning the user that a feature is unsupported. I assert that the negative effect from these incorrect results is alleviated thanks to SDK version checks, which will fail if the script expects v1.x but is actually running on 2.x. Incomplete
changing the examples to show view.set("defaultText", "text"); after the first makeWith() call Incomplete
changing behavior of defaultText and defaultData assign to resize the buffer to atleast the size specified by the default provided. for example, default is 4 bytes for byteinput, default data is 10 long so enlarge to 10, then we also have length prop provided, so enlarge or shrink as requested. this brings up another problem however: what if the user provides a defaultdata longer than the length they specify? if length is assigned first, then it will be resized later by default data. if defaultdata is assigned first, then it will be shrunk later by length. and we cannot solve this by enforcing the size specified by length, because thats the original issue: if defaultdata is assigned before than user provided length, then length is still default of 4 and default data is cutoff despire the user specifying otherwise. Incomplete
MJS_TAG_STRING_I and MJS_TAG_STRING_5: embedded in the lower 5 bytes of term. The tag is stored in the upper 2 bytes, leaving a byte in the middle which will be 0 b/c terms are zero-initialized, at least in mjs_mk_string. Incomplete
MJS_TAG_STRING_O: GC'ed owned strings, which are always stored with the terminator at runtime. The parser does store them without the terminator in the same giant buffer with all the other strings, but we never use raw internally referenced strings from the parser. The bytecode interpreter does, but it converts them into nice null-terminated strings with mjs_mk_string for us. Incomplete
MJS_TAG_STRING_F: foreign strings, not managed by mJS at all. The main use case is to avoid wasting RAM for strings that you already have in .rodata. One could sneak a non-null-terminated string into mjs_mk_string(/*...*/, false), but I'd just call that irresponsible, honestly. Either way, nothing in our codebase does that; not us, not mJS itself. Incomplete
MJS_TAG_STRING_C: seems to be used internally during garbage collection, never seen by us. Incomplete
MJS_TAG_STRING_D: all uses are commented out, the symbols in the commented out code are no longer there, I can't tell. Incomplete
I don't like how with the max length thing the fields have to be arranged in a specific way for the thing to work. Field ordering is absolutely not something that the ECMAScript specification guarantees. This is especially important with the new build pipeline underway. Incomplete
Not an issue with this particular PR, just a thing to note in regards to #3963: .d.ts files would have to be moved, and the new declarations would have to get @version doc comments. Incomplete
I don't like that the file browser is not a view. But that's an issue for later, I don't think that this PR needs to change this. Incomplete
API versioning in JS got different nature, not like api versioning in API symbols, it should be much easier to maintain it Incomplete
We'll have checkSdk() but as an optional canary Incomplete
If you call it we'll check version and ask user what to do if version doesn't match Incomplete
If your script doesn't contain it, then no checks is done and you'll crash later Incomplete
I'd like to see all of us cooperating on JS API: keep it consistent across all forks, simplifying portability between different firmware flavors. It make sense to expose additional API to query which firmware flavor we are running at Incomplete