lib/opal/rewriters/js_reserved_words.rb
# frozen_string_literal: true
require 'opal/rewriters/base'
require 'opal/regexp_anchors'
module Opal
module Rewriters
class JsReservedWords < Base
# Reserved javascript keywords - we cannot create variables with the
# same name (ref: http://stackoverflow.com/a/9337272/601782)
ES51_RESERVED_WORD = /#{REGEXP_START}(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)#{REGEXP_END}/.freeze
# ES3 reserved words that aren’t ES5.1 reserved words
ES3_RESERVED_WORD_EXCLUSIVE = /#{REGEXP_START}(?:int|byte|char|goto|long|final|float|short|double|native|throws|boolean|abstract|volatile|transient|synchronized)#{REGEXP_END}/.freeze
# Prototype special properties.
PROTO_SPECIAL_PROPS = /#{REGEXP_START}(?:constructor|displayName|__proto__|__parent__|__noSuchMethod__|__count__)#{REGEXP_END}/.freeze
# Prototype special methods.
PROTO_SPECIAL_METHODS = /#{REGEXP_START}(?:hasOwnProperty|valueOf)#{REGEXP_END}/.freeze
# Immutable properties of the global object
IMMUTABLE_PROPS = /#{REGEXP_START}(?:NaN|Infinity|undefined)#{REGEXP_END}/.freeze
# Doesn't take in account utf8
BASIC_IDENTIFIER_RULES = /#{REGEXP_START}[$_a-z][$_a-z\d]*#{REGEXP_END}/i.freeze
# Defining a local function like Array may break everything
RESERVED_FUNCTION_NAMES = /#{REGEXP_START}(?:Array)#{REGEXP_END}/.freeze
def self.valid_name?(name)
BASIC_IDENTIFIER_RULES =~ name && !(
ES51_RESERVED_WORD =~ name ||
ES3_RESERVED_WORD_EXCLUSIVE =~ name ||
IMMUTABLE_PROPS =~ name
)
end
def self.valid_ivar_name?(name)
!(PROTO_SPECIAL_PROPS =~ name || PROTO_SPECIAL_METHODS =~ name)
end
def fix_var_name(name)
self.class.valid_name?(name) ? name : "#{name}$".to_sym
end
def fix_ivar_name(name)
self.class.valid_ivar_name?(name.to_s[1..-1]) ? name : "#{name}$".to_sym
end
def on_lvar(node)
name, _ = *node
node = node.updated(nil, [fix_var_name(name)])
super(node)
end
def on_lvasgn(node)
name, value = *node
node =
if value
node.updated(nil, [fix_var_name(name), value])
else
node.updated(nil, [fix_var_name(name)])
end
super(node)
end
def on_ivar(node)
name, _ = *node
node = node.updated(nil, [fix_ivar_name(name)])
super(node)
end
def on_ivasgn(node)
name, value = *node
node =
if value
node.updated(nil, [fix_ivar_name(name), value])
else
node.updated(nil, [fix_ivar_name(name)])
end
super(node)
end
# Restarg and kwrestarg are special cases
# because they may have no name
# def m(*, **); end
def on_restarg(node)
name, _ = *node
if name
node = node.updated(nil, [fix_var_name(name)], meta: { arg_name: name })
end
node
end
alias on_kwrestarg on_restarg
def on_argument(node)
node = super(node)
name, value = *node
fixed_name = fix_var_name(name)
new_children = value ? [fixed_name, value] : [fixed_name]
node.updated(nil, new_children, meta: { arg_name: name })
end
end
end
end