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:
parent
9b948648b7
commit
b4ac2b3339
2 changed files with 131 additions and 25 deletions
|
@ -46,9 +46,18 @@ module Rhino
|
|||
def eval(javascript)
|
||||
new.eval(javascript)
|
||||
end
|
||||
|
||||
|
||||
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]
|
||||
|
@ -73,7 +83,12 @@ module Rhino
|
|||
end
|
||||
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,14 +132,28 @@ module Rhino
|
|||
end
|
||||
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
|
||||
def instruction_limit=(limit)
|
||||
@native.setInstructionObserverThreshold(limit)
|
||||
@factory.instruction_limit = limit
|
||||
# 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.
|
||||
def 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
|
||||
@native.getOptimizationLevel
|
||||
end
|
||||
|
@ -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,18 +244,72 @@ 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
|
||||
|
||||
# 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
|
||||
|
||||
def instruction_limit=(count)
|
||||
@limit = count
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
class ContextError < StandardError # :nodoc:
|
||||
end
|
||||
|
||||
|
|
|
@ -65,5 +65,29 @@ describe Rhino::Context do
|
|||
context.version = '1.7'
|
||||
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
|
Loading…
Reference in a new issue