asciidoctor-opal-runtime
Version:
Opal Runtime for Asciidoctor.js
1,595 lines (1,295 loc) • 683 kB
JavaScript
'use strict';
var __fs__ = require('fs');
var __path__ = require('path');
var __util__ = require('util');
var __glob__ = require('glob');
var __os__ = require('os');
var __xmlhttprequest__ = require('unxhr');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var __fs____default = /*#__PURE__*/_interopDefaultLegacy(__fs__);
var __path____default = /*#__PURE__*/_interopDefaultLegacy(__path__);
var __util____default = /*#__PURE__*/_interopDefaultLegacy(__util__);
var __glob____default = /*#__PURE__*/_interopDefaultLegacy(__glob__);
var __os____default = /*#__PURE__*/_interopDefaultLegacy(__os__);
var __xmlhttprequest____default = /*#__PURE__*/_interopDefaultLegacy(__xmlhttprequest__);
(function(global_object) {
// @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).
var console;
// Detect the global object
if (typeof(globalThis) !== 'undefined') { global_object = globalThis; }
else if (typeof(global) !== 'undefined') { global_object = global; }
else if (typeof(window) !== 'undefined') { global_object = window; }
// Setup a dummy console object if missing
if (typeof(global_object.console) === 'object') {
console = global_object.console;
} else if (global_object.console == null) {
console = global_object.console = {};
} else {
console = {};
}
if (!('log' in console)) { console.log = function () {}; }
if (!('warn' in console)) { console.warn = console.log; }
if (typeof(global_object.Opal) !== 'undefined') {
console.warn('Opal already loaded. Loading twice can cause troubles, please fix your setup.');
return global_object.Opal;
}
var nil;
// The actual class for BasicObject
var BasicObject;
// The actual Object class.
// The leading underscore is to avoid confusion with window.Object()
var _Object;
// The actual Module class
var Module;
// The actual Class class
var Class;
// The Opal.Opal class (helpers etc.)
var _Opal;
// The Kernel module
var Kernel;
// The Opal object that is exposed globally
var Opal = global_object.Opal = {};
// This is a useful reference to global object inside ruby files
Opal.global = global_object;
global_object.Opal = Opal;
// Configure runtime behavior with regards to require and unsupported features
Opal.config = {
missing_require_severity: 'error', // error, warning, ignore
unsupported_features_severity: 'warning', // error, warning, ignore
experimental_features_severity: 'warning',// warning, ignore
enable_stack_trace: true // true, false
};
// Minify common function calls
var $has_own = Object.hasOwnProperty;
var $bind = Function.prototype.bind;
var $set_proto = Object.setPrototypeOf;
var $slice = Array.prototype.slice;
var $splice = Array.prototype.splice;
// Nil object id is always 4
var nil_id = 4;
// Generates even sequential numbers greater than 4
// (nil_id) to serve as unique ids for ruby objects
var unique_id = nil_id;
// Return next unique id
Opal.uid = function() {
unique_id += 2;
return unique_id;
};
// Retrieve or assign the id of an object
Opal.id = function(obj) {
if (obj.$$is_number) return (obj * 2)+1;
if (obj.$$id != null) {
return obj.$$id;
}
$prop(obj, '$$id', Opal.uid());
return obj.$$id;
};
// Globals table
Opal.gvars = {};
// Exit function, this should be replaced by platform specific implementation
// (See nodejs and chrome for examples)
Opal.exit = function(status) { if (Opal.gvars.DEBUG) console.log('Exited with status '+status); };
// keeps track of exceptions for $!
Opal.exceptions = [];
// @private
// Pops an exception from the stack and updates `$!`.
Opal.pop_exception = function() {
var exception = Opal.exceptions.pop();
if (exception) {
Opal.gvars["!"] = exception;
Opal.gvars["@"] = exception.$backtrace();
}
else {
Opal.gvars["!"] = Opal.gvars["@"] = nil;
}
};
function $prop(object, name, initialValue) {
if (typeof(object) === "string") {
// 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;
} else {
Object.defineProperty(object, name, {
value: initialValue,
enumerable: false,
configurable: true,
writable: true
});
}
}
Opal.prop = $prop;
// @deprecated
Opal.defineProperty = Opal.prop;
Opal.slice = $slice;
// Helpers
// -----
var $truthy = Opal.truthy = function(val) {
return false !== val && nil !== val && undefined !== val && null !== val && (!(val instanceof Boolean) || true === val.valueOf());
};
Opal.falsy = function(val) {
return !$truthy(val);
};
Opal.type_error = function(object, type, method, coerced) {
object = object.$$class;
if (coerced && method) {
coerced = coerced.$$class;
return Opal.TypeError.$new(
"can't convert " + object + " into " + type +
" (" + object + "#" + method + " gives " + coerced + ")"
)
} else {
return Opal.TypeError.$new(
"no implicit conversion of " + object + " into " + type
)
}
};
Opal.coerce_to = function(object, type, method, args) {
if (type['$==='](object)) return object;
if (!object['$respond_to?'](method)) {
throw Opal.type_error(object, type);
}
if (args == null) args = [];
return Opal.send(object, method, args);
};
Opal.respond_to = function(obj, jsid, include_all) {
if (obj == null || !obj.$$class) return false;
include_all = !!include_all;
var body = obj[jsid];
if (obj['$respond_to?'].$$pristine) {
if (typeof(body) === "function" && !body.$$stub) {
return true;
}
if (!obj['$respond_to_missing?'].$$pristine) {
return Opal.send(obj, obj['$respond_to_missing?'], [jsid.substr(1), include_all]);
}
} else {
return Opal.send(obj, obj['$respond_to?'], [jsid.substr(1), include_all]);
}
};
// TracePoint support
// ------------------
//
// Support for `TracePoint.trace(:class) do ... end`
Opal.trace_class = false;
Opal.tracers_for_class = [];
function invoke_tracers_for_class(klass_or_module) {
var i, ii, tracer;
for(i = 0, ii = Opal.tracers_for_class.length; i < ii; i++) {
tracer = Opal.tracers_for_class[i];
tracer.trace_object = klass_or_module;
tracer.block.$call(tracer);
}
}
function handle_autoload(cref, name) {
if (!cref.$$autoload[name].loaded) {
cref.$$autoload[name].loaded = true;
try {
Opal.Kernel.$require(cref.$$autoload[name].path);
} catch (e) {
cref.$$autoload[name].exception = e;
throw e;
}
cref.$$autoload[name].required = true;
if (cref.$$const[name] != null) {
cref.$$autoload[name].success = true;
return cref.$$const[name];
}
} else if (cref.$$autoload[name].loaded && !cref.$$autoload[name].required) {
if (cref.$$autoload[name].exception) { throw cref.$$autoload[name].exception; }
}
}
// Constants
// ---------
//
// For future reference:
// - The Rails autoloading guide (http://guides.rubyonrails.org/v5.0/autoloading_and_reloading_constants.html)
// - @ConradIrwin's 2012 post on “Everything you ever wanted to know about constant lookup in Ruby” (http://cirw.in/blog/constant-lookup.html)
//
// 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]`
// 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);
}
}
}
// 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);
}
}
}
// Walk up the ancestors chain looking for the constant
function const_lookup_ancestors(cref, name) {
var i, ii, ancestors;
if (cref == null) return;
ancestors = Opal.ancestors(cref);
for (i = 0, ii = ancestors.length; i < ii; i++) {
if (ancestors[i].$$const && $has_own.call(ancestors[i].$$const, name)) {
return ancestors[i].$$const[name];
} else if (ancestors[i].$$autoload && ancestors[i].$$autoload[name]) {
return handle_autoload(ancestors[i], 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);
}
}
// Call const_missing if nothing else worked
function const_missing(cref, name, skip_missing) {
if (!skip_missing) {
return (cref || _Object).$const_missing(name);
}
}
// 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) {
throw new Opal.TypeError(cref.toString() + " is not a class/module");
}
result = const_get_name(cref, name); if (result != null) return result;
result = const_missing(cref, name, skip_missing); if (result != null) return result;
};
// 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) {
throw new 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 ? result : const_missing(cref, name, skip_missing);
};
// Initialize the top level constant cache generation counter
Opal.const_cache_version = 1;
// 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 ? result : const_missing(cref, name, skip_missing);
};
// Register the constant on a cref and opportunistically set the name of
// unnamed classes/modules.
function $const_set(cref, name, value) {
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));
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);
return value;
}
Opal.const_set = $const_set;
// 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(Opal.ancestors(cref));
if (inherit && cref.$$is_module) modules = modules.concat([Opal.Object]).concat(Opal.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);
};
// 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;
}
throw Opal.NameError.$new("constant "+cref+"::"+cref.$name()+" not defined");
};
// 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);
}
};
// Setup some shortcuts to reduce compiled size
Opal.$$ = Opal.const_get_relative;
Opal.$$$ = Opal.const_get_qualified;
Opal.$r = Opal.const_get_relative_factory;
// Modules & Classes
// -----------------
// 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
//
Opal.allocate_class = function(name, superclass, singleton) {
var klass, constructor;
if (superclass != null && superclass.$$bridge) {
// Inheritance from bridged classes requires
// calling original JS constructors
constructor = function() {
var args = $slice.call(arguments),
self = new ($bind.apply(superclass.$$constructor, [null].concat(args)))();
// and replacing a __proto__ manually
$set_proto(self, klass.$$prototype);
return self;
};
} else {
constructor = function(){};
}
if (name && name !== nil) {
$prop(constructor, 'displayName', '::'+name);
}
klass = constructor;
$prop(klass, '$$name', name);
$prop(klass, '$$constructor', constructor);
$prop(klass, '$$prototype', constructor.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;
};
function find_existing_class(scope, name) {
// Try to find the class in the current scope
var klass = const_get_name(scope, name);
// If the class exists in the scope, then we must use that
if (klass) {
// Make sure the existing constant is a class, or raise error
if (!klass.$$is_class) {
throw Opal.TypeError.$new(name + " is not a class");
}
return klass;
}
}
function ensureSuperclassMatch(klass, superclass) {
if (klass.$$super !== superclass) {
throw Opal.TypeError.$new("superclass mismatch for class " + klass.$$name);
}
}
Opal.klass = function(scope, superclass, name) {
var bridged;
if (scope == null || scope == '::') {
// Global scope
scope = _Object;
} else if (!scope.$$is_class && !scope.$$is_module) {
// Scope is an object, use its class
scope = scope.$$class;
}
// If the superclass is not an Opal-generated class then we're bridging a native JS class
if (
superclass != null && (!superclass.hasOwnProperty || (
superclass.hasOwnProperty && !superclass.hasOwnProperty('$$is_class')
))
) {
if (superclass.constructor && superclass.constructor.name == "Function") {
bridged = superclass;
superclass = _Object;
} else {
throw Opal.TypeError.$new("superclass must be a Class (" + (
(superclass.constructor && (superclass.constructor.name || superclass.constructor.$$name)) ||
typeof(superclass)
) + " given)");
}
}
var klass = find_existing_class(scope, name);
if (klass) {
if (superclass) {
// Make sure existing class has same superclass
ensureSuperclassMatch(klass, superclass);
}
if (Opal.trace_class) { invoke_tracers_for_class(klass); }
return klass;
}
// Class doesn't exist, create a new one with given superclass...
// Not specifying a superclass means we can assume it to be Object
if (superclass == null) {
superclass = _Object;
}
// Create the class object (instance of Class)
klass = Opal.allocate_class(name, superclass);
$const_set(scope, name, klass);
// Call .inherited() hook with new class on the superclass
if (superclass.$inherited) {
superclass.$inherited(klass);
}
if (bridged) {
Opal.bridge(bridged, klass);
}
if (Opal.trace_class) { invoke_tracers_for_class(klass); }
return klass;
};
// 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]
Opal.allocate_module = function(name) {
var constructor = function(){};
if (name) {
$prop(constructor, 'displayName', name+'.$$constructor');
}
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;
};
function find_existing_module(scope, name) {
var module = const_get_name(scope, name);
if (module == null && scope === _Object) module = const_lookup_ancestors(_Object, name);
if (module) {
if (!module.$$is_module && module !== _Object) {
throw Opal.TypeError.$new(name + " is not a module");
}
}
return module;
}
Opal.module = function(scope, name) {
var module;
if (scope == null || scope == '::') {
// Global scope
scope = _Object;
} else if (!scope.$$is_class && !scope.$$is_module) {
// Scope is an object, use its class
scope = scope.$$class;
}
module = find_existing_module(scope, name);
if (module) {
if (Opal.trace_class) { invoke_tracers_for_class(module); }
return module;
}
// Module doesnt exist, create a new one...
module = Opal.allocate_module(name);
$const_set(scope, name, module);
if (Opal.trace_class) { invoke_tracers_for_class(module); }
return module;
};
// 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);
}
};
// 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) {
var superclass, meta;
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`.
superclass = klass === BasicObject ? Class : Opal.get_singleton_class(klass.$$super);
meta = Opal.allocate_class(null, superclass, true);
$prop(meta, '$$is_singleton', true);
$prop(meta, '$$singleton_of', klass);
$prop(klass, '$$meta', meta);
$set_proto(klass, meta.$$prototype);
// Restoring ClassName.class
$prop(klass, '$$class', Opal.Class);
return meta;
};
Opal.build_module_singleton_class = function(mod) {
if (mod.$$meta) {
return mod.$$meta;
}
var meta = Opal.allocate_class(null, Opal.Module, true);
$prop(meta, '$$is_singleton', true);
$prop(meta, '$$singleton_of', mod);
$prop(mod, '$$meta', meta);
$set_proto(mod, meta.$$prototype);
// Restoring ModuleName.class
$prop(mod, '$$class', Opal.Module);
return meta;
};
// 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 = Opal.allocate_class(nil, superclass, true);
$prop(klass, '$$is_singleton', true);
$prop(klass, '$$singleton_of', object);
delete klass.$$prototype.$$class;
$prop(object, '$$meta', klass);
$set_proto(object, object.$$meta.$$prototype);
return klass;
};
Opal.is_method = function(prop) {
return (prop[0] === '$' && prop[1] !== '$');
};
Opal.instance_methods = function(mod) {
var exclude = [], results = [], ancestors = Opal.ancestors(mod);
for (var i = 0, l = ancestors.length; i < l; i++) {
var ancestor = ancestors[i],
proto = ancestor.$$prototype;
if (proto.hasOwnProperty('$$dummy')) {
proto = proto.$$define_methods_on;
}
var props = Object.getOwnPropertyNames(proto);
for (var j = 0, ll = props.length; j < ll; j++) {
var prop = props[j];
if (Opal.is_method(prop)) {
var method_name = prop.slice(1),
method = proto[prop];
if (method.$$stub && exclude.indexOf(method_name) === -1) {
exclude.push(method_name);
}
if (!method.$$stub && results.indexOf(method_name) === -1 && exclude.indexOf(method_name) === -1) {
results.push(method_name);
}
}
}
}
return results;
};
Opal.own_instance_methods = function(mod) {
var results = [],
proto = mod.$$prototype;
if (proto.hasOwnProperty('$$dummy')) {
proto = proto.$$define_methods_on;
}
var props = Object.getOwnPropertyNames(proto);
for (var i = 0, length = props.length; i < length; i++) {
var prop = props[i];
if (Opal.is_method(prop)) {
var method = proto[prop];
if (!method.$$stub) {
var method_name = prop.slice(1);
results.push(method_name);
}
}
}
return results;
};
Opal.methods = function(obj) {
return Opal.instance_methods(obj.$$meta || obj.$$class);
};
Opal.own_methods = function(obj) {
if (obj.$$meta) {
return Opal.own_instance_methods(obj.$$meta);
}
else {
return [];
}
};
Opal.receiver_methods = function(obj) {
var mod = Opal.get_singleton_class(obj);
var singleton_methods = Opal.own_instance_methods(mod);
var instance_methods = Opal.own_instance_methods(mod.$$super);
return singleton_methods.concat(instance_methods);
};
// 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 = Opal.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;
};
// 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 = Opal.ancestors(module),
i, length = ancestors.length;
for (i = length - 2; i >= 0; i--) {
var ancestor = ancestors[i];
if ($has_own.call(ancestor.$$cvars, name)) {
ancestor.$$cvars[name] = value;
return value;
}
}
module.$$cvars[name] = value;
return value;
};
// 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.call(module.$$cvars, name))
return module.$$cvars[name];
var ancestors = Opal.ancestors(module),
i, length = ancestors.length;
for (i = 0; i < length; i++) {
var ancestor = ancestors[i];
if ($has_own.call(ancestor.$$cvars, name)) {
return ancestor.$$cvars[name];
}
}
if (!tolerant)
throw Opal.NameError.$new('uninitialized class variable '+name+' in '+module.$name());
return nil;
};
function isRoot(proto) {
return proto.hasOwnProperty('$$iclass') && proto.hasOwnProperty('$$root');
}
function own_included_modules(module) {
var result = [], mod, proto = Object.getPrototypeOf(module.$$prototype);
while (proto) {
if (proto.hasOwnProperty('$$class')) {
// superclass
break;
}
mod = protoToModule(proto);
if (mod) {
result.push(mod);
}
proto = Object.getPrototypeOf(proto);
}
return result;
}
function own_prepended_modules(module) {
var result = [], mod, proto = Object.getPrototypeOf(module.$$prototype);
if (module.$$prototype.hasOwnProperty('$$dummy')) {
while (proto) {
if (proto === module.$$prototype.$$define_methods_on) {
break;
}
mod = protoToModule(proto);
if (mod) {
result.push(mod);
}
proto = Object.getPrototypeOf(proto);
}
}
return result;
}
// 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 = Opal.ancestors(module);
var iclasses = [];
if (module_ancestors.indexOf(includer) !== -1) {
throw Opal.ArgumentError.$new('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 = Opal.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++;
};
Opal.prepend_features = function(module, prepender) {
// Here we change the ancestors chain from
//
// prepender
// |
// parent
//
// to:
//
// dummy(prepender)
// |
// iclass(module)
// |
// iclass(prepender)
// |
// parent
var module_ancestors = Opal.ancestors(module);
var iclasses = [];
if (module_ancestors.indexOf(prepender) !== -1) {
throw Opal.ArgumentError.$new('cyclic prepend detected');
}
for (var i = 0, length = module_ancestors.length; i < length; i++) {
var ancestor = module_ancestors[i], iclass = create_iclass(ancestor);
$prop(iclass, '$$prepended', true);
iclasses.push(iclass);
}
var chain = chain_iclasses(iclasses),
dummy_prepender = prepender.$$prototype,
previous_parent = Object.getPrototypeOf(dummy_prepender),
prepender_iclass,
start_chain_after,
end_chain_on;
if (dummy_prepender.hasOwnProperty('$$dummy')) {
// 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;
} else {
// Making the module "dummy"
prepender_iclass = create_dummy_iclass(prepender);
flush_methods_in(prepender);
$prop(dummy_prepender, '$$dummy', true);
$prop(dummy_prepender, '$$define_methods_on', prepender_iclass);
// Converting
// dummy(prepender) -> previous_parent
// to
// dummy(prepender) -> iclass(prepender) -> previous_parent
$set_proto(dummy_prepender, prepender_iclass);
$set_proto(prepender_iclass, previous_parent);
}
var prepender_ancestors = Opal.ancestors(prepender);
if (prepender_ancestors.indexOf(module) === -1) {
// first time prepend
start_chain_after = dummy_prepender;
// next $$root or prepender_iclass or non-$$iclass
end_chain_on = Object.getPrototypeOf(dummy_prepender);
while (end_chain_on != null) {
if (
end_chain_on.hasOwnProperty('$$root') ||
end_chain_on === prepender_iclass ||
!end_chain_on.hasOwnProperty('$$iclass')
) {
break;
}
end_chain_on = Object.getPrototypeOf(end_chain_on);
}
} else {
throw Opal.RuntimeError.$new("Prepending a module multiple times is not supported");
}
$set_proto(start_chain_after, chain.first);
$set_proto(chain.last, end_chain_on);
// recalculate own_prepended_modules cache
prepender.$$own_prepended_modules = own_prepended_modules(prepender);
Opal.const_cache_version++;
};
function flush_methods_in(module) {
var proto = module.$$prototype,
props = Object.getOwnPropertyNames(proto);
for (var i = 0; i < props.length; i++) {
var prop = props[i];
if (Opal.is_method(prop)) {
delete proto[prop];
}
}
}
function create_iclass(module) {
var iclass = create_dummy_iclass(module);
if (module.$$is_module) {
module.$$iclasses.push(iclass);
}
return iclass;
}
// 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;
}
function chain_iclasses(iclasses) {
var length = iclasses.length, first = iclasses[0];
$prop(first, '$$root', true);
if (length === 1) {
return { first: first, last: first };
}
var previous = first;
for (var i = 1; i < length; i++) {
var current = iclasses[i];
$set_proto(previous, current);
previous = current;
}
return { first: iclasses[0], last: iclasses[length - 1] };
}
// 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')) {
throw Opal.ArgumentError.$new("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 protoToModule(proto) {
if (proto.hasOwnProperty('$$dummy')) {
return;
} else if (proto.hasOwnProperty('$$iclass')) {
return proto.$$module;
} else if (proto.hasOwnProperty('$$class')) {
return proto.$$class;
}
}
function own_ancestors(module) {
return module.$$own_prepended_modules.concat([module]).concat(module.$$own_included_modules);
}
// The Array of ancestors for a given module/class
Opal.ancestors = function(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 = Opal.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;
};
Opal.included_modules = function(module) {
var result = [], mod = null, proto = Object.getPrototypeOf(module.$$prototype);
for (; proto && Object.getPrototypeOf(proto); proto = Object.getPrototypeOf(proto)) {
mod = protoToModule(proto);
if (mod && mod.$$is_module && proto.$$iclass && proto.$$included) {
result.push(mod);
}
}
return result;
};
// Method Missing
// --------------
// 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 = '$'+stubs[i], existing_method = proto[stub];
if (existing_method == null || existing_method.$$stub) {
Opal.add_stub_for(proto, 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));
};
// 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)
delete method_missing_stub.$$p;
// 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
// -------
// 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;
throw Opal.ArgumentError.$new('[' + inspect + '] wrong number of arguments (given ' + actual + ', expected ' + expected + ')');
};
// 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 + "'";
throw Opal.ArgumentError.$new(inspect + ': wrong number of arguments (given ' + actual + ', expected ' + expected + ')');
};
// Super dispatcher
Opal.find_super = function(obj, mid, current_func, defcheck, allow_stubs) {
var jsid = '$' + mid, ancestors, super_method;
if (obj.hasOwnProperty('$$meta')) {
ancestors = Opal.ancestors(obj.$$meta);
} else {
ancestors = Opal.ancestors(obj.$$class);
}
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) {
//