repo | stars | last commit | comments |
---|---|---|---|
bellard/quickjs | WASM, benchmarks | ||
patriksimek/vm2 | nodejs | ||
NeilFraser/JS-Interpreter | based on vm-browserify, old, no package | ||
sablejs/sablejs | benchmarks, ES5.1 = ES2009 | ||
engine262/engine262 | ES2020, complex interface, stepped execution, empty sandbox | ||
bplok20010/eval5 | ES5 = ES2009 | ||
Siubaak/sval | ES2019, simple interface | ||
metaes/metaes |
last commit is over 1 year ago
repo | stars | last commit | comments |
---|---|---|---|
bramblex/jsjs | |||
jterrace/js.js | based on SpiderMonkey, last version 2012 | ||
mozilla/narcissus | archived | ||
zuluoaaa/makeJs | |||
jrainlau/canjs | |||
hacksparrow/safe-eval | nodejs | ||
browserify/vm-browserify | substack/vm-browserify | ||
jkeylu/evil-eval |
- browser or nodejs: does it run in a browser (web APIs)?
- error handling: message, location, stack
- simple interface or complex interface
- how empty is the default sandbox? for example, does
console.log
work? - simple interface between sandbox and environment: import, export, call, return, throw
- async code: does the interpreter block the main thread?
- stepped execution is supported? needed for debugging
- ecmascript version: what spec is implemented?
- spec compliance: how complete is the implementation?
- compatibility: can it run unmodified code?
createFunction
- does it have a replacement forconst f = new Function("x", "y", "return x + y")
?createModule
- does it have a replacement forconst m = await import("data:text/javascript,export function f(x, y) { return x + y; }")
?- performance: how much slower than native speed? is performance relevant for this use case?
- WASM or javascript? WASM is faster, but more complex
some test code
console.log("ES2009", Array.isArray([1, 2]))
console.log("ES2015", [1, 2].find(x => x == 1))
console.log("ES2016", [1, 2].includes(1))
console.log("ES2017", Object.entries({a: 1}))
console.log("ES2018", [ ...[1, 2] ])
console.log("ES2019", Object.fromEntries([["a",1]]))
console.log("ES2020", null ?? "asdf")
when do we need a javascript interpreter?
workaround for
globalThis.callback = (value) => console.log(value == 3)
const source = `
const x = 1
const y = 2
globalThis.callback(x + y)
`
const script = document.createElement("script")
script.textContent = source
document.body.append(script)
which can break with CSP
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'".
workaround for
const x = 1
const y = 2
const v = eval("x + y")
console.log(v == 3)
which can break with CSP
EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
workaround for
const f = new Function("x", "y", "return x + y")
console.log(f(1, 2) == 3)
console.log(f(3, 4) == 7)
which can break with CSP
EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
workaround for
(async () => {
const m = await import("data:text/javascript,export function f(x, y) { return x + y; }")
console.log(m.f(1, 2) == 3)
console.log(m.f(3, 4) == 7)
})()
which can break with CSP
Refused to load the script ... because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
a javascript interpreter is not needed is some cases
Loading content scripts
You can load a content script into a web page in one of three ways:
1. At install time, into pages that match URL patterns.
Using the content_scripts key in your manifest.json, you can ask the browser to load a content script whenever the browser loads a page whose URL matches a given pattern.
2. At runtime, into pages that match URL patterns.
Using the contentScripts API, you can ask the browser to load a content script whenever the browser loads a page whose URL matches a given pattern. (This is similar to method 1, except that you can add and remove content scripts at runtime.)
3. At runtime, into specific tabs.
In Manifest V2, using tabs.executeScript(), or Manifest V3, using scripting.executeScript(), you can load a content script into a specific tab whenever you want. (For example, in response to the user clicking on a browser action.)
There is only one global scope per frame, per extension. This means that variables from one content script can directly be accessed by another content script, regardless of how the content script was loaded.
Using methods (1) and (2), you can only load scripts into pages whose URLs can be represented using a match pattern.
Using method (3), you can also load scripts into pages packaged with your extension, but you can't load scripts into privileged browser pages (like "about:debugging" or "about:addons").
https://github.com/violentmonkey/violentmonkey/blob/master/src/background/utils/preinject.js
const contentScriptsAPI = browser.contentScripts;
// ...
function registerScriptDataFF(inject, url) {
for (const scr of inject[ENV_SCRIPTS]) {
scr.code = scr[__CODE];
}
return contentScriptsAPI.register({
js: [{
code: `${resolveDataCodeStr}(${JSON.stringify(inject)})`,
}],
matches: url.split('#', 1),
runAt: 'document_start',
});
}
- https://npmtrends.com/evil-eval-vs-js-interpreter-vs-vm-browserify-vs-vm2
- https://github.com/search?l=JavaScript&q=javascript+interpreter
- https://github.com/search?l=TypeScript&q=javascript+interpreter
- https://www.w3schools.com/Js/js_versions.asp - ECMAScript Editions
- https://skratchdot.com/2013/05/userscripts-and-content-security-policy/