lib/rhino/context.rb in therubyrhino-1.73.0 vs lib/rhino/context.rb in therubyrhino-1.73.1

- old
+ new

@@ -1,43 +1,41 @@ require 'stringio' module Rhino -# ==Overview -# All Javascript must be executed in a context which represents the execution environment in -# which scripts will run. The environment consists of the standard javascript objects -# and functions like Object, String, Array, etc... as well as any objects or functions which -# have been defined in it. e.g. -# -# Context.open do |cxt| -# cxt['num'] = 5 -# cxt.eval('num + 5') #=> 10 -# end -# -# == Multiple Contexts. -# The same object may appear in any number of contexts, but only one context may be executing javascript code -# in any given thread. If a new context is opened in a thread in which a context is already opened, the second -# context will "mask" the old context e.g. -# -# six = 6 -# Context.open do |cxt| -# cxt['num'] = 5 -# cxt.eval('num') # => 5 -# Context.open do |cxt| -# cxt['num'] = 10 -# cxt.eval('num') # => 10 -# cxt.eval('++num') # => 11 -# end -# cxt.eval('num') # => 5 -# end -# -# == Notes -# While there are many similarities between Rhino::Context and Java::org.mozilla.javascript.Context, they are not -# the same thing and should not be confused. - + # ==Overview + # All Javascript must be executed in a context which represents the execution environment in + # which scripts will run. The environment consists of the standard javascript objects + # and functions like Object, String, Array, etc... as well as any objects or functions which + # have been defined in it. e.g. + # + # Context.open do |cxt| + # cxt['num'] = 5 + # cxt.eval('num + 5') #=> 10 + # end + # + # == Multiple Contexts. + # The same object may appear in any number of contexts, but only one context may be executing javascript code + # in any given thread. If a new context is opened in a thread in which a context is already opened, the second + # context will "mask" the old context e.g. + # + # six = 6 + # Context.open do |cxt| + # cxt['num'] = 5 + # cxt.eval('num') # => 5 + # Context.open do |cxt| + # cxt['num'] = 10 + # cxt.eval('num') # => 10 + # cxt.eval('++num') # => 11 + # end + # cxt.eval('num') # => 5 + # end + # + # == Notes + # While there are many similarities between Rhino::Context and Java::org.mozilla.javascript.Context, they are not + # the same thing and should not be confused. class Context - attr_reader :scope class << self # initalize a new context with a fresh set of standard objects. All operations on the context # should be performed in the block that is passed. @@ -49,65 +47,63 @@ new.eval(javascript) end end + attr_reader :scope + # Create a new javascript environment for executing javascript and ruby code. # * <tt>:sealed</tt> - if this is true, then the standard objects such as Object, Function, Array will not be able to be modified # * <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: - ContextFactory.new.call do |native| - @native = native - @global = NativeObject.new(@native.initStandardObjects(nil, options[:sealed] == true)) + @factory = ContextFactory.new + @factory.call do |context| + @native = context + @global = @native.initStandardObjects(nil, options[:sealed] == true) if with = options[:with] - @scope = To.javascript(with) - @scope.setParentScope(@global.j) + @scope = Rhino.to_javascript(with) + @scope.setParentScope(@global) else @scope = @global end unless options[:java] for package in ["Packages", "java", "javax", "org", "com", "edu", "net"] - @global.j.delete(package) + @global.delete(package) end end end end # Read a value from the global scope of this context - def [](k) - @scope[k] + def [](key) + @scope[key] end # Set a value in the global scope of this context. This value will be visible to all the # javascript that is executed in this context. - def []=(k, v) - @scope[k] = v + def []=(key, val) + @scope[key] = val end # Evaluate a string of javascript in this context: # * <tt>source</tt> - the javascript source code to evaluate. This can be either a string or an IO object. # * <tt>source_name</tt> - associated name for this source code. Mainly useful for backtraces. # * <tt>line_number</tt> - associate this number with the first line of executing source. Mainly useful for backtraces def eval(source, source_name = "<eval>", line_number = 1) - self.open do - begin - scope = To.javascript(@scope) - if IO === source || StringIO === source - result = @native.evaluateReader(scope, IOReader.new(source), source_name, line_number, nil) - else - result = @native.evaluateString(scope, source.to_s, source_name, line_number, nil) - end - To.ruby result - rescue J::RhinoException => e - raise Rhino::JavascriptError, e + open do + if IO === source || StringIO === source + result = @native.evaluateReader(@scope, IOReader.new(source), source_name, line_number, nil) + else + result = @native.evaluateString(@scope, source.to_s, source_name, line_number, nil) end + Rhino.to_ruby(result) end end - + def evaluate(*args) # :nodoc: - self.eval(*args) + eval(*args) # an alias end # Read the contents of <tt>filename</tt> and evaluate it as javascript. Returns the result of evaluating the # javascript. e.g. # @@ -123,88 +119,119 @@ # 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); - @native.factory.instruction_limit = limit + @native.setInstructionObserverThreshold(limit) + @factory.instruction_limit = limit end + def optimization_level + @native.getOptimizationLevel + end + # Set the optimization level that this context will use. This is sometimes necessary # in Rhino, if the bytecode size of the compiled javascript exceeds the 64KB limit. # 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) - @native.setOptimizationLevel(level) + if @native.class.isValidOptimizationLevel(level) + @native.setOptimizationLevel(level) + level + else + @native.setOptimizationLevel(0) + nil + end end - # Enter this context for operations. Some methods such as eval() will - # fail unless this context is open - def open - begin - @native.factory.enterContext(@native) - yield self - ensure - J::Context.exit() - end if block_given? + # Get the JS interpreter version. + # Returns a number e.g. 1.7, nil if unknown and 0 for default. + def version + case const_value = @native.getLanguageVersion + when -1 then nil # VERSION_UNKNOWN + when 0 then 0 # VERSION_DEFAULT + else const_value / 100.0 # VERSION_1_1 (1.1 = 110 / 100) + end end - + + # Sets interpreter mode a.k.a. JS language version e.g. 1.7 (if supported). + 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) + @native.setLanguageVersion(const_value) + const_value + else + @native.setLanguageVersion(js_context::VERSION_DEFAULT) + nil + end + end + + # Enter this context for operations. + # Some methods such as eval() will fail unless this context is open ! + def open(&block) + do_open(&block) + rescue JS::RhinoException => e + raise Rhino::JSError.new(e) + end + + private + + def do_open + begin + @factory.enterContext(@native) + yield self + ensure + JS::Context.exit + end + end + end - class IOReader < java.io.Reader #:nodoc: + class IOReader < java.io.Reader # :nodoc: def initialize(io) @io = io end - def read(charbuffer, offset, length) + # implement int Reader#read(char[] buffer, int offset, int length) + def read(buffer, offset, length) + str = nil begin str = @io.read(length) - if str.nil? - return -1 - else - jstring = java.lang.String.new(str) - for i in 0 .. jstring.length - 1 - charbuffer[i + offset] = jstring.charAt(i) - end - return jstring.length - end rescue StandardError => e - raise java.io.IOException.new, "Failed reading from ruby IO object" + raise java.io.IOException.new("failed reading from ruby IO object") end + if str.nil? + return -1 + else + jstr = str.to_java + for i in 0 .. jstr.length - 1 + buffer[i + offset] = jstr.charAt(i) + end + return jstr.length + end end + end - class ContextFactory < J::ContextFactory # :nodoc: + class ContextFactory < JS::ContextFactory # :nodoc: def observeInstructionCount(cxt, count) raise RunawayScriptError, "script exceeded allowable instruction count" if count > @limit end def instruction_limit=(count) @limit = count end + end class ContextError < StandardError # :nodoc: - end - class JavascriptError < StandardError # :nodoc: - def initialize(native) - @native = native - end - - def message - @native.cause.details - end - - def javascript_backtrace - @native.getScriptStackTrace() - end + class RunawayScriptError < ContextError # :nodoc: end - - JSError = JavascriptError - - class RunawayScriptError < StandardError # :nodoc: - end + end