Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 182 lines (156 sloc) 4.735 kB
0be476d @sstephenson Extract ExecJS::Runtime
authored
1 require "tempfile"
2
3 module ExecJS
c5e22c4 @sstephenson Runtime -> ExternalRuntime
authored
4 class ExternalRuntime
82773bf @josh Context
josh authored
5 class Context
c595c07 @josh Remove pure evaluation
josh authored
6 def initialize(runtime, source = "")
043c06b @josh Encode strings to utf-8 that are passed to compile
josh authored
7 source = source.encode('UTF-8') if source.respond_to?(:encode)
8
82773bf @josh Context
josh authored
9 @runtime = runtime
043c06b @josh Encode strings to utf-8 that are passed to compile
josh authored
10 @source = source
50791dd @josh Add Context#call
josh authored
11 end
12
4bc602f @josh Pure function hint
josh authored
13 def eval(source, options = {})
ae253df @lautis Fix typo in variable name
lautis authored
14 source = source.encode('UTF-8') if source.respond_to?(:encode)
0b4977e @josh Try to convert to UTF8 early
josh authored
15
82773bf @josh Context
josh authored
16 if /\S/ =~ source
3097959 @josh Switch to multi_json lib
josh authored
17 exec("return eval(#{MultiJson.encode("(#{source})")})")
82773bf @josh Context
josh authored
18 end
19 end
20
4bc602f @josh Pure function hint
josh authored
21 def exec(source, options = {})
ae253df @lautis Fix typo in variable name
lautis authored
22 source = source.encode('UTF-8') if source.respond_to?(:encode)
0b4977e @josh Try to convert to UTF8 early
josh authored
23
c595c07 @josh Remove pure evaluation
josh authored
24 compile_to_tempfile([@source, source].join("\n")) do |file|
0dc15c3 @josh Make runner_source and exec_runtime protected
josh authored
25 extract_result(@runtime.send(:exec_runtime, file.path))
82773bf @josh Context
josh authored
26 end
27 end
28
f815272 @sstephenson properties -> identifier
authored
29 def call(identifier, *args)
3097959 @josh Switch to multi_json lib
josh authored
30 eval "#{identifier}.apply(this, #{MultiJson.encode(args)})"
50791dd @josh Add Context#call
josh authored
31 end
32
82773bf @josh Context
josh authored
33 protected
34 def compile_to_tempfile(source)
a1277a6 @josh Style
josh authored
35 tempfile = Tempfile.open(['execjs', '.js'])
82773bf @josh Context
josh authored
36 tempfile.write compile(source)
37 tempfile.close
38 yield tempfile
39 ensure
40 tempfile.close!
41 end
42
43 def compile(source)
0dc15c3 @josh Make runner_source and exec_runtime protected
josh authored
44 @runtime.send(:runner_source).dup.tap do |output|
bd4c71c @josh Fix sub \ escaping
josh authored
45 output.sub!('#{source}') do
46 source
47 end
67c8927 @josh Encode JS unicode literals for JSC runner
josh authored
48 output.sub!('#{encoded_source}') do
49 encoded_source = encode_unicode_codepoints(source)
50 MultiJson.encode("(function(){ #{encoded_source} })()")
51 end
82773bf @josh Context
josh authored
52 output.sub!('#{json2_source}') do
53 IO.read(ExecJS.root + "/support/json2.js")
54 end
55 end
56 end
57
58 def extract_result(output)
3097959 @josh Switch to multi_json lib
josh authored
59 status, value = output.empty? ? [] : MultiJson.decode(output)
82773bf @josh Context
josh authored
60 if status == "ok"
61 value
67c8927 @josh Encode JS unicode literals for JSC runner
josh authored
62 elsif value == "SyntaxError: Parse error"
63 raise RuntimeError, value
82773bf @josh Context
josh authored
64 else
65 raise ProgramError, value
66 end
67 end
67c8927 @josh Encode JS unicode literals for JSC runner
josh authored
68
69 if "".respond_to?(:codepoints)
70 def encode_unicode_codepoints(str)
71 str.gsub(/[\u0080-\uffff]/) do |ch|
72 "\\u%04x" % ch.codepoints.to_a
73 end
74 end
75 else
76 def encode_unicode_codepoints(str)
22ae39a @josh Faster 1.8 codepoint encoder
josh authored
77 str.gsub(/([\xC0-\xDF][\x80-\xBF]|
78 [\xE0-\xEF][\x80-\xBF]{2}|
79 [\xF0-\xF7][\x80-\xBF]{3})+/nx) do |ch|
80 "\\u%04x" % ch.unpack("U*")
81 end
67c8927 @josh Encode JS unicode literals for JSC runner
josh authored
82 end
83 end
82773bf @josh Context
josh authored
84 end
85
d19c760 @sstephenson Less magic
authored
86 attr_reader :name
87
954db08 @sstephenson Instantiate with options instead of subclassing
authored
88 def initialize(options)
d19c760 @sstephenson Less magic
authored
89 @name = options[:name]
954db08 @sstephenson Instantiate with options instead of subclassing
authored
90 @command = options[:command]
91 @runner_path = options[:runner_path]
5d0d51b @sstephenson therubyracer gem installs an incompatible `v8` binary - make sure we …
authored
92 @test_args = options[:test_args]
93 @test_match = options[:test_match]
adc3312 @wagenet CScript Unicode fix
wagenet authored
94 @encoding = options[:encoding]
c00ee39 @josh Support "nodejs" binary name
josh authored
95 @binary = locate_binary
954db08 @sstephenson Instantiate with options instead of subclassing
authored
96 end
97
82773bf @josh Context
josh authored
98 def exec(source)
99 context = Context.new(self)
c595c07 @josh Remove pure evaluation
josh authored
100 context.exec(source)
82773bf @josh Context
josh authored
101 end
102
0be476d @sstephenson Extract ExecJS::Runtime
authored
103 def eval(source)
82773bf @josh Context
josh authored
104 context = Context.new(self)
c595c07 @josh Remove pure evaluation
josh authored
105 context.eval(source)
0be476d @sstephenson Extract ExecJS::Runtime
authored
106 end
107
82773bf @josh Context
josh authored
108 def compile(source)
c595c07 @josh Remove pure evaluation
josh authored
109 Context.new(self, source)
c5e22c4 @sstephenson Runtime -> ExternalRuntime
authored
110 end
111
b22082f @sstephenson Add Runtime#available?
authored
112 def available?
3097959 @josh Switch to multi_json lib
josh authored
113 require "multi_json"
c00ee39 @josh Support "nodejs" binary name
josh authored
114 @binary ? true : false
b22082f @sstephenson Add Runtime#available?
authored
115 end
116
0dc15c3 @josh Make runner_source and exec_runtime protected
josh authored
117 protected
118 def runner_source
119 @runner_source ||= IO.read(@runner_path)
120 end
82773bf @josh Context
josh authored
121
0dc15c3 @josh Make runner_source and exec_runtime protected
josh authored
122 def exec_runtime(filename)
c9278db @josh Define sh helper specific to ruby env
josh authored
123 output = sh("#{@binary} #{filename} 2>&1")
0dc15c3 @josh Make runner_source and exec_runtime protected
josh authored
124 if $?.success?
125 output
126 else
127 raise RuntimeError, output
128 end
82773bf @josh Context
josh authored
129 end
130
c00ee39 @josh Support "nodejs" binary name
josh authored
131 def locate_binary
3cf772e @sstephenson Add `which` support for Windows
authored
132 if binary = which(@command)
c00ee39 @josh Support "nodejs" binary name
josh authored
133 if @test_args
134 output = `#{binary} #{@test_args} 2>&1`
135 binary if output.match(@test_match)
136 else
137 binary
138 end
139 end
140 end
141
3cf772e @sstephenson Add `which` support for Windows
authored
142 def which(command)
612c95e @josh Fix external runtime which to return the expanded path
josh authored
143 Array(command).each do |name|
88fa6b7 @swaits Preserve command flags passed to which
swaits authored
144 name, args = name.split(/\s+/, 2)
3cf772e @sstephenson Add `which` support for Windows
authored
145 result = if ExecJS.windows?
64eba84 @lukefx Path with spaces in Windows
lukefx authored
146 `"#{ExecJS.root}/support/which.bat" #{name}`
3cf772e @sstephenson Add `which` support for Windows
authored
147 else
da30b02 Fix external runtime detection under zsh and other shells.
Marcus Brito authored
148 `command -v #{name} 2>/dev/null`
3cf772e @sstephenson Add `which` support for Windows
authored
149 end
612c95e @josh Fix external runtime which to return the expanded path
josh authored
150
151 if path = result.strip.split("\n").first
88fa6b7 @swaits Preserve command flags passed to which
swaits authored
152 return args ? "#{path} #{args}" : path
612c95e @josh Fix external runtime which to return the expanded path
josh authored
153 end
3cf772e @sstephenson Add `which` support for Windows
authored
154 end
612c95e @josh Fix external runtime which to return the expanded path
josh authored
155 nil
3cf772e @sstephenson Add `which` support for Windows
authored
156 end
adc3312 @wagenet CScript Unicode fix
wagenet authored
157
c9278db @josh Define sh helper specific to ruby env
josh authored
158 if "".respond_to?(:force_encoding)
159 def sh(command)
160 output, options = nil, {}
161 options[:external_encoding] = @encoding if @encoding
164b541 @josh Ensure external runtime return value is utf8
josh authored
162 options[:internal_encoding] = Encoding.default_internal || 'UTF-8'
c9278db @josh Define sh helper specific to ruby env
josh authored
163 IO.popen(command, options) { |f| output = f.read }
164 output
165 end
166 else
167 require "iconv"
168
169 def sh(command)
170 output = nil
171 IO.popen(command) { |f| output = f.read }
172
173 if @encoding
174 Iconv.new('UTF-8', @encoding).iconv(output)
175 else
176 output
177 end
178 end
179 end
0be476d @sstephenson Extract ExecJS::Runtime
authored
180 end
181 end
Something went wrong with that request. Please try again.