runtime.js
Opal = this.Opal = {}
The Opal object that is exposed globally
var Opal = this.Opal = {};
bridged_classes = Opal.bridged_classes = []
All bridged classes - keep track to donate methods from Object
var bridged_classes = Opal.bridged_classes = [];
TopScope = function(){}
TopScope is used for inheriting constants from the top scope
var TopScope = function(){};
TopScope.prototype = Opal
Opal just acts as the top scope
TopScope.prototype = Opal;
Opal.constructor = TopScope
To inherit scopes
Opal.constructor = TopScope;
Opal.constants = []
List top scope constants
Opal.constants = [];
Opal.global = this
This is a useful reference to global object inside ruby files
Opal.global = this;
$hasOwn = Opal.hasOwnProperty
Minify common function calls
var $hasOwn = Opal.hasOwnProperty;
nil_id = 4
Nil object id is always 4
var nil_id = 4;
unique_id = nil_id
Generates even sequential numbers greater than 4 (nil_id) to serve as unique ids for ruby objects
var unique_id = nil_id;
Opal.uid = function()
Return next unique id
Opal.uid = function() {
unique_id += 2;
return unique_id;
};
Opal.cvars = {}
Table holds all class variables
Opal.cvars = {};
Opal.gvars = {}
Globals table
Opal.gvars = {};
Opal.exit = function(status) { if (Opal.gvars.DEBUG) console.log('Exited with status '+status); }
Exit function, this should be replaced by platform specific implementation (See nodejs and phantom for examples)
Opal.exit = function(status) { if (Opal.gvars.DEBUG) console.log('Exited with status '+status); };
Opal.get = function(name)
Get a constant on the given scope. Every class and module in Opal has a
scope used to store, and inherit, constants. For example, the top level
Object
in ruby has a scope accessible as Opal.Object.$$scope
.
To get the Array
class using this scope, you could use:
Opal.Object.$$scope.get("Array")
If a constant with the given name cannot be found, then a dispatch to the
class/module's #const_method
is called, which by default will raise an
error.
@param [String] name the name of the constant to lookup @returns [RubyObject]
Opal.get = function(name) {
var constant = this[name];
if (constant == null) {
return this.base.$const_missing(name);
}
return constant;
};
function create_scope(base, klass, id)
Create a new constants scope for the given class with the given base. Constants are looked up through their parents, so the base scope will be the outer scope of the new klass.
function create_scope(base, klass, id) {
var const_alloc = function() {};
var const_scope = const_alloc.prototype = new base.constructor();
klass.$$scope = const_scope;
klass.$$base_module = base.base;
const_scope.base = klass;
const_scope.constructor = const_alloc;
const_scope.constants = [];
if (id) {
klass.$$orig_scope = base;
base[id] = base.constructor[id] = klass;
base.constants.push(id);
}
}
Opal.klass = function(base, superklass, id, constructor)
A class Foo; end
expression in ruby is compiled to call this runtime
method which either returns an existing class of the given name, or creates
a new class in the given base
scope.
*
If a constant with the given name exists, then we check to make sure that
it is a class and also that the superclasses match. If either of these
fail, then we raise a TypeError
. Note, superklass may be null if one was
not specified in the ruby code.
*
We pass a constructor to this method of the form function ClassName() {}
simply so that classes show up with nicely formatted names inside debuggers
in the web browser (or node/sprockets).
*
The base
is the current self
value where the class is being created
from. We use this to get the scope for where the class should be created.
If base
is an object (not a class/module), we simple get its class and
use that as the base instead.
*
@param [Object] base where the class is being created
@param [Class] superklass superclass of the new class (may be null)
@param [String] id the name of the class to be created
@param [Function] constructor function to use as constructor
@return [Class] new or existing ruby class
Opal.klass = function(base, superklass, id, constructor) {
// If base is an object, use its class
if (!base.$$is_class) {
base = base.$$class;
}
// Not specifying a superclass means we can assume it to be Object
if (superklass === null) {
superklass = ObjectClass;
}
var klass = base.$$scope[id];
// If a constant exists in the scope, then we must use that
if ($hasOwn.call(base.$$scope, id) && klass.$$orig_scope === base.$$scope) {
// Make sure the existing constant is a class, or raise error
if (!klass.$$is_class) {
throw Opal.TypeError.$new(id + " is not a class");
}
// Make sure existing class has same superclass
if (superklass !== klass.$$super && superklass !== ObjectClass) {
throw Opal.TypeError.$new("superclass mismatch for class " + id);
}
}
else if (typeof(superklass) === 'function') {
// passed native constructor as superklass, so bridge it as ruby class
return bridge_class(id, superklass, base);
}
else {
// if class doesnt exist, create a new one with given superclass
klass = boot_class(superklass, constructor);
// name class using base (e.g. Foo or Foo::Baz)
klass.$$name = id;
// every class gets its own constant scope, inherited from current scope
create_scope(base.$$scope, klass, id);
// Name new class directly onto current scope (Opal.Foo.Baz = klass)
base[id] = base.$$scope[id] = klass;
// Copy all parent constants to child, unless parent is Object
if (superklass !== ObjectClass && superklass !== BasicObjectClass) {
donate_constants(superklass, klass);
}
// call .inherited() hook with new class on the superclass
if (superklass.$inherited) {
superklass.$inherited(klass);
}
}
return klass;
};
function boot_class(superklass, constructor)
Create generic class with given superclass.
function boot_class(superklass, constructor) {
var alloc = boot_class_alloc(null, constructor, superklass)
return boot_class_object(superklass, alloc);
}
Opal.boot = boot_class
Make boot_class
available to the JS-API
Opal.boot = boot_class;
function boot_class_object(superklass, alloc)
The class object itself (as in Class.new
)
*
@param [(Opal) Class] superklass Another class object (as in Class.new
)
@param [constructor] alloc The constructor that holds the prototype
that will be used for instances of the
newly constructed class.
function boot_class_object(superklass, alloc) {
var singleton_class = function() {};
singleton_class.prototype = superklass.constructor.prototype;
function OpalClass() {}
OpalClass.prototype = new singleton_class();
var klass = new OpalClass();
setup_module_or_class_object(klass, OpalClass, superklass, alloc.prototype);
// @property $$alloc This is the constructor of instances of the current
// class. Its prototype will be used for method lookup
klass.$$alloc = alloc;
// @property $$proto.$$class Make available to instances a reference to the
// class they belong to.
klass.$$proto.$$class = klass;
return klass;
}
function setup_module_or_class_object(module, constructor, superklass, prototype)
Adds common/required properties to a module or class object
(as in Module.new
/ Class.new
)
*
@param module The module or class that needs to be prepared
*
@param constructor The constructor of the module or class itself,
usually it's already assigned by using new
. Some
ipothesis on why it's needed can be found below.
*
@param superklass The superclass of the class/module object, for modules
is Module
(of ModuleClass
in JS context)
*
@param prototype The prototype on which the class/module methods will
be stored.
function setup_module_or_class_object(module, constructor, superklass, prototype) {
// @property $$id Each class is assigned a unique `id` that helps
// comparation and implementation of `#object_id`
module.$$id = Opal.uid();
// @property $$proto This is the prototype on which methods will be defined
module.$$proto = prototype;
// @property constructor keeps a ref to the constructor, but apparently the
// constructor is already set on:
//
// `var module = new constructor` is called.
//
// Maybe there are some browsers not abiding (IE6?)
module.constructor = constructor;
// @property $$is_class Clearly mark this as a class-like
module.$$is_class = true;
// @property $$super the superclass, doesn't get changed by module inclusions
module.$$super = superklass;
// @property $$parent direct parent class or module
// starts with the superclass, after module inclusion is
// the last included module
module.$$parent = superklass;
// @property $$methods keeps track of methods defined on the class
// but seems to be used just by `define_basic_object_method`
// and for donating (Ruby) Object methods to bridged classes
// TODO: check if it can be removed
module.$$methods = [];
// @property $$inc included modules
module.$$inc = [];
}
Opal.module = function(base, id)
Define new module (or return existing module). The given base
is basically
the current self
value the module
statement was defined in. If this is
a ruby module or class, then it is used, otherwise if the base is a ruby
object then that objects real ruby class is used (e.g. if the base is the
main object, then the top level Object
class is used as the base).
If a module of the given name is already defined in the base, then that instance is just returned.
If there is a class of the given name in the base, then an error is generated instead (cannot have a class and module of same name in same base).
Otherwise, a new module is created in the base with the given name, and that new instance is returned back (to be referenced at runtime).
@param [RubyModule or Class] base class or module this definition is inside @param [String] id the name of the new (or existing) module @returns [RubyModule]
Opal.module = function(base, id) {
var module;
if (!base.$$is_class) {
base = base.$$class;
}
if ($hasOwn.call(base.$$scope, id)) {
module = base.$$scope[id];
if (!module.$$is_mod && module !== ObjectClass) {
throw Opal.TypeError.$new(id + " is not a module");
}
}
else {
module = boot_module_object();
module.$$name = id;
create_scope(base.$$scope, module, id);
// Name new module directly onto current scope (Opal.Foo.Baz = module)
base[id] = base.$$scope[id] = module;
}
return module;
};
function boot_module_object()
Internal function to create a new module instance. This simply sets up the prototype hierarchy and method tables.
function boot_module_object() {
var mtor = function() {};
mtor.prototype = ModuleClass.constructor.prototype;
function module_constructor() {}
module_constructor.prototype = new mtor();
var module = new module_constructor();
var module_prototype = {};
setup_module_or_class_object(module, module_constructor, ModuleClass, module_prototype);
module.$$is_mod = true;
module.$$dep = [];
return module;
}
Opal.get_singleton_class = function(object)
Return the singleton class for the passed object.
If the given object alredy has a singleton class, then it will be stored on
the object as the $$meta
property. If this exists, then it is simply
returned back.
Otherwise, a new singleton object for the class or object is created, set on
the object at $$meta
for future use, and then returned.
@param [RubyObject] object the ruby object @returns [RubyClass] the singleton class for object
Opal.get_singleton_class = function(object) {
if (object.$$meta) {
return object.$$meta;
}
if (object.$$is_class) {
return build_class_singleton_class(object);
}
return build_object_singleton_class(object);
};
function build_class_singleton_class(klass)
Build the singleton class for an existing class.
NOTE: Actually in MRI a class' singleton class inherits from its superclass' singleton class which in turn inherits from Class.
@param [RubyClass] klass @returns [RubyClass]
function build_class_singleton_class(klass) {
var meta = new Opal.Class.$$alloc;
meta.$$class = Opal.Class;
meta.$$proto = klass.constructor.prototype;
meta.$$is_singleton = true;
meta.$$inc = [];
meta.$$methods = [];
meta.$$scope = klass.$$scope;
return klass.$$meta = meta;
}
function build_object_singleton_class(object)
Build the singleton class for a Ruby (non class) Object.
@param [RubyObject] object @returns [RubyClass]
function build_object_singleton_class(object) {
var orig_class = object.$$class,
class_id = "#<Class:#<" + orig_class.$$name + ":" + orig_class.$$id + ">>";
var Singleton = function () {};
var meta = Opal.boot(orig_class, Singleton);
meta.$$name = class_id;
meta.$$proto = object;
meta.$$class = orig_class.$$class;
meta.$$scope = orig_class.$$scope;
meta.$$parent = orig_class;
return object.$$meta = meta;
}
Opal.append_features = function(module, klass)
The actual inclusion of a module into a class.
Class $$parent
and iclass
To handle super
calls, every class has a $$parent
. This parent is
used to resolve the next class for a super call. A normal class would
have this point to its superclass. However, if a class includes a module
then this would need to take into account the module. The module would
also have to then point its $$parent
to the actual superclass. We
cannot modify modules like this, because it might be included in more
then one class. To fix this, we actually insert an iclass
as the class'
$$parent
which can then point to the superclass. The iclass
acts as
a proxy to the actual module, so the super
chain can then search it for
the required method.
@param [RubyModule] module the module to include @param [RubyClass] klass the target class to include module into @returns [null]
Opal.append_features = function(module, klass) {
var included = klass.$$inc;
// check if this module is already included in the klass
for (var j = 0, jj = included.length; j < jj; j++) {
if (included[j] === module) {
return;
}
}
included.push(module);
module.$$dep.push(klass);
// iclass
var iclass = {
$$name: module.$$name,
$$proto: module.$$proto,
$$parent: klass.$$parent,
$$module: module,
$$iclass: true
};
klass.$$parent = iclass;
var donator = module.$$proto,
prototype = klass.$$proto,
methods = module.$$methods;
for (var i = 0, length = methods.length; i < length; i++) {
var method = methods[i], current;
if ( prototype.hasOwnProperty(method) &&
!(current = prototype[method]).$$donated && !current.$$stub ) {
// if the target class already has a method of the same name defined
// and that method was NOT donated, then it must be a method defined
// by the class so we do not want to override it
}
else {
prototype[method] = donator[method];
prototype[method].$$donated = true;
}
}
if (klass.$$dep) {
donate_methods(klass, methods.slice(), true);
}
donate_constants(module, klass);
};
function boot_class_alloc(id, constructor, superklass)
Boot a base class (makes instances).
function boot_class_alloc(id, constructor, superklass) {
if (superklass) {
var ctor = function() {};
ctor.prototype = superklass.$$proto || superklass.prototype;
if (id) {
ctor.displayName = id;
}
constructor.prototype = new ctor();
}
constructor.prototype.constructor = constructor;
return constructor;
}
function boot_core_class_object(id, alloc, superclass)
Builds the class object for core classes:
- make the class object have a singleton class
- make the singleton class inherit from its parent singleton class * @param id [String] the name of the class @param alloc [Function] the constructor for the core class instances @param superclass [Class alloc] the constructor of the superclass
function boot_core_class_object(id, alloc, superclass) {
var superclass_constructor = function() {};
superclass_constructor.prototype = superclass.prototype;
var singleton_class = function() {};
singleton_class.prototype = new superclass_constructor();
singleton_class.displayName = "#<Class:"+id+">";
// the singleton_class acts as the class object constructor
var klass = new singleton_class();
setup_module_or_class_object(klass, singleton_class, superclass, alloc.prototype);
klass.$$alloc = alloc;
klass.$$name = id;
// Give all instances a ref to their class
alloc.prototype.$$class = klass;
Opal[id] = klass;
Opal.constants.push(id);
return klass;
}
function bridge_class(name, constructor, base)
For performance, some core ruby classes are toll-free bridged to their
native javascript counterparts (e.g. a ruby Array is a javascript Array).
*
This method is used to setup a native constructor (e.g. Array), to have
its prototype act like a normal ruby class. Firstly, a new ruby class is
created using the native constructor so that its prototype is set as the
target for th new class. Note: all bridged classes are set to inherit
from Object.
*
Bridged classes are tracked in bridged_classes
array so that methods
defined on Object can be "donated" to all bridged classes. This allows
us to fake the inheritance of a native prototype from our Object
prototype.
*
Example:
*
bridge_class("Proc", Function);
*
@param [String] name the name of the ruby class to create
@param [Function] constructor native javascript constructor to use
@param [Object] base where the bridge class is being created. If none is supplied, the top level scope (Opal) will be used
@return [Class] returns new ruby class
function bridge_class(name, constructor, base) {
var klass = boot_class_object(ObjectClass, constructor);
klass.$$name = name;
if (base === undefined) {
base = Opal;
}
else {
base = base.$$scope;
}
create_scope(base, klass, name);
bridged_classes.push(klass);
var object_methods = BasicObjectClass.$$methods.concat(ObjectClass.$$methods);
for (var i = 0, len = object_methods.length; i < len; i++) {
var meth = object_methods[i];
constructor.prototype[meth] = ObjectClass.$$proto[meth];
}
add_stubs_subscriber(constructor.prototype);
return klass;
}
Opal.casgn = function(base_module, name, value)
constant assign
Opal.casgn = function(base_module, name, value) {
var scope = base_module.$$scope;
if (value.$$is_class && value.$$name === nil) {
value.$$name = name;
}
if (value.$$is_class) {
value.$$base_module = base_module;
}
scope.constants.push(name);
return scope[name] = value;
};
Opal.cdecl = function(base_scope, name, value)
constant decl
Opal.cdecl = function(base_scope, name, value) {
base_scope.constants.push(name);
return base_scope[name] = value;
};
function donate_constants(source_mod, target_mod)
When a source module is included into the target module, we must also copy its constants to the target.
function donate_constants(source_mod, target_mod) {
var source_constants = source_mod.$$scope.constants,
target_scope = target_mod.$$scope,
target_constants = target_scope.constants;
for (var i = 0, length = source_constants.length; i < length; i++) {
target_constants.push(source_constants[i]);
target_scope[source_constants[i]] = source_mod.$$scope[source_constants[i]];
}
};
Opal.add_stubs = function(stubs)
Methods stubs are used to facilitate method_missing in opal. A stub is a
placeholder function which just calls method_missing
on the receiver.
If no method with the given name is actually defined on an object, then it
is obvious to say that the stub will be called instead, and then in turn
method_missing will be called.
*
When a file in ruby gets compiled to javascript, it includes a call to
this function which adds stubs for every method name in the compiled file.
It should then be safe to assume that method_missing will work for any
method call detected.
*
Method stubs are added to the BasicObject prototype, which every other
ruby object inherits, so all objects should handle method missing. A stub
is only added if the given property name (method name) is not already
defined.
*
Note: all ruby methods have a $
prefix in javascript, so all stubs will
have this prefix as well (to make this method more performant).
*
Opal.add_stubs(["$foo", "$bar", "$baz="]);
*
All stub functions will have a private $$stub
property set to true so
that other internal methods can detect if a method is just a stub or not.
Kernel#respond_to?
uses this property to detect a methods presence.
*
@param [Array] stubs an array of method stubs to add
Opal.add_stubs = function(stubs) {
var subscribers = Opal.stub_subscribers;
var subscriber;
for (var i = 0, length = stubs.length; i < length; i++) {
var method_name = stubs[i], stub = stub_for(method_name);
for (var j = 0; j < subscribers.length; j++) {
subscriber = subscribers[j];
if (!(method_name in subscriber)) {
subscriber[method_name] = stub;
}
}
}
};
function add_stubs_subscriber(prototype)
Add a prototype to the subscribers list, and (TODO) add previously stubbed methods. * @param [Prototype]
function add_stubs_subscriber(prototype) {
// TODO: Add previously stubbed methods too.
Opal.stub_subscribers.push(prototype);
}
Opal.stub_subscribers = [BasicObject.prototype]
Keep a list of prototypes that want method_missing stubs to be added. * @default [Prototype List] BasicObject.prototype
Opal.stub_subscribers = [BasicObject.prototype];
function add_stub_for(prototype, stub)
Add a method_missing stub function to the given prototype for the given name. * @param [Prototype] prototype the target prototype @param [String] stub stub name to add (e.g. "$foo")
function add_stub_for(prototype, stub) {
var method_missing_stub = stub_for(stub);
prototype[stub] = method_missing_stub;
}
function stub_for(method_name)
Generate the method_missing stub for a given method name. * @param [String] method_name The js-name of the method to stub (e.g. "$foo")
function stub_for(method_name) {
function method_missing_stub() {
// Copy any given block onto the method_missing dispatcher
this.$method_missing.$$p = method_missing_stub.$$p;
// Set block property to null ready for the next call (stop false-positives)
method_missing_stub.$$p = null;
// call method missing with correct args (remove '$' prefix on method name)
return this.$method_missing.apply(this, [method_name.slice(1)].concat($slice.call(arguments)));
}
method_missing_stub.$$stub = true;
return method_missing_stub;
}
Opal.add_stub_for = add_stub_for
Expose for other parts of Opal to use
Opal.add_stub_for = add_stub_for;
Opal.ac = function(actual, expected, object, meth)
Arity count error dispatcher
Opal.ac = function(actual, expected, object, meth) {
var inspect = (object.$$is_class ? object.$$name + '.' : object.$$class.$$name + '#') + meth;
var msg = '[' + inspect + '] wrong number of arguments(' + actual + ' for ' + expected + ')';
throw Opal.ArgumentError.$new(msg);
};
Opal.find_super_dispatcher = function(obj, jsid, current_func, iter, defs)
Super dispatcher
Opal.find_super_dispatcher = function(obj, jsid, current_func, iter, defs) {
var dispatcher;
if (defs) {
dispatcher = obj.$$is_class ? defs.$$super : obj.$$class.$$proto;
}
else {
if (obj.$$is_class) {
dispatcher = obj.$$super;
}
else {
dispatcher = find_obj_super_dispatcher(obj, jsid, current_func);
}
}
dispatcher = dispatcher['$' + jsid];
dispatcher.$$p = iter;
return dispatcher;
};
Opal.find_iter_super_dispatcher = function(obj, jsid, current_func, iter, defs)
Iter dispatcher for super in a block
Opal.find_iter_super_dispatcher = function(obj, jsid, current_func, iter, defs) {
if (current_func.$$def) {
return Opal.find_super_dispatcher(obj, current_func.$$jsid, current_func, iter, defs);
}
else {
return Opal.find_super_dispatcher(obj, jsid, current_func, iter, defs);
}
};
break
ok
break;
if (!klass)
if we arent in a class, we couldnt find current?
if (!klass) {
while (klass)
else, let's find the next one
while (klass) {
break
ok
break;
Opal.ret = function(val)
Used to return as an expression. Sometimes, we can't simply return from a javascript function as if we were a method, as the return is used as an expression, or even inside a block which must "return" to the outer method. This helper simply throws an error which is then caught by the method. This approach is expensive, so it is only used when absolutely needed.
Opal.ret = function(val) {
Opal.returner.$v = val;
throw Opal.returner;
};
Opal.yield1 = function(block, arg)
handles yield calls for 1 yielded arg
Opal.yield1 = function(block, arg) {
if (typeof(block) !== "function") {
throw Opal.LocalJumpError.$new("no block given");
}
if (block.length > 1 && arg.$$is_array) {
return block.apply(null, arg);
}
else {
return block(arg);
}
};
Opal.yieldX = function(block, args)
handles yield for > 1 yielded arg
Opal.yieldX = function(block, args) {
if (typeof(block) !== "function") {
throw Opal.LocalJumpError.$new("no block given");
}
if (block.length > 1 && args.length == 1) {
if (args[0].$$is_array) {
return block.apply(null, args[0]);
}
}
if (!args.$$is_array) {
args = $slice.call(args);
}
return block.apply(null, args);
};
Opal.rescue = function(exception, candidates)
Finds the corresponding exception match in candidates. Each candidate can be a value, or an array of values. Returns null if not found.
Opal.rescue = function(exception, candidates) {
for (var i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
if (candidate.$$is_array) {
var result = Opal.rescue(exception, candidate);
if (result) {
return result;
}
}
else if (candidate['$==='](exception)) {
return candidate;
}
}
return null;
};
Opal.to_ary = function(value)
Helper to convert the given object to an array
Opal.to_ary = function(value) {
if (value.$$is_array) {
return value;
}
else if (value.$to_ary && !value.$to_ary.$$stub) {
return value.$to_ary();
}
return [value];
};
Opal.kwrestargs = function(given_args, used_args)
Used to get a list of rest keyword arguments. Method takes the given keyword args, i.e. the hash literal passed to the method containing all keyword arguemnts passed to method, as well as the used args which are the names of required and optional arguments defined. This method then just returns all key/value pairs which have not been used, in a new hash literal.
@param given_args [Hash] all kwargs given to method
@param used_args [Object
Opal.kwrestargs = function(given_args, used_args) {
var keys = [],
map = {},
key = null,
given_map = given_args.smap;
for (key in given_map) {
if (!used_args[key]) {
keys.push(key);
map[key] = given_map[key];
}
}
return Opal.hash2(keys, map);
};
Opal.send = function(recv, mid)
Call a ruby method on a ruby object with some arguments: * var my_array = [1, 2, 3, 4] Opal.send(my_array, 'length') # => 4 Opal.send(my_array, 'reverse!') # => [4, 3, 2, 1] * A missing method will be forwarded to the object via method_missing. * The result of either call with be returned. * @param [Object] recv the ruby object @param [String] mid ruby method to call
Opal.send = function(recv, mid) {
var args = $slice.call(arguments, 2),
func = recv['$' + mid];
if (func) {
return func.apply(recv, args);
}
return recv.$method_missing.apply(recv, [mid].concat(args));
};
function donate_methods(klass, defined, indirect)
Donate methods for a class/module
function donate_methods(klass, defined, indirect) {
var methods = klass.$$methods, included_in = klass.$$dep;
// if (!indirect) {
klass.$$methods = methods.concat(defined);
// }
if (included_in) {
for (var i = 0, length = included_in.length; i < length; i++) {
var includee = included_in[i];
var dest = includee.$$proto;
for (var j = 0, jj = defined.length; j < jj; j++) {
var method = defined[j];
dest[method] = klass.$$proto[method];
dest[method].$$donated = true;
}
if (includee.$$dep) {
donate_methods(includee, defined, true);
}
}
}
};
function define_module_method(module, jsid, body)
Define the given method on the module.
This also handles donating methods to all classes that include this module. Method conflicts are also handled here, where a class might already have defined a method of the same name, or another included module defined the same method.
@param [RubyModule] module the module method defined on @param [String] jsid javascript friendly method name (e.g. "$foo") @param [Function] body method body of actual function
function define_module_method(module, jsid, body) {
module.$$proto[jsid] = body;
body.$$owner = module;
module.$$methods.push(jsid);
if (module.$$module_function) {
module[jsid] = body;
}
var included_in = module.$$dep;
if (included_in) {
for (var i = 0, length = included_in.length; i < length; i++) {
var includee = included_in[i];
var dest = includee.$$proto;
var current = dest[jsid];
if (dest.hasOwnProperty(jsid) && !current.$$donated && !current.$$stub) {
// target class has already defined the same method name - do nothing
}
else if (dest.hasOwnProperty(jsid) && !current.$$stub) {
// target class includes another module that has defined this method
var klass_includees = includee.$$inc;
for (var j = 0, jj = klass_includees.length; j < jj; j++) {
if (klass_includees[j] === current.$$owner) {
var current_owner_index = j;
}
if (klass_includees[j] === module) {
var module_index = j;
}
}
// only redefine method on class if the module was included AFTER
// the module which defined the current method body. Also make sure
// a module can overwrite a method it defined before
if (current_owner_index <= module_index) {
dest[jsid] = body;
dest[jsid].$$donated = true;
}
}
else {
// neither a class, or module included by class, has defined method
dest[jsid] = body;
dest[jsid].$$donated = true;
}
if (includee.$$dep) {
donate_methods(includee, [jsid], true);
}
}
}
}
Opal.defn = function(obj, jsid, body)
Used to define methods on an object. This is a helper method, used by the
compiled source to define methods on special case objects when the compiler
can not determine the destination object, or the object is a Module
instance. This can get called by Module#define_method
as well.
Modules
Any method defined on a module will come through this runtime helper.
The method is added to the module body, and the owner of the method is
set to be the module itself. This is used later when choosing which
method should show on a class if more than 1 included modules define
the same method. Finally, if the module is in module_function
mode,
then the method is also defined onto the module itself.
Classes
This helper will only be called for classes when a method is being
defined indirectly; either through Module#define_method
, or by a
literal def
method inside an instance_eval
or class_eval
body. In
either case, the method is simply added to the class' prototype. A special
exception exists for BasicObject
and Object
. These two classes are
special because they are used in toll-free bridged classes. In each of
these two cases, extra work is required to define the methods on toll-free
bridged class' prototypes as well.
Objects
If a simple ruby object is the object, then the method is simply just
defined on the object as a singleton method. This would be the case when
a method is defined inside an instance_eval
block.
@param [RubyObject or Class] obj the actual obj to define method for @param [String] jsid the javascript friendly method name (e.g. '$foo') @param [Function] body the literal javascript function used as method @returns [null]
Opal.defn = function(obj, jsid, body) {
if (obj.$$is_mod) {
define_module_method(obj, jsid, body);
}
else if (obj.$$is_class) {
obj.$$proto[jsid] = body;
if (obj === BasicObjectClass) {
define_basic_object_method(jsid, body);
}
else if (obj === ObjectClass) {
donate_methods(obj, [jsid]);
}
}
else {
obj[jsid] = body;
}
return nil;
};
Opal.defs = function(obj, jsid, body)
Define a singleton method on the given object.
Opal.defs = function(obj, jsid, body) {
if (obj.$$is_class || obj.$$is_mod) {
obj.constructor.prototype[jsid] = body;
}
else {
obj[jsid] = body;
}
};
Opal.undef = function(obj, jsid)
Called to remove a method.
Opal.undef = function(obj, jsid) {
delete obj.$$proto[jsid];
};
Opal.hash2 = function(keys, map)
hash2 is a faster creator for hashes that just use symbols and strings as keys. The map and keys array can be constructed at compile time, so they are just added here by the constructor function
Opal.hash2 = function(keys, map) {
var hash = new Opal.Hash.$$alloc();
hash.keys = keys;
hash.map = {};
hash.smap = map;
return hash;
};
Opal.range = function(first, last, exc)
Create a new range instance with first and last values, and whether the range excludes the last value.
Opal.range = function(first, last, exc) {
var range = new Opal.Range.$$alloc();
range.begin = first;
range.end = last;
range.exclude = exc;
return range;
};
(function(Opal)
Require system
(function(Opal) {
var loaded_features = ['corelib/runtime'],
require_table = {'corelib/runtime': true},
modules = {};
var current_dir = '.';
function mark_as_loaded(filename) {
if (require_table[filename]) {
return false;
}
loaded_features.push(filename);
require_table[filename] = true;
return true;
}
function normalize_loadable_path(path) {
var parts, part, new_parts = [], SEPARATOR = '/';
if (current_dir !== '.') {
path = current_dir.replace(/\/*$/, '/') + path;
}
path = path.replace(/\.(rb|opal|js)$/, '');
parts = path.split(SEPARATOR);
for (var i = 0, ii = parts.length; i < ii; i++) {
part = parts[i];
if (part == '') continue;
(part === '..') ? new_parts.pop() : new_parts.push(part)
}
return new_parts.join(SEPARATOR);
}
function load(path) {
mark_as_loaded(path);
var module = modules[path];
if (module) {
module(Opal);
}
else {
var severity = Opal.dynamic_require_severity || 'warning';
var message = 'cannot load such file -- ' + path;
if (severity === "error") {
Opal.LoadError ? Opal.LoadError.$new(message) : function(){throw message}();
}
else if (severity === "warning") {
console.warn('WARNING: LoadError: ' + message);
}
}
return true;
}
function require(path) {
if (require_table[path]) {
return false;
}
return load(path);
}
Opal.modules = modules;
Opal.loaded_features = loaded_features;
Opal.normalize_loadable_path = normalize_loadable_path;
Opal.mark_as_loaded = mark_as_loaded;
Opal.load = load;
Opal.require = require;
})(Opal);
``
Initialization
BasicObjectClass
The actual class for BasicObject
var BasicObjectClass;
ObjectClass
The actual Object class
var ObjectClass;
ModuleClass
The actual Module class
var ModuleClass;
ClassClass
The actual Class class
var ClassClass;
function: BasicObject()
Constructor for instances of BasicObject
function BasicObject(){}
function: Object()
Constructor for instances of Object
function Object(){}
function: Class()
Constructor for instances of Class
function Class(){}
function: Module()
Constructor for instances of Module
function Module(){}
function: NilClass()
Constructor for instances of NilClass (nil)
function NilClass(){}
boot_class_alloc('BasicObject', BasicObject)
Constructors for instances of core objects
boot_class_alloc('BasicObject', BasicObject);
BasicObjectClass = boot_core_class_object('BasicObject', BasicObject, Class)
Constructors for classes of core objects
BasicObjectClass = boot_core_class_object('BasicObject', BasicObject, Class);
BasicObjectClass.$$class = ClassClass
Fix booted classes to use their metaclass
BasicObjectClass.$$class = ClassClass;
BasicObjectClass.$$super = null
Fix superclasses of booted classes
BasicObjectClass.$$super = null;
ObjectClass.$$dep = bridged_classes
Internally, Object acts like a module as it is "included" into bridged classes. In other words, we donate methods from Object into our bridged classes as their prototypes don't inherit from our root Object, so they act like module includes.
ObjectClass.$$dep = bridged_classes;
Opal.klass(ObjectClass, ObjectClass, 'NilClass', NilClass)
Nil
Opal.klass(ObjectClass, ObjectClass, 'NilClass', NilClass);