diff --git a/lib/rhino/context.rb b/lib/rhino/context.rb
index b17cc73..6868e81 100644
--- a/lib/rhino/context.rb
+++ b/lib/rhino/context.rb
@@ -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
# * :with - use this ruby object as the root scope for all javascript that is evaluated
# * :java - 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
diff --git a/spec/rhino/context_spec.rb b/spec/rhino/context_spec.rb
index 7ff0f7a..7d76f6e 100644
--- a/spec/rhino/context_spec.rb
+++ b/spec/rhino/context_spec.rb
@@ -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
\ No newline at end of file