runtime.js

``

@note A few conventions for the documentation of this file:

  1. Always use "//" (in contrast with "/**/")
  2. The syntax used is Yardoc (yardoc.org), which is intended for Ruby (se below)
  3. @param and @return types should be preceded by JS. when referring to JavaScript constructors (e.g. JS.Function) otherwise Ruby is assumed.
  4. nil and null being unambiguous refer to the respective objects/values in Ruby and JavaScript
  5. 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()

@private Pops an exception from the stack and updates $!.

Opal.pop_exception = function() {
  var exception = Opal.exceptions.pop();
  if (exception) {
    $gvars["!"] = exception;
    $gvars["@"] = exception.$backtrace();
  }
  else {
    $gvars["!"] = $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);
  }
}

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;

  if (superclass != null && superclass.$$bridge) {
    // Inheritance from bridged classes requires
    // calling original JS constructors
    klass = function() {
      var args = $slice(arguments),
          self = new ($bind.apply(superclass.$$constructor, [null].concat(args)))();

      // 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.$$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);

  $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.$$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);
  }
};

function set_meta(obj, meta)

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)
    var args_ary = new Array(arguments.length);
    for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i]; }

    return this.$method_missing.apply(this, [method_name.slice(1)].concat(args_ary));
  }

  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, super_method;

  ancestors = get_ancestors(obj);

  var current_index = ancestors.indexOf(current_func.$$owner);

  for (var i = current_index + 1; i < ancestors.length; i++) {
    var ancestor = ancestors[i],
        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;

  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) {
    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") {
    $raise(Opal.LocalJumpError, "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) {
    var args_ary = new Array(args.length);
    for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = args[i]; }

    return block.apply(null, args_ary);
  }

  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];
  }
};

Opal.extract_kwargs = function(parameters)

Used for extracting keyword arguments from arguments passed to JS function. If provided +arguments+ list doesn't have a Hash as a last item, returns a blank Hash.

@param parameters [Array] @return [Hash]

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 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] all keys used as named kwargs @return [Hash]

Opal.kwrestargs = function(given_args, used_args) {
  var keys      = [],
      map       = {},
      key           ,
      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);
};

jsid_cache = {}

Optimization for a costly operation of prepending '$' to method names

var jsid_cache = {}

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.

  1. 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.
  2. 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);
};

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];

  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);

  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 = function()

We need a wrapper because otherwise properties would be overwritten on the original body.

alias = function() {
  var block = alias.$$p, args, i, ii;

  args = new Array(arguments.length);
  for(i = 0, ii = arguments.length; i < ii; i++) {
    args[i] = arguments[i];
  }

  alias.$$p = null;

  return Opal.send(this, body, args, block);
};

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 {

alias.displayName = name

Try to make the browser pick the right name

alias.displayName       = name;

``

Hashes


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) {
  var hash = new Opal.Hash();

  hash.$$smap = smap;
  hash.$$map  = Object.create(null);
  hash.$$keys = keys;

  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();
      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), Opal.hash2(["receiver"], {"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(),
          Opal.hash({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 Opal.hash2([], {});
  } 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.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 args = $slice(arguments);
  var block = top_define_method.$$p;
  top_define_method.$$p = null;
  return Opal.send(_Object, 'define_method', args, block)
};

Opal.NilClass = $allocate_class('NilClass', Opal.Object)

Nil

Opal.NilClass = $allocate_class('NilClass', Opal.Object);

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 = {};