(function(undefined) { 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 bridges = {}; // TopScope is used for inheriting constants from the top scope var TopScope = function(){}; // Opal just acts as the top scope TopScope.prototype = Opal; // To inherit scopes Opal.constructor = TopScope; // List top scope constants Opal.constants = []; // This is a useful reference to global object inside ruby files Opal.global = this; // Minify common function calls var $hasOwn = Opal.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; }; // Table holds all class variables Opal.cvars = {}; // 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 = []; // Get a constant on the given scope. Every class and module in Opal has a // scope used to store, and inherit, constants. For example, the top level // `Object` in ruby has a scope accessible as `Opal.Object.$$scope`. // // To get the `Array` class using this scope, you could use: // // Opal.Object.$$scope.get("Array") // // If a constant with the given name cannot be found, then a dispatch to the // class/module's `#const_method` is called, which by default will raise an // error. // // @param [String] name the name of the constant to lookup // @return [RubyObject] // Opal.get = function(name) { var constant = this[name]; if (constant == null) { return this.base.$const_get(name); } return constant; }; // Create a new constants scope for the given class with the given // base. Constants are looked up through their parents, so the base // scope will be the outer scope of the new klass. // Opal.create_scope = function(base, klass, id) { var const_alloc = function() {}; var const_scope = const_alloc.prototype = new base.constructor(); klass.$$scope = const_scope; klass.$$base_module = base.base; const_scope.base = klass; const_scope.constructor = const_alloc; const_scope.constants = []; if (id) { Opal.cdecl(base, id, klass); const_alloc.displayName = id+"_alloc"; } } // A `class Foo; end` expression in ruby is compiled to call this runtime // method which either returns an existing class of the given name, or creates // a new class in the given `base` scope. // // If a constant with the given name exists, then we check to make sure that // it is a class and also that the superclasses match. If either of these // fail, then we raise a `TypeError`. Note, superklass may be null if one was // not specified in the ruby code. // // We pass a constructor to this method of the form `function ClassName() {}` // simply so that classes show up with nicely formatted names inside debuggers // in the web browser (or node/sprockets). // // The `base` is the current `self` value where the class is being created // from. We use this to get the scope for where the class should be created. // If `base` is an object (not a class/module), we simple get its class and // use that as the base instead. // // @param [Object] base where the class is being created // @param [Class] superklass superclass of the new class (may be null) // @param [String] id the name of the class to be created // @param [Function] constructor function to use as constructor // @return [Class] new or existing ruby class // Opal.klass = function(base, superklass, id, constructor) { var klass, bridged, alloc; // 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(superklass) === 'function') { bridged = superklass; superklass = _Object; } // Try to find the class in the current scope klass = base.$$scope[id]; // If the class exists in the scope, then we must use that if (klass && klass.$$orig_scope === base.$$scope) { // Make sure the existing constant is a class, or raise error if (!klass.$$is_class) { throw Opal.TypeError.$new(id + " is not a class"); } // Make sure existing class has same superclass if (superklass && klass.$$super !== superklass) { throw Opal.TypeError.$new("superclass mismatch for class " + id); } 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 (superklass == null) { superklass = _Object; } // If bridged the JS class will also be the alloc function alloc = bridged || boot_class_alloc(id, constructor, superklass); // Create the class object (instance of Class) klass = boot_class_object(id, superklass, alloc); // Name the class klass.$$name = id; klass.displayName = id; // Mark the object as a class klass.$$is_class = true; // Every class gets its own constant scope, inherited from current scope Opal.create_scope(base.$$scope, klass, id); // Name new class directly onto current scope (Opal.Foo.Baz = klass) base[id] = base.$$scope[id] = klass; if (bridged) { Opal.bridge(klass, alloc); } else { // Copy all parent constants to child, unless parent is Object if (superklass !== _Object && superklass !== BasicObject) { donate_constants(superklass, klass); } // Call .inherited() hook with new class on the superclass if (superklass.$inherited) { superklass.$inherited(klass); } } return klass; }; // Create generic class with given superclass. Opal.boot_class = function(superklass, constructor) { var alloc = boot_class_alloc(null, constructor, superklass) return boot_class_object(null, superklass, alloc); } // The class object itself (as in `Class.new`) // // @param superklass [(Opal) Class] Another class object (as in `Class.new`) // @param alloc [constructor] The constructor that holds the prototype // that will be used for instances of the // newly constructed class. function boot_class_object(id, superklass, alloc) { // Grab the superclass prototype and use it to build an intermediary object // in the prototype chain. function Superclass_alloc_proxy() {}; Superclass_alloc_proxy.prototype = superklass.constructor.prototype; function SingletonClass_alloc() {} SingletonClass_alloc.prototype = new Superclass_alloc_proxy(); if (id) { SingletonClass_alloc.displayName = "SingletonClass_alloc("+id+")"; } // The built class is the only instance of its singleton_class var klass = new SingletonClass_alloc(); setup_module_or_class_object(klass, SingletonClass_alloc, superklass, alloc.prototype); // @property $$alloc This is the constructor of instances of the current // class. Its prototype will be used for method lookup klass.$$alloc = alloc; // @property $$proto.$$class Make available to instances a reference to the // class they belong to. klass.$$proto.$$class = klass; return klass; } // Adds common/required properties to a module or class object // (as in `Module.new` / `Class.new`) // // @param module The module or class that needs to be prepared // // @param constructor The constructor of the module or class itself, // usually it's already assigned by using `new`. Some // ipothesis on why it's needed can be found below. // // @param superklass The superclass of the class/module object, for modules // is `Module` (of `Module` in JS context) // // @param prototype The prototype on which the class/module methods will // be stored. // function setup_module_or_class_object(module, constructor, superklass, prototype) { // @property $$id Each class is assigned a unique `id` that helps // comparation and implementation of `#object_id` module.$$id = Opal.uid(); // @property $$proto This is the prototype on which methods will be defined module.$$proto = prototype; // @property constructor keeps a ref to the constructor, but apparently the // constructor is already set on: // // `var module = new constructor` is called. // // Maybe there are some browsers not abiding (IE6?) module.constructor = constructor; if (superklass === Module) { // @property $$is_module Clearly mark this as a module module.$$is_module = true; module.$$class = Module; } else { // @property $$is_class Clearly mark this as a class module.$$is_class = true; module.$$class = Class; } // @property $$super the superclass, doesn't get changed by module inclusions module.$$super = superklass; // @property $$parent direct parent class or module // starts with the superclass, after module inclusion is // the last included module module.$$parent = superklass; // @property $$inc included modules module.$$inc = []; } // Define new module (or return existing module). The given `base` is basically // the current `self` value the `module` statement was defined in. If this is // a ruby module or class, then it is used, otherwise if the base is a ruby // object then that objects real ruby class is used (e.g. if the base is the // main object, then the top level `Object` class is used as the base). // // If a module of the given name is already defined in the base, then that // instance is just returned. // // If there is a class of the given name in the base, then an error is // generated instead (cannot have a class and module of same name in same base). // // Otherwise, a new module is created in the base with the given name, and that // new instance is returned back (to be referenced at runtime). // // @param [RubyModule or Class] base class or module this definition is inside // @param [String] id the name of the new (or existing) module // @return [RubyModule] // Opal.module = function(base, id) { var module; if (!base.$$is_class && !base.$$is_module) { base = base.$$class; } if ($hasOwn.call(base.$$scope, id)) { module = base.$$scope[id]; if (!module.$$is_module && module !== _Object) { throw Opal.TypeError.$new(id + " is not a module"); } } else { module = boot_module_object(); // name module using base (e.g. Foo or Foo::Baz) module.$$name = id; // mark the object as a module module.$$is_module = true; // initialize dependency tracking module.$$dep = []; Opal.create_scope(base.$$scope, module, id); // Name new module directly onto current scope (Opal.Foo.Baz = module) base[id] = base.$$scope[id] = module; } return module; }; // Internal function to create a new module instance. This simply sets up // the prototype hierarchy and method tables. // function boot_module_object() { var mtor = function() {}; mtor.prototype = Module_alloc.prototype; function module_constructor() {} module_constructor.prototype = new mtor(); var module = new module_constructor(); var module_prototype = {}; setup_module_or_class_object(module, module_constructor, Module, module_prototype); return module; } // Make `boot_module_object` available to the JS-API Opal.boot_module_object = boot_module_object; // Return the singleton class for the passed object. // // If the given object alredy has a singleton class, then it will be stored on // the object as the `$$meta` property. If this exists, then it is simply // returned back. // // Otherwise, a new singleton object for the class or object is created, set on // the object at `$$meta` for future use, and then returned. // // @param [RubyObject] object the ruby object // @return [RubyClass] 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 build_class_singleton_class(object); } return build_object_singleton_class(object); }; // Build the singleton class for an existing class. // // NOTE: Actually in MRI a class' singleton class inherits from its // superclass' singleton class which in turn inherits from Class. // // @param [RubyClass] klass // @return [RubyClass] // function build_class_singleton_class(klass) { var meta = new Opal.Class.$$alloc(); meta.$$class = Opal.Class; meta.$$proto = klass.constructor.prototype; meta.$$is_singleton = true; meta.$$singleton_of = klass; meta.$$inc = []; meta.$$scope = klass.$$scope; return klass.$$meta = meta; } // Build the singleton class for a Ruby (non class) Object. // // @param [RubyObject] object // @return [RubyClass] // function build_object_singleton_class(object) { var orig_class = object.$$class, class_id = "#>"; var Singleton = function() {}; var meta = Opal.boot_class(orig_class, Singleton); meta.$$name = class_id; meta.$$proto = object; meta.$$class = orig_class.$$class; meta.$$scope = orig_class.$$scope; meta.$$parent = orig_class; meta.$$is_singleton = true; meta.$$singleton_of = object; return object.$$meta = meta; } // Bridges a single method. function bridge_method(target, from, name, body) { var ancestors, i, ancestor, length; ancestors = target.$$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.prototype[name] = body break; } } } // Bridges from *donator* to a *target*. function _bridge(target, donator) { var id, methods, method, i, bridged; if (typeof(target) === "function") { id = donator.$__id__(); methods = donator.$instance_methods(); for (i = methods.length - 1; i >= 0; i--) { method = '$' + methods[i]; bridge_method(target, donator, method, donator.$$proto[method]); } if (!bridges[id]) { bridges[id] = []; } bridges[id].push(target); } else { bridged = bridges[target.$__id__()]; if (bridged) { for (i = bridged.length - 1; i >= 0; i--) { _bridge(bridged[i], donator); } bridges[donator.$__id__()] = bridged.slice(); } } } // The actual inclusion of a module into a class. // // ## Class `$$parent` and `iclass` // // To handle `super` calls, every class has a `$$parent`. This parent is // used to resolve the next class for a super call. A normal class would // have this point to its superclass. However, if a class includes a module // then this would need to take into account the module. The module would // also have to then point its `$$parent` to the actual superclass. We // cannot modify modules like this, because it might be included in more // then one class. To fix this, we actually insert an `iclass` as the class' // `$$parent` which can then point to the superclass. The `iclass` acts as // a proxy to the actual module, so the `super` chain can then search it for // the required method. // // @param [RubyModule] module the module to include // @param [RubyClass] klass the target class to include module into // @return [null] // Opal.append_features = function(module, klass) { var iclass, donator, prototype, methods, id, i; // check if this module is already included in the class for (i = klass.$$inc.length - 1; i >= 0; i--) { if (klass.$$inc[i] === module) { return; } } klass.$$inc.push(module); module.$$dep.push(klass); _bridge(klass, module); // iclass iclass = { $$name: module.$$name, $$proto: module.$$proto, $$parent: klass.$$parent, $$module: module, $$iclass: true }; klass.$$parent = iclass; donator = module.$$proto; prototype = klass.$$proto; methods = module.$instance_methods(); for (i = methods.length - 1; i >= 0; i--) { id = '$' + methods[i]; // if the target class already has a method of the same name defined // and that method was NOT donated, then it must be a method defined // by the class so we do not want to override it if ( prototype.hasOwnProperty(id) && !prototype[id].$$donated && !prototype[id].$$stub) { continue; } prototype[id] = donator[id]; prototype[id].$$donated = module; } donate_constants(module, klass); }; // Boot a base class (makes instances). function boot_class_alloc(id, constructor, superklass) { if (superklass) { var alloc_proxy = function() {}; alloc_proxy.prototype = superklass.$$proto || superklass.prototype; constructor.prototype = new alloc_proxy(); } if (id) { constructor.displayName = id+'_alloc'; } constructor.prototype.constructor = constructor; return constructor; } // Builds the class object for core classes: // - make the class object have a singleton class // - make the singleton class inherit from its parent singleton class // // @param id [String] the name of the class // @param alloc [Function] the constructor for the core class instances // @param superclass [Class alloc] the constructor of the superclass // function boot_core_class_object(id, alloc, superclass) { var superclass_constructor = function() {}; superclass_constructor.prototype = superclass.prototype; var singleton_class = function() {}; singleton_class.prototype = new superclass_constructor(); singleton_class.displayName = "#"; // the singleton_class acts as the class object constructor var klass = new singleton_class(); setup_module_or_class_object(klass, singleton_class, superclass, alloc.prototype); klass.$$alloc = alloc; klass.$$name = id; klass.displayName = id; // Give all instances a ref to their class alloc.prototype.$$class = klass; Opal[id] = klass; Opal.constants.push(id); return klass; } // For performance, some core Ruby classes are toll-free bridged to their // native JavaScript counterparts (e.g. a Ruby Array is a JavaScript Array). // // This method is used to setup a native constructor (e.g. Array), to have // its prototype act like a normal Ruby class. Firstly, a new Ruby class is // created using the native constructor so that its prototype is set as the // target for th new class. Note: all bridged classes are set to inherit // from Object. // // Example: // // Opal.bridge(self, Function); // // @param [Class] klass the Ruby class to bridge // @param [Function] constructor 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); 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--) { _bridge(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; } // constant assign // Opal.casgn = function(base_module, name, value) { function update(klass, name) { klass.$$name = name; for (name in klass.$$scope) { var value = klass.$$scope[name]; if (value.$$name === nil && (value.$$is_class || value.$$is_module)) { update(value, name) } } } var scope = base_module.$$scope; if (value.$$is_class || value.$$is_module) { // only checking _Object prevents setting a const on an anonymous class that has a superclass that's not Object if (value.$$is_class || value.$$base_module === _Object) { value.$$base_module = base_module; } if (value.$$name === nil && value.$$base_module.$$name !== nil) { update(value, name); } } scope.constants.push(name); return scope[name] = value; }; // constant decl Opal.cdecl = function(base_scope, name, value) { if ((value.$$is_class || value.$$is_module) && value.$$orig_scope == null) { value.$$name = name; value.$$orig_scope = base_scope; base_scope.constructor[name] = value; } base_scope.constants.push(name); return base_scope[name] = value; }; // When a source module is included into the target module, we must also copy // its constants to the target. // function donate_constants(source_mod, target_mod) { var source_constants = source_mod.$$scope.constants, target_scope = target_mod.$$scope, target_constants = target_scope.constants; for (var i = 0, length = source_constants.length; i < length; i++) { target_constants.push(source_constants[i]); target_scope[source_constants[i]] = source_mod.$$scope[source_constants[i]]; } }; // Donate methods for a module. function donate(module, jsid) { var included_in = module.$$dep, body = module.$$proto[jsid], i, length, includee, dest, current, klass_includees, j, jj, current_owner_index, module_index; if (!included_in) { return; } for (i = 0, length = included_in.length; i < length; i++) { includee = included_in[i]; dest = includee.$$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 = includee.$$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 (includee.$$dep) { donate(includee, jsid); } } }; // Methods stubs are used to facilitate method_missing in opal. A stub is a // placeholder function which just calls `method_missing` on the receiver. // If no method with the given name is actually defined on an object, then it // is obvious to say that the stub will be called instead, and then in turn // method_missing will be called. // // When a file in ruby gets compiled to javascript, it includes a call to // this function which adds stubs for every method name in the compiled file. // It should then be safe to assume that method_missing will work for any // method call detected. // // Method stubs are added to the BasicObject prototype, which every other // ruby object inherits, so all objects should handle method missing. A stub // is only added if the given property name (method name) is not already // defined. // // Note: all ruby methods have a `$` prefix in javascript, so all stubs will // have this prefix as well (to make this method more performant). // // Opal.add_stubs(["$foo", "$bar", "$baz="]); // // All stub functions will have a private `$$stub` property set to true so // that other internal methods can detect if a method is just a stub or not. // `Kernel#respond_to?` uses this property to detect a methods presence. // // @param [Array] stubs an array of method stubs to add // Opal.add_stubs = function(stubs) { var subscriber, subscribers = Opal.stub_subscribers, i, ilength = stubs.length, j, jlength = subscribers.length, method_name, stub; for (i = 0; i < ilength; i++) { method_name = stubs[i]; stub = 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 [String] stub stub name to add (e.g. "$foo") // Opal.add_stub_for = function(prototype, stub) { var method_missing_stub = stub_for(stub); prototype[stub] = method_missing_stub; } // Generate the method_missing stub for a given method name. // // @param [String] method_name The js-name of the method to stub (e.g. "$foo") // function stub_for(method_name) { function method_missing_stub() { // Copy any given block onto the method_missing dispatcher this.$method_missing.$$p = method_missing_stub.$$p; // Set block property to null ready for the next call (stop false-positives) method_missing_stub.$$p = null; // call method missing with correct args (remove '$' prefix on method name) return this.$method_missing.apply(this, [method_name.slice(1)].concat($slice.call(arguments))); } method_missing_stub.$$stub = true; return method_missing_stub; } // Arity count error dispatcher 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 + ')'); }; // The Array of ancestors for a given module/class Opal.ancestors = function(module_or_class) { var parent = module_or_class, result = []; while (parent) { result.push(parent); for (var i=0; i < parent.$$inc.length; i++) { result = result.concat(Opal.ancestors(parent.$$inc[i])); } parent = parent.$$is_class ? parent.$$super : null; } return result; } // Super dispatcher Opal.find_super_dispatcher = function(obj, jsid, current_func, iter, defs) { var dispatcher; if (defs) { if (obj.$$is_class || obj.$$is_module) { dispatcher = defs.$$super; } else { dispatcher = obj.$$class.$$proto; } } else { if (obj.$$is_class || obj.$$is_module) { dispatcher = obj.$$super; } else { dispatcher = find_obj_super_dispatcher(obj, jsid, current_func); } } dispatcher = dispatcher['$' + jsid]; dispatcher.$$p = iter; return dispatcher; }; // Iter dispatcher for super in a block Opal.find_iter_super_dispatcher = function(obj, jsid, current_func, iter, defs) { if (current_func.$$def) { return Opal.find_super_dispatcher(obj, current_func.$$jsid, current_func, iter, defs); } else { return Opal.find_super_dispatcher(obj, jsid, current_func, iter, defs); } }; function find_obj_super_dispatcher(obj, jsid, current_func) { var klass = obj.$$meta || obj.$$class; jsid = '$' + jsid; while (klass) { if (klass.$$proto[jsid] === current_func) { // ok break; } klass = klass.$$parent; } // if we arent in a class, we couldnt find current? if (!klass) { throw new Error("could not find current class for super()"); } klass = klass.$$parent; // else, let's find the next one 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; }; // handles yield calls for 1 yielded arg Opal.yield1 = function(block, arg) { if (typeof(block) !== "function") { throw Opal.LocalJumpError.$new("no block given"); } if (block.length > 1 && arg.$$is_array) { return block.apply(null, arg); } else { return block(arg); } }; // handles yield for > 1 yielded arg Opal.yieldX = function(block, args) { if (typeof(block) !== "function") { throw Opal.LocalJumpError.$new("no block given"); } if (block.length > 1 && args.length === 1) { if (args[0].$$is_array) { return block.apply(null, args[0]); } } if (!args.$$is_array) { args = $slice.call(args); } return block.apply(null, args); }; // Finds the corresponding exception match in candidates. Each candidate can // be a value, or an array of values. Returns null if not found. Opal.rescue = function(exception, candidates) { for (var i = 0; i < candidates.length; i++) { var candidate = candidates[i]; if (candidate.$$is_array) { var result = Opal.rescue(exception, candidate); if (result) { return result; } } else if (candidate['$==='](exception)) { return candidate; } } return null; }; Opal.is_a = function(object, klass) { if (object.$$meta === klass) { return true; } var search = object.$$class; while (search) { if (search === klass) { return true; } for (var i = 0, length = search.$$inc.length; i < length; i++) { if (search.$$inc[i] === klass) { return true; } } search = search.$$super; } return false; }; // Helpers for implementing multiple assignment // Our code for extracting the values and assigning them only works if the // return value is a JS array // So if we get an Array subclass, extract the wrapped JS array from it Opal.to_ary = function(value) { // Used for: a, b = something (no splat) if (value.$$is_array) { return (value.constructor === Array) ? value : value.literal; } 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.constructor === Array) ? ary : ary.literal; } else { throw Opal.TypeError.$new("Can't convert " + value.$$class + " to Array (" + value.$$class + "#to_ary gives " + ary.$$class + ")"); } } else { return [value]; } }; Opal.to_a = function(value) { // Used for: a, b = *something (with splat) if (value.$$is_array) { // A splatted array must be copied return (value.constructor === Array) ? value.slice() : value.literal.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.constructor === Array) ? ary : ary.literal; } else { throw Opal.TypeError.$new("Can't convert " + value.$$class + " to Array (" + value.$$class + "#to_a gives " + ary.$$class + ")"); } } else { return [value]; } }; // 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); }; // Call a ruby method on a ruby object with some arguments: // // var my_array = [1, 2, 3, 4] // Opal.send(my_array, 'length') # => 4 // Opal.send(my_array, 'reverse!') # => [4, 3, 2, 1] // // A missing method will be forwarded to the object via // method_missing. // // The result of either call with be returned. // // @param [Object] recv the ruby object // @param [String] mid ruby method to call // Opal.send = function(recv, mid) { var args = $slice.call(arguments, 2), func = recv['$' + mid]; if (func) { return func.apply(recv, args); } return recv.$method_missing.apply(recv, [mid].concat(args)); }; Opal.block_send = function(recv, mid, block) { var args = $slice.call(arguments, 3), func = recv['$' + mid]; if (func) { func.$$p = block; return func.apply(recv, args); } return recv.$method_missing.apply(recv, [mid].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 [RubyObject or Class] obj the actual obj to define method for // @param [String] jsid the javascript friendly method name (e.g. '$foo') // @param [Function] body the literal javascript function used as method // @return [null] // Opal.defn = function(obj, jsid, body) { obj.$$proto[jsid] = body; if (obj.$$is_module) { donate(obj, jsid); if (obj.$$module_function) { Opal.defs(obj, jsid, body); } } if (obj.$__id__ && !obj.$__id__.$$stub) { var bridged = bridges[obj.$__id__()]; if (bridged) { for (var i = bridged.length - 1; i >= 0; i--) { bridge_method(bridged[i], obj, jsid, body); } } } if (obj.$method_added && !obj.$method_added.$$stub) { obj.$method_added(jsid.substr(1)); } var singleton_of = obj.$$singleton_of; 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. Opal.defs = function(obj, jsid, body) { Opal.defn(Opal.get_singleton_class(obj), jsid, body) }; 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); } }; // Called from #remove_method. Opal.rdef = function(obj, jsid) { // TODO: remove from bridges 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]; // instance_eval is being run on a class/module, so that need to alias 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() + "'") } } Opal.defn(obj, id, body); 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; }; Opal.hash_init = function(hash) { hash.$$map = {}; hash.$$smap = {}; 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, length = keys.length, key, value; i < length; i++) { key = from_hash.$$keys[i]; if (key.$$is_string) { value = from_hash.$$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 (!hash.$$smap.hasOwnProperty(key)) { hash.$$keys.push(key); } hash.$$smap[key] = value; return; } var key_hash = key.$hash(), bucket, last_bucket; if (!hash.$$map.hasOwnProperty(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 (hash.$$smap.hasOwnProperty(key)) { return hash.$$smap[key]; } return; } var key_hash = key.$hash(), bucket; if (hash.$$map.hasOwnProperty(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 (!hash.$$smap.hasOwnProperty(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 (!hash.$$map.hasOwnProperty(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 (!hash.$$map.hasOwnProperty(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 (args.hasOwnProperty(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; }; // hash2 is a faster creator for hashes that just use symbols and // strings as keys. The map and keys array can be constructed at // compile time, so they are just added here by the constructor // function // Opal.hash2 = function(keys, smap) { var hash = new Opal.Hash.$$alloc(); hash.$$map = {}; hash.$$keys = keys; hash.$$smap = smap; 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; }; 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; }; // Require system // -------------- Opal.modules = {}; Opal.loaded_features = ['corelib/runtime']; Opal.current_dir = '.' Opal.require_table = {'corelib/runtime': true}; function normalize(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 = normalize(paths[i]); if (Opal.require_table[path]) { return; } Opal.loaded_features.push(path); Opal.require_table[path] = true; } } Opal.load = function(path) { path = normalize(path); Opal.loaded([path]); var module = Opal.modules[path]; if (module) { module(Opal); } else { var severity = Opal.dynamic_require_severity || 'warning'; var message = 'cannot load such file -- ' + path; if (severity === "error") { Opal.LoadError ? Opal.LoadError.$new(message) : function(){throw message}(); } else if (severity === "warning") { console.warn('WARNING: LoadError: ' + message); } } return true; } Opal.require = function(path) { path = normalize(path); if (Opal.require_table[path]) { return false; } return Opal.load(path); } // Initialization // -------------- // Constructors for *instances* of core objects boot_class_alloc('BasicObject', BasicObject_alloc); boot_class_alloc('Object', Object_alloc, BasicObject_alloc); boot_class_alloc('Module', Module_alloc, Object_alloc); boot_class_alloc('Class', Class_alloc, Module_alloc); // Constructors for *classes* of core objects BasicObject = boot_core_class_object('BasicObject', BasicObject_alloc, Class_alloc); _Object = boot_core_class_object('Object', Object_alloc, BasicObject.constructor); Module = boot_core_class_object('Module', Module_alloc, _Object.constructor); Class = boot_core_class_object('Class', Class_alloc, Module.constructor); // 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; Opal.base = _Object; BasicObject.$$scope = _Object.$$scope = Opal; BasicObject.$$orig_scope = _Object.$$orig_scope = Opal; Module.$$scope = _Object.$$scope; Module.$$orig_scope = _Object.$$orig_scope; Class.$$scope = _Object.$$scope; Class.$$orig_scope = _Object.$$orig_scope; _Object.$$proto.toString = function() { return this.$to_s(); }; _Object.$$proto.$require = Opal.require; 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'); Opal.returner = new Error('unexpected return'); TypeError.$$super = Error; }).call(this); if (typeof(global) !== 'undefined') { global.Opal = this.Opal; Opal.global = global; } if (typeof(window) !== 'undefined') { window.Opal = this.Opal; Opal.global = window; }