Skip to content

Commit

Permalink
Module#prepend (#1826)
Browse files Browse the repository at this point in the history
* Massive rewrite of runtime.js: inheritance and modules inclusion is implemented using prototypes.

* Added Module#prepend.
  • Loading branch information
iliabylich committed May 25, 2018
1 parent 2c2093c commit 3d46dd4
Show file tree
Hide file tree
Showing 25 changed files with 800 additions and 1,054 deletions.
2 changes: 1 addition & 1 deletion lib/opal/nodes/class.rb
Expand Up @@ -19,7 +19,7 @@ def compile

in_scope do
scope.name = name
add_temp "#{scope.proto} = self.$$proto"
add_temp "#{scope.proto} = self.prototype"
add_temp '$nesting = [self].concat($parent_nesting)'

body_code = self.body_code
Expand Down
5 changes: 3 additions & 2 deletions lib/opal/nodes/module.rb
Expand Up @@ -14,11 +14,12 @@ def compile
helper :module

push '(function($base, $parent_nesting) {'
line " var $#{name}, self = $#{name} = $module($base, '#{name}');"
line " function $#{name}() {};"
line " var self = $#{name} = $module($base, '#{name}', $#{name});"

in_scope do
scope.name = name
add_temp "#{scope.proto} = self.$$proto"
add_temp "#{scope.proto} = self.prototype"
add_temp '$nesting = [self].concat($parent_nesting)'

body_code = stmt(body || s(:nil))
Expand Down
2 changes: 1 addition & 1 deletion lib/opal/nodes/singleton_class.rb
Expand Up @@ -13,7 +13,7 @@ def compile
push '(function(self, $parent_nesting) {'

in_scope do
add_temp 'def = self.$$proto'
add_temp 'def = self.prototype'
add_temp '$nesting = [self].concat($parent_nesting)'

body_stmt = stmt(compiler.returns(body))
Expand Down
10 changes: 2 additions & 8 deletions lib/opal/nodes/super.rb
Expand Up @@ -73,7 +73,7 @@ def def_scope_identity

def super_method_invocation
if def_scope.defs
class_name = def_scope.parent.name ? "$#{def_scope.parent.name}" : 'self.$$class.$$proto'
class_name = def_scope.parent.name ? "$#{def_scope.parent.name}" : 'self.$$class.prototype'
"Opal.find_super_dispatcher(self, '#{method_id}', #{def_scope_identity}, #{defined_check_param}, #{class_name})"
else
"Opal.find_super_dispatcher(self, '#{method_id}', #{def_scope_identity}, #{defined_check_param})"
Expand Down Expand Up @@ -105,13 +105,7 @@ def compile
compile_receiver
compile_method

# will never come back null with method missing on
if compiler.method_missing?
wrap '(!(', '.$$stub) ? "super" : nil)'
else
# TODO: With method_missing support off, something breaks in runtime.js's chain
wrap '((', ') != null ? "super" : nil)'
end
wrap '((', ') != null ? "super" : nil)'
end
end

Expand Down
2 changes: 1 addition & 1 deletion opal/corelib/array.rb
Expand Up @@ -2334,7 +2334,7 @@ def zip(*others, &block)

def self.inherited(klass)
%x{
klass.$$proto.$to_a = function() {
klass.prototype.$to_a = function() {
return this.slice(0, this.length);
}
}
Expand Down
13 changes: 3 additions & 10 deletions opal/corelib/class.rb
Expand Up @@ -7,23 +7,16 @@ def self.new(superclass = Object, &block)
throw Opal.TypeError.$new("superclass must be a Class");
}
var alloc = Opal.boot_class_alloc(null, function(){}, superclass);
var klass = Opal.setup_class_object(null, alloc, superclass.$$name, superclass.constructor);
klass.$$super = superclass;
klass.$$parent = superclass;
var klass = Opal.allocate_class(nil, superclass, function(){});
superclass.$inherited(klass);
Opal.module_initialize(klass, block);
Opal.refresh_ancestors(klass);
#{`klass`.class_eval(&block) if block_given?}
return klass;
}
end

