opal/corelib/runtime.js in opal-0.10.6 vs opal/corelib/runtime.js in opal-0.11.0.rc1
- old
+ new
@@ -11,10 +11,28 @@
// 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;
}
@@ -50,35 +68,25 @@
// The Opal object that is exposed globally
var Opal = this.Opal = {};
// All bridged classes - keep track to donate methods from Object
- var bridges = {};
+ var BridgedClasses = {};
- // 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;
+ 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
+ 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 = Opal.hasOwnProperty;
+ var $hasOwn = Object.hasOwnProperty;
var $slice = Opal.slice = Array.prototype.slice;
// Nil object id is always 4
var nil_id = 4;
@@ -90,12 +98,15 @@
Opal.uid = function() {
unique_id += 2;
return unique_id;
};
- // Table holds all class variables
- Opal.cvars = {};
+ // 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
@@ -109,139 +120,225 @@
// 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
// ---------
-
- // 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:
+ // 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)
//
- // 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 name [String] the name of the constant to lookup
- // @return [Object]
- //
- Opal.get = function(name) {
- var constant = this[name];
+ // 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]`
- if (constant == null) {
- return this.base.$const_get(name);
+ // 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;
}
+ }
- return constant;
- };
+ // Walk up the ancestors chain looking for the constant
+ function const_lookup_ancestors(cref, name) {
+ var i, ii, result, ancestors;
- // 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.
- //
- // @param base_scope [$$scope] the scope in which the new scope should be created
- // @param klass [Class]
- // @param id [String, null] the name of the newly created scope
- //
- Opal.create_scope = function(base_scope, klass, id) {
- var const_alloc = function() {};
- var const_scope = const_alloc.prototype = new base_scope.constructor();
+ if (cref == null) return;
- klass.$$scope = const_scope;
- klass.$$base_module = base_scope.base;
+ ancestors = Opal.ancestors(cref);
- const_scope.base = klass;
- const_scope.constructor = const_alloc;
- const_scope.constants = [];
+ for (i = 0, ii = ancestors.length; i < ii; i++) {
+ if (ancestors[i].$$const && $hasOwn.call(ancestors[i].$$const, name)) {
+ return ancestors[i].$$const[name];
+ }
+ }
+ }
- if (id) {
- Opal.cdecl(base_scope, id, klass);
- const_alloc.displayName = id+"_scope_alloc";
+ // 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);
};
- // Constant assignment, see also `Opal.cdecl`
- //
- // @param base_module [Module, Class] the constant namespace
- // @param name [String] the name of the constant
- // @param value [Object] the value of the constant
- //
- // @example Assigning a namespaced constant
- // self::FOO = 'bar'
- //
- // @example Assigning with Module#const_set
- // Foo.const_set :BAR, 123
- //
- Opal.casgn = function(base_module, name, value) {
- function update(klass, name) {
- klass.$$name = name;
+ // Initialize the top level constant cache generation counter
+ Opal.const_cache_version = 1;
- for (name in klass.$$scope) {
- var value = klass.$$scope[name];
+ // 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 (value.$$name === nil && (value.$$is_class || value.$$is_module)) {
- update(value, name)
- }
- }
+ if (nesting.$$const_cache == null) {
+ cache = nesting.$$const_cache = Object.create(null);
+ } else {
+ cache = nesting.$$const_cache;
}
+ cached = cache[name];
- var scope = base_module.$$scope;
+ 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);
- 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;
- }
+ cache[name] = [current_version, result];
+ } else {
+ result = cached[1];
+ }
- if (value.$$name === nil && value.$$base_module.$$name !== nil) {
- update(value, name);
- }
+ 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;
}
- scope.constants.push(name);
- scope[name] = value;
+ cref.$$const = (cref.$$const || Object.create(null));
+ cref.$$const[name] = value;
- // If we dynamically declare a constant in a module,
- // we should populate all the classes that include this module
- // with the same constant
- if (base_module.$$is_module && base_module.$$dep) {
- for (var i = 0; i < base_module.$$dep.length; i++) {
- var dep = base_module.$$dep[i];
- Opal.casgn(dep, 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 value;
+ return Object.keys(constants);
};
- // Constant declaration
- //
- // @example
- // FOO = :bar
- //
- // @param base_scope [$$scope] the current scope
- // @param name [String] the name of the constant
- // @param value [Object] the value of the constant
- 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;
- // Here we should explicitly set a base module
- // (a module where the constant was initially defined)
- value.$$base_module = base_scope.base;
- base_scope.constructor[name] = value;
+ // 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;
}
- base_scope.constants.push(name);
- return base_scope[name] = value;
+ 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
// -----------------
@@ -272,10 +369,14 @@
// @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;
}
@@ -284,14 +385,14 @@
bridged = superclass;
superclass = _Object;
}
// Try to find the class in the current scope
- klass = base.$$scope[name];
+ klass = const_get_name(base, name);
// If the class exists in the scope, then we must use that
- if (klass && klass.$$orig_scope === base.$$scope) {
+ 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");
}
@@ -322,25 +423,19 @@
// @property $$parent direct parent class
// starts with the superclass, after klass inclusion is
// the last included klass
klass.$$parent = superclass;
- // Every class gets its own constant scope, inherited from current scope
- Opal.create_scope(base.$$scope, klass, name);
+ 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 {
- // Copy all parent constants to child, unless parent is Object
- if (superclass !== _Object && superclass !== BasicObject) {
- Opal.donate_constants(superclass, klass);
- }
-
// Call .inherited() hook with new class on the superclass
if (superclass.$inherited) {
superclass.$inherited(klass);
}
}
@@ -355,11 +450,11 @@
// @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;
+ alloc_proxy.prototype = superclass.$$proto || superclass.prototype;
constructor.prototype = new alloc_proxy();
}
if (name) {
constructor.displayName = name+'_alloc';
@@ -368,10 +463,34 @@
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
@@ -396,20 +515,18 @@
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;
- // @property $$id Each class is assigned a unique `id` that helps
- // comparation and implementation of `#object_id`
- klass.$$id = Opal.uid();
-
// Set a displayName for the singleton_class
singleton_class_alloc.displayName = "#<Class:"+(name || ("#<Class:"+klass.$$id+">"))+">";
// @property $$proto This is the prototype on which methods will be defined
klass.$$proto = alloc.prototype;
@@ -430,13 +547,10 @@
klass.$$is_class = true;
// @property $$class Classes are instances of the class Class
klass.$$class = Class;
- // @property $$inc included modules
- klass.$$inc = [];
-
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
@@ -458,24 +572,29 @@
//
// @return [Module]
Opal.module = function(base, name) {
var module;
+ if (base == null) {
+ base = _Object;
+ }
+
if (!base.$$is_class && !base.$$is_module) {
base = base.$$class;
}
- if ($hasOwn.call(base.$$scope, name)) {
- module = base.$$scope[name];
+ 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.create_scope(base.$$scope, module, name);
+ Opal.const_set(base, name, module);
}
return module;
};
@@ -498,20 +617,21 @@
//
Opal.module_allocate = function(superclass) {
var mtor = function() {};
mtor.prototype = superclass.$$alloc.prototype;
- function module_constructor() {}
+ var module_constructor = function() {};
module_constructor.prototype = new mtor();
var module = new module_constructor();
var module_prototype = {};
- // @property $$id Each class is assigned a unique `id` that helps
- // comparation and implementation of `#object_id`
- module.$$id = Opal.uid();
+ Opal.setup_module_or_class(module);
+ // initialize dependency tracking
+ module.$$included_in = [];
+
// Set the display name of the singleton prototype holder
module_constructor.displayName = "#<Class:#<Module:"+module.$$id+">>"
// @property $$proto This is the prototype on which methods will be defined
module.$$proto = module_prototype;
@@ -537,22 +657,10 @@
// direct parent class or module
// starts with the superclass, after module inclusion is
// the last included module
module.$$parent = superclass;
- // @property $$inc included modules
- module.$$inc = [];
-
- // mark the object as a module
- module.$$is_module = true;
-
- // initialize dependency tracking
- module.$$dep = [];
-
- // initialize the name with nil
- module.$$name = nil;
-
return module;
};
// Return the singleton class for the passed object.
//
@@ -601,16 +709,13 @@
// 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.$$super = superclass;
klass.$$parent = superclass;
- // The singleton_class retains the same scope as the original class
- Opal.create_scope(object.$$scope, klass);
-
klass.$$is_singleton = true;
klass.$$singleton_of = object;
return object.$$meta = klass;
};
@@ -627,24 +732,74 @@
var klass = Opal.setup_class_object(name, alloc, superclass.$$name, superclass.constructor);
klass.$$super = superclass;
klass.$$parent = superclass;
klass.$$class = superclass.$$class;
- klass.$$scope = superclass.$$scope;
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.
- Opal.bridge_method = function(target, from, name, body) {
+ //
+ // @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.$$bridge.$ancestors();
+ 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];
@@ -656,50 +811,88 @@
ancestor !== from) {
break;
}
if (ancestor === from) {
- target.prototype[name] = body
+ target_constructor.prototype[name] = body
break;
}
}
-
};
// Bridges from *donator* to a *target*.
- Opal._bridge = function(target, donator) {
- var id, methods, method, i, bridged;
+ //
+ // @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 (typeof(target) === "function") {
- id = donator.$__id__();
- methods = donator.$instance_methods();
+ if (bridged) {
+ BridgedClasses[donator_id] = bridged.slice();
- for (i = methods.length - 1; i >= 0; i--) {
- method = '$' + methods[i];
-
- Opal.bridge_method(target, donator, method, donator.$$proto[method]);
+ for (i = bridged.length - 1; i >= 0; i--) {
+ Opal_bridge_methods_to_constructor(bridged[i], donator)
}
+ }
+ };
- if (!bridges[id]) {
- bridges[id] = [];
- }
+ // Actually bridge methods to the bridged (shared) prototype.
+ function Opal_bridge_methods_to_constructor(target_constructor, donator) {
+ var i,
+ method,
+ methods = donator.$instance_methods();
- bridges[id].push(target);
+ for (i = methods.length - 1; i >= 0; i--) {
+ method = '$' + methods[i];
+ Opal.bridge_method(target_constructor, donator, method, donator.$$proto[method]);
}
- else {
- bridged = bridges[target.$__id__()];
+ }
- if (bridged) {
- for (i = bridged.length - 1; i >= 0; i--) {
- Opal._bridge(bridged[i], donator);
- }
+ // Associate the target as a bridged class for the current "donator"
+ function Opal_add_bridged_constructor(target_constructor, donator) {
+ var donator_id = donator.$__id__();
- bridges[donator.$__id__()] = bridged.slice();
+ 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<Module>] 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
@@ -712,58 +905,49 @@
// `$$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 klass [Class] the target class to include module into
+ // @param includer [Module] the target class to include module into
// @return [null]
- Opal.append_features = function(module, klass) {
+ 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 = klass.$$inc.length - 1; i >= 0; i--) {
- if (klass.$$inc[i] === module) {
+ for (i = includer.$$inc.length - 1; i >= 0; i--) {
+ if (includer.$$inc[i] === module) {
return;
}
}
- klass.$$inc.push(module);
- module.$$dep.push(klass);
- Opal._bridge(klass, module);
+ // 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: klass.$$parent,
+ $$parent: includer.$$parent,
$$module: module,
$$iclass: true
};
- klass.$$parent = iclass;
+ includer.$$parent = iclass;
- donator = module.$$proto;
- prototype = klass.$$proto;
- methods = module.$instance_methods();
+ 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;
+ Opal.update_includer(module, includer, '$' + methods[i])
}
-
- Opal.donate_constants(module, klass);
};
// 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 = {};
@@ -805,11 +989,12 @@
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._bridge(constructor, ancestors[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];
@@ -819,88 +1004,83 @@
}
return klass;
};
- // 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]];
- }
- };
-
- // Donate methods for a module.
- Opal.donate = function(module, jsid) {
- var included_in = module.$$dep,
- body = module.$$proto[jsid],
- i, length, includee, dest, current,
+ // 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;
- if (!included_in) {
- return;
+ 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 (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;
- }
+ for (j = 0, jj = klass_includees.length; j < jj; j++) {
+ if (klass_includees[j] === current.$$donated) {
+ current_owner_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;
+ if (klass_includees[j] === module) {
+ module_index = j;
}
}
- else {
- // neither a class, or module included by class, has defined method
+
+ // 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) {
- Opal.donate(includee, jsid);
- }
+ // 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;
+ modules, i, ii, j, jj;
while (parent) {
result.push(parent);
- for (var i=0; i < parent.$$inc.length; i++) {
+ for (i = parent.$$inc.length-1; i >= 0; i--) {
modules = Opal.ancestors(parent.$$inc[i]);
- for(var j = 0; j < modules.length; j++) {
+ for(j = 0, jj = modules.length; j < jj; j++) {
result.push(modules[j]);
}
}
// only the actual singleton class gets included in its ancestry
@@ -949,23 +1129,27 @@
// @return [undefined]
Opal.add_stubs = function(stubs) {
var subscriber, subscribers = Opal.stub_subscribers,
i, ilength = stubs.length,
j, jlength = subscribers.length,
- method_name, stub;
+ method_name, stub,
+ opal_stubs = Opal.stubs;
for (i = 0; i < ilength; i++) {
method_name = stubs[i];
- // 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(!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);
- if (!(method_name in subscriber)) {
- subscriber[method_name] = stub;
+ for (j = 0; j < jlength; j++) {
+ subscriber = subscribers[j];
+
+ if (!(method_name in subscriber)) {
+ subscriber[method_name] = stub;
+ }
}
}
}
};
@@ -1042,36 +1226,36 @@
// @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, jsid, current_func, defcheck, defs) {
- var 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, jsid, current_func);
+ dispatcher = Opal.find_obj_super_dispatcher(obj, mid, current_func);
}
- dispatcher = dispatcher['$' + jsid];
+ super_method = dispatcher['$' + mid];
- if (!defcheck && dispatcher.$$stub && Opal.Kernel.$method_missing === obj.$method_missing) {
+ 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 `'+jsid+"' for "+obj, jsid);
+ throw Opal.NoMethodError.$new('super: no superclass method `'+mid+"' for "+obj, mid);
}
- return dispatcher;
+ 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;
@@ -1089,22 +1273,21 @@
}
return Opal.find_super_dispatcher(obj, call_jsid, current_func, defcheck);
};
- Opal.find_obj_super_dispatcher = function(obj, jsid, current_func) {
+ 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()");
}
- jsid = '$' + jsid;
- return Opal.find_super_func(klass, jsid, current_func);
+ return Opal.find_super_func(klass, '$' + mid, current_func);
};
Opal.find_owning_class = function(klass, current_func) {
var owner = current_func.$$owner;
@@ -1231,32 +1414,31 @@
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) {
+ if (object.$$meta === klass || object.$$class === klass) {
return true;
}
- var i, length, ancestors = Opal.ancestors(object.$$class);
-
- for (i = 0, length = ancestors.length; i < length; i++) {
- if (ancestors[i] === klass) {
- return true;
- }
+ if (object.$$is_number && klass.$$is_number_class) {
+ return true;
}
- ancestors = Opal.ancestors(object.$$meta);
+ 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;
}
@@ -1381,52 +1563,45 @@
}
return Opal.hash2(keys, map);
};
- // Call a ruby method on a ruby object with some arguments:
+ // 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, 'reverse!') # => [4, 3, 2, 1]
+ // Opal.send(my_array, 'length') # => 4
+ // Opal.send(my_array, my_array.$length) # => 4
//
- // A missing method will be forwarded to the object via
- // method_missing.
+ // Opal.send(my_array, 'reverse!') # => [4, 3, 2, 1]
+ // Opal.send(my_array, my_array['$reverse!']') # => [4, 3, 2, 1]
//
- // The result of either call with be returned.
- //
- // @param recv [Object] the ruby object
- // @param mid [String] ruby method to call
- // @return [Object] forwards the return value of the method (or of method_missing)
- Opal.send = function(recv, mid) {
- var args_ary = new Array(Math.max(arguments.length - 2, 0));
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i + 2]; }
+ // @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;
- var func = recv['$' + mid];
-
- if (func) {
- return func.apply(recv, args_ary);
+ if (body != null) {
+ body.$$p = block;
+ return body.apply(recv, args);
}
- return recv.$method_missing.apply(recv, [mid].concat(args_ary));
- };
+ return recv.$method_missing.apply(recv, [method].concat(args));
+ }
- Opal.block_send = function(recv, mid, block) {
- var args_ary = new Array(Math.max(arguments.length - 3, 0));
- for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i + 3]; }
-
- var func = recv['$' + mid];
-
- if (func) {
- func.$$p = block;
- return func.apply(recv, args_ary);
- }
-
- return recv.$method_missing.apply(recv, [mid].concat(args_ary));
- };
-
// 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.
//
@@ -1459,33 +1634,44 @@
// @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.donate(obj, jsid);
+ Opal.update_includers(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--) {
- Opal.bridge_method(bridged[i], 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) {
@@ -1493,28 +1679,18 @@
}
return nil;
};
- // Define a singleton method on the given object.
+ // 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)
};
- 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
+ // 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());
}
@@ -1553,13 +1729,14 @@
};
Opal.alias = function(obj, name, old) {
var id = '$' + name,
old_id = '$' + old,
- body = obj.$$proto['$' + old];
+ body = obj.$$proto['$' + old],
+ alias;
- // instance_eval is being run on a class/module, so that need to alias class methods
+ // 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) {
@@ -1573,12 +1750,40 @@
if (typeof(body) !== "function" || body.$$stub) {
throw Opal.NameError.$new("undefined method `" + old + "' for class `" + obj.$name() + "'")
}
}
- Opal.defn(obj, id, body);
+ // 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,
@@ -1596,24 +1801,24 @@
// Hashes
// ------
Opal.hash_init = function(hash) {
- hash.$$smap = {};
- hash.$$map = {};
+ 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, length = keys.length, key, value; i < length; i++) {
- key = from_hash.$$keys[i];
+ 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 = from_hash.$$smap[key];
+ value = smap[key];
} else {
value = key.value;
key = key.key;
}
@@ -1621,20 +1826,21 @@
}
};
Opal.hash_put = function(hash, key, value) {
if (key.$$is_string) {
- if (!hash.$$smap.hasOwnProperty(key)) {
+ if (!$hasOwn.call(hash.$$smap, key)) {
hash.$$keys.push(key);
}
hash.$$smap[key] = value;
return;
}
- var key_hash = key.$hash(), bucket, last_bucket;
+ var key_hash, bucket, last_bucket;
+ key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
- if (!hash.$$map.hasOwnProperty(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;
}
@@ -1658,19 +1864,20 @@
}
};
Opal.hash_get = function(hash, key) {
if (key.$$is_string) {
- if (hash.$$smap.hasOwnProperty(key)) {
+ if ($hasOwn.call(hash.$$smap, key)) {
return hash.$$smap[key];
}
return;
}
- var key_hash = key.$hash(), bucket;
+ var key_hash, bucket;
+ key_hash = hash.$$by_identity ? Opal.id(key) : key.$hash();
- if (hash.$$map.hasOwnProperty(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;
@@ -1682,11 +1889,11 @@
Opal.hash_delete = function(hash, key) {
var i, keys = hash.$$keys, length = keys.length, value;
if (key.$$is_string) {
- if (!hash.$$smap.hasOwnProperty(key)) {
+ if (!$hasOwn.call(hash.$$smap, key)) {
return;
}
for (i = 0; i < length; i++) {
if (keys[i] === key) {
@@ -1700,11 +1907,11 @@
return value;
}
var key_hash = key.$hash();
- if (!hash.$$map.hasOwnProperty(key_hash)) {
+ if (!$hasOwn.call(hash.$$map, key_hash)) {
return;
}
var bucket = hash.$$map[key_hash], last_bucket;
@@ -1775,11 +1982,11 @@
bucket = bucket.next;
}
hash.$$keys[i].key_hash = key_hash;
- if (!hash.$$map.hasOwnProperty(key_hash)) {
+ if (!$hasOwn.call(hash.$$map, key_hash)) {
hash.$$map[key_hash] = hash.$$keys[i];
continue;
}
bucket = hash.$$map[key_hash];
@@ -1829,11 +2036,11 @@
}
if (arguments_length === 1) {
args = arguments[0];
for (key in args) {
- if (args.hasOwnProperty(key)) {
+ if ($hasOwn.call(args, key)) {
value = args[key];
Opal.hash_put(hash, key, value);
}
}
@@ -1853,20 +2060,20 @@
}
return hash;
};
- // hash2 is a faster creator for hashes that just use symbols and
+ // 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
+ // function.
//
Opal.hash2 = function(keys, smap) {
var hash = new Opal.Hash.$$alloc();
hash.$$smap = smap;
- hash.$$map = {};
+ hash.$$map = Object.create(null);
hash.$$keys = keys;
return hash;
};
@@ -1880,10 +2087,13 @@
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" ||
@@ -1902,10 +2112,25 @@
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'];
@@ -1995,15 +2220,20 @@
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);
- Opal.constants.push("BasicObject");
- Opal.constants.push("Object");
- Opal.constants.push("Module");
- Opal.constants.push("Class");
+ // 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;
@@ -2017,19 +2247,10 @@
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;
-
// Forward .toString() to #to_s
_Object.$$proto.toString = function() {
return this.$to_s();
};
@@ -2048,15 +2269,5 @@
Opal.breaker = new Error('unexpected break (old)');
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;
-}