runtime.js
+
+
``
+
+
@note
+ A few conventions for the documentation of this file:
+
+
+- Always use "//" (in contrast with "/**/")
+- The syntax used is Yardoc (yardoc.org), which is intended for Ruby (se below)
+@param
and @return
types should be preceded by JS.
when referring to
+ JavaScript constructors (e.g. JS.Function
) otherwise Ruby is assumed.
+nil
and null
being unambiguous refer to the respective
+ objects/values in Ruby and JavaScript
+- This is still WIP :) so please give feedback and suggestions on how
+ to improve or for alternative solutions
+
+
+
The way the code is digested before going through Yardoc is a secret kept
+ in the docs repo (https://github.com/opal/docs/tree/master).
+
+
+
+
+
if (typeof(globalThis) !== 'undefined') { global_object = globalThis; }
+
+
Detect the global object
+
+
if (typeof(globalThis) !== 'undefined') { global_object = globalThis; }
+
+
+
if (global_object.console == null)
+
+
Setup a dummy console object if missing
+
+
if (global_object.console == null) {
+
+
+
BasicObject
+
+
The actual class for BasicObject
+
+
var BasicObject;
+
+
+
_Object
+
+
The actual Object class.
+The leading underscore is to avoid confusion with window.Object()
+
+
var _Object;
+
+
+
Module
+
+
The actual Module class
+
+
var Module;
+
+
+
Class
+
+
The actual Class class
+
+
var Class;
+
+
+
_Opal
+
+
The Opal.Opal class (helpers etc.)
+
+
var _Opal;
+
+
+
Kernel
+
+
The Kernel module
+
+
var Kernel;
+
+
+
Opal = global_object.Opal = {}
+
+
The Opal object that is exposed globally
+
+
var Opal = global_object.Opal = {};
+
+
+
Opal.global = global_object
+
+
This is a useful reference to global object inside ruby files
+
+
Opal.global = global_object;
+
+
+
Opal.config =
+
+
Configure runtime behavior with regards to require and unsupported features
+
+
Opal.config = {
+
+
+
$call = Function.prototype.call
+
+
Minify common function calls
+
+
var $call = Function.prototype.call;
+
+
+
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;
+
+
+
function $uid()
+
+
Return next unique id
+
+
function $uid() {
+ unique_id += 2;
+ return unique_id;
+};
+
+
+
Opal.id = function(obj)
+
+
Retrieve or assign the id of an object
+
+
Opal.id = function(obj) {
+ if (obj.$$is_number) return (obj * 2)+1;
+ if (obj.$$id == null) {
+ $prop(obj, '$$id', $uid());
+ }
+ return obj.$$id;
+};
+
+
+
$gvars = Opal.gvars = {}
+
+
Globals table
+
+
var $gvars = Opal.gvars = {};
+
+
+
Opal.exit = function(status) { if ($gvars.DEBUG) console.log('Exited with status '+status); }
+
+
Exit function, this should be replaced by platform specific implementation
+(See nodejs and chrome for examples)
+
+
Opal.exit = function(status) { if ($gvars.DEBUG) console.log('Exited with status '+status); };
+
+
+
Opal.exceptions = []
+
+
keeps track of exceptions for $!
+
+
Opal.exceptions = [];
+
+
+
Opal.pop_exception = function(rescued_exception)
+
+
@private
+Pops an exception from the stack and updates $!
.
+
+
Opal.pop_exception = function(rescued_exception) {
+ var exception = Opal.exceptions.pop();
+ if (exception === rescued_exception) {
+ // Current $! is raised in the rescue block, so we don't update it
+ }
+ else if (exception) {
+ $gvars["!"] = exception;
+ }
+ else {
+ $gvars["!"] = nil;
+ }
+};
+
+
+
function $raise(klass, message)
+
+
A helper function for raising things, that gracefully degrades if necessary
+functionality is not yet loaded.
+
+
function $raise(klass, message) {
+ // Raise Exception, so we can know that something wrong is going on.
+ if (!klass) klass = Opal.Exception || Error;
+
+ if (Kernel && Kernel.$raise) {
+ if (arguments.length > 2) {
+ Kernel.$raise(klass.$new.apply(klass, $slice(arguments, 1)));
+ }
+ else {
+ Kernel.$raise(klass, message);
+ }
+ }
+ else if (!klass.$new) {
+ throw new klass(message);
+ }
+ else {
+ throw klass.$new(message);
+ }
+}
+
+
+
prop_options =
+
+
Reuse the same object for performance/memory sake
+
+
var prop_options = {
+
+
+
object[name] = initialValue
+
+
Special case for:
+ s = "string"
+ def s.m; end
+String class is the only class that:
+
+
+- compiles to JS primitive
+- allows method definition directly on instances
+numbers, true, false and null do not support it.
+
+
+
object[name] = initialValue;
+
+
+
Opal.defineProperty = Opal.prop
+
+
@deprecated
+
+
Opal.defineProperty = Opal.prop;
+
+
+
``
+
+
Helpers
+
+
+
+
+
if (object['$respond_to?'].$$pristine && object.$method_missing.$$pristine)
+
+
Fast path for the most common situation
+
+
if (object['$respond_to?'].$$pristine && object.$method_missing.$$pristine) {
+
+
+
Opal.trace_class = false
+
+
TracePoint support
+
+
Support for TracePoint.trace(:class) do ... end
+
+
Opal.trace_class = false;
+
+
+
``
+
+
Constants
+
+
For future reference:
+
+
+
+
Legend of MRI concepts/names:
+
+
+- constant reference (cref): the module/class that acts as a namespace
+- nesting: the namespaces wrapping the current scope, e.g. nesting inside
+
module A; module B::C; end; end
is [B::C, A]
+
+
+
+
+
+
function const_get_name(cref, name)
+
+
Get the constant in the scope of the current cref
+
+
function const_get_name(cref, name) {
+ if (cref) {
+ if (cref.$$const[name] != null) { return cref.$$const[name]; }
+ if (cref.$$autoload && cref.$$autoload[name]) {
+ return handle_autoload(cref, name);
+ }
+ }
+}
+
+
+
function const_lookup_nesting(nesting, name)
+
+
Walk up the nesting array looking for the constant
+
+
function const_lookup_nesting(nesting, name) {
+ var i, ii, constant;
+
+ if (nesting.length === 0) return;
+
+ // If the nesting is not empty the constant is looked up in its elements
+ // and in order. The ancestors of those elements are ignored.
+ for (i = 0, ii = nesting.length; i < ii; i++) {
+ constant = nesting[i].$$const[name];
+ if (constant != null) {
+ return constant;
+ } else if (nesting[i].$$autoload && nesting[i].$$autoload[name]) {
+ return handle_autoload(nesting[i], name);
+ }
+ }
+}
+
+
+
function const_lookup_ancestors(cref, name)
+
+
Walk up the ancestors chain looking for the constant
+
+
function const_lookup_ancestors(cref, name) {
+ var i, ii, ancestors;
+
+ if (cref == null) return;
+
+ ancestors = $ancestors(cref);
+
+ for (i = 0, ii = ancestors.length; i < ii; i++) {
+ if (ancestors[i].$$const && $has_own(ancestors[i].$$const, name)) {
+ return ancestors[i].$$const[name];
+ } else if (ancestors[i].$$autoload && ancestors[i].$$autoload[name]) {
+ return handle_autoload(ancestors[i], name);
+ }
+ }
+}
+
+
+
function const_lookup_Object(cref, name)
+
+
Walk up Object's ancestors chain looking for the constant,
+but only if cref is missing or a module.
+
+
function const_lookup_Object(cref, name) {
+ if (cref == null || cref.$$is_module) {
+ return const_lookup_ancestors(_Object, name);
+ }
+}
+
+
+
function const_missing(cref, name)
+
+
Call const_missing if nothing else worked
+
+
function const_missing(cref, name) {
+ return (cref || _Object).$const_missing(name);
+}
+
+
+
Opal.const_get_local = function(cref, name, skip_missing)
+
+
Look for the constant just in the current cref or call #const_missing
+
+
Opal.const_get_local = function(cref, name, skip_missing) {
+ var result;
+
+ if (cref == null) return;
+
+ if (cref === '::') cref = _Object;
+
+ if (!cref.$$is_module && !cref.$$is_class) {
+ $raise(Opal.TypeError, cref.toString() + " is not a class/module");
+ }
+
+ result = const_get_name(cref, name);
+ return result != null || skip_missing ? result : const_missing(cref, name);
+};
+
+
+
Opal.const_get_qualified = function(cref, name, skip_missing)
+
+
Look for the constant relative to a cref or call #const_missing
(when the
+constant is prefixed by ::
).
+
+
Opal.const_get_qualified = function(cref, name, skip_missing) {
+ var result, cache, cached, current_version = Opal.const_cache_version;
+
+ if (name == null) {
+ // A shortpath for calls like ::String => $$$("String")
+ result = const_get_name(_Object, cref);
+
+ if (result != null) return result;
+ return Opal.const_get_qualified(_Object, cref, skip_missing);
+ }
+
+ if (cref == null) return;
+
+ if (cref === '::') cref = _Object;
+
+ if (!cref.$$is_module && !cref.$$is_class) {
+ $raise(Opal.TypeError, cref.toString() + " is not a class/module");
+ }
+
+ if ((cache = cref.$$const_cache) == null) {
+ $prop(cref, '$$const_cache', Object.create(null));
+ cache = cref.$$const_cache;
+ }
+ cached = cache[name];
+
+ if (cached == null || cached[0] !== current_version) {
+ ((result = const_get_name(cref, name)) != null) ||
+ ((result = const_lookup_ancestors(cref, name)) != null);
+ cache[name] = [current_version, result];
+ } else {
+ result = cached[1];
+ }
+
+ return result != null || skip_missing ? result : const_missing(cref, name);
+};
+
+
+
Opal.const_cache_version = 1
+
+
Initialize the top level constant cache generation counter
+
+
Opal.const_cache_version = 1;
+
+
+
Opal.const_get_relative = function(nesting, name, skip_missing)
+
+
Look for the constant in the open using the current nesting and the nearest
+cref ancestors or call #const_missing
(when the constant has no :: prefix).
+
+
Opal.const_get_relative = function(nesting, name, skip_missing) {
+ var cref = nesting[0], result, current_version = Opal.const_cache_version, cache, cached;
+
+ if ((cache = nesting.$$const_cache) == null) {
+ $prop(nesting, '$$const_cache', Object.create(null));
+ cache = nesting.$$const_cache;
+ }
+ cached = cache[name];
+
+ if (cached == null || cached[0] !== current_version) {
+ ((result = const_get_name(cref, name)) != null) ||
+ ((result = const_lookup_nesting(nesting, name)) != null) ||
+ ((result = const_lookup_ancestors(cref, name)) != null) ||
+ ((result = const_lookup_Object(cref, name)) != null);
+
+ cache[name] = [current_version, result];
+ } else {
+ result = cached[1];
+ }
+
+ return result != null || skip_missing ? result : const_missing(cref, name);
+};
+
+
+
function $const_set(cref, name, value)
+
+
Register the constant on a cref and opportunistically set the name of
+unnamed classes/modules.
+
+
function $const_set(cref, name, value) {
+ var new_const = true;
+
+ if (cref == null || cref === '::') cref = _Object;
+
+ if (value.$$is_a_module) {
+ if (value.$$name == null || value.$$name === nil) value.$$name = name;
+ if (value.$$base_module == null) value.$$base_module = cref;
+ }
+
+ cref.$$const = (cref.$$const || Object.create(null));
+
+ if (name in cref.$$const || ("$$autoload" in cref && name in cref.$$autoload)) {
+ new_const = false;
+ }
+
+ cref.$$const[name] = value;
+
+ // Add a short helper to navigate constants manually.
+ // @example
+ // Opal.$$.Regexp.$$.IGNORECASE
+ cref.$$ = cref.$$const;
+
+ Opal.const_cache_version++;
+
+ // Expose top level constants onto the Opal object
+ if (cref === _Object) Opal[name] = value;
+
+ // Name new class directly onto current scope (Opal.Foo.Baz = klass)
+ $prop(cref, name, value);
+
+ if (new_const && cref.$const_added && !cref.$const_added.$$pristine) {
+ cref.$const_added(name);
+ }
+
+ return value;
+};
+
+
+
Opal.constants = function(cref, inherit)
+
+
Get all the constants reachable from a given cref, by default will include
+inherited constants.
+
+
Opal.constants = function(cref, inherit) {
+ if (inherit == null) inherit = true;
+
+ var module, modules = [cref], i, ii, constants = {}, constant;
+
+ if (inherit) modules = modules.concat($ancestors(cref));
+ if (inherit && cref.$$is_module) modules = modules.concat([Opal.Object]).concat($ancestors(Opal.Object));
+
+ for (i = 0, ii = modules.length; i < ii; i++) {
+ module = modules[i];
+
+ // Do not show Objects constants unless we're querying Object itself
+ if (cref !== _Object && module == _Object) break;
+
+ for (constant in module.$$const) {
+ constants[constant] = true;
+ }
+ if (module.$$autoload) {
+ for (constant in module.$$autoload) {
+ constants[constant] = true;
+ }
+ }
+ }
+
+ return Object.keys(constants);
+};
+
+
+
Opal.const_remove = function(cref, name)
+
+
Remove a constant from a cref.
+
+
Opal.const_remove = function(cref, name) {
+ Opal.const_cache_version++;
+
+ if (cref.$$const[name] != null) {
+ var old = cref.$$const[name];
+ delete cref.$$const[name];
+ return old;
+ }
+
+ if (cref.$$autoload && cref.$$autoload[name]) {
+ delete cref.$$autoload[name];
+ return nil;
+ }
+
+ $raise(Opal.NameError, "constant "+cref+"::"+cref.$name()+" not defined");
+};
+
+
+
Opal.const_get_relative_factory = function(nesting)
+
+
Generates a function that is a curried const_get_relative.
+
+
Opal.const_get_relative_factory = function(nesting) {
+ return function(name, skip_missing) {
+ return Opal.$$(nesting, name, skip_missing);
+ }
+}
+
+
+
Opal.$$ = Opal.const_get_relative
+
+
Setup some shortcuts to reduce compiled size
+
+
Opal.$$ = Opal.const_get_relative;
+
+
+
``
+
+
Modules & Classes
+
+
+
+
+
function $allocate_class(name, superclass, singleton)
+
+
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, superclass
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 scope
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 scope
is an object (not a class/module), we simple get its class and
+use that as the scope instead.
+
+
@param scope [Object] where the class is being created
+@param superclass [Class,null] superclass of the new class (may be null)
+@param singleton [Boolean,null] a true value denotes we want to allocate
+ a singleton
+
+
@return new [Class] or existing ruby class
+
+
function $allocate_class(name, superclass, singleton) {
+ var klass, bridged_descendant;
+
+ if (bridged_descendant = descends_from_bridged_class(superclass)) {
+ // Inheritance from bridged classes requires
+ // calling original JS constructors
+ klass = function() {
+ var self = new ($bind.apply(bridged_descendant.$$constructor, $prepend(null, arguments)))();
+
+ // and replacing a __proto__ manually
+ $set_proto(self, klass.$$prototype);
+ return self;
+ }
+ } else {
+ klass = function(){};
+ }
+
+ if (name && name !== nil) {
+ $prop(klass, 'displayName', '::'+name);
+ }
+
+ $prop(klass, '$$name', name);
+ $prop(klass, '$$constructor', klass);
+ $prop(klass, '$$prototype', klass.prototype);
+ $prop(klass, '$$const', {});
+ $prop(klass, '$$is_class', true);
+ $prop(klass, '$$is_a_module', true);
+ $prop(klass, '$$super', superclass);
+ $prop(klass, '$$cvars', {});
+ $prop(klass, '$$own_included_modules', []);
+ $prop(klass, '$$own_prepended_modules', []);
+ $prop(klass, '$$ancestors', []);
+ $prop(klass, '$$ancestors_cache_version', null);
+ $prop(klass, '$$subclasses', []);
+ $prop(klass, '$$cloned_from', []);
+
+ $prop(klass.$$prototype, '$$class', klass);
+
+ // By default if there are no singleton class methods
+ // __proto__ is Class.prototype
+ // Later singleton methods generate a singleton_class
+ // and inject it into ancestors chain
+ if (Opal.Class) {
+ $set_proto(klass, Opal.Class.prototype);
+ }
+
+ if (superclass != null) {
+ $set_proto(klass.$$prototype, superclass.$$prototype);
+
+ if (singleton !== true) {
+ // Let's not forbid GC from cleaning up our
+ // subclasses.
+ if (typeof WeakRef !== 'undefined') {
+ // First, let's clean up our array from empty objects.
+ var i, subclass, rebuilt_subclasses = [];
+ for (i = 0; i < superclass.$$subclasses.length; i++) {
+ subclass = superclass.$$subclasses[i];
+ if (subclass.deref() !== undefined) {
+ rebuilt_subclasses.push(subclass);
+ }
+ }
+ // Now, let's add our class.
+ rebuilt_subclasses.push(new WeakRef(klass));
+ superclass.$$subclasses = rebuilt_subclasses;
+ }
+ else {
+ superclass.$$subclasses.push(klass);
+ }
+ }
+
+ if (superclass.$$meta) {
+ // If superclass has metaclass then we have explicitely inherit it.
+ Opal.build_class_singleton_class(klass);
+ }
+ }
+
+ return klass;
+};
+
+
+
klass = const_get_name(scope, name)
+
+
Try to find the class in the current scope
+
+
var klass = const_get_name(scope, name);
+
+
+
if (klass)
+
+
If the class exists in the scope, then we must use that
+
+
if (klass) {
+
+
+
if (!klass.$$is_class)
+
+
Make sure the existing constant is a class, or raise error
+
+
if (!klass.$$is_class) {
+
+
+
scope = _Object
+
+
Global scope
+
+
scope = _Object;
+
+
+
scope = scope.$$class
+
+
Scope is an object, use its class
+
+
scope = scope.$$class;
+
+
+
if (
+
+
If the superclass is not an Opal-generated class then we're bridging a native JS class
+
+
if (
+
+
+
ensureSuperclassMatch(klass, superclass)
+
+
Make sure existing class has same superclass
+
+
ensureSuperclassMatch(klass, superclass);
+
+
+
``
+
+
Class doesn't exist, create a new one with given superclass...
+
+
+
+
+
if (superclass == null)
+
+
Not specifying a superclass means we can assume it to be Object
+
+
if (superclass == null) {
+
+
+
klass = $allocate_class(name, superclass)
+
+
Create the class object (instance of Class)
+
+
klass = $allocate_class(name, superclass);
+
+
+
if (superclass.$inherited)
+
+
Call .inherited() hook with new class on the superclass
+
+
if (superclass.$inherited) {
+
+
+
function $allocate_module(name)
+
+
Define new module (or return existing module). The given scope
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 scope is a ruby
+object then that objects real ruby class is used (e.g. if the scope is the
+main object, then the top level Object
class is used as the scope).
+
+
If a module of the given name is already defined in the scope, then that
+instance is just returned.
+
+
If there is a class of the given name in the scope, then an error is
+generated instead (cannot have a class and module of same name in same scope).
+
+
Otherwise, a new module is created in the scope with the given name, and that
+new instance is returned back (to be referenced at runtime).
+
+
@param scope [Module, Class] class or module this definition is inside
+@param id [String] the name of the new (or existing) module
+
+
@return [Module]
+
+
function $allocate_module(name) {
+ var constructor = function(){};
+ var module = constructor;
+
+ if (name)
+ $prop(constructor, 'displayName', name+'.constructor');
+
+ $prop(module, '$$name', name);
+ $prop(module, '$$prototype', constructor.prototype);
+ $prop(module, '$$const', {});
+ $prop(module, '$$is_module', true);
+ $prop(module, '$$is_a_module', true);
+ $prop(module, '$$cvars', {});
+ $prop(module, '$$iclasses', []);
+ $prop(module, '$$own_included_modules', []);
+ $prop(module, '$$own_prepended_modules', []);
+ $prop(module, '$$ancestors', [module]);
+ $prop(module, '$$ancestors_cache_version', null);
+ $prop(module, '$$cloned_from', []);
+
+ $set_proto(module, Opal.Module.prototype);
+
+ return module;
+};
+
+
+
scope = _Object
+
+
Global scope
+
+
scope = _Object;
+
+
+
scope = scope.$$class
+
+
Scope is an object, use its class
+
+
scope = scope.$$class;
+
+
+
module = $allocate_module(name)
+
+
Module doesnt exist, create a new one...
+
+
module = $allocate_module(name);
+
+
+
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 object [Object] the ruby object
+@return [Class] the singleton class for object
+
+
Opal.get_singleton_class = function(object) {
+ if (object.$$is_number) {
+ $raise(Opal.TypeError, "can't define singleton");
+ }
+ if (object.$$meta) {
+ return object.$$meta;
+ }
+
+ if (object.hasOwnProperty('$$is_class')) {
+ return Opal.build_class_singleton_class(object);
+ } else if (object.hasOwnProperty('$$is_module')) {
+ return Opal.build_module_singleton_class(object);
+ } else {
+ return Opal.build_object_singleton_class(object);
+ }
+};
+
+
+
+
+
helper to set $$meta on klass, module or instance
+
+
function set_meta(obj, meta) {
+ if (obj.hasOwnProperty('$$meta')) {
+ obj.$$meta = meta;
+ } else {
+ $prop(obj, '$$meta', meta);
+ }
+ if (obj.$$frozen) {
+ // If a object is frozen (sealed), freeze $$meta too.
+ // No need to inject $$meta.$$prototype in the prototype chain,
+ // as $$meta cannot be modified anyway.
+ obj.$$meta.$freeze();
+ } else {
+ $set_proto(obj, meta.$$prototype);
+ }
+};
+
+
+
Opal.build_class_singleton_class = function(klass)
+
+
Build the singleton class for an existing class. Class object are built
+with their singleton class already in the prototype chain and inheriting
+from their superclass object (up to Class
itself).
+
+
NOTE: Actually in MRI a class' singleton class inherits from its
+superclass' singleton class which in turn inherits from Class.
+
+
@param klass [Class]
+@return [Class]
+
+
Opal.build_class_singleton_class = function(klass) {
+ if (klass.$$meta) {
+ return klass.$$meta;
+ }
+
+ // The singleton_class superclass is the singleton_class of its superclass;
+ // but BasicObject has no superclass (its `$$super` is null), thus we
+ // fallback on `Class`.
+ var superclass = klass === BasicObject ? Class : Opal.get_singleton_class(klass.$$super);
+
+ var meta = $allocate_class(null, superclass, true);
+
+ $prop(meta, '$$is_singleton', true);
+ $prop(meta, '$$singleton_of', klass);
+ set_meta(klass, meta);
+ // Restoring ClassName.class
+ $prop(klass, '$$class', Opal.Class);
+
+ return meta;
+};
+
+
+
$prop(mod, '$$class', Opal.Module)
+
+
Restoring ModuleName.class
+
+
$prop(mod, '$$class', Opal.Module);
+
+
+
Opal.build_object_singleton_class = function(object)
+
+
Build the singleton class for a Ruby (non class) Object.
+
+
@param object [Object]
+@return [Class]
+
+
Opal.build_object_singleton_class = function(object) {
+ var superclass = object.$$class,
+ klass = $allocate_class(nil, superclass, true);
+
+ $prop(klass, '$$is_singleton', true);
+ $prop(klass, '$$singleton_of', object);
+
+ delete klass.$$prototype.$$class;
+
+ set_meta(object, klass);
+
+ return klass;
+};
+
+
+
Opal.class_variables = function(module)
+
+
Returns an object containing all pairs of names/values
+for all class variables defined in provided +module+
+and its ancestors.
+
+
@param module [Module]
+@return [Object]
+
+
Opal.class_variables = function(module) {
+ var ancestors = $ancestors(module),
+ i, length = ancestors.length,
+ result = {};
+
+ for (i = length - 1; i >= 0; i--) {
+ var ancestor = ancestors[i];
+
+ for (var cvar in ancestor.$$cvars) {
+ result[cvar] = ancestor.$$cvars[cvar];
+ }
+ }
+
+ return result;
+};
+
+
+
Opal.class_variable_set = function(module, name, value)
+
+
Sets class variable with specified +name+ to +value+
+in provided +module+
+
+
@param module [Module]
+@param name [String]
+@param value [Object]
+
+
Opal.class_variable_set = function(module, name, value) {
+ var ancestors = $ancestors(module),
+ i, length = ancestors.length;
+
+ for (i = length - 2; i >= 0; i--) {
+ var ancestor = ancestors[i];
+
+ if ($has_own(ancestor.$$cvars, name)) {
+ ancestor.$$cvars[name] = value;
+ return value;
+ }
+ }
+
+ module.$$cvars[name] = value;
+
+ return value;
+};
+
+
+
Opal.class_variable_get = function(module, name, tolerant)
+
+
Gets class variable with specified +name+ from provided +module+
+
+
@param module [Module]
+@param name [String]
+
+
Opal.class_variable_get = function(module, name, tolerant) {
+ if ($has_own(module.$$cvars, name))
+ return module.$$cvars[name];
+
+ var ancestors = $ancestors(module),
+ i, length = ancestors.length;
+
+ for (i = 0; i < length; i++) {
+ var ancestor = ancestors[i];
+
+ if ($has_own(ancestor.$$cvars, name)) {
+ return ancestor.$$cvars[name];
+ }
+ }
+
+ if (!tolerant)
+ $raise(Opal.NameError, 'uninitialized class variable '+name+' in '+module.$name());
+
+ return nil;
+}
+
+
+
break
+
+
superclass
+
+
break;
+
+
+
Opal.append_features = function(module, includer)
+
+
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 module [Module] the module to include
+@param includer [Module] the target class to include module into
+@return [null]
+
+
Opal.append_features = function(module, includer) {
+ var module_ancestors = $ancestors(module);
+ var iclasses = [];
+
+ if (module_ancestors.indexOf(includer) !== -1) {
+ $raise(Opal.ArgumentError, 'cyclic include detected');
+ }
+
+ for (var i = 0, length = module_ancestors.length; i < length; i++) {
+ var ancestor = module_ancestors[i], iclass = create_iclass(ancestor);
+ $prop(iclass, '$$included', true);
+ iclasses.push(iclass);
+ }
+ var includer_ancestors = $ancestors(includer),
+ chain = chain_iclasses(iclasses),
+ start_chain_after,
+ end_chain_on;
+
+ if (includer_ancestors.indexOf(module) === -1) {
+ // first time include
+
+ // includer -> chain.first -> ...chain... -> chain.last -> includer.parent
+ start_chain_after = includer.$$prototype;
+ end_chain_on = Object.getPrototypeOf(includer.$$prototype);
+ } else {
+ // The module has been already included,
+ // we don't need to put it into the ancestors chain again,
+ // but this module may have new included modules.
+ // If it's true we need to copy them.
+ //
+ // The simplest way is to replace ancestors chain from
+ // parent
+ // |
+ // `module` iclass (has a $$root flag)
+ // |
+ // ...previos chain of module.included_modules ...
+ // |
+ // "next ancestor" (has a $$root flag or is a real class)
+ //
+ // to
+ // parent
+ // |
+ // `module` iclass (has a $$root flag)
+ // |
+ // ...regenerated chain of module.included_modules
+ // |
+ // "next ancestor" (has a $$root flag or is a real class)
+ //
+ // because there are no intermediate classes between `parent` and `next ancestor`.
+ // It doesn't break any prototypes of other objects as we don't change class references.
+
+ var parent = includer.$$prototype, module_iclass = Object.getPrototypeOf(parent);
+
+ while (module_iclass != null) {
+ if (module_iclass.$$module === module && isRoot(module_iclass)) {
+ break;
+ }
+
+ parent = module_iclass;
+ module_iclass = Object.getPrototypeOf(module_iclass);
+ }
+
+ if (module_iclass) {
+ // module has been directly included
+ var next_ancestor = Object.getPrototypeOf(module_iclass);
+
+ // skip non-root iclasses (that were recursively included)
+ while (next_ancestor.hasOwnProperty('$$iclass') && !isRoot(next_ancestor)) {
+ next_ancestor = Object.getPrototypeOf(next_ancestor);
+ }
+
+ start_chain_after = parent;
+ end_chain_on = next_ancestor;
+ } else {
+ // module has not been directly included but was in ancestor chain because it was included by another module
+ // include it directly
+ start_chain_after = includer.$$prototype;
+ end_chain_on = Object.getPrototypeOf(includer.$$prototype);
+ }
+ }
+
+ $set_proto(start_chain_after, chain.first);
+ $set_proto(chain.last, end_chain_on);
+
+ // recalculate own_included_modules cache
+ includer.$$own_included_modules = own_included_modules(includer);
+
+ Opal.const_cache_version++;
+};
+
+
+
module_ancestors = $ancestors(module)
+
+
Here we change the ancestors chain from
+
+
prepender
+ |
+ parent
+
+
to:
+
+
dummy(prepender)
+ |
+ iclass(module)
+ |
+iclass(prepender)
+ |
+ parent
+
+
var module_ancestors = $ancestors(module);
+
+
+
prepender_iclass = dummy_prepender.$$define_methods_on
+
+
The module already has some prepended modules
+which means that we don't need to make it "dummy"
+
+
prepender_iclass = dummy_prepender.$$define_methods_on;
+
+
+
prepender_iclass = create_dummy_iclass(prepender)
+
+
Making the module "dummy"
+
+
prepender_iclass = create_dummy_iclass(prepender);
+
+
+
$set_proto(dummy_prepender, prepender_iclass)
+
+
Converting
+ dummy(prepender) -> previous_parent
+to
+ dummy(prepender) -> iclass(prepender) -> previous_parent
+
+
$set_proto(dummy_prepender, prepender_iclass);
+
+
+
``
+
+
first time prepend
+
+
+
+
+
end_chain_on = Object.getPrototypeOf(dummy_prepender)
+
+
next $$root or prepender_iclass or non-$$iclass
+
+
end_chain_on = Object.getPrototypeOf(dummy_prepender);
+
+
+
prepender.$$own_prepended_modules = own_prepended_modules(prepender)
+
+
recalculate own_prepended_modules cache
+
+
prepender.$$own_prepended_modules = own_prepended_modules(prepender);
+
+
+
function create_dummy_iclass(module)
+
+
Dummy iclass doesn't receive updates when the module gets a new method.
+
+
function create_dummy_iclass(module) {
+ var iclass = {},
+ proto = module.$$prototype;
+
+ if (proto.hasOwnProperty('$$dummy')) {
+ proto = proto.$$define_methods_on;
+ }
+
+ var props = Object.getOwnPropertyNames(proto),
+ length = props.length, i;
+
+ for (i = 0; i < length; i++) {
+ var prop = props[i];
+ $prop(iclass, prop, proto[prop]);
+ }
+
+ $prop(iclass, '$$iclass', true);
+ $prop(iclass, '$$module', module);
+
+ return iclass;
+}
+
+
+
Opal.bridge = function(native_klass, klass)
+
+
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 the new class. Note: all bridged classes are set to inherit
+from Object.
+
+
Example:
+
+
Opal.bridge(self, Function);
+
+
@param klass [Class] the Ruby class to bridge
+@param constructor [JS.Function] native JavaScript constructor to use
+@return [Class] returns the passed Ruby class
+
+
Opal.bridge = function(native_klass, klass) {
+ if (native_klass.hasOwnProperty('$$bridge')) {
+ $raise(Opal.ArgumentError, "already bridged");
+ }
+
+ // constructor is a JS function with a prototype chain like:
+ // - constructor
+ // - super
+ //
+ // What we need to do is to inject our class (with its prototype chain)
+ // between constructor and super. For example, after injecting ::Object
+ // into JS String we get:
+ //
+ // - constructor (window.String)
+ // - Opal.Object
+ // - Opal.Kernel
+ // - Opal.BasicObject
+ // - super (window.Object)
+ // - null
+ //
+ $prop(native_klass, '$$bridge', klass);
+ $set_proto(native_klass.prototype, (klass.$$super || Opal.Object).$$prototype);
+ $prop(klass, '$$prototype', native_klass.prototype);
+
+ $prop(klass.$$prototype, '$$class', klass);
+ $prop(klass, '$$constructor', native_klass);
+ $prop(klass, '$$bridge', true);
+};
+
+
+
function $ancestors(module)
+
+
The Array of ancestors for a given module/class
+
+
function $ancestors(module) {
+ if (!module) { return []; }
+
+ if (module.$$ancestors_cache_version === Opal.const_cache_version) {
+ return module.$$ancestors;
+ }
+
+ var result = [], i, mods, length;
+
+ for (i = 0, mods = own_ancestors(module), length = mods.length; i < length; i++) {
+ result.push(mods[i]);
+ }
+
+ if (module.$$super) {
+ for (i = 0, mods = $ancestors(module.$$super), length = mods.length; i < length; i++) {
+ result.push(mods[i]);
+ }
+ }
+
+ module.$$ancestors_cache_version = Opal.const_cache_version;
+ module.$$ancestors = result;
+
+ return result;
+};
+
+
+
``
+
+
Method Missing
+
+
+
+
+
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 stubs [Array] an array of method stubs to add
+@return [undefined]
+
+
Opal.add_stubs = function(stubs) {
+ var proto = Opal.BasicObject.$$prototype;
+ var stub, existing_method;
+ stubs = stubs.split(',');
+
+ for (var i = 0, length = stubs.length; i < length; i++) {
+ stub = $jsid(stubs[i]), existing_method = proto[stub];
+
+ if (existing_method == null || existing_method.$$stub) {
+ Opal.add_stub_for(proto, stub);
+ }
+ }
+};
+
+
+
Opal.add_stub_for = function(prototype, stub)
+
+
Add a method_missing stub function to the given prototype for the
+given name.
+
+
@param prototype [Prototype] the target prototype
+@param stub [String] stub name to add (e.g. "$foo")
+@return [undefined]
+
+
Opal.add_stub_for = function(prototype, stub) {
+ // Opal.stub_for(stub) is the method_missing_stub
+ $prop(prototype, stub, Opal.stub_for(stub));
+};
+
+
+
Opal.stub_for = function(method_name)
+
+
Generate the method_missing stub for a given method name.
+
+
@param method_name [String] The js-name of the method to stub (e.g. "$foo")
+@return [undefined]
+
+
Opal.stub_for = function(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, $prepend(method_name.slice(1), arguments));
+ };
+
+ method_missing_stub.$$stub = true;
+
+ return method_missing_stub;
+};
+
+
+
``
+
+
Methods
+
+
+
+
+
Opal.ac = function(actual, expected, object, meth)
+
+
Arity count error dispatcher for methods
+
+
@param actual [Fixnum] number of arguments given to method
+@param expected [Fixnum] expected number of arguments
+@param object [Object] owner of the method +meth+
+@param meth [String] method name that got wrong number of arguments
+@raise [ArgumentError]
+
+
Opal.ac = function(actual, expected, object, meth) {
+ var inspect = '';
+ if (object.$$is_a_module) {
+ inspect += object.$$name + '.';
+ }
+ else {
+ inspect += object.$$class.$$name + '#';
+ }
+ inspect += meth;
+
+ $raise(Opal.ArgumentError, '[' + inspect + '] wrong number of arguments (given ' + actual + ', expected ' + expected + ')');
+};
+
+
+
Opal.block_ac = function(actual, expected, context)
+
+
Arity count error dispatcher for blocks
+
+
@param actual [Fixnum] number of arguments given to block
+@param expected [Fixnum] expected number of arguments
+@param context [Object] context of the block definition
+@raise [ArgumentError]
+
+
Opal.block_ac = function(actual, expected, context) {
+ var inspect = "`block in " + context + "'";
+
+ $raise(Opal.ArgumentError, inspect + ': wrong number of arguments (given ' + actual + ', expected ' + expected + ')');
+};
+
+
+
Opal.find_super = function(obj, mid, current_func, defcheck, allow_stubs)
+
+
Super dispatcher
+
+
Opal.find_super = function(obj, mid, current_func, defcheck, allow_stubs) {
+ var jsid = $jsid(mid), ancestors, ancestor, super_method, method_owner, current_index = -1, i;
+
+ ancestors = get_ancestors(obj);
+ method_owner = current_func.$$owner;
+
+ for (i = 0; i < ancestors.length; i++) {
+ ancestor = ancestors[i];
+ if (ancestor === method_owner || ancestor.$$cloned_from.indexOf(method_owner) !== -1) {
+ current_index = i;
+ break;
+ }
+ }
+
+ for (i = current_index + 1; i < ancestors.length; i++) {
+ ancestor = ancestors[i];
+ var proto = ancestor.$$prototype;
+
+ if (proto.hasOwnProperty('$$dummy')) {
+ proto = proto.$$define_methods_on;
+ }
+
+ if (proto.hasOwnProperty(jsid)) {
+ super_method = proto[jsid];
+ break;
+ }
+ }
+
+ if (!defcheck && super_method && super_method.$$stub && obj.$method_missing.$$pristine) {
+ // method_missing hasn't been explicitly defined
+ $raise(Opal.NoMethodError, 'super: no superclass method `'+mid+"' for "+obj, mid);
+ }
+
+ return (super_method.$$stub && !allow_stubs) ? null : super_method;
+};
+
+
+
Opal.find_block_super = function(obj, jsid, current_func, defcheck, implicit)
+
+
Iter dispatcher for super in a block
+
+
Opal.find_block_super = function(obj, jsid, current_func, defcheck, implicit) {
+ var call_jsid = jsid;
+
+ if (!current_func) {
+ $raise(Opal.RuntimeError, "super called outside of method");
+ }
+
+ if (implicit && current_func.$$define_meth) {
+ $raise(Opal.RuntimeError,
+ "implicit argument passing of super from method defined by define_method() is not supported. " +
+ "Specify all arguments explicitly"
+ );
+ }
+
+ if (current_func.$$def) {
+ call_jsid = current_func.$$jsid;
+ }
+
+ return Opal.find_super(obj, call_jsid, current_func, defcheck);
+};
+
+
+
Opal.find_super_dispatcher = Opal.find_super
+
+
@deprecated
+
+
Opal.find_super_dispatcher = Opal.find_super;
+
+
+
Opal.find_iter_super_dispatcher = Opal.find_block_super
+
+
@deprecated
+
+
Opal.find_iter_super_dispatcher = Opal.find_block_super;
+
+
+
Opal.yield1 = function(block, arg)
+
+
handles yield calls for 1 yielded arg
+
+
Opal.yield1 = function(block, arg) {
+ if (typeof(block) !== "function") {
+ $raise(Opal.LocalJumpError, "no block given");
+ }
+
+ var has_mlhs = block.$$has_top_level_mlhs_arg,
+ has_trailing_comma = block.$$has_trailing_comma_in_args,
+ is_returning_lambda = block.$$is_lambda && block.$$ret;
+
+ if (block.length > 1 || ((has_mlhs || has_trailing_comma) && block.length === 1)) {
+ arg = Opal.to_ary(arg);
+ }
+
+ if ((block.length > 1 || (has_trailing_comma && block.length === 1)) && arg.$$is_array) {
+ if (is_returning_lambda) {
+ return call_lambda(block.apply.bind(block, null), arg, block.$$ret);
+ }
+ return block.apply(null, arg);
+ }
+ else {
+ if (is_returning_lambda) {
+ return call_lambda(block, arg, block.$$ret);
+ }
+ return block(arg);
+ }
+};
+
+
+
Opal.yieldX = function(block, args)
+
+
handles yield for > 1 yielded arg
+
+
Opal.yieldX = function(block, args) {
+ if (typeof(block) !== "function") {
+ $raise(Opal.LocalJumpError, "no block given");
+ }
+
+ if (block.length > 1 && args.length === 1) {
+ if (args[0].$$is_array) {
+ args = args[0];
+ }
+ }
+
+ if (block.$$is_lambda && block.$$ret) {
+ return call_lambda(block.apply.bind(block, null), args, block.$$ret);
+ }
+ 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 === Opal.JS.Error || candidate['$==='](exception)) {
+ return candidate;
+ }
+ }
+
+ return null;
+};
+
+
+
Opal.to_hash = function(value)
+
+
Helpers for extracting kwsplats
+Used for: { **h }
+
+
Opal.to_hash = function(value) {
+ if (value.$$is_hash) {
+ return value;
+ }
+ else if (value['$respond_to?']('to_hash', true)) {
+ var hash = value.$to_hash();
+ if (hash.$$is_hash) {
+ return hash;
+ }
+ else {
+ $raise(Opal.TypeError, "Can't convert " + value.$$class +
+ " to Hash (" + value.$$class + "#to_hash gives " + hash.$$class + ")");
+ }
+ }
+ else {
+ $raise(Opal.TypeError, "no implicit conversion of " + value.$$class + " into Hash");
+ }
+};
+
+
+
``
+
+
Helpers for implementing multiple assignment
+Our code for extracting the values and assigning them only works if the
+return value is a JS array.
+So if we get an Array subclass, extract the wrapped JS array from it
+
+
+
+
+
Opal.to_ary = function(value)
+
+
Used for: a, b = something (no splat)
+
+
Opal.to_ary = function(value) {
+ if (value.$$is_array) {
+ return value;
+ }
+ else if (value['$respond_to?']('to_ary', true)) {
+ var ary = value.$to_ary();
+ if (ary === nil) {
+ return [value];
+ }
+ else if (ary.$$is_array) {
+ return ary;
+ }
+ else {
+ $raise(Opal.TypeError, "Can't convert " + value.$$class +
+ " to Array (" + value.$$class + "#to_ary gives " + ary.$$class + ")");
+ }
+ }
+ else {
+ return [value];
+ }
+};
+
+
+
Opal.to_a = function(value)
+
+
Used for: a, b = *something (with splat)
+
+
Opal.to_a = function(value) {
+ if (value.$$is_array) {
+ // A splatted array must be copied
+ return value.slice();
+ }
+ else if (value['$respond_to?']('to_a', true)) {
+ var ary = value.$to_a();
+ if (ary === nil) {
+ return [value];
+ }
+ else if (ary.$$is_array) {
+ return ary;
+ }
+ else {
+ $raise(Opal.TypeError, "Can't convert " + value.$$class +
+ " to Array (" + value.$$class + "#to_a gives " + ary.$$class + ")");
+ }
+ }
+ else {
+ return [value];
+ }
+};
+
+
+
+
+
Used for extracting keyword arguments from arguments passed to
+JS function.
+
+
@param parameters [Array]
+@return [Hash] or undefined
+
+
Opal.extract_kwargs = function(parameters) {
+ var kwargs = parameters[parameters.length - 1];
+ if (kwargs != null && Opal.respond_to(kwargs, '$to_hash', true)) {
+ $splice(parameters, parameters.length - 1);
+ return kwargs;
+ }
+};
+
+
+
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 arguments 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] all keys used as named kwargs
+@return [Hash]
+
+
Opal.kwrestargs = function(given_args, used_args) {
+ var map = new Map();
+
+ Opal.hash_each(given_args, false, function(key, value) {
+ if (!used_args[key]) {
+ Opal.hash_put(map, key, value);
+ }
+ return [false, false];
+ });
+
+ return map;
+};
+
+
+
jsid_cache = new Map()
+
+
Optimization for a costly operation of prepending '$' to method names
+
+
var jsid_cache = new Map();
+
+
+
Opal.send = function(recv, method, args, block, blockopts)
+
+
Calls passed method on a ruby object with arguments and block:
+
+
Can take a method or a method name.
+
+
+- When method name gets passed it invokes it by its name
+and calls 'method_missing' when object doesn't have this method.
+Used internally by Opal to invoke method that takes a block or a splat.
+- When method (i.e. method body) gets passed, it doesn't trigger 'method_missing'
+because it doesn't know the name of the actual method.
+Used internally by Opal to invoke 'super'.
+
+
+
@example
+ var my_array = [1, 2, 3, 4]
+ Opal.send(my_array, 'length') # => 4
+ Opal.send(my_array, my_array.$length) # => 4
+
+
Opal.send(my_array, 'reverse!') # => [4, 3, 2, 1]
+ Opal.send(my_array, my_array['$reverse!']') # => [4, 3, 2, 1]
+
+
@param recv [Object] ruby object
+@param method [Function, String] method body or name of the method
+@param args [Array] arguments that will be passed to the method call
+@param block [Function] ruby block
+@param blockopts [Object, Number] optional properties to set on the block
+@return [Object] returning value of the method call
+
+
Opal.send = function(recv, method, args, block, blockopts) {
+ var body;
+
+ if (typeof(method) === 'function') {
+ body = method;
+ method = null;
+ } else if (typeof(method) === 'string') {
+ body = recv[$jsid(method)];
+ } else {
+ $raise(Opal.NameError, "Passed method should be a string or a function");
+ }
+
+ return Opal.send2(recv, body, method, args, block, blockopts);
+};
+
+
+
for (i = 0; i < ancestors.length; i++)
+
+
For all ancestors that there are, starting from the closest to the furthest...
+
+
for (i = 0; i < ancestors.length; i++) {
+
+
+
for (j = 0; j < refinement_groups.length; j++)
+
+
For all refinement groups there are, starting from the closest scope to the furthest...
+
+
for (j = 0; j < refinement_groups.length; j++) {
+
+
+
for (k = refinements.length - 1; k >= 0; k--)
+
+
For all refinements there are, starting from the last using
call to the furthest...
+
+
for (k = refinements.length - 1; k >= 0; k--) {
+
+
+
refine_modules = refinement.$$refine_modules
+
+
A single module being given as an argument of the using
call contains multiple
+refinement modules
+
+
refine_modules = refinement.$$refine_modules;
+
+
+
if (typeof refine_modules[ancestor] === 'undefined') continue
+
+
Does this module refine a given call for a given ancestor module?
+
+
if (typeof refine_modules[ancestor] === 'undefined') continue;
+
+
+
if (typeof refine_module.$$prototype[$jsid(method)] !== 'undefined')
+
+
Does this module define a method we want to call?
+
+
if (typeof refine_module.$$prototype[$jsid(method)] !== 'undefined') {
+
+
+
Opal.def = function(obj, jsid, body, blockopts)
+
+
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 obj [Object, Class] the actual obj to define method for
+@param jsid [String] the JavaScript friendly method name (e.g. '$foo')
+@param body [JS.Function] the literal JavaScript function used as method
+@param blockopts [Object, Number] optional properties to set on the body
+@return [null]
+
+
Opal.def = function(obj, jsid, body, blockopts) {
+ apply_blockopts(body, blockopts);
+
+ // Special case for a method definition in the
+ // top-level namespace
+ if (obj === Opal.top) {
+ return Opal.defn(Opal.Object, jsid, body);
+ }
+ // if instance_eval is invoked on a module/class, it sets inst_eval_mod
+ else if (!obj.$$eval && obj.$$is_a_module) {
+ return Opal.defn(obj, jsid, body);
+ }
+ else {
+ return Opal.defs(obj, jsid, body);
+ }
+};
+
+
+
Opal.defn = function(module, jsid, body)
+
+
Define method on a module or class (see Opal.def).
+
+
Opal.defn = function(module, jsid, body) {
+ $deny_frozen_access(module);
+
+ body.displayName = jsid;
+ body.$$owner = module;
+
+ var name = jsid.substr(1);
+
+ var proto = module.$$prototype;
+ if (proto.hasOwnProperty('$$dummy')) {
+ proto = proto.$$define_methods_on;
+ }
+ $prop(proto, jsid, body);
+
+ if (module.$$is_module) {
+ if (module.$$module_function) {
+ Opal.defs(module, jsid, body)
+ }
+
+ for (var i = 0, iclasses = module.$$iclasses, length = iclasses.length; i < length; i++) {
+ var iclass = iclasses[i];
+ $prop(iclass, jsid, body);
+ }
+ }
+
+ var singleton_of = module.$$singleton_of;
+ if (module.$method_added && !module.$method_added.$$stub && !singleton_of) {
+ module.$method_added(name);
+ }
+ else if (singleton_of && singleton_of.$singleton_method_added && !singleton_of.$singleton_method_added.$$stub) {
+ singleton_of.$singleton_method_added(name);
+ }
+
+ return name;
+};
+
+
+
Opal.defs = function(obj, jsid, body, blockopts)
+
+
Define a singleton method on the given object (see Opal.def).
+
+
Opal.defs = function(obj, jsid, body, blockopts) {
+ apply_blockopts(body, blockopts);
+
+ if (obj.$$is_string || obj.$$is_number) {
+ $raise(Opal.TypeError, "can't define singleton");
+ }
+ return Opal.defn(Opal.get_singleton_class(obj), jsid, body);
+};
+
+
+
function remove_method_from_iclasses(obj, jsid)
+
+
Since JavaScript has no concept of modules, we create proxy classes
+called iclasses
that store copies of methods loaded. We need to
+update them if we remove a method.
+
+
function remove_method_from_iclasses(obj, jsid) {
+ if (obj.$$is_module) {
+ for (var i = 0, iclasses = obj.$$iclasses, length = iclasses.length; i < length; i++) {
+ var iclass = iclasses[i];
+ delete iclass[jsid];
+ }
+ }
+}
+
+
+
Opal.rdef = function(obj, jsid)
+
+
Called from #remove_method.
+
+
Opal.rdef = function(obj, jsid) {
+ if (!$has_own(obj.$$prototype, jsid)) {
+ $raise(Opal.NameError, "method '" + jsid.substr(1) + "' not defined in " + obj.$name());
+ }
+
+ delete obj.$$prototype[jsid];
+
+ remove_method_from_iclasses(obj, jsid);
+
+ if (obj.$$is_singleton) {
+ if (obj.$$prototype.$singleton_method_removed && !obj.$$prototype.$singleton_method_removed.$$stub) {
+ obj.$$prototype.$singleton_method_removed(jsid.substr(1));
+ }
+ }
+ else {
+ if (obj.$method_removed && !obj.$method_removed.$$stub) {
+ obj.$method_removed(jsid.substr(1));
+ }
+ }
+};
+
+
+
Opal.udef = function(obj, jsid)
+
+
Called from #undef_method.
+
+
Opal.udef = function(obj, jsid) {
+ if (!obj.$$prototype[jsid] || obj.$$prototype[jsid].$$stub) {
+ $raise(Opal.NameError, "method '" + jsid.substr(1) + "' not defined in " + obj.$name());
+ }
+
+ Opal.add_stub_for(obj.$$prototype, jsid);
+
+ remove_method_from_iclasses(obj, jsid);
+
+ if (obj.$$is_singleton) {
+ if (obj.$$prototype.$singleton_method_undefined && !obj.$$prototype.$singleton_method_undefined.$$stub) {
+ obj.$$prototype.$singleton_method_undefined(jsid.substr(1));
+ }
+ }
+ else {
+ if (obj.$method_undefined && !obj.$method_undefined.$$stub) {
+ obj.$method_undefined(jsid.substr(1));
+ }
+ }
+};
+
+
+
if (typeof obj.$$prototype === 'undefined')
+
+
Aliasing on main means aliasing on Object...
+
+
if (typeof obj.$$prototype === 'undefined') {
+
+
+
if (obj.$$eval)
+
+
When running inside #instance_eval the alias refers to class methods.
+
+
if (obj.$$eval) {
+
+
+
body = Opal.Object.$$prototype[old_id]
+
+
try to look into Object
+
+
body = Opal.Object.$$prototype[old_id]
+
+
+
if (body.$$alias_of) body = body.$$alias_of
+
+
If the body is itself an alias use the original body
+to keep the max depth at 1.
+
+
if (body.$$alias_of) body = body.$$alias_of;
+
+
+
alias = Opal.wrapMethodBody(body)
+
+
We need a wrapper because otherwise properties
+would be overwritten on the original body.
+
+
alias = Opal.wrapMethodBody(body);
+
+
+
alias.displayName = name
+
+
Try to make the browser pick the right name
+
+
alias.displayName = name;
+
+
+
try
+
+
Assign the 'length' value with defineProperty because
+in strict mode the property is not writable.
+It doesn't work in older browsers (like Chrome 38), where
+an exception is thrown breaking Opal altogether.
+
+
try {
+
+
+
``
+
+
Hashes
+
+
+
+
+
objects.splice(objects.indexOf(object), 1)
+
+
got a duplicate, remove it
+
+
objects.splice(objects.indexOf(object), 1);
+
+
+
Opal.hash2 = function(keys, smap)
+
+
A faster Hash 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, smap) {
+ console.warn("DEPRECATION: `Opal.hash2` is deprecated and will be removed in Opal 2.0. Use `new Map()` with an array of key/value pairs instead.");
+
+ var hash = new Map();
+ for (var i = 0, max = keys.length; i < max; i++) {
+ hash.set(keys[i], smap[keys[i]]);
+ }
+ return hash;
+};
+
+
+
res
+
+
dres = default result, returned if hash is empty
+fun is called as fun(key, value) and must return a array with [break, result]
+if break is true, iteration stops and result is returned
+if break is false, iteration continues and eventually the last result is returned
+
+
var res;
+
+
+
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();
+ range.begin = first;
+ range.end = last;
+ range.excl = exc;
+
+ return range;
+};
+
+
+
"constructor", "displayName", "__count__", "__noSuchMethod__",
+
+
properties
+
+
"constructor", "displayName", "__count__", "__noSuchMethod__",
+
+
+
"hasOwnProperty", "valueOf"
+
+
methods
+
+
"hasOwnProperty", "valueOf"
+
+
+
Opal.ivar = function(name)
+
+
Get the ivar name for a given name.
+Mostly adds a trailing $ to reserved names.
+
+
Opal.ivar = function(name) {
+ if (reserved_ivar_names.indexOf(name) !== -1) {
+ name += "$";
+ }
+
+ return name;
+};
+
+
+
``
+
+
Support for #freeze
+
+
+
+
+
function $deny_frozen_access(obj)
+
+
helper that can be used from methods
+
+
function $deny_frozen_access(obj) {
+ if (obj.$$frozen) {
+ $raise(Opal.FrozenError, "can't modify frozen " + (obj.$class()) + ": " + (obj), new Map([["receiver", obj]]));
+ }
+};
+
+
+
Opal.freeze = function(obj)
+
+
common #freeze runtime support
+
+
Opal.freeze = function(obj) {
+ $prop(obj, "$$frozen", true);
+
+ // set $$id
+ if (!obj.hasOwnProperty('$$id')) { $prop(obj, '$$id', $uid()); }
+
+ if (obj.hasOwnProperty('$$meta')) {
+ // freeze $$meta if it has already been set
+ obj.$$meta.$freeze();
+ } else {
+ // ensure $$meta can be set lazily, $$meta is frozen when set in runtime.js
+ $prop(obj, '$$meta', null);
+ }
+
+ // $$comparable is used internally and set multiple times
+ // defining it before sealing ensures it can be modified later on
+ if (!obj.hasOwnProperty('$$comparable')) { $prop(obj, '$$comparable', null); }
+
+ // seal the Object
+ Object.seal(obj);
+
+ return obj;
+};
+
+
+
Opal.freeze_props = function(obj)
+
+
freze props, make setters of instance variables throw FrozenError
+
+
Opal.freeze_props = function(obj) {
+ var prop, prop_type, desc;
+
+ for(prop in obj) {
+ prop_type = typeof(prop);
+
+ // prop_type "object" here is a String(), skip $ props
+ if ((prop_type === "string" || prop_type === "object") && prop[0] === '$') {
+ continue;
+ }
+
+ desc = Object.getOwnPropertyDescriptor(obj, prop);
+ if (desc && desc.enumerable && desc.writable) {
+ // create closure to retain current value as cv
+ // for Opal 2.0 let for cv should do the trick, instead of a function
+ (function() {
+ // set v to undefined, as if the property is not set
+ var cv = obj[prop];
+ Object.defineProperty(obj, prop, {
+ get: function() { return cv; },
+ set: function(_val) { $deny_frozen_access(obj); },
+ enumerable: true
+ });
+ })();
+ }
+ }
+};
+
+
+
``
+
+
Regexps
+
+
+
+
+
Opal.escape_regexp = function(str)
+
+
Escape Regexp special chars letting the resulting string be used to build
+a new Regexp.
+
+
Opal.escape_regexp = function(str) {
+ return str.replace(/([-[\]\/{}()*+?.^$\\| ])/g, '\\$1')
+ .replace(/[\n]/g, '\\n')
+ .replace(/[\r]/g, '\\r')
+ .replace(/[\f]/g, '\\f')
+ .replace(/[\t]/g, '\\t');
+};
+
+
+
Opal.global_regexp = function(pattern)
+
+
Create a global Regexp from a RegExp object and cache the result
+on the object itself ($$g attribute).
+
+
Opal.global_regexp = function(pattern) {
+ if (pattern.global) {
+ return pattern; // RegExp already has the global flag
+ }
+ if (pattern.$$g == null) {
+ pattern.$$g = new RegExp(pattern.source, (pattern.multiline ? 'gm' : 'g') + (pattern.ignoreCase ? 'i' : ''));
+ } else {
+ pattern.$$g.lastIndex = null; // reset lastIndex property
+ }
+ return pattern.$$g;
+};
+
+
+
Opal.global_multiline_regexp = function(pattern)
+
+
Create a global multiline Regexp from a RegExp object and cache the result
+on the object itself ($$gm or $$g attribute).
+
+
Opal.global_multiline_regexp = function(pattern) {
+ var result, flags;
+
+ // RegExp already has the global and multiline flag
+ if (pattern.global && pattern.multiline) return pattern;
+
+ flags = 'gm' + (pattern.ignoreCase ? 'i' : '');
+ if (pattern.multiline) {
+ // we are using the $$g attribute because the Regexp is already multiline
+ if (pattern.$$g == null) {
+ pattern.$$g = new RegExp(pattern.source, flags);
+ }
+ result = pattern.$$g;
+ } else {
+ if (pattern.$$gm == null) {
+ pattern.$$gm = new RegExp(pattern.source, flags);
+ }
+ result = pattern.$$gm;
+ }
+ result.lastIndex = null; // reset lastIndex property
+ return result;
+};
+
+
+
Opal.regexp = function(parts, flags)
+
+
Combine multiple regexp parts together
+
+
Opal.regexp = function(parts, flags) {
+ var part;
+ var ignoreCase = typeof flags !== 'undefined' && flags && flags.indexOf('i') >= 0;
+
+ for (var i = 0, ii = parts.length; i < ii; i++) {
+ part = parts[i];
+ if (part instanceof RegExp) {
+ if (part.ignoreCase !== ignoreCase)
+ Opal.Kernel.$warn(
+ "ignore case doesn't match for " + part.source.$inspect(),
+ new Map([['uplevel', 1]])
+ )
+
+ part = part.source;
+ }
+ if (part === '') part = '(?:' + part + ')';
+ parts[i] = part;
+ }
+
+ if (flags) {
+ return new RegExp(parts.join(''), flags);
+ } else {
+ return new RegExp(parts.join(''));
+ }
+};
+
+
+
``
+
+
Require system
+
+
+
+
+
return retval.then($return_val(true))
+
+
A special case of require having an async top:
+We will need to await it.
+
+
return retval.then($return_val(true));
+
+
+
``
+
+
Strings
+
+
+
+
+
Opal.set_encoding = function(str, name, type)
+
+
Sets the encoding on a string, will treat string literals as frozen strings
+raising a FrozenError.
+
+
@param str [String] the string on which the encoding should be set
+@param name [String] the canonical name of the encoding
+@param type [String] possible values are either "encoding"
, "internal_encoding"
, or `undefined
+
+
Opal.set_encoding = function(str, name, type) {
+ if (typeof type === "undefined") type = "encoding";
+ if (typeof str === 'string' || str.$$frozen === true)
+ $raise(Opal.FrozenError, "can't modify frozen String");
+
+ var encoding = Opal.find_encoding(name);
+
+ if (encoding === str[type]) { return str; }
+
+ str[type] = encoding;
+
+ return str;
+};
+
+
+
Opal.find_encoding = function(name)
+
+
Fetches the encoding for the given name or raises ArgumentError.
+
+
Opal.find_encoding = function(name) {
+ var register = Opal.encodings;
+ var encoding = register[name] || register[name.toUpperCase()];
+ if (!encoding) $raise(Opal.ArgumentError, "unknown encoding name - " + name);
+ return encoding;
+}
+
+
+
Opal.enc = function(str, name)
+
+
@returns a String object with the encoding set from a string literal
+
+
Opal.enc = function(str, name) {
+ var dup = new String(str);
+ dup = Opal.set_encoding(dup, name);
+ dup.internal_encoding = dup.encoding;
+ return dup
+}
+
+
+
Opal.binary = function(str)
+
+
@returns a String object with the internal encoding set to Binary
+
+
Opal.binary = function(str) {
+ var dup = new String(str);
+ return Opal.set_encoding(dup, "binary", "internal_encoding");
+}
+
+
+
Opal.queue = function(proc)
+
+
Run a block of code, but if it returns a Promise, don't run the next
+one, but queue it.
+
+
Opal.queue = function(proc) {
+ if (Opal.last_promise) {
+ // The async path is taken only if anything before returned a
+ // Promise(V2).
+ Opal.last_promise = Opal.last_promise.then(function() {
+ if (!Opal.promise_unhandled_exception) return proc(Opal);
+ })['catch'](function(error) {
+ if (Opal.respond_to(error, '$full_message')) {
+ error = error.$full_message();
+ }
+ console.error(error);
+ // Abort further execution
+ Opal.promise_unhandled_exception = true;
+ Opal.exit(1);
+ });
+ return Opal.last_promise;
+ }
+ else {
+ var ret = proc(Opal);
+ if (typeof Promise === 'function' && typeof ret === 'object' && ret instanceof Promise) {
+ Opal.last_promise = ret;
+ }
+ return ret;
+ }
+}
+
+
+
``
+
+
Operator helpers
+
+
+
+
+
function are_both_numbers_or_strings(lhs, rhs)
+
+
Optimized helpers for calls like $truthy((a)'$===') -> $eqeqeq(a, b)
+
+
function are_both_numbers_or_strings(lhs, rhs) {
+ return (typeof lhs === 'number' && typeof rhs === 'number') ||
+ (typeof lhs === 'string' && typeof rhs === 'string');
+}
+
+
+
function $return_val(arg)
+
+
Shortcuts - optimized function generators for simple kinds of functions
+
+
function $return_val(arg) {
+ return function() {
+ return arg;
+ }
+}
+
+
+
Opal.ensure_kwargs = function(kwargs)
+
+
Primitives for handling parameters
+
+
Opal.ensure_kwargs = function(kwargs) {
+ if (kwargs == null) {
+ return new Map();
+ } else if (kwargs.$$is_hash) {
+ return kwargs;
+ } else {
+ $raise(Opal.ArgumentError, 'expected kwargs');
+ }
+}
+
+
+
Opal.large_array_unpack = function(str)
+
+
Arrays of size > 32 elements that contain only strings,
+symbols, integers and nils are compiled as a self-extracting
+string.
+
+
Opal.large_array_unpack = function(str) {
+ var array = str.split(","), length = array.length, i;
+ for (i = 0; i < length; i++) {
+ switch(array[i][0]) {
+ case undefined:
+ array[i] = nil
+ break;
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ array[i] = +array[i];
+ }
+ }
+ return array;
+}
+
+
+
Opal.opal32_init = $return_val(0x4f70616c)
+
+
Opal32-checksum algorithm for #hash
+
+
Opal.opal32_init = $return_val(0x4f70616c);
+
+
+
Opal.BasicObject = BasicObject = $allocate_class('BasicObject', null)
+
+
Initialization
+
+
Opal.BasicObject = BasicObject = $allocate_class('BasicObject', null);
+
+
+
BasicObject.$$const.BasicObject = BasicObject
+
+
BasicObject can reach itself, avoid const_set to skip the $$base_module logic
+
+
BasicObject.$$const.BasicObject = BasicObject;
+
+
+
$const_set(_Object, "BasicObject", BasicObject)
+
+
Assign basic constants
+
+
$const_set(_Object, "BasicObject", BasicObject);
+
+
+
BasicObject.$$class = Class
+
+
Fix booted classes to have correct .class value
+
+
BasicObject.$$class = Class;
+
+
+
$prop(_Object.$$prototype, 'toString', function()
+
+
Forward .toString() to #to_s
+
+
$prop(_Object.$$prototype, 'toString', function() {
+ var to_s = this.$to_s();
+ if (to_s.$$is_string && typeof(to_s) === 'object') {
+ // a string created using new String('string')
+ return to_s.valueOf();
+ } else {
+ return to_s;
+ }
+});
+
+
+
$prop(_Object.$$prototype, '$require', Opal.require)
+
+
Make Kernel#require immediately available as it's needed to require all the
+other corelib files.
+
+
$prop(_Object.$$prototype, '$require', Opal.require);
+
+
+
Opal.top = new _Object()
+
+
Instantiate the main object
+
+
Opal.top = new _Object();
+
+
+
function top_define_method()
+
+
Foward calls to define_method on the top object to Object
+
+
function top_define_method() {
+ var block = top_define_method.$$p;
+ top_define_method.$$p = null;
+ return Opal.send(_Object, 'define_method', arguments, block)
+};
+
+
+
Opal.NilClass = $allocate_class('NilClass', Opal.Object)
+
+
Nil
+
+
Opal.NilClass = $allocate_class('NilClass', Opal.Object);
+
+
+
Object.defineProperty($gvars, "@",
+
+
Define a "$@" global variable, which would compute and return a backtrace on demand.
+
+
Object.defineProperty($gvars, "@", {
+
+
+
Opal.file_sources = {}
+
+
If enable-file-source-embed compiler option is enabled, each module loaded will add its
+sources to this object
+
+
Opal.file_sources = {};
+
+