def allocate
%x{
var obj = new self.$$alloc();
var obj = new self();
obj.$$id = Opal.uid();
return obj;
}
Expand Down
2 changes: 1 addition & 1 deletion opal/corelib/error.rb
Expand Up @@ -5,7 +5,7 @@ class Exception < `Error`
def self.new(*args)
%x{
var message = (args.length > 0) ? args[0] : nil;
var error = new self.$$alloc(message);
var error = new self(message);
error.name = self.$$name;
error.message = message;
Opal.send(error, error.$initialize, args);
Expand Down
6 changes: 3 additions & 3 deletions opal/corelib/hash.rb
Expand Up @@ -65,7 +65,7 @@ def self.[](*argv)

def self.allocate
%x{
var hash = new self.$$alloc();
var hash = new self();
Opal.hash_init(hash);
Expand Down Expand Up @@ -221,7 +221,7 @@ def clear

def clone
%x{
var hash = new self.$$class.$$alloc();
var hash = new self.$$class();
Opal.hash_init(hash);
Opal.hash_clone(self, hash);
Expand Down Expand Up @@ -1052,7 +1052,7 @@ def to_h
return self;
}
var hash = new Opal.Hash.$$alloc();
var hash = new Opal.Hash();
Opal.hash_init(hash);
Opal.hash_clone(self, hash);
Expand Down
6 changes: 3 additions & 3 deletions opal/corelib/helpers.rb
@@ -1,6 +1,6 @@
module Opal
def self.bridge(klass, constructor)
`Opal.bridge(klass, constructor)`
def self.bridge(constructor, klass)
`Opal.bridge(constructor, klass)`
end

def self.type_error(object, type, method = nil, coerced = nil)
Expand Down Expand Up @@ -155,7 +155,7 @@ def self.pristine(owner_class, *method_names)
var method_name, method;
for (var i = method_names.length - 1; i >= 0; i--) {
method_name = method_names[i];
method = owner_class.$$proto['$'+method_name];
method = owner_class.prototype['$'+method_name];
if (method && !method.$$stub) {
method.$$pristine = true;
Expand Down
16 changes: 11 additions & 5 deletions opal/corelib/kernel.rb
Expand Up @@ -108,16 +108,22 @@ def copy_singleton_methods(other)
var i, name, names, length;
if (other.hasOwnProperty('$$meta')) {
var other_singleton_class_proto = Opal.get_singleton_class(other).$$proto;
var self_singleton_class_proto = Opal.get_singleton_class(self).$$proto;
names = Object.getOwnPropertyNames(other_singleton_class_proto);
var other_singleton_class = Opal.get_singleton_class(other);
var self_singleton_class = Opal.get_singleton_class(self);
names = Object.getOwnPropertyNames(other_singleton_class.prototype);
for (i = 0, length = names.length; i < length; i++) {
name = names[i];
if (name.charAt(0) === '$') {
self_singleton_class_proto[name] = other_singleton_class_proto[name];
if (Opal.is_method(name)) {
self_singleton_class.prototype[name] = other_singleton_class.prototype[name];
}
}
self_singleton_class.$$const = Object.assign({}, other_singleton_class.$$const);
Object.setPrototypeOf(
self_singleton_class.prototype,
Object.getPrototypeOf(other_singleton_class.prototype)
);
}
for (i = 0, names = Object.getOwnPropertyNames(other), length = names.length; i < length; i++) {
Expand Down
75 changes: 53 additions & 22 deletions opal/corelib/module.rb
@@ -1,15 +1,23 @@
class Module
def self.allocate
%x{
var module;
module = Opal.module_allocate(self);
var module = Opal.allocate_module(nil, function(){});
return module;
}
end

def self.inherited(klass)
%x{
klass.$allocate = function() {
var module = Opal.allocate_module(nil, function(){});
Object.setPrototypeOf(module, klass.prototype);
return module;
}
}
end

def initialize(&block)
`Opal.module_initialize(self, block)`
module_eval(&block) if block_given?
end

def ===(object)
Expand Down Expand Up @@ -111,7 +119,7 @@ def attr_accessor(*names)

def attr_reader(*names)
%x{
var proto = self.$$proto;
var proto = self.prototype;
for (var i = names.length - 1; i >= 0; i--) {
var name = names[i],
Expand Down Expand Up @@ -146,7 +154,7 @@ def attr_reader(*names)

def attr_writer(*names)
%x{
var proto = self.$$proto;
var proto = self.prototype;
for (var i = names.length - 1; i >= 0; i--) {
var name = names[i],
Expand Down Expand Up @@ -407,19 +415,7 @@ def include(*mods)
end

def included_modules
%x{
var results = [];
for (var i = 0, ancestors = self.$$ancestors, length = ancestors.length; i < length; i++) {
var ancestor = ancestors[i];
if (ancestor !== self && ancestor.$$is_module) {
results.push(ancestor);
}
}
return results;
}
`Opal.included_modules(self)`
end

def include?(mod)
Expand All @@ -443,7 +439,7 @@ def include?(mod)

def instance_method(name)
%x{
var meth = self.$$proto['$' + name];
var meth = self.prototype['$' + name];
if (!meth || meth.$$stub) {
#{raise NameError.new("undefined method `#{name}' for class `#{self.name}'", name)};
Expand Down Expand Up @@ -535,7 +531,7 @@ def module_exec(*args, &block)

def method_defined?(method)
%x{
var body = self.$$proto['$' + method];
var body = self.prototype['$' + method];
return (!!body) && !body.$$stub;
}
end
Expand All @@ -549,7 +545,7 @@ def module_function(*methods)
for (var i = 0, length = methods.length; i < length; i++) {
var meth = methods[i],
id = '$' + meth,
func = self.$$proto[id];
func = self.prototype[id];
Opal.defs(self, id, func);
}
Expand Down Expand Up @@ -588,6 +584,41 @@ def name
}
end

def prepend(*mods)
%x{
if (mods.length === 0) {
#{raise ArgumentError, 'wrong number of arguments (given 0, expected 1+)'}
}
for (var i = mods.length - 1; i >= 0; i--) {
var mod = mods[i];
if (!mod.$$is_module) {
#{raise TypeError, "wrong argument type #{`mod`.class} (expected Module)"};
}
#{`mod`.prepend_features self};
#{`mod`.prepended self};
}
}

self
end

def prepend_features(prepender)
%x{
if (!self.$$is_module) {
#{raise TypeError, "wrong argument type #{self.class} (expected Module)"};
}
Opal.prepend_features(self, prepender)
}
self
end

def prepended(mod)
end

def remove_const(name)
`Opal.const_remove(self, name)`
end
Expand Down
2 changes: 1 addition & 1 deletion opal/corelib/number.rb
@@ -1,7 +1,7 @@
require 'corelib/numeric'

class Number < Numeric
Opal.bridge(self, `Number`)
Opal.bridge(`Number`, self)
`Opal.defineProperty(Number.prototype, '$$is_number', true)`
`self.$$is_number_class = true`

Expand Down
2 changes: 1 addition & 1 deletion opal/corelib/regexp.rb
Expand Up @@ -103,7 +103,7 @@ def new(regexp, options = undefined)
end

def ==(other)
`other.constructor == RegExp && self.toString() === other.toString()`
`other instanceof RegExp && self.toString() === other.toString()`
end

def ===(string)
Expand Down

0 comments on commit 3d46dd4

Please sign in to comment.