1
0
Fork 0
mirror of https://github.com/rubyjs/therubyrhino synced 2023-03-27 23:21:34 -04:00

extract out instruction limiting into a separate Context impl that does tracking itself - make a native context factory shared by default + allow a factory option when creating a context

This commit is contained in:
kares 2012-02-15 11:04:14 +01:00
parent 9b948648b7
commit b4ac2b3339
2 changed files with 131 additions and 25 deletions

View file

@ -49,6 +49,15 @@ module Rhino
end
@@default_factory = nil
def self.default_factory
@@default_factory ||= ContextFactory.new
end
def self.default_factory=(factory)
@@default_factory = factory
end
attr_reader :scope
# Create a new javascript environment for executing javascript and ruby code.
@ -56,8 +65,9 @@ module Rhino
# * <tt>:with</tt> - use this ruby object as the root scope for all javascript that is evaluated
# * <tt>:java</tt> - if true, java packages will be accessible from within javascript
def initialize(options = {}) #:nodoc:
@factory = ContextFactory.new
@factory.call do |context|
factory = options[:factory] ||
(options[:restrictable] ? RestrictableContextFactory.instance : self.class.default_factory)
factory.call do |context|
@native = context
@global = @native.initStandardObjects(nil, options[:sealed] == true)
if with = options[:with]
@ -74,6 +84,11 @@ module Rhino
end
end
# Returns the ContextFactory used while creating this context.
def factory
@native.getFactory
end
# Read a value from the global scope of this context
def [](key)
@scope[key]
@ -117,12 +132,26 @@ module Rhino
end
end
# Returns true if this context supports restrictions.
def restrictable?
@native.is_a?(RestrictableContextFactory::Context)
end
def instruction_limit
restrictable? ? @native.instruction_limit : false
end
# Set the maximum number of instructions that this context will execute.
# If this instruction limit is exceeded, then a Rhino::RunawayScriptError
# will be raised
# If this instruction limit is exceeded, then a #Rhino::RunawayScriptError
# will be raised.
def instruction_limit=(limit)
@native.setInstructionObserverThreshold(limit)
@factory.instruction_limit = limit
if restrictable?
@native.instruction_limit = limit
else
warn "setting an instruction_limit has no effect on this context, use " +
"Context.open(:restricted => true) to gain a restrictable instance"
nil
end
end
def optimization_level
@ -134,7 +163,7 @@ module Rhino
# By using the -1 optimization level, you tell Rhino to run in interpretative mode,
# taking a hit to performance but escaping the Java bytecode limit.
def optimization_level=(level)
if @native.class.isValidOptimizationLevel(level)
if JS::Context.isValidOptimizationLevel(level)
@native.setOptimizationLevel(level)
level
else
@ -157,13 +186,12 @@ module Rhino
def version=(version)
const = version.to_s.gsub('.', '_').upcase
const = "VERSION_#{const}" if const[0, 7] != 'VERSION'
js_context = @native.class # Context
if js_context.constants.include?(const)
const_value = js_context.const_get(const)
if JS::Context.constants.include?(const)
const_value = JS::Context.const_get(const)
@native.setLanguageVersion(const_value)
const_value
else
@native.setLanguageVersion(js_context::VERSION_DEFAULT)
@native.setLanguageVersion(JS::Context::VERSION_DEFAULT)
nil
end
end
@ -179,11 +207,11 @@ module Rhino
private
def do_open
factory.enterContext(@native)
begin
@factory.enterContext(@native)
yield self
ensure
JS::Context.exit
factory.exit
end
end
@ -216,14 +244,68 @@ module Rhino
end
class ContextFactory < JS::ContextFactory # :nodoc:
ContextFactory = JS::ContextFactory # :nodoc: backward compatibility
def observeInstructionCount(cxt, count)
raise RunawayScriptError, "script exceeded allowable instruction count" if count > @limit
class RestrictableContextFactory < ContextFactory # :nodoc:
@@instance = nil
def self.instance
@@instance ||= new
end
def instruction_limit=(count)
@limit = count
# protected Context makeContext()
def makeContext
Context.new(self)
end
# protected void observeInstructionCount(Context context, int instructionCount)
def observeInstructionCount(context, count)
if context.is_a?(Context)
context.instruction_count += count
context.check!
end
end
# protected Object doTopCall(Callable callable, Context context,
# Scriptable scope, Scriptable thisObj, Object[] args)
def doTopCall(callable, context, scope, this, args)
context.instruction_count = 0 if context.is_a?(Context)
super
end
class Context < JS::Context # :nodoc:
def initialize(factory)
super(factory)
reset!
end
attr_reader :instruction_limit
def instruction_limit=(limit)
treshold = getInstructionObserverThreshold
if limit && (treshold == 0 || treshold > limit)
setInstructionObserverThreshold(limit)
end
@instruction_limit = limit
end
attr_accessor :instruction_count
def check!
if instruction_limit && instruction_count > instruction_limit
raise RunawayScriptError, "script exceeded allowable instruction count: #{instruction_limit}"
end
end
private
def reset!
self.instruction_count = 0
self.instruction_limit = nil
self
end
end
end

View file

@ -66,4 +66,28 @@ describe Rhino::Context do
context.version.should == 1.7
end
it "should have a (shared) factory by default" do
context1 = Rhino::Context.new
context1.factory.should_not be nil
context1.factory.should be_a(Rhino::JS::ContextFactory)
context1.factory.should be Rhino::Context.default_factory
context2 = Rhino::Context.new
context2.factory.should be context1.factory
end
it "allows limiting instruction count" do
context = Rhino::Context.new :restrictable => true
context.instruction_limit = 100
lambda {
context.eval %Q{ for (var i = 0; i < 100; i++) Number(i).toString(); }
}.should raise_error(Rhino::RunawayScriptError)
context.instruction_limit = nil
lambda {
context.eval %Q{ for (var i = 0; i < 100; i++) Number(i).toString(); }
}.should_not raise_error(Rhino::RunawayScriptError)
end
end