-
Notifications
You must be signed in to change notification settings - Fork 357
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
Passing compiler Functions/Mixins across different compilations should not be allowed #2542
Comments
This is currently "undefined behavior" that works in dart-sass, but probably it shouldn't even be allowed. There are a few things need to be done to address this:
|
The title refers to host functions, but I don't think we necessarily need to make this as complicated as attaching registries to function objects. All we really need is some tag (that can be completely opaque to the host) indicating to the compiler which compilation a given function belongs to. We could even reserve a few bits in the existing ID to use for this. Either way we should definitely make it explicit in the JS API specification that passing a function object between compilations is an error. |
Sorry, that was a typo.
Exactly. Anything that has 1-1 mapping with each compilation would work. In ruby implementation I just create a blank |
What I mean is, it doesn't necessarily need to be the host's concern at all. The compiler itself should provide this guarantee. |
I'm not sure if that can be done on compiler side reliably. In theory, the host side can retain a compiler function object in memory indefinitely, so that no matter what kind of bits we add, there is always a risk of collision at some point. |
In additional, I found the compiler has special logic for argument list that may cause problem as well: dart-sass/lib/src/embedded/protofier.dart Lines 235 to 238 in e6589fe
In theory, I should be able to save an ArgumentList from compilation A, and then pass it to compilation B. However, currently this will break. - Probably the host should check set the id to 0 before passing argument list to a different compilation. |
You're right that if we reserve (for example) eight bytes of the ID for a compilation ID, there will be a chance that you can get overlap once you have 256+ compilations running concurrently. But even then passing functions across compilation contexts will only work randomly and only very infrequently, so there's no real risk that it would hide errors from the user. Even then, we could still handle it on the compiler side by adding a separate For |
It’s not just concurrent compilations. Even if I do them sequentially, I can save a “compiler function” to a local variable, and then return it in a different compilation much later. Also, in implementation like the ruby host, the next compilation id is reset to 1 every time the compiler becomes idle, it’s not safe even if it’s a separate field. |
Note in the following example, both compilations have the same compilation id 0 with or without using persisted compiler due to implementation detail on the host side: So relying on compilation id to determine if the variable comes from the same compilation is not going to work. const sass = require('sass-embedded');
// or
// const sass = require('sass');
const first = `
@use 'sass:meta';
@function foo($n) {@return $n + 1}
a {b: meta.call(bar(meta.get-function('foo')), 0); }
`
const second = `
@use 'sass:meta';
@function foo($n) {@return $n + 2}
a {b: meta.call(bar(meta.get-function('foo')), 0); }
`
let fn
sass.compileString(first, {
functions: {
'bar($arg)': (args) => {
fn = args[0]
return fn
}
}
})
console.log(sass.compileString(second, {
functions: {
'bar($arg)': (args) => {
const fn2 = args[0]
console.log(fn.equals(fn2))
return fn
}
},
}).css) |
See the following code as an illustration of the issue. On
sass
this "worked correctly", because callables from other function registries with in the same dart/js VM memory space can be directly accessed and called. However, onsass-embedded
this is broken, as only the function id is being passed, thus it will refer to a function in the current register even if we passed the function from a different registry.The text was updated successfully, but these errors were encountered: