Skip to content

Instantly share code, notes, and snippets.

@justinstoller
Last active May 13, 2020 04:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinstoller/89fe0c392e4c5d7940b2ad9a6f3867dc to your computer and use it in GitHub Desktop.
Save justinstoller/89fe0c392e4c5d7940b2ad9a6f3867dc to your computer and use it in GitHub Desktop.
Potential 2018.1 patches to aid JRuby 1.7 vs 9k performance disparity
# Steps are to:
# 1. Use `wget` to download the raw content of one of the two included changesets
# 2. Use `patch` to install
#
# Patches can be optionally reverted with `patch` by adding the `--reverse` flag
# The two patches in this gist are mutually exclusive (full-changeset.patch is a superset of the unasserted-iterables.patch).
# The PR corresponding to the unasserted-iterables.patch is here:
# https://github.com/puppetlabs/puppet/pull/8150
# The full-changeset.patch includes the above plus the contents of these PRs (as of 12 May):
# https://github.com/puppetlabs/puppet/pull/8151
# https://github.com/puppetlabs/puppet/pull/8152
# https://github.com/puppetlabs/puppet/pull/8154
# https://github.com/puppetlabs/puppet/pull/8155
#
$ wget https://gist.githubusercontent.com/justinstoller/89fe0c392e4c5d7940b2ad9a6f3867dc/raw/bd5ab68159b148ccd410bdcea754683c3e6cd029/full-changeset.patch
Resolving gist.githubusercontent.com (gist.githubusercontent.com)... 151.101.128.133, 151.101.192.133, 151.101.0.133, ...
Connecting to gist.githubusercontent.com (gist.githubusercontent.com)|151.101.128.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 29405 (29K) [text/plain]
Saving to: ‘full-changeset.patch’
100%[======================================================================================================================>] 29,405 --.-K/s in 0.004s
$ patch --backup --directory /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/ --strip 2 < full-changeset.patch
patching file puppet/functions/reverse_each.rb
patching file puppet/functions/step.rb
patching file puppet/node/environment.rb
patching file puppet/parser/functions.rb
patching file puppet/parser/resource.rb
patching file puppet/parser/resource/param.rb
patching file puppet/pops/adaptable.rb
patching file puppet/pops/adapters.rb
patching file puppet/pops/evaluator/evaluator_impl.rb
patching file puppet/pops/evaluator/runtime3_resource_support.rb
patching file puppet/pops/evaluator/runtime3_support.rb
patching file puppet/pops/model/ast.rb
patching file puppet/pops/types/iterable.rb
patching file puppet/pops/validation/checker4_0.rb
patching file puppet/resource.rb
patching file puppet/util/autoload.rb
$ systemctl restart pe-puppetserver
================================
or
================================
$ wget https://gist.githubusercontent.com/justinstoller/89fe0c392e4c5d7940b2ad9a6f3867dc/raw/93ee92c363f3e2f8fe9ff9d28553530c57b73a59/unasserted-iterables.patch
Resolving gist.githubusercontent.com (gist.githubusercontent.com)... 151.101.64.133, 151.101.128.133, 151.101.0.133, ...
Connecting to gist.githubusercontent.com (gist.githubusercontent.com)|151.101.64.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3515 (3.4K) [text/plain]
Saving to: ‘unasserted-iterables.patch’
100%[======================================================================================================================>] 3,515 --.-K/s in 0s
$ patch --backup --directory /opt/puppetlabs/puppet/lib/ruby/vendor_ruby/ --strip 2 < unasserted-iterables.patch
patching file puppet/functions/reverse_each.rb
patching file puppet/functions/step.rb
patching file puppet/pops/types/iterable.rb
$ systemctl restart pe-puppetserver
diff --git a/lib/puppet/functions/reverse_each.rb b/lib/puppet/functions/reverse_each.rb
index fb81a99..3b0c907 100644
--- a/lib/puppet/functions/reverse_each.rb
+++ b/lib/puppet/functions/reverse_each.rb
@@ -84,7 +84,7 @@ Puppet::Functions.create_function(:reverse_each) do
def reverse_each(iterable)
# produces an Iterable
- Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).reverse_each
+ Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).reverse_each
end
def reverse_each_block(iterable, &block)
diff --git a/lib/puppet/functions/step.rb b/lib/puppet/functions/step.rb
index 8860149..d6e1ad1 100644
--- a/lib/puppet/functions/step.rb
+++ b/lib/puppet/functions/step.rb
@@ -88,7 +88,7 @@ Puppet::Functions.create_function(:step) do
def step(iterable, step)
# produces an Iterable
- Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).step(step)
+ Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).step(step)
end
def step_block(iterable, step, &block)
diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb
index d0e9238..745c99e 100644
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@ -285,7 +285,7 @@ class Puppet::Node::Environment
# @param name [String] The module name
# @return [Puppet::Module, nil] The module if found, else nil
def module(name)
- modules.find {|mod| mod.name == name}
+ modules_by_name[name]
end
# Locate a module instance by the full forge name (EG authorname/module)
@@ -340,6 +340,11 @@ class Puppet::Node::Environment
@modules
end
+ # @api private
+ def modules_by_name
+ @modules_by_name ||= Hash[modules.map { |mod| [mod.name, mod] }]
+ end
+
# Generate a warning if the given directory in a module path entry is named `lib`.
#
# @api private
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 6c695f5..804eb6a 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -44,15 +44,10 @@ module Puppet::Parser::Functions
# @api private
class AnonymousModuleAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :module
- end
- # Get the module that functions are mixed into corresponding to an
- # environment
- #
- # @api private
- def self.environment_module(env)
- AnonymousModuleAdapter.adapt(env) do |a|
- a.module ||= Module.new do
+ def self.create_adapter(o)
+ a = super(o)
+ a.module = Module.new do
@metadata = {}
def self.all_function_info
@@ -67,7 +62,16 @@ module Puppet::Parser::Functions
@metadata[name] = info
end
end
- end.module
+ a
+ end
+ end
+
+ # Get the module that functions are mixed into corresponding to an
+ # environment
+ #
+ # @api private
+ def self.environment_module(env)
+ AnonymousModuleAdapter.adapt(env).module
end
# Create a new Puppet DSL function.
diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb
index 6f29de2..3bb7e73 100644
--- a/lib/puppet/parser/resource.rb
+++ b/lib/puppet/parser/resource.rb
@@ -14,7 +14,6 @@ class Puppet::Parser::Resource < Puppet::Resource
attr_accessor :source, :scope, :collector_id
attr_accessor :virtual, :override, :translated, :catalog, :evaluated
- attr_accessor :file, :line
attr_reader :exported, :parameters
diff --git a/lib/puppet/parser/resource/param.rb b/lib/puppet/parser/resource/param.rb
index 3f55581..6aacb84 100644
--- a/lib/puppet/parser/resource/param.rb
+++ b/lib/puppet/parser/resource/param.rb
@@ -4,7 +4,7 @@ class Puppet::Parser::Resource::Param
include Puppet::Util::Errors
include Puppet::Util::MethodHelper
- attr_accessor :name, :value, :source, :add, :file, :line
+ attr_accessor :name, :value, :source, :add, :ast_node
def initialize(hash)
set_options(hash)
@@ -12,6 +12,22 @@ class Puppet::Parser::Resource::Param
@name = @name.intern
end
+ def line
+ @line ||= @ast_node && @ast_node.line
+ end
+
+ def line=(lineno)
+ @line = lineno
+ end
+
+ def file
+ @file ||= @ast_node && @ast_node.file
+ end
+
+ def file=(filepath)
+ @file = filepath
+ end
+
def line_to_i
line ? Integer(line) : nil
end
diff --git a/lib/puppet/pops/adaptable.rb b/lib/puppet/pops/adaptable.rb
index 3072d54..a11c89a 100644
--- a/lib/puppet/pops/adaptable.rb
+++ b/lib/puppet/pops/adaptable.rb
@@ -69,11 +69,7 @@ module Adaptable
#
def self.get(o)
attr_name = self_attr_name
- if o.instance_variable_defined?(attr_name)
- o.instance_variable_get(attr_name)
- else
- nil
- end
+ o.instance_variable_get(attr_name)
end
# Returns an existing adapter for the given object, or creates a new adapter if the
@@ -94,17 +90,16 @@ module Adaptable
#
def self.adapt(o, &block)
attr_name = self_attr_name
- adapter = if o.instance_variable_defined?(attr_name) && value = o.instance_variable_get(attr_name)
+ adapter = if value = o.instance_variable_get(attr_name)
value
else
associate_adapter(create_adapter(o), o)
end
if block_given?
- case block.arity
- when 1
- block.call(adapter)
- else
- block.call(adapter, o)
+ if block.arity == 1
+ block.call(adapter)
+ else
+ block.call(adapter, o)
end
end
adapter
@@ -130,8 +125,7 @@ module Adaptable
def self.adapt_new(o, &block)
adapter = associate_adapter(create_adapter(o), o)
if block_given?
- case block.arity
- when 1
+ if block.arity == 1
block.call(adapter)
else
block.call(adapter, o)
diff --git a/lib/puppet/pops/adapters.rb b/lib/puppet/pops/adapters.rb
index 3e6d20c..aba4ab2 100644
--- a/lib/puppet/pops/adapters.rb
+++ b/lib/puppet/pops/adapters.rb
@@ -85,6 +85,13 @@ module Adapters
class PathsAndNameCacheAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :cache, :paths
+
+ def self.create_adapter(env)
+ a = super(env)
+ a.paths = env.modulepath.map { |p| Pathname.new(p) }
+ a.cache = {}
+ a
+ end
end
# Attempts to find the module that `instance` originates from by looking at it's {SourcePosAdapter} and
@@ -103,10 +110,7 @@ module Adapters
def self.loader_name_by_source(environment, instance, file)
file = instance.file if file.nil?
return nil if file.nil? || EMPTY_STRING == file
- pn_adapter = PathsAndNameCacheAdapter.adapt(environment) do |a|
- a.paths ||= environment.modulepath.map { |p| Pathname.new(p) }
- a.cache ||= {}
- end
+ pn_adapter = PathsAndNameCacheAdapter.adapt(environment)
dir = File.dirname(file)
pn_adapter.cache.fetch(dir) do |key|
mod = find_module_for_dir(environment, pn_adapter.paths, dir)
@@ -128,7 +132,7 @@ module Adapters
end
if relative_path.length > 1
mod = environment.module(relative_path[0])
- return mod unless mod.nil?
+ return mod
end
end
nil
diff --git a/lib/puppet/pops/evaluator/evaluator_impl.rb b/lib/puppet/pops/evaluator/evaluator_impl.rb
index c154ba1..9bb9282 100644
--- a/lib/puppet/pops/evaluator/evaluator_impl.rb
+++ b/lib/puppet/pops/evaluator/evaluator_impl.rb
@@ -50,9 +50,6 @@ class EvaluatorImpl
# @api private
def static_initialize
@@eval_visitor ||= Visitor.new(self, "eval", 1, 1)
- @@lvalue_visitor ||= Visitor.new(self, "lvalue", 1, 1)
- @@assign_visitor ||= Visitor.new(self, "assign", 3, 3)
- @@string_visitor ||= Visitor.new(self, "string", 1, 1)
@@type_calculator ||= Types::TypeCalculator.singleton
@@ -137,7 +134,15 @@ class EvaluatorImpl
# @api private
#
def assign(target, value, o, scope)
- @@assign_visitor.visit_this_3(self, target, value, o, scope)
+ if target.is_a?(String)
+ assign_String(target, value, o, scope)
+ elsif target.is_a?(Array)
+ assign_Array(target, value, o, scope)
+ elsif target.is_a?(Numeric)
+ fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, {:varname => n.to_s})
+ else
+ fail(Issues::ILLEGAL_ASSIGNMENT, o)
+ end
end
# Computes a value that can be used as the LHS in an assignment.
@@ -147,7 +152,17 @@ class EvaluatorImpl
# @api private
#
def lvalue(o, scope)
- @@lvalue_visitor.visit_this_1(self, o, scope)
+ if o.is_a?(Model::VariableExpression)
+ if o.expr.instance_of?(Model::QualifiedName)
+ o.expr.value
+ else
+ evaluate(o.expr, scope)
+ end
+ elsif o.is_a?(Model::LiteralList)
+ o.values.map {|x| lvalue(x, scope) }
+ else
+ fail(Issues::ILLEGAL_ASSIGNMENT, o)
+ end
end
# Produces a String representation of the given object _o_ as used in interpolation.
@@ -157,9 +172,26 @@ class EvaluatorImpl
# @api public
#
def string(o, scope)
- @@string_visitor.visit_this_1(self, o, scope)
+ if o.instance_of?(String)
+ o
+ elsif o.instance_of?(Symbol)
+ if :undef == o # optimized comparison 1.44 vs 1.95
+ EMPTY_STRING
+ else
+ o.to_s
+ end
+ elsif o.instance_of?(Regexp)
+ Types::PRegexpType.regexp_to_s_with_delimiters(o)
+ elsif o.instance_of?(Array)
+ "[#{o.map {|e| string(e, scope)}.join(COMMA_SEPARATOR)}]"
+ elsif o.instance_of?(Hash)
+ "{#{o.map {|k,v| "#{string(k, scope)} => #{string(v, scope)}"}.join(COMMA_SEPARATOR)}}"
+ else
+ o.to_s
+ end
end
+
# Evaluate a BlockExpression in a new scope with variables bound to the
# given values.
#
@@ -189,22 +221,6 @@ class EvaluatorImpl
protected
- def lvalue_VariableExpression(o, scope)
- # evaluate the name
- evaluate(o.expr, scope)
- end
-
- # Catches all illegal lvalues
- #
- def lvalue_Object(o, scope)
- fail(Issues::ILLEGAL_ASSIGNMENT, o)
- end
-
- # An array is assignable if all entries are lvalues
- def lvalue_LiteralList(o, scope)
- o.values.map {|x| lvalue(x, scope) }
- end
-
# Assign value to named variable.
# The '$' sign is never part of the name.
# @example In Puppet DSL
@@ -223,16 +239,6 @@ class EvaluatorImpl
value
end
- def assign_Numeric(n, value, o, scope)
- fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, {:varname => n.to_s})
- end
-
- # Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc)
- #
- def assign_Object(name, value, o, scope)
- fail(Issues::ILLEGAL_ASSIGNMENT, o)
- end
-
def assign_Array(lvalues, values, o, scope)
if values.is_a?(Hash)
lvalues.map do |lval|
@@ -842,7 +848,11 @@ class EvaluatorImpl
# Store evaluated parameters in a hash associated with the body, but do not yet create resource
# since the entry containing :defaults may appear later
body_to_params[body] = body.operations.reduce({}) do |param_memo, op|
- params = evaluate(op, scope)
+ params = if op.instance_of?(Model::AttributeOperation)
+ eval_AttributeOperation(op, scope)
+ else
+ evaluate(op, scope)
+ end
params = [params] unless params.is_a?(Array)
params.each do |p|
if param_memo.include? p.name
@@ -875,7 +885,18 @@ class EvaluatorImpl
# Produces 3x parameter
def eval_AttributeOperation(o, scope)
- create_resource_parameter(o, scope, o.attribute_name, evaluate(o.value_expr, scope), o.operator)
+ value_expr = o.value_expr
+ value = if value_expr.instance_of?(Model::VariableExpression)
+ eval_VariableExpression(value_expr, scope)
+ elsif value_expr.instance_of?(Model::LiteralString)
+ value_expr.value
+ elsif value_expr.instance_of?(Model::ConcatenatedString)
+ eval_ConcatenatedString(value_expr, scope)
+ else
+ evaluate(value_expr, scope)
+ end
+
+ create_resource_parameter(o, scope, o.attribute_name, value, o.operator)
end
def eval_AttributesOperation(o, scope)
@@ -1051,7 +1072,11 @@ class EvaluatorImpl
# Evaluator is not too fussy about what constitutes a name as long as the result
# is a String and a valid variable name
#
- name = evaluate(o.expr, scope)
+ name = if o.expr.instance_of?(Model::QualifiedName)
+ o.expr.value
+ else
+ evaluate(o.expr, scope)
+ end
# Should be caught by validation, but make this explicit here as well, or mysterious evaluation issues
# may occur for some evaluation use cases.
@@ -1067,7 +1092,17 @@ class EvaluatorImpl
# Evaluates double quoted strings that may contain interpolation
#
def eval_ConcatenatedString o, scope
- o.segments.collect {|expr| string(evaluate(expr, scope), scope)}.join
+ o.segments.collect do |expr|
+ string(
+ if expr.instance_of?(Model::LiteralString)
+ expr.value
+ elsif expr.instance_of?(Model::TextExpression)
+ eval_TextExpression(expr, scope)
+ else
+ evaluate(expr, scope)
+ end,
+ scope)
+ end.join
end
@@ -1085,38 +1120,16 @@ class EvaluatorImpl
if o.expr.is_a?(Model::QualifiedName)
string(get_variable_value(o.expr.value, o, scope), scope)
else
- string(evaluate(o.expr, scope), scope)
- end
- end
-
- def string_Object(o, scope)
- o.to_s
- end
-
- def string_Symbol(o, scope)
- if :undef == o # optimized comparison 1.44 vs 1.95
- EMPTY_STRING
- else
- o.to_s
+ string(
+ if o.expr.instance_of?(Model::VariableExpression)
+ eval_VariableExpression(o.expr, scope)
+ else
+ evaluate(o.expr, scope)
+ end,
+ scope)
end
end
- def string_Array(o, scope)
- "[#{o.map {|e| string(e, scope)}.join(COMMA_SEPARATOR)}]"
- end
-
- def string_Hash(o, scope)
- "{#{o.map {|k,v| "#{string(k, scope)} => #{string(v, scope)}"}.join(COMMA_SEPARATOR)}}"
- end
-
- def string_Regexp(o, scope)
- Types::PRegexpType.regexp_to_s_with_delimiters(o)
- end
-
- def string_PAnyType(o, scope)
- o.to_s
- end
-
# Produces concatenation / merge of x and y.
#
# When x is an Array, y of type produces:
@@ -1267,7 +1280,13 @@ class EvaluatorImpl
if x.is_a?(Model::UnfoldExpression)
result.concat(evaluate(x, scope))
else
- result << evaluate(x, scope)
+ result << if x.instance_of?(Model::LiteralString)
+ x.value
+ elsif x.instance_of?(Model::VariableExpression)
+ eval_VariableExpression(x, scope)
+ else
+ evaluate(x, scope)
+ end
end
end
result
diff --git a/lib/puppet/pops/evaluator/runtime3_resource_support.rb b/lib/puppet/pops/evaluator/runtime3_resource_support.rb
index b271924..4818072 100644
--- a/lib/puppet/pops/evaluator/runtime3_resource_support.rb
+++ b/lib/puppet/pops/evaluator/runtime3_resource_support.rb
@@ -5,7 +5,7 @@ module Evaluator
module Runtime3ResourceSupport
CLASS_STRING = 'class'.freeze
- def self.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
+ def self.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters, ast_node = nil)
env = scope.environment
# loader = Adapters::LoaderAdapter.loader_for_model_object(o, scope)
@@ -31,22 +31,28 @@ module Runtime3ResourceSupport
raise ArgumentError, _("Unknown resource type: '%{type}'") % { type: type_name }
end
+ resource_attributes = {
+ :parameters => evaluated_parameters,
+ :exported => exported,
+ :virtual => virtual,
+ # WTF is this? Which source is this? The file? The name of the context ?
+ :source => scope.source,
+ :scope => scope,
+ :strict => true
+ }
+
+ if ast_node
+ resource_attributes[:ast_node] = ast_node
+ else
+ resource_attributes[:file] = file
+ resource_attributes[:line] = line
+ end
+
# Build a resource for each title - use the resolved *type* as opposed to a reference
# as this makes the created resource retain the type instance.
#
resource_titles.map do |resource_title|
- resource = Puppet::Parser::Resource.new(
- resolved_type, resource_title,
- :parameters => evaluated_parameters,
- :file => file,
- :line => line,
- :exported => exported,
- :virtual => virtual,
- # WTF is this? Which source is this? The file? The name of the context ?
- :source => scope.source,
- :scope => scope,
- :strict => true
- )
+ resource = Puppet::Parser::Resource.new(resolved_type, resource_title, resource_attributes)
# If this resource type supports inheritance (e.g. 'class') the parent chain must be walked
# This impl delegates to the resource type to figure out what is needed.
@@ -114,4 +120,4 @@ module Runtime3ResourceSupport
end
end
-end
\ No newline at end of file
+end
diff --git a/lib/puppet/pops/evaluator/runtime3_support.rb b/lib/puppet/pops/evaluator/runtime3_support.rb
index 8a81768..01f4ba3 100644
--- a/lib/puppet/pops/evaluator/runtime3_support.rb
+++ b/lib/puppet/pops/evaluator/runtime3_support.rb
@@ -318,11 +318,11 @@ module Runtime3Support
# The o is used for source reference
def create_resource_parameter(o, scope, name, value, operator)
- file, line = extract_file_line(o)
Puppet::Parser::Resource::Param.new(
:name => name,
:value => convert(value, scope, nil), # converted to 3x since 4x supports additional objects / types
- :source => scope.source, :line => line, :file => file,
+ :source => scope.source,
+ :ast_node => o,
:add => operator == '+>'
)
end
@@ -332,13 +332,7 @@ module Runtime3Support
end
def create_resources(o, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
- # Not 100% accurate as this is the resource expression location and each title is processed separately
- # The titles are however the result of evaluation and they have no location at this point (an array
- # of positions for the source expressions are required for this to work).
- # TODO: Revisit and possible improve the accuracy.
- #
- file, line = extract_file_line(o)
- Runtime3ResourceSupport.create_resources(file, line, scope, virtual, exported, type_name, resource_titles, evaluated_parameters)
+ Runtime3ResourceSupport.create_resources(nil, nil, scope, virtual, exported, type_name, resource_titles, evaluated_parameters, o)
end
# Defines default parameters for a type with the given name.
@@ -362,12 +356,6 @@ module Runtime3Support
# evaluated parameters are applied to all.
#
def create_resource_overrides(o, scope, evaluated_resources, evaluated_parameters)
- # Not 100% accurate as this is the resource expression location and each title is processed separately
- # The titles are however the result of evaluation and they have no location at this point (an array
- # of positions for the source expressions are required for this to work.
- # TODO: Revisit and possible improve the accuracy.
- #
- file, line = extract_file_line(o)
# A *=> results in an array of arrays
evaluated_parameters = evaluated_parameters.flatten
evaluated_resources.each do |r|
@@ -378,8 +366,7 @@ module Runtime3Support
resource = Puppet::Parser::Resource.new(
t, r.title, {
:parameters => evaluated_parameters,
- :file => file,
- :line => line,
+ :ast_node => o,
# WTF is this? Which source is this? The file? The name of the context ?
:source => scope.source,
:scope => scope
diff --git a/lib/puppet/pops/model/ast.rb b/lib/puppet/pops/model/ast.rb
index a4093c9..fecdea5 100644
--- a/lib/puppet/pops/model/ast.rb
+++ b/lib/puppet/pops/model/ast.rb
@@ -104,11 +104,11 @@ class Positioned < PopsObject
end
def line
- @locator.line_for_offset(@offset)
+ @located_line ||= @locator.line_for_offset(@offset)
end
def pos
- @locator.pos_on_line(@offset)
+ @located_pos ||= @locator.pos_on_line(@offset)
end
def initialize(locator, offset, length)
diff --git a/lib/puppet/pops/types/iterable.rb b/lib/puppet/pops/types/iterable.rb
index 2c7a385..cf22923 100644
--- a/lib/puppet/pops/types/iterable.rb
+++ b/lib/puppet/pops/types/iterable.rb
@@ -27,9 +27,9 @@ module Puppet::Pops::Types
# @return [Iterable,nil] The produced `Iterable`
# @raise [ArgumentError] In case an `Iterable` cannot be produced
# @api public
- def self.asserted_iterable(caller, obj)
- iter = self.on(obj)
- raise ArgumentError, "#{caller.class}(): wrong argument type (#{obj.class}; is not Iterable." if iter.nil?
+ def self.asserted_iterable(my_caller, obj, infer_elements = false)
+ iter = self.on(obj, nil, infer_elements)
+ raise ArgumentError, "#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable." if iter.nil?
iter
end
@@ -52,7 +52,7 @@ module Puppet::Pops::Types
# @return [Iterable,nil] The produced `Iterable` or `nil` if it couldn't be produced
#
# @api public
- def self.on(o, element_type = nil)
+ def self.on(o, element_type = nil, infer_elements = true)
case o
when IteratorProducer
o.iterator
@@ -64,7 +64,7 @@ module Puppet::Pops::Types
if o.empty?
Iterator.new(PUnitType::DEFAULT, o.each)
else
- if element_type.nil?
+ if element_type.nil? && infer_elements
tc = TypeCalculator.singleton
element_type = PVariantType.maybe_create(o.map {|e| tc.infer_set(e) })
end
@@ -75,7 +75,7 @@ module Puppet::Pops::Types
if o.empty?
HashIterator.new(PHashType::DEFAULT_KEY_PAIR_TUPLE, o.each)
else
- if element_type.nil?
+ if element_type.nil? && infer_elements
tc = TypeCalculator.singleton
element_type = PTupleType.new([
PVariantType.maybe_create(o.keys.map {|e| tc.infer_set(e) }),
@@ -202,6 +202,26 @@ module Puppet::Pops::Types
@enumeration.send(name, *arguments, &block)
end
+ def next
+ @enumeration.next
+ end
+
+ def map(*args, &block)
+ @enumeration.map(*args, &block)
+ end
+
+ def reduce(*args, &block)
+ @enumeration.reduce(*args, &block)
+ end
+
+ def all?(&block)
+ @enumeration.all?(&block)
+ end
+
+ def any?(&block)
+ @enumeration.any?(&block)
+ end
+
def step(step, &block)
raise ArgumentError if step <= 0
r = self
diff --git a/lib/puppet/pops/validation/checker4_0.rb b/lib/puppet/pops/validation/checker4_0.rb
index 578d4f8..13c1179 100644
--- a/lib/puppet/pops/validation/checker4_0.rb
+++ b/lib/puppet/pops/validation/checker4_0.rb
@@ -575,36 +575,40 @@ class Checker4_0 < Evaluator::LiteralEvaluator
# @api private
class Puppet::Util::FileNamespaceAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :file_to_namespace
+
+ def self.create_adapter(env)
+ a = super(env)
+ a.file_to_namespace = {}
+ a
+ end
end
def namespace_for_file(file)
env = Puppet.lookup(:current_environment)
return NO_NAMESPACE if env.nil?
- Puppet::Util::FileNamespaceAdapter.adapt(env) do |adapter|
- adapter.file_to_namespace ||= {}
+ adapter = Puppet::Util::FileNamespaceAdapter.adapt(env)
- file_namespace = adapter.file_to_namespace[file]
- return file_namespace unless file_namespace.nil? # No cache entry, so we do the calculation
+ file_namespace = adapter.file_to_namespace[file]
+ return file_namespace unless file_namespace.nil? # No cache entry, so we do the calculation
- path = Pathname.new(file)
+ path = Pathname.new(file)
- return adapter.file_to_namespace[file] = NO_NAMESPACE if path.extname != ".pp"
+ return adapter.file_to_namespace[file] = NO_NAMESPACE if path.extname != ".pp"
- path = path.expand_path
+ path = path.expand_path
- return adapter.file_to_namespace[file] = NO_NAMESPACE if initial_manifest?(path, env.manifest)
+ return adapter.file_to_namespace[file] = NO_NAMESPACE if initial_manifest?(path, env.manifest)
- #All auto-loaded files from modules come from a module search path dir
- relative_path = get_module_relative_path(path, env.full_modulepath)
+ #All auto-loaded files from modules come from a module search path dir
+ relative_path = get_module_relative_path(path, env.full_modulepath)
- return adapter.file_to_namespace[file] = NO_NAMESPACE if relative_path == NO_PATH
+ return adapter.file_to_namespace[file] = NO_NAMESPACE if relative_path == NO_PATH
- #If a file comes from a module, but isn't in the right place, always error
- names = dir_to_names(relative_path)
+ #If a file comes from a module, but isn't in the right place, always error
+ names = dir_to_names(relative_path)
- return adapter.file_to_namespace[file] = (names == BAD_MODULE_FILE ? BAD_MODULE_FILE : names.join("::").freeze)
- end
+ return adapter.file_to_namespace[file] = (names == BAD_MODULE_FILE ? BAD_MODULE_FILE : names.join("::").freeze)
end
def initial_manifest?(path, manifest_setting)
diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb
index 6924c09..db15b21 100644
--- a/lib/puppet/resource.rb
+++ b/lib/puppet/resource.rb
@@ -11,7 +11,7 @@ class Puppet::Resource
include Puppet::Util::PsychSupport
include Enumerable
- attr_accessor :file, :line, :catalog, :exported, :virtual, :strict
+ attr_accessor :catalog, :exported, :virtual, :strict, :ast_node
attr_reader :type, :title, :parameters, :rich_data_enabled
# @!attribute [rw] sensitive_parameters
@@ -66,12 +66,32 @@ class Puppet::Resource
tag(*tags)
end
+ if data[:ast_node]
+ self.ast_node = data[:ast_node]
+ end
+
ATTRIBUTES.each do |a|
value = data[a.to_s]
send("#{a}=", value) unless value.nil?
end
end
+ def file
+ @file ||= @ast_node && @ast_node.file
+ end
+
+ def file=(filepath)
+ @file = filepath
+ end
+
+ def line
+ @line ||= @ast_node && @ast_node.line
+ end
+
+ def line=(lineno)
+ @line = lineno
+ end
+
def inspect
"#{@type}[#{@title}]#{to_hash.inspect}"
end
@@ -224,8 +244,11 @@ class Puppet::Resource
if type.is_a?(Puppet::Resource)
# Copy constructor. Let's avoid munging, extracting, tagging, etc
src = type
- self.file = src.file
- self.line = src.line
+ self.ast_node = src.ast_node
+ unless ast_node
+ self.file = src.file
+ self.line = src.line
+ end
self.exported = src.exported
self.virtual = src.virtual
self.set_tags(src)
@@ -658,7 +681,7 @@ class Puppet::Resource
type.title_patterns.each { |regexp, symbols_and_lambdas|
if captures = regexp.match(title.to_s)
symbols_and_lambdas.zip(captures[1..-1]).each do |symbol_and_lambda,capture|
- symbol, proc = symbol_and_lambda
+ symbol, procedure = symbol_and_lambda
# Many types pass "identity" as the proc; we might as well give
# them a shortcut to delivering that without the extra cost.
#
@@ -667,8 +690,8 @@ class Puppet::Resource
#
# This was worth about 8MB of memory allocation saved in my
# testing, so is worth the complexity for the API.
- if proc then
- h[symbol] = proc.call(capture)
+ if procedure then
+ h[symbol] = procedure.call(capture)
else
h[symbol] = capture
end
diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb
index 7778c8d..66bbe20 100644
--- a/lib/puppet/util/autoload.rb
+++ b/lib/puppet/util/autoload.rb
@@ -10,6 +10,14 @@ require 'puppet/pops/adaptable'
# @api private
class Puppet::Util::ModuleDirectoriesAdapter < Puppet::Pops::Adaptable::Adapter
attr_accessor :directories
+
+ def self.create_adapter(env)
+ a = super(env)
+ a.directories = env.modulepath.flat_map do |dir|
+ Dir.glob(File.join(dir, '*', 'lib'))
+ end
+ a
+ end
end
# Autoload paths, either based on names or all at once.
@@ -132,13 +140,7 @@ class Puppet::Util::Autoload
if env
# if the app defaults have been initialized then it should be safe to access the module path setting.
- Puppet::Util::ModuleDirectoriesAdapter.adapt(env) do |a|
- a.directories ||= env.modulepath.collect do |dir|
- Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f| File.join(dir, f, "lib") }
- end.flatten.find_all do |d|
- FileTest.directory?(d)
- end
- end.directories
+ Puppet::Util::ModuleDirectoriesAdapter.adapt(env).directories
else
[]
end
diff --git a/lib/puppet/functions/reverse_each.rb b/lib/puppet/functions/reverse_each.rb
index fb81a99..3b0c907 100644
--- a/lib/puppet/functions/reverse_each.rb
+++ b/lib/puppet/functions/reverse_each.rb
@@ -84,7 +84,7 @@ Puppet::Functions.create_function(:reverse_each) do
def reverse_each(iterable)
# produces an Iterable
- Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).reverse_each
+ Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).reverse_each
end
def reverse_each_block(iterable, &block)
diff --git a/lib/puppet/functions/step.rb b/lib/puppet/functions/step.rb
index 8860149..d6e1ad1 100644
--- a/lib/puppet/functions/step.rb
+++ b/lib/puppet/functions/step.rb
@@ -88,7 +88,7 @@ Puppet::Functions.create_function(:step) do
def step(iterable, step)
# produces an Iterable
- Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable).step(step)
+ Puppet::Pops::Types::Iterable.asserted_iterable(self, iterable, true).step(step)
end
def step_block(iterable, step, &block)
diff --git a/lib/puppet/pops/types/iterable.rb b/lib/puppet/pops/types/iterable.rb
index 2c7a385..cf22923 100644
--- a/lib/puppet/pops/types/iterable.rb
+++ b/lib/puppet/pops/types/iterable.rb
@@ -27,9 +27,9 @@ module Puppet::Pops::Types
# @return [Iterable,nil] The produced `Iterable`
# @raise [ArgumentError] In case an `Iterable` cannot be produced
# @api public
- def self.asserted_iterable(caller, obj)
- iter = self.on(obj)
- raise ArgumentError, "#{caller.class}(): wrong argument type (#{obj.class}; is not Iterable." if iter.nil?
+ def self.asserted_iterable(my_caller, obj, infer_elements = false)
+ iter = self.on(obj, nil, infer_elements)
+ raise ArgumentError, "#{my_caller.class}(): wrong argument type (#{obj.class}; is not Iterable." if iter.nil?
iter
end
@@ -52,7 +52,7 @@ module Puppet::Pops::Types
# @return [Iterable,nil] The produced `Iterable` or `nil` if it couldn't be produced
#
# @api public
- def self.on(o, element_type = nil)
+ def self.on(o, element_type = nil, infer_elements = true)
case o
when IteratorProducer
o.iterator
@@ -64,7 +64,7 @@ module Puppet::Pops::Types
if o.empty?
Iterator.new(PUnitType::DEFAULT, o.each)
else
- if element_type.nil?
+ if element_type.nil? && infer_elements
tc = TypeCalculator.singleton
element_type = PVariantType.maybe_create(o.map {|e| tc.infer_set(e) })
end
@@ -75,7 +75,7 @@ module Puppet::Pops::Types
if o.empty?
HashIterator.new(PHashType::DEFAULT_KEY_PAIR_TUPLE, o.each)
else
- if element_type.nil?
+ if element_type.nil? && infer_elements
tc = TypeCalculator.singleton
element_type = PTupleType.new([
PVariantType.maybe_create(o.keys.map {|e| tc.infer_set(e) }),
@@ -202,6 +202,26 @@ module Puppet::Pops::Types
@enumeration.send(name, *arguments, &block)
end
+ def next
+ @enumeration.next
+ end
+
+ def map(*args, &block)
+ @enumeration.map(*args, &block)
+ end
+
+ def reduce(*args, &block)
+ @enumeration.reduce(*args, &block)
+ end
+
+ def all?(&block)
+ @enumeration.all?(&block)
+ end
+
+ def any?(&block)
+ @enumeration.any?(&block)
+ end
+
def step(step, &block)
raise ArgumentError if step <= 0
r = self
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment