-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use JSR-223 API to select a JS engine #52
Conversation
…clj-jsr223-v8 or JDK8.
This is some impressive work and a major milestone for Optimus. Thanks for all your efforts, @arbscht! I'll sit down and read through + test this tonight, and hopefully we can look at a 1.0 release in not too long. |
Agreed. Context is too vague. Engine is a fine choice.
What is your thinking on this?
Agreed on the no-fallback principle. Invalid choices should lead to exceptions, not unexpected behaviors. |
The code looks good to me. Nashorn is pretty slow compared to clj-v8 tho: v8:
Nashorn:
|
Hmm, it actually turns out that the CSS-files minified via Nashorn differ from those created with V8. There are some rules missing in the Nashorn version. I'll have to investigate that. |
Nashorn and clj-v8 behaves differently like this:
clj-v8 (correctly) minifies it to:
while Nashorn minifies it to:
I guess it has nothing to do with this pull request, but it does mean that you can't really trust your CSS to the Nashorn-version. Any ideas? |
Here's a test to reproduce the issue: (fact
"It correctly minifies several rules for the same selector, on both nashorn and
clj-v8."
(minify-css (create-clean-css-context "clj-v8" ) "table,div {border:0} table {margin:0}" {}) => "div,table{border:0}table{margin:0}"
(minify-css (create-clean-css-context "nashorn") "table,div {border:0} table {margin:0}" {}) => "div,table{border:0}table{margin:0}") It requires this modified code:
I have tried updating to the latest clean-css (3.4.12), but that didn't help any. |
Hm, the clean-css+nashorn situation is concerning. I looked into it a bit and found that running a test script in JS via Avatar JS CLI (using Nashorn) works the same as node/V8 CLI, but Nashorn via Clojure REPL does not. I may be missing some initialization config or ceremony, which perhaps clean-css assumes about its environment. I'll investigate more. Performance in Nashorn is slower in general I suspect because we're crudely isolating code using fresh engine instances instead of JSR-223's ScriptContexts/scoping on a singleton engine. This is because clj-jsr223-v8 doesn't yet support that part of the API (yet happens to be fast because clj-v8 does a similar thing), so uniform client code can't be written. Wiring it up is on the todo list for clj-jsr223-v8, after which we can run a fair comparison. (That may also obviate the context/engine terminology mismatch.) Regarding environ, it may be fine to use for this one setting. I just haven't looked into how it fits with existing configuration options and whether that's inconsistent, or if environ would be a better configuration story for any of them too. Thanks for reviewing. |
…horn and the de facto convention assumed by most libraries (e.g. CleanCSS); add init-engine multimethod hook; tweak tests
Well, what do you know. Nashorn implements Meanwhile, Avatar JS takes the approach of monkey-patching I think there are not likely to be very many of these issues, so I'd continue to have confidence in Nashorn. The suspect splice call is at while (tokenIdx >= 0) {
if ((tokenIdx === 0 || (optimizedBody.tokenized[propertyIdx] && bodiesAsList[tokenIdx].indexOf(optimizedBody.tokenized[propertyIdx].value) > -1)) && propertyIdx > -1) {
propertyIdx--;
continue;
}
var newBody = {
list: optimizedBody.list.splice(propertyIdx + 1),
tokenized: optimizedBody.tokenized.splice(propertyIdx + 1)
};
options.callback(tokens[processedTokens[tokenIdx]], newBody, processedCount, tokenIdx);
tokenIdx--;
} So I added a new (function(){
var nashorn_splice = Array.prototype.splice;
Array.prototype.splice = function() {
if (arguments.length === 1) {
return nashorn_splice.call(this, arguments[0], this.length);
} else {
return nashorn_splice.apply(this, arguments);
}}})(); |
Another thing to consider: how should Optimus depend on JDK8+ for testing? Unit tests targeting "nashorn" won't work in older JDKs (as currently configured in Travis). We may need platform-specific test profiles or guards. |
Impressive detective work. Well done!
|
I'm wary of using Here's another possibility, fully specifying the Travis build matrix: language: clojure
lein: lein2
script: lein2 midje
matrix:
include:
- jdk: openjdk6
- jdk: openjdk7
- jdk: oraclejdk7
- jdk: oraclejdk8
- jdk: oraclejdk8
env: OPTIMUS_JS_ENGINE=nashorn Outside of Travis, developers would have to select their JS engine locally as needed, rather than always running all combinations of available engines. Does that seem reasonable? If not, I'll dig into guarding unit tests based on versions. |
If we have the ability to do runtime checks to determine the presence of
|
Runtime checking is doable. One way is to wrap platform-specific test bodies in conditionals checking against
My first instinct was to use Midje's filter-by-metadata capability. That allows tagging facts for, say, a specific platform. But I can't see how to apply the filter without manual intervention anyway. Likewise for switching on/off certain test paths etc etc. Anyway, here's a potential Nashorn-specific fact check at runtime: (when (optimus.js/engine-exists? "nashorn")
(fact
"It correctly minifies several rules for the same selector."
(minify-css (create-clean-css-context (optimus.js/get-engine "nashorn")) "table,div {border:0} table {margin:0}" {}) => "div,table{border:0}table{margin:0}")) Acceptable? |
That looks good to me. |
Actually, I've just learned how to configure Midje to programmatically filter facts tagged with metadata. I think this is better: (fact
:nashorn-only
"It correctly minifies several rules for the same selector."
(minify-css (create-clean-css-context (optimus.js/get-engine "nashorn")) "table,div {border:0} table {margin:0}" {}) => "div,table{border:0}table{margin:0}"))
(require 'optimus.js)
;; Exclude :nashorn-only tests if we can't detect nashorn in this JDK
(when-not (optimus.js/engine-exists? "nashorn")
(change-defaults :fact-filter (complement :nashorn-only))) I need to refactor some code and tidy up docs as well. Updates to come. |
Hi, Any progress on this PR? |
Closing in favor of #66. |
Changes:
optimus.js
talks tojavax.script.*
API to get/control script engines.optimus.optimizations.minify
usesoptimus.js
to create contexts and evaluate scripts.:optimus-js-engine
setting via environ.profiles.clj
, shell environment, or anywhere else as documented.OPTIMUS_JS_ENGINE=nashorn lein midje
will run tests using Nashorn (assuming JDK8).Testing:
optimus.js-test
.openjdk version "1.8.0_77-Debian"
,lein midje
reports "All checks (1908) succeeded." for either "clj-v8" or "nashorn" as:optimus-js-engine
.Some other aspects to review:
eval(…)
, not directly as before.JSON.stringify
in JS andclojure.data.json/read-str in Clojure
).cleanup-engine
is optional if relying onfinalize()
is acceptable (but I've left it in anyway for now).