Skip to content

Fix comparing and disallow passing functions/mixins across compilations #297

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

Merged
merged 1 commit into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/sass/compiler/host/function_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ class Host
#
# It stores sass custom functions and handles function calls.
class FunctionRegistry
attr_reader :global_functions
attr_reader :environment, :global_functions

def initialize(functions, alert_color:)
functions = functions.transform_keys(&:to_s)

@environment = BasicObject.new
@global_functions = functions.keys
@functions_by_name = functions.transform_keys do |signature|
index = signature.index('(')
Expand Down
31 changes: 23 additions & 8 deletions lib/sass/compiler/host/protofier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ def to_proto(obj)
)
when Sass::Value::Function
if obj.instance_variable_defined?(:@id)
assert_compiler_value(obj)

EmbeddedProtocol::Value.new(
compiler_function: EmbeddedProtocol::Value::CompilerFunction.new(
id: obj.instance_variable_get(:@id)
Expand All @@ -78,6 +80,8 @@ def to_proto(obj)
)
end
when Sass::Value::Mixin
assert_compiler_value(obj)

EmbeddedProtocol::Value.new(
compiler_mixin: EmbeddedProtocol::Value::CompilerMixin.new(
id: obj.instance_variable_get(:@id)
Expand Down Expand Up @@ -148,17 +152,11 @@ def from_proto(proto)
end
)
when :compiler_function
Sass::Value::Function.allocate.instance_eval do
@id = obj.id
self
end
compiler_value(Sass::Value::Function, obj.id)
when :host_function
raise Sass::ScriptError, 'The compiler may not send Value.host_function to host'
when :compiler_mixin
Sass::Value::Mixin.allocate.instance_eval do
@id = obj.id
self
end
compiler_value(Sass::Value::Mixin, obj.id)
when :calculation
Calculation.from_proto(obj)
when :singleton
Expand All @@ -177,6 +175,23 @@ def from_proto(proto)
end
end

private

def assert_compiler_value(value)
unless value.instance_variable_get(:@environment) == @function_registry.environment
raise Sass::ScriptError, "Returned #{value} does not belong to this compilation"
end

value
end

def compiler_value(klass, id)
value = klass.allocate
value.instance_variable_set(:@environment, @function_registry.environment)
value.instance_variable_set(:@id, id)
value
end

# The {Number} Protofier.
module Number
module_function
Expand Down
9 changes: 6 additions & 3 deletions lib/sass/value/function.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def initialize(signature, &callback)
@callback = callback.freeze
end

# @return [Object, nil]
protected attr_reader :environment

# @return [Integer, nil]
protected attr_reader :id

Expand All @@ -26,10 +29,10 @@ def initialize(signature, &callback)

# @return [::Boolean]
def ==(other)
if id.nil?
other.equal?(self)
if defined?(@id)
other.is_a?(Sass::Value::Function) && other.environment == environment && other.id == id
else
other.is_a?(Sass::Value::Function) && other.id == id
other.equal?(self)
end
end

Expand Down
5 changes: 4 additions & 1 deletion lib/sass/value/mixin.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@ class << self
private :new
end

# @return [Object]
protected attr_reader :environment

# @return [Integer]
protected attr_reader :id

# @return [::Boolean]
def ==(other)
other.is_a?(Sass::Value::Mixin) && other.id == id
other.is_a?(Sass::Value::Mixin) && other.environment == environment && other.id == id
end

# @return [Integer]
Expand Down