-
Notifications
You must be signed in to change notification settings - Fork 152
feat: Binary runtime helper. #255
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
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, after reviewing this I have some nontrivial feedback that buckets into a few main categories:
- The need for expressions to reference the result of another expression.
- Making references to runtime values more transparent and adding explicit support for boolean/truthy values.
- Having an encoding preamble to make the expression evaluation at runtime more robust.
- Embracing stack order evaluation.
@amiller-gh you requested a code example explaining expression references. here it is: Consider the following classes: .foo {
color: red;
width: 50%
}
.bar {
color: blue;
width: 100%;
}
.baz {
color: red;
width: 100%;
} Which optimizes to: .a {
color: red;
}
.b {
width: 50%;
}
.c {
color: blue;
}
.d {
width: 100%;
} Template logic applies these source classes using something like: <div class="${isFooOrBar ? 'foo' : 'bar'} ${bazEnabled ? 'baz'}"> Ideally the template rewriter (in dev mode) would rewrite to something like: <div class="${runtime('devshape', ['foo', 'bar', 'baz'], [isFooOrBar, bazEnabled])}"> and the opcodes would be (with a stack-based representation):
and when the optimizer is enabled it would become: <div class="${runtime('optimizedshape', ['a', 'b', 'c', 'd'], [isFooOrBar, bazEnabled])}"> Now the opcodes would be (with a stack-based representation and expression references):
In this example the logic for the values is very trivial so it seems like |
@chriseppstein, this is ready for review again 🎉 Fun fact, entire runtime minifies to 397 bytes: function r(r,e,n){for(var f,t,i,a=n.slice(),l=n.length,o="",g=0,h=-1,u=0,c=2,s=0;s<r.length;s++)for(var v=parseInt(r[s],36),b=32;b--;){if(3==t&&(g=h=0),!c){for(;l-1>>c++;);c=g?c:2}if(u+=v%2*(1<<--c||1),v>>>=1,!c){if(i=!!(g&&+!!a[u>>>1]^u%2),g||(t=u),1==g&&(f=i),2==g&&(0==t&&(i=f||i),1==t&&(i=f&&i),2==t&&(i=f===i),a[l++]=i,~h&&(o+=i?(o?" ":"")+e[h]:"",++h==e.length)))break;u=0,g=++g%3}}return o} Before I land this I'd like to update the runtime readme with a detailed description of our opcode packing scheme. I updated it a little more from our past conversation. Instead of a preamble counting the number of expected args and expressions, I've used our spare opcode for a Take a look and let me know what you think – theres a lot of binary operators packed in there for terseness. Its well commented, but happy to answer questions 👍 |
val = !!(state && (+!!exprs[working >>> 1] ^ (working % 2))); // tslint:disable-line | ||
|
||
// If discovering as opcode, save as an opcode. | ||
if (state == STATE.OP) { op = working; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm intentionally avoiding switch
statements here to keep the minified runtime tiny. These static if statements should be just as performant for such a small option set. I also left out the else if
expressions to keep byte size to a minimum. Its a tradeoff between size and couple extra comparison operations at runtime (that for all we know the compiler optimizes away for us). Let me know if you think a different tradeoff makes sense.
@amiller-gh Instead of comments, I've pushed changes for the things I wanted changed. I've pushed them as discreet commits so you can review each one and so they can be easily backed out. |
Approved: Feel free to merge this when you're ready. |
Working binary runtime package. Currently clocks in at under 681 bytes minified and will greatly reduce the possibility for template bloat on rewrite. See #47 for implementation details. To complete:
I expect that the base
ElementAnalysis
object delivered in core will proxy some version of the expression constructor to analyzers. The core rewrite transport object will then expose the three generated array parts – expression shapes array, classes array, and expressions array – to the rewriters to apply as the wish.