mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 11:44:49 +08:00
FEATURE: upgrade from therubyracer to mini_racer
This pushes our internal V8 JavaScript engine from Chrome 32 to 50. It also resolves some long standing issues we had with the old wrapper.
This commit is contained in:
parent
f387dfe226
commit
695773db1c
3
Gemfile
3
Gemfile
|
@ -106,7 +106,8 @@ gem 'sidekiq-statistic'
|
|||
# for sidekiq web
|
||||
gem 'sinatra', require: false
|
||||
|
||||
gem 'therubyracer'
|
||||
gem 'execjs', github: 'rails/execjs', require: false
|
||||
gem 'mini_racer'
|
||||
gem 'thin', require: false
|
||||
gem 'highline', require: false
|
||||
gem 'rack-protection' # security
|
||||
|
|
18
Gemfile.lock
18
Gemfile.lock
|
@ -1,3 +1,9 @@
|
|||
GIT
|
||||
remote: git://github.com/rails/execjs.git
|
||||
revision: 22476229323cbec3befa8b690cc6c7c3957a0044
|
||||
specs:
|
||||
execjs (2.6.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
|
@ -94,7 +100,6 @@ GEM
|
|||
erubis (2.7.0)
|
||||
eventmachine (1.2.0.1)
|
||||
excon (0.45.4)
|
||||
execjs (2.6.0)
|
||||
exifr (1.2.4)
|
||||
fabrication (2.9.8)
|
||||
fakeweb (1.3.0)
|
||||
|
@ -144,7 +149,7 @@ GEM
|
|||
librarian (0.1.2)
|
||||
highline
|
||||
thor (~> 0.15)
|
||||
libv8 (3.16.14.13)
|
||||
libv8 (5.0.71.48.3)
|
||||
listen (0.7.3)
|
||||
logster (1.2.3)
|
||||
loofah (2.0.3)
|
||||
|
@ -159,6 +164,8 @@ GEM
|
|||
method_source (0.8.2)
|
||||
mime-types (2.99.1)
|
||||
mini_portile2 (2.1.0)
|
||||
mini_racer (0.1.3)
|
||||
libv8 (~> 5.0)
|
||||
minitest (5.8.4)
|
||||
mocha (1.1.0)
|
||||
metaclass (~> 0.0.1)
|
||||
|
@ -284,7 +291,6 @@ GEM
|
|||
redis (3.3.0)
|
||||
redis-namespace (1.5.2)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
ref (2.0.0)
|
||||
rest-client (1.8.0)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 3.0)
|
||||
|
@ -373,9 +379,6 @@ GEM
|
|||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
stackprof (0.2.9)
|
||||
therubyracer (0.12.2)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thin (1.6.4)
|
||||
daemons (~> 1.0, >= 1.0.9)
|
||||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
|
@ -417,6 +420,7 @@ DEPENDENCIES
|
|||
ember-rails (= 0.18.5)
|
||||
ember-source (= 1.12.2)
|
||||
excon
|
||||
execjs!
|
||||
fabrication (= 2.9.8)
|
||||
fakeweb (~> 1.3.0)
|
||||
fast_blank
|
||||
|
@ -439,6 +443,7 @@ DEPENDENCIES
|
|||
memory_profiler
|
||||
message_bus (= 2.0.0.beta.11)
|
||||
mime-types
|
||||
mini_racer
|
||||
minitest
|
||||
mocha
|
||||
mock_redis
|
||||
|
@ -493,7 +498,6 @@ DEPENDENCIES
|
|||
sinatra
|
||||
spork-rails
|
||||
stackprof
|
||||
therubyracer
|
||||
thin
|
||||
timecop
|
||||
uglifier
|
||||
|
|
|
@ -59,7 +59,7 @@ SCRIPT
|
|||
begin
|
||||
code = transpile(node.inner_html, node['version'])
|
||||
node.replace("<script>#{code}</script>")
|
||||
rescue Tilt::ES6ModuleTranspilerTemplate::JavaScriptError => ex
|
||||
rescue MiniRacer::RuntimeError => ex
|
||||
node.replace("<script type='text/discourse-js-error'>#{ex.message}</script>")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
require 'execjs'
|
||||
require 'babel/transpiler'
|
||||
require 'mini_racer'
|
||||
|
||||
module Tilt
|
||||
|
||||
class Console
|
||||
def initialize(prefix=nil)
|
||||
@prefix = prefix || ''
|
||||
end
|
||||
|
||||
def log(msg)
|
||||
Rails.logger.info("#{@prefix}#{msg}")
|
||||
end
|
||||
|
||||
def error(msg)
|
||||
Rails.logger.error("#{@prefix}#{msg}")
|
||||
end
|
||||
end
|
||||
|
||||
class ES6ModuleTranspilerTemplate < Tilt::Template
|
||||
self.default_mime_type = 'application/javascript'
|
||||
|
||||
|
@ -30,10 +17,19 @@ module Tilt
|
|||
|
||||
def self.create_new_context
|
||||
# timeout any eval that takes longer than 15 seconds
|
||||
ctx = V8::Context.new(timeout: 15000)
|
||||
ctx = MiniRacer::Context.new(timeout: 15000)
|
||||
ctx.eval("var self = this; #{File.read(Babel::Transpiler.script_path)}")
|
||||
ctx.eval("module = {}; exports = {};");
|
||||
ctx.load("#{Rails.root}/lib/es6_module_transpiler/support/es6-module-transpiler.js")
|
||||
ctx.attach("rails.logger.info", proc{|err| Rails.logger.info(err.to_s)})
|
||||
ctx.attach("rails.logger.error", proc{|err| Rails.logger.error(err.to_s)})
|
||||
ctx.eval <<JS
|
||||
console = {
|
||||
prefix: "",
|
||||
log: function(msg){ rails.logger.info(console.prefix + msg); },
|
||||
error: function(msg){ rails.logger.error(console.prefix + msg); }
|
||||
}
|
||||
JS
|
||||
ctx
|
||||
end
|
||||
|
||||
|
@ -60,19 +56,9 @@ module Tilt
|
|||
end
|
||||
|
||||
def self.protect
|
||||
rval = nil
|
||||
@mutex.synchronize do
|
||||
begin
|
||||
rval = yield
|
||||
# This may seem a bit odd, but we don't want to leak out
|
||||
# objects that require locks on the v8 vm, to get a backtrace
|
||||
# you need a lock, if this happens in the wrong spot you can
|
||||
# deadlock a process
|
||||
rescue V8::Error => e
|
||||
raise JavaScriptError.new(e.message, e.backtrace)
|
||||
end
|
||||
yield
|
||||
end
|
||||
rval
|
||||
end
|
||||
|
||||
def whitelisted?(path)
|
||||
|
@ -98,7 +84,7 @@ module Tilt
|
|||
def babel_transpile(source)
|
||||
klass = self.class
|
||||
klass.protect do
|
||||
klass.v8['console'] = Console.new("BABEL: babel-eval: ")
|
||||
klass.v8.eval("console.prefix = 'BABEL: babel-eval: ';")
|
||||
@output = klass.v8.eval(babel_source(source))
|
||||
end
|
||||
end
|
||||
|
@ -108,7 +94,7 @@ module Tilt
|
|||
|
||||
klass = self.class
|
||||
klass.protect do
|
||||
klass.v8['console'] = Console.new("BABEL: #{scope.logical_path}: ")
|
||||
klass.v8.eval("console.prefix = 'BABEL: #{scope.logical_path}: ';")
|
||||
@output = klass.v8.eval(generate_source(scope))
|
||||
end
|
||||
|
||||
|
|
|
@ -154,15 +154,25 @@ module JsLocaleHelper
|
|||
result
|
||||
end
|
||||
|
||||
def self.compile_message_format(locale, format)
|
||||
ctx = V8::Context.new
|
||||
ctx.load(Rails.root + 'lib/javascripts/messageformat.js')
|
||||
path = Rails.root + "lib/javascripts/locale/#{locale}.js"
|
||||
ctx.load(path) if File.exists?(path)
|
||||
ctx.eval("mf = new MessageFormat('#{locale}');")
|
||||
ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")
|
||||
@mutex = Mutex.new
|
||||
def self.with_context
|
||||
@mutex.synchronize do
|
||||
yield @ctx ||= begin
|
||||
ctx = MiniRacer::Context.new
|
||||
ctx.load(Rails.root + 'lib/javascripts/messageformat.js')
|
||||
ctx
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
rescue V8::Error => e
|
||||
def self.compile_message_format(locale, format)
|
||||
with_context do |ctx|
|
||||
path = Rails.root + "lib/javascripts/locale/#{locale}.js"
|
||||
ctx.load(path) if File.exists?(path)
|
||||
ctx.eval("mf = new MessageFormat('#{locale}');")
|
||||
ctx.eval("mf.precompile(mf.parse(#{format.inspect}))")
|
||||
end
|
||||
rescue MiniRacer::EvalError => e
|
||||
message = "Invalid Format: " << e.message
|
||||
"function(){ return #{message.inspect};}"
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require 'v8'
|
||||
require 'mini_racer'
|
||||
require 'nokogiri'
|
||||
require_dependency 'url_helper'
|
||||
require_dependency 'excerpt_parser'
|
||||
|
@ -7,7 +7,9 @@ require_dependency 'discourse_tagging'
|
|||
|
||||
module PrettyText
|
||||
|
||||
class Helpers
|
||||
module Helpers
|
||||
extend self
|
||||
|
||||
def t(key, opts)
|
||||
key = "js." + key
|
||||
unless opts
|
||||
|
@ -81,6 +83,7 @@ module PrettyText
|
|||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@mutex = Mutex.new
|
||||
|
@ -92,9 +95,11 @@ module PrettyText
|
|||
|
||||
def self.create_new_context
|
||||
# timeout any eval that takes longer than 15 seconds
|
||||
ctx = V8::Context.new(timeout: 15000)
|
||||
ctx = MiniRacer::Context.new(timeout: 15000)
|
||||
|
||||
ctx["helpers"] = Helpers.new
|
||||
Helpers.instance_methods.each do |method|
|
||||
ctx.attach("helpers.#{method}", Helpers.method(method))
|
||||
end
|
||||
|
||||
ctx_load(ctx,
|
||||
"vendor/assets/javascripts/md5.js",
|
||||
|
@ -198,6 +203,7 @@ module PrettyText
|
|||
# we use the exact same markdown converter as the client
|
||||
# TODO: use the same extensions on both client and server (in particular the template for mentions)
|
||||
baked = nil
|
||||
text = text || ""
|
||||
|
||||
protect do
|
||||
context = v8
|
||||
|
@ -206,8 +212,9 @@ module PrettyText
|
|||
|
||||
context_opts = opts || {}
|
||||
context_opts[:sanitize] = true unless context_opts[:sanitize] == false
|
||||
context['opts'] = context_opts
|
||||
context['raw'] = text
|
||||
|
||||
context.eval("opts = #{context_opts.to_json};")
|
||||
context.eval("raw = #{text.inspect};")
|
||||
|
||||
if Post.white_listed_image_classes.present?
|
||||
Post.white_listed_image_classes.each do |klass|
|
||||
|
@ -258,8 +265,10 @@ module PrettyText
|
|||
# leaving this here, cause it invokes v8, don't want to implement twice
|
||||
def self.avatar_img(avatar_template, size)
|
||||
protect do
|
||||
v8['avatarTemplate'] = avatar_template
|
||||
v8['size'] = size
|
||||
v8.eval <<JS
|
||||
avatarTemplate = #{avatar_template.inspect};
|
||||
size = #{size.to_i};
|
||||
JS
|
||||
decorate_context(v8)
|
||||
v8.eval("Discourse.Utilities.avatarImg({ avatarTemplate: avatarTemplate, size: size });")
|
||||
end
|
||||
|
@ -267,9 +276,8 @@ module PrettyText
|
|||
|
||||
def self.unescape_emoji(title)
|
||||
protect do
|
||||
v8["title"] = title
|
||||
decorate_context(v8)
|
||||
v8.eval("Discourse.Emoji.unescape(title)")
|
||||
v8.eval("Discourse.Emoji.unescape(#{title.inspect})")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -413,15 +421,7 @@ module PrettyText
|
|||
def self.protect
|
||||
rval = nil
|
||||
@mutex.synchronize do
|
||||
begin
|
||||
rval = yield
|
||||
# This may seem a bit odd, but we don't want to leak out
|
||||
# objects that require locks on the v8 vm, to get a backtrace
|
||||
# you need a lock, if this happens in the wrong spot you can
|
||||
# deadlock a process
|
||||
rescue V8::Error => e
|
||||
raise JavaScriptError.new(e.message, e.backtrace)
|
||||
end
|
||||
rval = yield
|
||||
end
|
||||
rval
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require 'rails_helper'
|
||||
require_dependency 'js_locale_helper'
|
||||
require 'mini_racer'
|
||||
|
||||
describe JsLocaleHelper do
|
||||
|
||||
|
@ -25,7 +26,7 @@ describe JsLocaleHelper do
|
|||
end
|
||||
|
||||
def setup_message_format(format)
|
||||
@ctx = V8::Context.new
|
||||
@ctx = MiniRacer::Context.new
|
||||
@ctx.eval('MessageFormat = {locale: {}};')
|
||||
@ctx.load(Rails.root + 'lib/javascripts/locale/en.js')
|
||||
compiled = JsLocaleHelper.compile_message_format('en', format)
|
||||
|
@ -72,7 +73,7 @@ describe JsLocaleHelper do
|
|||
end
|
||||
|
||||
it 'handles message format special keys' do
|
||||
ctx = V8::Context.new
|
||||
ctx = MiniRacer::Context.new
|
||||
ctx.eval("I18n = {};")
|
||||
|
||||
JsLocaleHelper.set_translations 'en', {
|
||||
|
@ -149,7 +150,7 @@ describe JsLocaleHelper do
|
|||
SiteSetting.default_locale = 'ru'
|
||||
I18n.locale = :uk
|
||||
|
||||
ctx = V8::Context.new
|
||||
ctx = MiniRacer::Context.new
|
||||
ctx.eval('var window = this;')
|
||||
ctx.load(Rails.root + 'app/assets/javascripts/locales/i18n.js')
|
||||
ctx.eval(JsLocaleHelper.output_locale(I18n.locale))
|
||||
|
@ -167,7 +168,7 @@ describe JsLocaleHelper do
|
|||
LocaleSiteSetting.values.each do |locale|
|
||||
it "generates valid date helpers for #{locale[:value]} locale" do
|
||||
js = JsLocaleHelper.output_locale(locale[:value])
|
||||
ctx = V8::Context.new
|
||||
ctx = MiniRacer::Context.new
|
||||
ctx.eval('var window = this;')
|
||||
ctx.load(Rails.root + 'app/assets/javascripts/locales/i18n.js')
|
||||
ctx.eval(js)
|
||||
|
|
Loading…
Reference in New Issue
Block a user