(function(undefined) { // @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 global_object = this, console; // Detect the global object if (typeof(global) !== 'undefined') { global_object = global; } 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(this.Opal) !== 'undefined') { console.warn('Opal already loaded. Loading twice can cause troubles, please fix your setup.'); return this.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; // Constructor for instances of BasicObject function BasicObject_alloc(){} // Constructor for instances of Object function Object_alloc(){} // Constructor for instances of Class function Class_alloc(){} // Constructor for instances of Module function Module_alloc(){} // Constructor for instances of NilClass (nil) function NilClass_alloc(){} // The Opal object that is exposed globally var Opal = this.Opal = {}; // All bridged classes - keep track to donate methods from Object var BridgedClasses = {}; // 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 fearures Opal.config = { missing_require_severity: 'error', // error, warning, ignore unsupported_features_severity: 'warning', // error, warning, ignore enable_stack_trace: true // true, false } // Minify common function calls var $hasOwn = Object.hasOwnProperty; var $slice = Opal.slice = Array.prototype.slice; // 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; return obj.$$id || (obj.$$id = Opal.uid()); }; // Globals table Opal.gvars = {}; // Exit function, this should be replaced by platform specific implementation // (See nodejs and phantom for examples) Opal.exit = function(status) { if (Opal.gvars.DEBUG) console.log('Exited with status '+status); }; // keeps track of exceptions for $! Opal.exceptions = []; // @private // Pops an exception from the stack and updates `$!`. Opal.pop_exception = function() { Opal.gvars["!"] = Opal.exceptions.pop() || nil; } // Inspect any kind of object, including non Ruby ones Opal.inspect = function(obj) { if (obj === undefined) { return "undefined"; } else if (obj === null) { return "null"; } else if (!obj.$$class) { return obj.toString(); } else { return obj.$inspect(); } } // 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 cosntant in the scope of the current cref function const_get_name(cref, name) { if (cref) return cref.$$const[name]; } // Walk up the nesting array looking for the constant function const_lookup_nesting(nesting, name) { var i, ii, result, 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; } } // Walk up the ancestors chain looking for the constant function const_lookup_ancestors(cref, name) { var i, ii, result, ancestors; if (cref == null) return; ancestors = Opal.ancestors(cref); for (i = 0, ii = ancestors.length; i < ii; i++) { if (ancestors[i].$$const && $hasOwn.call(ancestors[i].$$const, name)) { return ancestors[i].$$const[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_a_module) { 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 (cref == null) return; if (cref === '::') cref = _Object; if (!cref.$$is_a_module) { throw new Opal.TypeError(cref.toString() + " is not a class/module"); } if (cref.$$const_cache == null) { cache = cref.$$const_cache = Object.create(null); } else { 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 (nesting.$$const_cache == null) { cache = nesting.$$const_cache = Object.create(null); } else { 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. Opal.const_set = function(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; Opal.const_cache_version++; // Expose top level constants onto the Opal object if (cref === _Object) Opal[name] = value; return value; }; // 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], module_constants, 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]; // Don not show Objects constants unless we're querying Object itself if (cref !== _Object && module == _Object) break; for (constant in module.$$const) { 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 != null && cref.$$autoload[name] != null) { delete cref.$$autoload[name]; return nil; } throw Opal.NameError.$new("constant "+cref+"::"+cref.$name()+" not defined"); }; // 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 `base` is the current `self` value where the class is being created // from. We use this to get the scope for where the class should be created. // If `base` is an object (not a class/module), we simple get its class and // use that as the base instead. // // @param base [Object] where the class is being created // @param superclass [Class,null] superclass of the new class (may be null) // @param id [String] the name of the class to be created // @param constructor [JS.Function] function to use as constructor // // @return new [Class] or existing ruby class // Opal.klass = function(base, superclass, name, constructor) { var klass, bridged, alloc; if (base == null) { base = _Object; } // If base is an object, use its class if (!base.$$is_class && !base.$$is_module) { base = base.$$class; } // If the superclass is a function then we're bridging a native JS class if (typeof(superclass) === 'function') { bridged = superclass; superclass = _Object; } // Try to find the class in the current scope klass = const_get_name(base, 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"); } // Make sure existing class has same superclass if (superclass && klass.$$super !== superclass) { throw Opal.TypeError.$new("superclass mismatch for class " + name); } return klass; } // Class doesnt 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; } // If bridged the JS class will also be the alloc function alloc = bridged || Opal.boot_class_alloc(name, constructor, superclass); // Create the class object (instance of Class) klass = Opal.setup_class_object(name, alloc, superclass.$$name, superclass.constructor); // @property $$super the superclass, doesn't get changed by module inclusions klass.$$super = superclass; // @property $$parent direct parent class // starts with the superclass, after klass inclusion is // the last included klass klass.$$parent = superclass; Opal.const_set(base, name, klass); // Name new class directly onto current scope (Opal.Foo.Baz = klass) base[name] = klass; if (bridged) { Opal.bridge(klass, alloc); } else { // Call .inherited() hook with new class on the superclass if (superclass.$inherited) { superclass.$inherited(klass); } } return klass; }; // Boot a base class (makes instances). // // @param name [String,null] the class name // @param constructor [JS.Function] the class' instances constructor/alloc function // @param superclass [Class,null] the superclass object // @return [JS.Function] the consturctor holding the prototype for the class' instances Opal.boot_class_alloc = function(name, constructor, superclass) { if (superclass) { var alloc_proxy = function() {}; alloc_proxy.prototype = superclass.$$proto || superclass.prototype; constructor.prototype = new alloc_proxy(); } if (name) { constructor.displayName = name+'_alloc'; } constructor.prototype.constructor = constructor; return constructor; }; Opal.setup_module_or_class = function(module) { // @property $$id Each class/module is assigned a unique `id` that helps // comparation and implementation of `#object_id` module.$$id = Opal.uid(); // @property $$is_a_module Will be true for Module and its subclasses // instances (namely: Class). module.$$is_a_module = true; // @property $$inc included modules module.$$inc = []; // initialize the name with nil module.$$name = nil; // Initialize the constants table module.$$const = Object.create(null); // @property $$cvars class variables defined in the current module module.$$cvars = Object.create(null); } // Adds common/required properties to class object (as in `Class.new`) // // @param name [String,null] The name of the class // // @param alloc [JS.Function] The constructor of the class' instances // // @param superclass_name [String,null] // The name of the super class, this is // usefule to build the `.displayName` of the singleton class // // @param superclass_alloc [JS.Function] // The constructor of the superclass from which the singleton_class is // derived. // // @return [Class] Opal.setup_class_object = function(name, alloc, superclass_name, superclass_alloc) { // Grab the superclass prototype and use it to build an intermediary object // in the prototype chain. var superclass_alloc_proxy = function() {}; superclass_alloc_proxy.prototype = superclass_alloc.prototype; superclass_alloc_proxy.displayName = superclass_name; var singleton_class_alloc = function() {} singleton_class_alloc.prototype = new superclass_alloc_proxy(); // The built class is the only instance of its singleton_class var klass = new singleton_class_alloc(); Opal.setup_module_or_class(klass); // @property $$alloc This is the constructor of instances of the current // class. Its prototype will be used for method lookup klass.$$alloc = alloc; klass.$$name = name || nil; // Set a displayName for the singleton_class singleton_class_alloc.displayName = "#"))+">"; // @property $$proto This is the prototype on which methods will be defined klass.$$proto = alloc.prototype; // @property $$proto.$$class Make available to instances a reference to the // class they belong to. klass.$$proto.$$class = klass; // @property constructor keeps a ref to the constructor, but apparently the // constructor is already set on: // // `var klass = new constructor` is called. // // Maybe there are some browsers not abiding (IE6?) klass.constructor = singleton_class_alloc; // @property $$is_class Clearly mark this as a class klass.$$is_class = true; // @property $$class Classes are instances of the class Class klass.$$class = Class; return klass; }; // Define new module (or return existing module). The given `base` is basically // the current `self` value the `module` statement was defined in. If this is // a ruby module or class, then it is used, otherwise if the base is a ruby // object then that objects real ruby class is used (e.g. if the base is the // main object, then the top level `Object` class is used as the base). // // If a module of the given name is already defined in the base, then that // instance is just returned. // // If there is a class of the given name in the base, then an error is // generated instead (cannot have a class and module of same name in same base). // // Otherwise, a new module is created in the base with the given name, and that // new instance is returned back (to be referenced at runtime). // // @param base [Module, Class] class or module this definition is inside // @param id [String] the name of the new (or existing) module // // @return [Module] Opal.module = function(base, name) { var module; if (base == null) { base = _Object; } if (!base.$$is_class && !base.$$is_module) { base = base.$$class; } module = const_get_name(base, name); if (module == null && base === _Object) module = const_lookup_ancestors(_Object, name); if (module) { if (!module.$$is_module && module !== _Object) { throw Opal.TypeError.$new(name + " is not a module"); } } else { module = Opal.module_allocate(Module); Opal.const_set(base, name, module); } return module; }; // The implementation for Module#initialize // @param module [Module] // @param block [Proc,nil] // @return nil Opal.module_initialize = function(module, block) { if (block !== nil) { var block_self = block.$$s; block.$$s = null; block.call(module); block.$$s = block_self; } return nil; }; // Internal function to create a new module instance. This simply sets up // the prototype hierarchy and method tables. // Opal.module_allocate = function(superclass) { var mtor = function() {}; mtor.prototype = superclass.$$alloc.prototype; var module_constructor = function() {}; module_constructor.prototype = new mtor(); var module = new module_constructor(); var module_prototype = {}; Opal.setup_module_or_class(module); // initialize dependency tracking module.$$included_in = []; // Set the display name of the singleton prototype holder module_constructor.displayName = "#>" // @property $$proto This is the prototype on which methods will be defined module.$$proto = module_prototype; // @property constructor // keeps a ref to the constructor, but apparently the // constructor is already set on: // // `var module = new constructor` is called. // // Maybe there are some browsers not abiding (IE6?) module.constructor = module_constructor; // @property $$is_module Clearly mark this as a module module.$$is_module = true; module.$$class = Module; // @property $$super // the superclass, doesn't get changed by module inclusions module.$$super = superclass; // @property $$parent // direct parent class or module // starts with the superclass, after module inclusion is // the last included module module.$$parent = superclass; 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.$$is_class || object.$$is_module) { return Opal.build_class_singleton_class(object); } 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(object) { var alloc, superclass, klass; if (object.$$meta) { return object.$$meta; } // The constructor and prototype of the singleton_class instances is the // current class constructor and prototype. alloc = object.constructor; // 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 = object === BasicObject ? Class : Opal.build_class_singleton_class(object.$$super); klass = Opal.setup_class_object(null, alloc, superclass.$$name, superclass.constructor); klass.$$super = superclass; klass.$$parent = superclass; klass.$$is_singleton = true; klass.$$singleton_of = object; return object.$$meta = klass; }; // 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, name = "#>"; var alloc = Opal.boot_class_alloc(name, function(){}, superclass) var klass = Opal.setup_class_object(name, alloc, superclass.$$name, superclass.constructor); klass.$$super = superclass; klass.$$parent = superclass; klass.$$class = superclass.$$class; klass.$$proto = object; klass.$$is_singleton = true; klass.$$singleton_of = object; return object.$$meta = klass; }; // 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 ($hasOwn.call(ancestor.$$cvars, name)) { ancestor.$$cvars[name] = value; return value; } } module.$$cvars[name] = value; return value; } // Bridges a single method. // // @param target [JS::Function] the constructor of the bridged class // @param from [Module] the module/class we are importing the method from // @param name [String] the method name in JS land (i.e. starting with $) // @param body [JS::Function] the body of the method Opal.bridge_method = function(target_constructor, from, name, body) { var ancestors, i, ancestor, length; ancestors = target_constructor.$$bridge.$ancestors(); // order important here, we have to check for method presence in // ancestors from the bridged class to the last ancestor for (i = 0, length = ancestors.length; i < length; i++) { ancestor = ancestors[i]; if ($hasOwn.call(ancestor.$$proto, name) && ancestor.$$proto[name] && !ancestor.$$proto[name].$$donated && !ancestor.$$proto[name].$$stub && ancestor !== from) { break; } if (ancestor === from) { target_constructor.prototype[name] = body break; } } }; // Bridges from *donator* to a *target*. // // @param target [Module] the potentially associated with bridged classes module // @param donator [Module] the module/class source of the methods that should be bridged Opal.bridge_methods = function(target, donator) { var i, bridged = BridgedClasses[target.$__id__()], donator_id = donator.$__id__(); if (bridged) { BridgedClasses[donator_id] = bridged.slice(); for (i = bridged.length - 1; i >= 0; i--) { Opal_bridge_methods_to_constructor(bridged[i], donator) } } }; // Actually bridge methods to the bridged (shared) prototype. function Opal_bridge_methods_to_constructor(target_constructor, donator) { var i, method, methods = donator.$instance_methods(); for (i = methods.length - 1; i >= 0; i--) { method = '$' + methods[i]; Opal.bridge_method(target_constructor, donator, method, donator.$$proto[method]); } } // Associate the target as a bridged class for the current "donator" function Opal_add_bridged_constructor(target_constructor, donator) { var donator_id = donator.$__id__(); if (!BridgedClasses[donator_id]) { BridgedClasses[donator_id] = []; } BridgedClasses[donator_id].push(target_constructor); } // Walks the dependency tree detecting the presence of the base among its // own dependencies. // // @param [Integer] base_id The id of the base module (eg. the "includer") // @param [Array] deps The array of dependencies (eg. the included module, included.$$deps) // @param [String] prop The property that holds dependencies (eg. "$$deps") // @param [JS::Object] seen A JS object holding the cache of already visited objects // @return [Boolean] true if a cyclic dependency is present Opal.has_cyclic_dep = function has_cyclic_dep(base_id, deps, prop, seen) { var i, dep_id, dep; for (i = deps.length - 1; i >= 0; i--) { dep = deps[i]; dep_id = dep.$$id; if (seen[dep_id]) { continue; } seen[dep_id] = true; if (dep_id === base_id) { return true; } if (has_cyclic_dep(base_id, dep[prop], prop, seen)) { return true; } } return false; } // 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 iclass, donator, prototype, methods, id, i; // check if this module is already included in the class for (i = includer.$$inc.length - 1; i >= 0; i--) { if (includer.$$inc[i] === module) { return; } } // Check that the base module is not also a dependency, classes can't be // dependencies so we have a special case for them. if (!includer.$$is_class && Opal.has_cyclic_dep(includer.$$id, [module], '$$inc', {})) { throw Opal.ArgumentError.$new('cyclic include detected') } Opal.const_cache_version++; includer.$$inc.push(module); module.$$included_in.push(includer); Opal.bridge_methods(includer, module); // iclass iclass = { $$name: module.$$name, $$proto: module.$$proto, $$parent: includer.$$parent, $$module: module, $$iclass: true }; includer.$$parent = iclass; methods = module.$instance_methods(); for (i = methods.length - 1; i >= 0; i--) { Opal.update_includer(module, includer, '$' + methods[i]) } }; // Table that holds all methods that have been defined on all objects // It is used for defining method stubs for new coming native classes Opal.stubs = {}; // For performance, some core Ruby classes are toll-free bridged to their // native JavaScript counterparts (e.g. a Ruby Array is a JavaScript Array). // // This method is used to setup a native constructor (e.g. Array), to have // its prototype act like a normal Ruby class. Firstly, a new Ruby class is // created using the native constructor so that its prototype is set as the // target for th new class. Note: all bridged classes are set to inherit // from Object. // // 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(klass, constructor) { if (constructor.$$bridge) { throw Opal.ArgumentError.$new("already bridged"); } Opal.stub_subscribers.push(constructor.prototype); // Populate constructor with previously stored stubs for (var method_name in Opal.stubs) { if (!(method_name in constructor.prototype)) { constructor.prototype[method_name] = Opal.stub_for(method_name); } } constructor.prototype.$$class = klass; constructor.$$bridge = klass; var ancestors = klass.$ancestors(); // order important here, we have to bridge from the last ancestor to the // bridged class for (var i = ancestors.length - 1; i >= 0; i--) { Opal_add_bridged_constructor(constructor, ancestors[i]); Opal_bridge_methods_to_constructor(constructor, ancestors[i]); } for (var name in BasicObject_alloc.prototype) { var method = BasicObject_alloc.prototype[method]; if (method && method.$$stub && !(name in constructor.prototype)) { constructor.prototype[name] = method; } } return klass; }; // Update `jsid` method cache of all classes / modules including `module`. Opal.update_includer = function(module, includer, jsid) { var dest, current, body, klass_includees, j, jj, current_owner_index, module_index; body = module.$$proto[jsid]; dest = includer.$$proto; current = dest[jsid]; if (dest.hasOwnProperty(jsid) && !current.$$donated && !current.$$stub) { // target class has already defined the same method name - do nothing } else if (dest.hasOwnProperty(jsid) && !current.$$stub) { // target class includes another module that has defined this method klass_includees = includer.$$inc; for (j = 0, jj = klass_includees.length; j < jj; j++) { if (klass_includees[j] === current.$$donated) { current_owner_index = j; } if (klass_includees[j] === module) { module_index = j; } } // only redefine method on class if the module was included AFTER // the module which defined the current method body. Also make sure // a module can overwrite a method it defined before if (current_owner_index <= module_index) { dest[jsid] = body; dest[jsid].$$donated = module; } } else { // neither a class, or module included by class, has defined method dest[jsid] = body; dest[jsid].$$donated = module; } // if the includer is a module, recursively update all of its includres. if (includer.$$included_in) { Opal.update_includers(includer, jsid); } }; // Update `jsid` method cache of all classes / modules including `module`. Opal.update_includers = function(module, jsid) { var i, ii, includee, included_in; included_in = module.$$included_in; if (!included_in) { return; } for (i = 0, ii = included_in.length; i < ii; i++) { includee = included_in[i]; Opal.update_includer(module, includee, jsid); } }; // The Array of ancestors for a given module/class Opal.ancestors = function(module_or_class) { var parent = module_or_class, result = [], modules, i, ii, j, jj; while (parent) { result.push(parent); for (i = parent.$$inc.length-1; i >= 0; i--) { modules = Opal.ancestors(parent.$$inc[i]); for(j = 0, jj = modules.length; j < jj; j++) { result.push(modules[j]); } } // only the actual singleton class gets included in its ancestry // after that, traverse the normal class hierarchy if (parent.$$is_singleton && parent.$$singleton_of.$$is_module) { parent = parent.$$singleton_of.$$super; } else { parent = parent.$$is_class ? parent.$$super : null; } } 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 subscriber, subscribers = Opal.stub_subscribers, i, ilength = stubs.length, j, jlength = subscribers.length, method_name, stub, opal_stubs = Opal.stubs; for (i = 0; i < ilength; i++) { method_name = stubs[i]; if(!opal_stubs.hasOwnProperty(method_name)) { // Save method name to populate other subscribers with this stub opal_stubs[method_name] = true; stub = Opal.stub_for(method_name); for (j = 0; j < jlength; j++) { subscriber = subscribers[j]; if (!(method_name in subscriber)) { subscriber[method_name] = stub; } } } } }; // Keep a list of prototypes that want method_missing stubs to be added. // // @default [Prototype List] BasicObject_alloc.prototype // Opal.stub_subscribers = [BasicObject_alloc.prototype]; // 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) { var method_missing_stub = Opal.stub_for(stub); prototype[stub] = method_missing_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) 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 // ------- // 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_class || object.$$is_module) { inspect += object.$$name + '.'; } else { inspect += object.$$class.$$name + '#'; } inspect += meth; throw Opal.ArgumentError.$new('[' + inspect + '] wrong number of arguments(' + actual + ' for ' + 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 (' + actual + ' for ' + expected + ')'); }; // Super dispatcher Opal.find_super_dispatcher = function(obj, mid, current_func, defcheck, defs) { var dispatcher, super_method; if (defs) { if (obj.$$is_class || obj.$$is_module) { dispatcher = defs.$$super; } else { dispatcher = obj.$$class.$$proto; } } else { dispatcher = Opal.find_obj_super_dispatcher(obj, mid, current_func); } super_method = dispatcher['$' + mid]; if (!defcheck && super_method.$$stub && Opal.Kernel.$method_missing === obj.$method_missing) { // method_missing hasn't been explicitly defined throw Opal.NoMethodError.$new('super: no superclass method `'+mid+"' for "+obj, mid); } return super_method; }; // Iter dispatcher for super in a block Opal.find_iter_super_dispatcher = function(obj, jsid, current_func, defcheck, implicit) { var call_jsid = jsid; if (!current_func) { throw Opal.RuntimeError.$new("super called outside of method"); } if (implicit && current_func.$$define_meth) { throw Opal.RuntimeError.$new("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_dispatcher(obj, call_jsid, current_func, defcheck); }; Opal.find_obj_super_dispatcher = function(obj, mid, current_func) { var klass = obj.$$meta || obj.$$class; // first we need to find the class/module current_func is located on klass = Opal.find_owning_class(klass, current_func); if (!klass) { throw new Error("could not find current class for super()"); } return Opal.find_super_func(klass, '$' + mid, current_func); }; Opal.find_owning_class = function(klass, current_func) { var owner = current_func.$$owner; while (klass) { // repeating for readability if (klass.$$iclass && klass.$$module === current_func.$$donated) { // this klass was the last one the module donated to // case is also hit with multiple module includes break; } else if (klass.$$iclass && klass.$$module === owner) { // module has donated to other classes but klass isn't one of those break; } else if (owner.$$is_singleton && klass === owner.$$singleton_of.$$class) { // cases like stdlib `Singleton::included` that use a singleton of a singleton break; } else if (klass === owner) { // no modules, pure class inheritance break; } klass = klass.$$parent; } return klass; }; Opal.find_super_func = function(owning_klass, jsid, current_func) { var klass = owning_klass.$$parent; // now we can find the super while (klass) { var working = klass.$$proto[jsid]; if (working && working !== current_func) { // ok break; } klass = klass.$$parent; } return klass.$$proto; }; // Used to return as an expression. Sometimes, we can't simply return from // a javascript function as if we were a method, as the return is used as // an expression, or even inside a block which must "return" to the outer // method. This helper simply throws an error which is then caught by the // method. This approach is expensive, so it is only used when absolutely // needed. // Opal.ret = function(val) { Opal.returner.$v = val; throw Opal.returner; }; // Used to break out of a block. Opal.brk = function(val, breaker) { breaker.$v = val; throw breaker; }; // Builds a new unique breaker, this is to avoid multiple nested breaks to get // in the way of each other. Opal.new_brk = function() { return new Error('unexpected break'); }; // handles yield calls for 1 yielded arg Opal.yield1 = function(block, arg) { if (typeof(block) !== "function") { throw Opal.LocalJumpError.$new("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); } }; // handles yield for > 1 yielded arg Opal.yieldX = function(block, args) { if (typeof(block) !== "function") { throw Opal.LocalJumpError.$new("no block given"); } if (block.length > 1 && args.length === 1) { if (args[0].$$is_array) { return block.apply(null, args[0]); } } if (!args.$$is_array) { 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); }; // 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) { return candidate; } else if (candidate['$==='](exception)) { return candidate; } } return null; }; Opal.is_a = function(object, klass) { if (object.$$meta === klass || object.$$class === klass) { return true; } if (object.$$is_number && klass.$$is_number_class) { return true; } var i, length, ancestors = Opal.ancestors(object.$$is_class ? Opal.get_singleton_class(object) : (object.$$meta || object.$$class)); for (i = 0, length = ancestors.length; i < length; i++) { if (ancestors[i] === klass) { return true; } } return false; }; // 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 { throw Opal.TypeError.$new("Can't convert " + value.$$class + " to Hash (" + value.$$class + "#to_hash gives " + hash.$$class + ")"); } } else { throw Opal.TypeError.$new("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 // 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 { throw Opal.TypeError.$new("Can't convert " + value.$$class + " to Array (" + value.$$class + "#to_ary gives " + ary.$$class + ")"); } } else { return [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 { throw Opal.TypeError.$new("Can't convert " + value.$$class + " to Array (" + value.$$class + "#to_a gives " + ary.$$class + ")"); } } else { return [value]; } }; // Used for extracting keyword arguments from arguments passed to // JS function. 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 && kwargs['$respond_to?']('to_hash', true)) { Array.prototype.splice.call(parameters, parameters.length - 1, 1); return kwargs.$to_hash(); } else { return Opal.hash2([], {}); } } // 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 = null, given_map = given_args.$$smap; for (key in given_map) { if (!used_args[key]) { keys.push(key); map[key] = given_map[key]; } } return Opal.hash2(keys, map); }; // 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 // @return [Object] returning value of the method call Opal.send = function(recv, method, args, block) { var body = (typeof(method) === 'string') ? recv['$'+method] : method; if (body != null) { body.$$p = block; return body.apply(recv, args); } return recv.$method_missing.apply(recv, [method].concat(args)); } // 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 // @return [null] // Opal.def = function(obj, jsid, body) { // if instance_eval is invoked on a module/class, it sets inst_eval_mod if (!obj.$$eval && (obj.$$is_class || obj.$$is_module)) { Opal.defn(obj, jsid, body); } else { Opal.defs(obj, jsid, body); } }; // Define method on a module or class (see Opal.def). Opal.defn = function(obj, jsid, body) { obj.$$proto[jsid] = body; // for super dispatcher, etc. body.$$owner = obj; // is it a module? if (obj.$$is_module) { Opal.update_includers(obj, jsid); if (obj.$$module_function) { Opal.defs(obj, jsid, body); } } // is it a bridged class? var bridged = obj.$__id__ && !obj.$__id__.$$stub && BridgedClasses[obj.$__id__()]; if (bridged) { for (var i = bridged.length - 1; i >= 0; i--) { Opal.bridge_method(bridged[i], obj, jsid, body); } } // method_added/singleton_method_added hooks var singleton_of = obj.$$singleton_of; if (obj.$method_added && !obj.$method_added.$$stub && !singleton_of) { obj.$method_added(jsid.substr(1)); } else if (singleton_of && singleton_of.$singleton_method_added && !singleton_of.$singleton_method_added.$$stub) { singleton_of.$singleton_method_added(jsid.substr(1)); } return nil; }; // Define a singleton method on the given object (see Opal.def). Opal.defs = function(obj, jsid, body) { Opal.defn(Opal.get_singleton_class(obj), jsid, body) }; // Called from #remove_method. Opal.rdef = function(obj, jsid) { // TODO: remove from BridgedClasses as well if (!$hasOwn.call(obj.$$proto, jsid)) { throw Opal.NameError.$new("method '" + jsid.substr(1) + "' not defined in " + obj.$name()); } delete obj.$$proto[jsid]; if (obj.$$is_singleton) { if (obj.$$proto.$singleton_method_removed && !obj.$$proto.$singleton_method_removed.$$stub) { obj.$$proto.$singleton_method_removed(jsid.substr(1)); } } else { if (obj.$method_removed && !obj.$method_removed.$$stub) { obj.$method_removed(jsid.substr(1)); } } }; // Called from #undef_method. Opal.udef = function(obj, jsid) { if (!obj.$$proto[jsid] || obj.$$proto[jsid].$$stub) { throw Opal.NameError.$new("method '" + jsid.substr(1) + "' not defined in " + obj.$name()); } Opal.add_stub_for(obj.$$proto, jsid); if (obj.$$is_singleton) { if (obj.$$proto.$singleton_method_undefined && !obj.$$proto.$singleton_method_undefined.$$stub) { obj.$$proto.$singleton_method_undefined(jsid.substr(1)); } } else { if (obj.$method_undefined && !obj.$method_undefined.$$stub) { obj.$method_undefined(jsid.substr(1)); } } }; Opal.alias = function(obj, name, old) { var id = '$' + name, old_id = '$' + old, body = obj.$$proto['$' + old], alias; // When running inside #instance_eval the alias refers to class methods. if (obj.$$eval) { return Opal.alias(Opal.get_singleton_class(obj), name, old); } if (typeof(body) !== "function" || body.$$stub) { var ancestor = obj.$$super; while (typeof(body) !== "function" && ancestor) { body = ancestor[old_id]; ancestor = ancestor.$$super; } if (typeof(body) !== "function" || body.$$stub) { throw Opal.NameError.$new("undefined method `" + old + "' for class `" + obj.$name() + "'") } } // 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; // We need a wrapper because otherwise method $$owner and other properties // would be ovrewritten 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]; } if (block != null) { alias.$$p = null } return Opal.send(this, body, args, block); }; // Try to make the browser pick the right name alias.displayName = name; alias.length = body.length; alias.$$arity = body.$$arity; alias.$$parameters = body.$$parameters; alias.$$source_location = body.$$source_location; alias.$$alias_of = body; alias.$$alias_name = name; Opal.defn(obj, id, alias); return obj; }; Opal.alias_native = function(obj, name, native_name) { var id = '$' + name, body = obj.$$proto[native_name]; if (typeof(body) !== "function" || body.$$stub) { throw Opal.NameError.$new("undefined native method `" + native_name + "' for class `" + obj.$name() + "'") } Opal.defn(obj, id, body); return obj; }; // Hashes // ------ Opal.hash_init = function(hash) { hash.$$smap = Object.create(null); hash.$$map = Object.create(null); hash.$$keys = []; }; Opal.hash_clone = function(from_hash, to_hash) { to_hash.$$none = from_hash.$$none; to_hash.$$proc = from_hash.$$proc; for (var i = 0, keys = from_hash.$$keys, smap = from_hash.$$smap, len = keys.length, key, value; i < len; i++) { key = keys[i]; if (key.$$is_string) { value = smap[key]; } else { value = key.value; key = key.key; } Opal.hash_put(to_hash, key, value); } }; Opal.hash_put = function(hash, key, value) { if (key.$$is_string) { if (!$hasOwn.call(hash.$$smap, key)) { hash.$$keys.push(key); } hash.$$smap[key] = value; return; } var key_hash, bucket, last_bucket; key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash(); if (!$hasOwn.call(hash.$$map, key_hash)) { bucket = {key: key, key_hash: key_hash, value: value}; hash.$$keys.push(bucket); hash.$$map[key_hash] = bucket; return; } bucket = hash.$$map[key_hash]; while (bucket) { if (key === bucket.key || key['$eql?'](bucket.key)) { last_bucket = undefined; bucket.value = value; break; } last_bucket = bucket; bucket = bucket.next; } if (last_bucket) { bucket = {key: key, key_hash: key_hash, value: value}; hash.$$keys.push(bucket); last_bucket.next = bucket; } }; Opal.hash_get = function(hash, key) { if (key.$$is_string) { if ($hasOwn.call(hash.$$smap, key)) { return hash.$$smap[key]; } return; } var key_hash, bucket; key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash(); if ($hasOwn.call(hash.$$map, key_hash)) { bucket = hash.$$map[key_hash]; while (bucket) { if (key === bucket.key || key['$eql?'](bucket.key)) { return bucket.value; } bucket = bucket.next; } } }; Opal.hash_delete = function(hash, key) { var i, keys = hash.$$keys, length = keys.length, value; if (key.$$is_string) { if (!$hasOwn.call(hash.$$smap, key)) { return; } for (i = 0; i < length; i++) { if (keys[i] === key) { keys.splice(i, 1); break; } } value = hash.$$smap[key]; delete hash.$$smap[key]; return value; } var key_hash = key.$hash(); if (!$hasOwn.call(hash.$$map, key_hash)) { return; } var bucket = hash.$$map[key_hash], last_bucket; while (bucket) { if (key === bucket.key || key['$eql?'](bucket.key)) { value = bucket.value; for (i = 0; i < length; i++) { if (keys[i] === bucket) { keys.splice(i, 1); break; } } if (last_bucket && bucket.next) { last_bucket.next = bucket.next; } else if (last_bucket) { delete last_bucket.next; } else if (bucket.next) { hash.$$map[key_hash] = bucket.next; } else { delete hash.$$map[key_hash]; } return value; } last_bucket = bucket; bucket = bucket.next; } }; Opal.hash_rehash = function(hash) { for (var i = 0, length = hash.$$keys.length, key_hash, bucket, last_bucket; i < length; i++) { if (hash.$$keys[i].$$is_string) { continue; } key_hash = hash.$$keys[i].key.$hash(); if (key_hash === hash.$$keys[i].key_hash) { continue; } bucket = hash.$$map[hash.$$keys[i].key_hash]; last_bucket = undefined; while (bucket) { if (bucket === hash.$$keys[i]) { if (last_bucket && bucket.next) { last_bucket.next = bucket.next; } else if (last_bucket) { delete last_bucket.next; } else if (bucket.next) { hash.$$map[hash.$$keys[i].key_hash] = bucket.next; } else { delete hash.$$map[hash.$$keys[i].key_hash]; } break; } last_bucket = bucket; bucket = bucket.next; } hash.$$keys[i].key_hash = key_hash; if (!$hasOwn.call(hash.$$map, key_hash)) { hash.$$map[key_hash] = hash.$$keys[i]; continue; } bucket = hash.$$map[key_hash]; last_bucket = undefined; while (bucket) { if (bucket === hash.$$keys[i]) { last_bucket = undefined; break; } last_bucket = bucket; bucket = bucket.next; } if (last_bucket) { last_bucket.next = hash.$$keys[i]; } } }; Opal.hash = function() { var arguments_length = arguments.length, args, hash, i, length, key, value; if (arguments_length === 1 && arguments[0].$$is_hash) { return arguments[0]; } hash = new Opal.Hash.$$alloc(); Opal.hash_init(hash); if (arguments_length === 1 && arguments[0].$$is_array) { args = arguments[0]; length = args.length; for (i = 0; i < length; i++) { if (args[i].length !== 2) { throw Opal.ArgumentError.$new("value not of length 2: " + args[i].$inspect()); } key = args[i][0]; value = args[i][1]; Opal.hash_put(hash, key, value); } return hash; } if (arguments_length === 1) { args = arguments[0]; for (key in args) { if ($hasOwn.call(args, key)) { value = args[key]; Opal.hash_put(hash, key, value); } } return hash; } if (arguments_length % 2 !== 0) { throw Opal.ArgumentError.$new("odd number of arguments for Hash"); } for (i = 0; i < arguments_length; i += 2) { key = arguments[i]; value = arguments[i + 1]; Opal.hash_put(hash, key, value); } return hash; }; // 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.$$alloc(); hash.$$smap = smap; hash.$$map = Object.create(null); hash.$$keys = keys; return hash; }; // Create a new range instance with first and last values, and whether the // range excludes the last value. // Opal.range = function(first, last, exc) { var range = new Opal.Range.$$alloc(); range.begin = first; range.end = last; range.exclude = exc; return range; }; // Get the ivar name for a given name. // Mostly adds a trailing $ to reserved names. // Opal.ivar = function(name) { if ( // properties name === "constructor" || name === "displayName" || name === "__count__" || name === "__noSuchMethod__" || name === "__parent__" || name === "__proto__" || // methods name === "hasOwnProperty" || name === "valueOf" ) { return name + "$"; } return name; }; // Regexps // ------- // 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'); } // Require system // -------------- Opal.modules = {}; Opal.loaded_features = ['corelib/runtime']; Opal.current_dir = '.' Opal.require_table = {'corelib/runtime': true}; Opal.normalize = function(path) { var parts, part, new_parts = [], SEPARATOR = '/'; if (Opal.current_dir !== '.') { path = Opal.current_dir.replace(/\/*$/, '/') + path; } path = path.replace(/\.(rb|opal|js)$/, ''); parts = path.split(SEPARATOR); for (var i = 0, ii = parts.length; i < ii; i++) { part = parts[i]; if (part === '') continue; (part === '..') ? new_parts.pop() : new_parts.push(part) } return new_parts.join(SEPARATOR); }; Opal.loaded = function(paths) { var i, l, path; for (i = 0, l = paths.length; i < l; i++) { path = Opal.normalize(paths[i]); if (Opal.require_table[path]) { return; } Opal.loaded_features.push(path); Opal.require_table[path] = true; } }; Opal.load = function(path) { path = Opal.normalize(path); Opal.loaded([path]); var module = Opal.modules[path]; if (module) { module(Opal); } else { var severity = Opal.config.missing_require_severity; var message = 'cannot load such file -- ' + path; if (severity === "error") { Opal.LoadError ? Opal.LoadError.$new(message) : function(){throw message}(); } else if (severity === "warning") { console.warn('WARNING: LoadError: ' + message); } } return true; }; Opal.require = function(path) { path = Opal.normalize(path); if (Opal.require_table[path]) { return false; } return Opal.load(path); }; // Initialization // -------------- // Constructors for *instances* of core objects Opal.boot_class_alloc('BasicObject', BasicObject_alloc); Opal.boot_class_alloc('Object', Object_alloc, BasicObject_alloc); Opal.boot_class_alloc('Module', Module_alloc, Object_alloc); Opal.boot_class_alloc('Class', Class_alloc, Module_alloc); // Constructors for *classes* of core objects Opal.BasicObject = BasicObject = Opal.setup_class_object('BasicObject', BasicObject_alloc, 'Class', Class_alloc); Opal.Object = _Object = Opal.setup_class_object('Object', Object_alloc, 'BasicObject', BasicObject.constructor); Opal.Module = Module = Opal.setup_class_object('Module', Module_alloc, 'Object', _Object.constructor); Opal.Class = Class = Opal.setup_class_object('Class', Class_alloc, 'Module', Module.constructor); // BasicObject can reach itself, avoid const_set to skip the $$base_module logic BasicObject.$$const["BasicObject"] = BasicObject; // Assign basic constants Opal.const_set(_Object, "BasicObject", BasicObject); Opal.const_set(_Object, "Object", _Object); Opal.const_set(_Object, "Module", Module); Opal.const_set(_Object, "Class", Class); // Fix booted classes to use their metaclass BasicObject.$$class = Class; _Object.$$class = Class; Module.$$class = Class; Class.$$class = Class; // Fix superclasses of booted classes BasicObject.$$super = null; _Object.$$super = BasicObject; Module.$$super = _Object; Class.$$super = Module; BasicObject.$$parent = null; _Object.$$parent = BasicObject; Module.$$parent = _Object; Class.$$parent = Module; // Forward .toString() to #to_s _Object.$$proto.toString = function() { return this.$to_s(); }; // Make Kernel#require immediately available as it's needed to require all the // other corelib files. _Object.$$proto.$require = Opal.require; // Instantiate the top object Opal.top = new _Object.$$alloc(); // Nil Opal.klass(_Object, _Object, 'NilClass', NilClass_alloc); nil = Opal.nil = new NilClass_alloc(); nil.$$id = nil_id; nil.call = nil.apply = function() { throw Opal.LocalJumpError.$new('no block given'); }; Opal.breaker = new Error('unexpected break (old)'); Opal.returner = new Error('unexpected return'); TypeError.$$super = Error; }).call(this);