(function(undefined) { // The Opal object that is exposed globally var Opal = this.Opal = {}; // The actual class for BasicObject var RubyBasicObject; // The actual Object class var RubyObject; // The actual Module class var RubyModule; // The actual Class class var RubyClass; // Constructor for instances of BasicObject function BasicObject(){} // Constructor for instances of Object function Object(){} // Constructor for instances of Class function Class(){} // Constructor for instances of Module function Module(){} // Constructor for instances of NilClass (nil) function NilClass(){} // All bridged classes - keep track to donate methods from Object var bridged_classes = []; // 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; 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; // Generates unique id for every ruby object var unique_id = 0; // Return next unique id Opal.uid = function() { return unique_id++; }; // Table holds all class variables Opal.cvars = {}; // Globals table Opal.gvars = {}; /* * 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. */ function create_scope(base, klass, id) { var const_alloc = function() {}; var const_scope = const_alloc.prototype = new base.constructor(); klass._scope = const_scope; const_scope.base = klass; klass._base_module = base.base; const_scope.constructor = const_alloc; const_scope.constants = []; if (id) { klass._orig_scope = base; base[id] = base.constructor[id] = klass; base.constants.push(id); } } Opal.create_scope = create_scope; /* * 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) { // If base is an object, use its class if (!base._isClass) { base = base._klass; } // Not specifying a superclass means we can assume it to be Object if (superklass === null) { superklass = RubyObject; } var klass = base._scope[id]; // If a constant exists in the scope, then we must use that if ($hasOwn.call(base._scope, id) && klass._orig_scope === base._scope) { // Make sure the existing constant is a class, or raise error if (!klass._isClass) { throw Opal.TypeError.$new(id + " is not a class"); } // Make sure existing class has same superclass if (superklass !== klass._super && superklass !== RubyObject) { throw Opal.TypeError.$new("superclass mismatch for class " + id); } } else if (typeof(superklass) === 'function') { // passed native constructor as superklass, so bridge it as ruby class return bridge_class(id, superklass); } else { // if class doesnt exist, create a new one with given superclass klass = boot_class(superklass, constructor); // name class using base (e.g. Foo or Foo::Baz) klass._name = id; // every class gets its own constant scope, inherited from current scope create_scope(base._scope, klass, id); // Name new class directly onto current scope (Opal.Foo.Baz = klass) base[id] = base._scope[id] = klass; // Copy all parent constants to child, unless parent is Object if (superklass !== RubyObject && superklass !== RubyBasicObject) { Opal.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. var boot_class = Opal.boot = function(superklass, constructor) { // instances var ctor = function() {}; ctor.prototype = superklass._proto; constructor.prototype = new ctor(); constructor.prototype.constructor = constructor; return boot_class_meta(superklass, constructor); }; // class itself function boot_class_meta(superklass, constructor) { var mtor = function() {}; mtor.prototype = superklass.constructor.prototype; function OpalClass() {}; OpalClass.prototype = new mtor(); var klass = new OpalClass(); klass._id = unique_id++; klass._alloc = constructor; klass._isClass = true; klass.constructor = OpalClass; klass._super = superklass; klass._methods = []; klass.__inc__ = []; klass.__parent = superklass; klass._proto = constructor.prototype; constructor.prototype._klass = klass; return klass; } // Define new module (or return existing module) Opal.module = function(base, id) { var module; if (!base._isClass) { base = base._klass; } if ($hasOwn.call(base._scope, id)) { module = base._scope[id]; if (!module.__mod__ && module !== RubyObject) { throw Opal.TypeError.$new(id + " is not a module") } } else { module = boot_module() module._name = id; 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() { var mtor = function() {}; mtor.prototype = RubyModule.constructor.prototype; function OpalModule() {}; OpalModule.prototype = new mtor(); var module = new OpalModule(); module._id = unique_id++; module._isClass = true; module.constructor = OpalModule; module._super = RubyModule; module._methods = []; module.__inc__ = []; module.__parent = RubyModule; module._proto = {}; module.__mod__ = true; module.__dep__ = []; return module; } // Boot a base class (makes instances). var boot_defclass = function(id, constructor, superklass) { if (superklass) { var ctor = function() {}; ctor.prototype = superklass.prototype; constructor.prototype = new ctor(); } constructor.prototype.constructor = constructor; return constructor; }; // Boot the actual (meta?) classes of core classes var boot_makemeta = function(id, constructor, superklass) { var mtor = function() {}; mtor.prototype = superklass.prototype; function OpalClass() {}; OpalClass.prototype = new mtor(); var klass = new OpalClass(); klass._id = unique_id++; klass._alloc = constructor; klass._isClass = true; klass._name = id; klass._super = superklass; klass.constructor = OpalClass; klass._methods = []; klass.__inc__ = []; klass.__parent = superklass; klass._proto = constructor.prototype; constructor.prototype._klass = 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. * * Bridged classes are tracked in `bridged_classes` array so that methods * defined on Object can be "donated" to all bridged classes. This allows * us to fake the inheritance of a native prototype from our Object * prototype. * * Example: * * bridge_class("Proc", Function); * * @param [String] name the name of the ruby class to create * @param [Function] constructor native javascript constructor to use * @return [Class] returns new ruby class */ function bridge_class(name, constructor) { var klass = boot_class_meta(RubyObject, constructor); klass._name = name; create_scope(Opal, klass, name); bridged_classes.push(klass); var object_methods = RubyBasicObject._methods.concat(RubyObject._methods); for (var i = 0, len = object_methods.length; i < len; i++) { var meth = object_methods[i]; constructor.prototype[meth] = RubyObject._proto[meth]; } return klass; }; /* * constant assign */ Opal.casgn = function(base_module, name, value) { var scope = base_module._scope; if (value._isClass && value._name === nil) { value._name = name; } if (value._isClass) { value._base_module = base_module; } scope.constants.push(name); return scope[name] = value; }; /* * constant decl */ Opal.cdecl = function(base_scope, name, value) { base_scope.constants.push(name); return base_scope[name] = value; }; /* * constant get */ Opal.cget = function(base_scope, path) { if (path == null) { path = base_scope; base_scope = Opal.Object; } var result = base_scope; path = path.split('::'); while (path.length != 0) { result = result.$const_get(path.shift()); } return result; } /* * When a source module is included into the target module, we must also copy * its constants to the target. */ Opal.donate_constants = function(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]]; } }; /* * 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 `rb_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) { for (var i = 0, length = stubs.length; i < length; i++) { var stub = stubs[i]; if (!BasicObject.prototype[stub]) { BasicObject.prototype[stub] = true; add_stub_for(BasicObject.prototype, stub); } } }; /* * Actuall 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") */ function add_stub_for(prototype, stub) { 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, [stub.slice(1)].concat($slice.call(arguments))); } method_missing_stub.rb_stub = true; prototype[stub] = method_missing_stub; } // Expose for other parts of Opal to use Opal.add_stub_for = add_stub_for; // Const missing dispatcher Opal.cm = function(name) { return this.base.$const_missing(name); }; // Arity count error dispatcher Opal.ac = function(actual, expected, object, meth) { var inspect = (object._isClass ? object._name + '.' : object._klass._name + '#') + meth; var msg = '[' + inspect + '] wrong number of arguments(' + actual + ' for ' + expected + ')'; throw Opal.ArgumentError.$new(msg); }; // Super dispatcher Opal.find_super_dispatcher = function(obj, jsid, current_func, iter, defs) { var dispatcher; if (defs) { dispatcher = obj._isClass ? defs._super : obj._klass._proto; } else { if (obj._isClass) { 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); } }; var find_obj_super_dispatcher = function(obj, jsid, current_func) { var klass = obj.__meta__ || obj._klass; 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.$return = 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) { if (arg._isArray) { return block.apply(null, arg); } else { return block(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]._isArray) { return block.apply(null, args[0]); } } if (!args._isArray) { 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._isArray) { var subresult; if (subresult = Opal.$rescue(exception, candidate)) { return subresult; } } else if (candidate['$==='](exception)) { return candidate; } } return null; }; Opal.is_a = function(object, klass) { if (object.__meta__ === klass) { return true; } var search = object._klass; 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; } // Helper to convert the given object to an array Opal.to_ary = function(value) { if (value._isArray) { return value; } else if (value.$to_ary && !value.$to_ary.rb_stub) { return value.$to_ary(); } return [value]; }; /* 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)); }; /** * Donate methods for a class/module */ Opal.donate = function(klass, defined, indirect) { var methods = klass._methods, included_in = klass.__dep__; // if (!indirect) { klass._methods = methods.concat(defined); // } if (included_in) { for (var i = 0, length = included_in.length; i < length; i++) { var includee = included_in[i]; var dest = includee._proto; for (var j = 0, jj = defined.length; j < jj; j++) { var method = defined[j]; dest[method] = klass._proto[method]; dest[method]._donated = true; } if (includee.__dep__) { Opal.donate(includee, defined, true); } } } }; Opal.defn = function(obj, jsid, body) { if (obj.__mod__) { obj._proto[jsid] = body; Opal.donate(obj, [jsid]); } else if (obj._isClass) { obj._proto[jsid] = body; if (obj === RubyBasicObject) { define_basic_object_method(jsid, body); } else if (obj === RubyObject) { Opal.donate(obj, [jsid]); } } else { obj[jsid] = body; } return nil; }; /* * Define a singleton method on the given object. */ Opal.defs = function(obj, jsid, body) { if (obj._isClass || obj.__mod__) { obj.constructor.prototype[jsid] = body; } else { obj[jsid] = body; } }; function define_basic_object_method(jsid, body) { RubyBasicObject._methods.push(jsid); for (var i = 0, len = bridged_classes.length; i < len; i++) { bridged_classes[i]._proto[jsid] = body; } } Opal.hash = function() { if (arguments.length == 1 && arguments[0]._klass == Opal.Hash) { return arguments[0]; } var hash = new Opal.Hash._alloc, keys = [], assocs = {}; hash.map = assocs; hash.keys = keys; if (arguments.length == 1) { if (arguments[0]._isArray) { var args = arguments[0]; for (var i = 0, length = args.length; i < length; i++) { var pair = args[i]; if (pair.length !== 2) { throw Opal.ArgumentError.$new("value not of length 2: " + pair.$inspect()); } var key = pair[0], obj = pair[1]; if (assocs[key] == null) { keys.push(key); } assocs[key] = obj; } } else { var obj = arguments[0]; for (var key in obj) { assocs[key] = obj[key]; keys.push(key); } } } else { var length = arguments.length; if (length % 2 !== 0) { throw Opal.ArgumentError.$new("odd number of arguments for Hash"); } for (var i = 0; i < length; i++) { var key = arguments[i], obj = arguments[++i]; if (assocs[key] == null) { keys.push(key); } assocs[key] = obj; } } 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, map) { var hash = new Opal.Hash._alloc; hash.keys = keys; hash.map = map; 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; }; // Initialization // -------------- // Constructors for *instances* of core objects boot_defclass('BasicObject', BasicObject); boot_defclass('Object', Object, BasicObject); boot_defclass('Module', Module, Object); boot_defclass('Class', Class, Module); // Constructors for *classes* of core objects RubyBasicObject = boot_makemeta('BasicObject', BasicObject, Class); RubyObject = boot_makemeta('Object', Object, RubyBasicObject.constructor); RubyModule = boot_makemeta('Module', Module, RubyObject.constructor); RubyClass = boot_makemeta('Class', Class, RubyModule.constructor); // Fix booted classes to use their metaclass RubyBasicObject._klass = RubyClass; RubyObject._klass = RubyClass; RubyModule._klass = RubyClass; RubyClass._klass = RubyClass; // Fix superclasses of booted classes RubyBasicObject._super = null; RubyObject._super = RubyBasicObject; RubyModule._super = RubyObject; RubyClass._super = RubyModule; // Internally, Object acts like a module as it is "included" into bridged // classes. In other words, we donate methods from Object into our bridged // classes as their prototypes don't inherit from our root Object, so they // act like module includes. RubyObject.__dep__ = bridged_classes; Opal.base = RubyObject; RubyBasicObject._scope = RubyObject._scope = Opal; RubyBasicObject._orig_scope = RubyObject._orig_scope = Opal; Opal.Kernel = RubyObject; RubyModule._scope = RubyObject._scope; RubyClass._scope = RubyObject._scope; RubyModule._orig_scope = RubyObject._orig_scope; RubyClass._orig_scope = RubyObject._orig_scope; RubyObject._proto.toString = function() { return this.$to_s(); }; Opal.top = new RubyObject._alloc(); Opal.klass(RubyObject, RubyObject, 'NilClass', NilClass); var nil = Opal.nil = new NilClass; nil.call = nil.apply = function() { throw Opal.LocalJumpError.$new('no block given'); }; Opal.breaker = new Error('unexpected break'); Opal.returner = new Error('unexpected return'); bridge_class('Array', Array); bridge_class('Boolean', Boolean); bridge_class('Numeric', Number); bridge_class('String', String); bridge_class('Proc', Function); bridge_class('Exception', Error); bridge_class('Regexp', RegExp); bridge_class('Time', Date); TypeError._super = Error; }).call(this);