opal/corelib/runtime.js in opal-1.5.1 vs opal/corelib/runtime.js in opal-1.6.0.alpha1

- old
+ new

@@ -21,14 +21,16 @@ if (typeof(globalThis) !== 'undefined') { global_object = globalThis; } else if (typeof(global) !== 'undefined') { global_object = global; } else if (typeof(window) !== 'undefined') { global_object = window; } // Setup a dummy console object if missing + if (global_object.console == null) { + global_object.console = {}; + } + 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 () {}; } @@ -63,11 +65,10 @@ // The Opal object that is exposed globally var Opal = global_object.Opal = {}; // This is a useful reference to global object inside ruby files Opal.global = global_object; - global_object.Opal = Opal; // Configure runtime behavior with regards to require and unsupported features Opal.config = { missing_require_severity: 'error', // error, warning, ignore unsupported_features_severity: 'warning', // error, warning, ignore @@ -88,45 +89,45 @@ // 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() { + function $uid() { unique_id += 2; return unique_id; }; + Opal.uid = $uid; // Retrieve or assign the id of an object Opal.id = function(obj) { if (obj.$$is_number) return (obj * 2)+1; - if (obj.$$id != null) { - return obj.$$id; + if (obj.$$id == null) { + $prop(obj, '$$id', $uid()); } - $prop(obj, '$$id', Opal.uid()); return obj.$$id; }; // Globals table - Opal.gvars = {}; + var $gvars = Opal.gvars = {}; // Exit function, this should be replaced by platform specific implementation // (See nodejs and chrome for examples) - Opal.exit = function(status) { if (Opal.gvars.DEBUG) console.log('Exited with status '+status); }; + Opal.exit = function(status) { if ($gvars.DEBUG) console.log('Exited with status '+status); }; // keeps track of exceptions for $! Opal.exceptions = []; // @private // Pops an exception from the stack and updates `$!`. Opal.pop_exception = function() { var exception = Opal.exceptions.pop(); if (exception) { - Opal.gvars["!"] = exception; - Opal.gvars["@"] = exception.$backtrace(); + $gvars["!"] = exception; + $gvars["@"] = exception.$backtrace(); } else { - Opal.gvars["!"] = Opal.gvars["@"] = nil; + $gvars["!"] = $gvars["@"] = nil; } }; function $prop(object, name, initialValue) { if (typeof(object) === "string") { @@ -306,11 +307,11 @@ function const_lookup_ancestors(cref, name) { var i, ii, ancestors; if (cref == null) return; - ancestors = Opal.ancestors(cref); + ancestors = $ancestors(cref); for (i = 0, ii = ancestors.length; i < ii; i++) { if (ancestors[i].$$const && $has_own.call(ancestors[i].$$const, name)) { return ancestors[i].$$const[name]; } else if (ancestors[i].$$autoload && ancestors[i].$$autoload[name]) { @@ -326,14 +327,12 @@ 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); - } + function const_missing(cref, name) { + 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; @@ -344,12 +343,12 @@ if (!cref.$$is_module && !cref.$$is_class) { throw new Opal.TypeError(cref.toString() + " is not a class/module"); } - result = const_get_name(cref, name); if (result != null) return result; - result = const_missing(cref, name, skip_missing); if (result != null) return result; + result = const_get_name(cref, name); + return result != null || skip_missing ? result : const_missing(cref, name); }; // 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) { @@ -383,11 +382,11 @@ cache[name] = [current_version, result]; } else { result = cached[1]; } - return result != null ? result : const_missing(cref, name, skip_missing); + return result != null || skip_missing ? result : const_missing(cref, name); }; // Initialize the top level constant cache generation counter Opal.const_cache_version = 1; @@ -411,11 +410,11 @@ cache[name] = [current_version, result]; } else { result = cached[1]; } - return result != null ? result : const_missing(cref, name, skip_missing); + return result != null || skip_missing ? result : const_missing(cref, name); }; // Register the constant on a cref and opportunistically set the name of // unnamed classes/modules. function $const_set(cref, name, value) { @@ -452,12 +451,12 @@ Opal.constants = function(cref, inherit) { if (inherit == null) inherit = true; var module, modules = [cref], i, ii, constants = {}, constant; - if (inherit) modules = modules.concat(Opal.ancestors(cref)); - if (inherit && cref.$$is_module) modules = modules.concat([Opal.Object]).concat(Opal.ancestors(Opal.Object)); + if (inherit) modules = modules.concat($ancestors(cref)); + if (inherit && cref.$$is_module) modules = modules.concat([Opal.Object]).concat($ancestors(Opal.Object)); for (i = 0, ii = modules.length; i < ii; i++) { module = modules[i]; // Do not show Objects constants unless we're querying Object itself @@ -532,37 +531,35 @@ // @param singleton [Boolean,null] a true value denotes we want to allocate // a singleton // // @return new [Class] or existing ruby class // - Opal.allocate_class = function(name, superclass, singleton) { - var klass, constructor; + function $allocate_class(name, superclass, singleton) { + var klass; if (superclass != null && superclass.$$bridge) { // Inheritance from bridged classes requires // calling original JS constructors - constructor = function() { + klass = function() { var args = $slice.call(arguments), self = new ($bind.apply(superclass.$$constructor, [null].concat(args)))(); // and replacing a __proto__ manually $set_proto(self, klass.$$prototype); return self; } } else { - constructor = function(){}; + klass = function(){}; } if (name && name !== nil) { - $prop(constructor, 'displayName', '::'+name); + $prop(klass, 'displayName', '::'+name); } - klass = constructor; - $prop(klass, '$$name', name); - $prop(klass, '$$constructor', constructor); - $prop(klass, '$$prototype', constructor.prototype); + $prop(klass, '$$constructor', klass); + $prop(klass, '$$prototype', klass.prototype); $prop(klass, '$$const', {}); $prop(klass, '$$is_class', true); $prop(klass, '$$is_a_module', true); $prop(klass, '$$super', superclass); $prop(klass, '$$cvars', {}); @@ -612,10 +609,11 @@ } } return klass; }; + Opal.allocate_class = $allocate_class; function find_existing_class(scope, name) { // Try to find the class in the current scope var klass = const_get_name(scope, name); @@ -665,41 +663,38 @@ } } var klass = find_existing_class(scope, name); - if (klass) { + if (klass != null) { if (superclass) { // Make sure existing class has same superclass ensureSuperclassMatch(klass, superclass); } - - if (Opal.trace_class) { invoke_tracers_for_class(klass); } - - return klass; } + else { + // Class doesn't exist, create a new one with given superclass... - // Class doesn't exist, create a new one with given superclass... + // Not specifying a superclass means we can assume it to be Object + if (superclass == null) { + superclass = _Object; + } - // Not specifying a superclass means we can assume it to be Object - if (superclass == null) { - superclass = _Object; - } + // Create the class object (instance of Class) + klass = $allocate_class(name, superclass); + $const_set(scope, name, klass); - // Create the class object (instance of Class) - klass = Opal.allocate_class(name, superclass); - $const_set(scope, name, klass); + // Call .inherited() hook with new class on the superclass + if (superclass.$inherited) { + superclass.$inherited(klass); + } - // Call .inherited() hook with new class on the superclass - if (superclass.$inherited) { - superclass.$inherited(klass); + if (bridged) { + Opal.bridge(bridged, klass); + } } - if (bridged) { - Opal.bridge(bridged, klass); - } - if (Opal.trace_class) { invoke_tracers_for_class(klass); } return klass; }; @@ -720,16 +715,12 @@ // // @param scope [Module, Class] class or module this definition is inside // @param id [String] the name of the new (or existing) module // // @return [Module] - Opal.allocate_module = function(name) { + function $allocate_module(name) { var constructor = function(){}; - if (name) { - $prop(constructor, 'displayName', name+'.$$constructor'); - } - var module = constructor; if (name) $prop(constructor, 'displayName', name+'.constructor'); @@ -747,10 +738,11 @@ $set_proto(module, Opal.Module.prototype); return module; }; + Opal.allocate_module = $allocate_module; function find_existing_module(scope, name) { var module = const_get_name(scope, name); if (module == null && scope === _Object) module = const_lookup_ancestors(_Object, name); @@ -774,21 +766,16 @@ scope = scope.$$class; } module = find_existing_module(scope, name); - if (module) { - - if (Opal.trace_class) { invoke_tracers_for_class(module); } - - return module; + if (module == null) { + // Module doesnt exist, create a new one... + module = $allocate_module(name); + $const_set(scope, name, module); } - // Module doesnt exist, create a new one... - module = Opal.allocate_module(name); - $const_set(scope, name, module); - if (Opal.trace_class) { invoke_tracers_for_class(module); } return module; }; @@ -815,37 +802,51 @@ } else { return Opal.build_object_singleton_class(object); } }; + // helper to set $$meta on klass, module or instance + function set_meta(obj, meta) { + if (obj.hasOwnProperty('$$meta')) { + obj.$$meta = meta; + } else { + $prop(obj, '$$meta', meta); + } + if (obj.$$frozen) { + // If a object is frozen (sealed), freeze $$meta too. + // No need to inject $$meta.$$prototype in the prototype chain, + // as $$meta cannot be modified anyway. + obj.$$meta.$freeze(); + } else { + $set_proto(obj, meta.$$prototype); + } + }; + // Build the singleton class for an existing class. Class object are built // with their singleton class already in the prototype chain and inheriting // from their superclass object (up to `Class` itself). // // NOTE: Actually in MRI a class' singleton class inherits from its // superclass' singleton class which in turn inherits from Class. // // @param klass [Class] // @return [Class] Opal.build_class_singleton_class = function(klass) { - var superclass, meta; - if (klass.$$meta) { return klass.$$meta; } // The singleton_class superclass is the singleton_class of its superclass; // but BasicObject has no superclass (its `$$super` is null), thus we // fallback on `Class`. - superclass = klass === BasicObject ? Class : Opal.get_singleton_class(klass.$$super); + var superclass = klass === BasicObject ? Class : Opal.get_singleton_class(klass.$$super); - meta = Opal.allocate_class(null, superclass, true); + var meta = $allocate_class(null, superclass, true); $prop(meta, '$$is_singleton', true); $prop(meta, '$$singleton_of', klass); - $prop(klass, '$$meta', meta); - $set_proto(klass, meta.$$prototype); + set_meta(klass, meta); // Restoring ClassName.class $prop(klass, '$$class', Opal.Class); return meta; }; @@ -853,16 +854,15 @@ Opal.build_module_singleton_class = function(mod) { if (mod.$$meta) { return mod.$$meta; } - var meta = Opal.allocate_class(null, Opal.Module, true); + var meta = $allocate_class(null, Opal.Module, true); $prop(meta, '$$is_singleton', true); $prop(meta, '$$singleton_of', mod); - $prop(mod, '$$meta', meta); - $set_proto(mod, meta.$$prototype); + set_meta(mod, meta); // Restoring ModuleName.class $prop(mod, '$$class', Opal.Module); return meta; }; @@ -871,30 +871,28 @@ // // @param object [Object] // @return [Class] Opal.build_object_singleton_class = function(object) { var superclass = object.$$class, - klass = Opal.allocate_class(nil, superclass, true); + klass = $allocate_class(nil, superclass, true); $prop(klass, '$$is_singleton', true); $prop(klass, '$$singleton_of', object); delete klass.$$prototype.$$class; - $prop(object, '$$meta', klass); + set_meta(object, klass); - $set_proto(object, object.$$meta.$$prototype); - return klass; }; Opal.is_method = function(prop) { return (prop[0] === '$' && prop[1] !== '$'); }; Opal.instance_methods = function(mod) { - var exclude = [], results = [], ancestors = Opal.ancestors(mod); + var exclude = [], results = [], ancestors = $ancestors(mod); for (var i = 0, l = ancestors.length; i < l; i++) { var ancestor = ancestors[i], proto = ancestor.$$prototype; @@ -954,16 +952,11 @@ Opal.methods = function(obj) { return Opal.instance_methods(obj.$$meta || obj.$$class); }; Opal.own_methods = function(obj) { - if (obj.$$meta) { - return Opal.own_instance_methods(obj.$$meta); - } - else { - return []; - } + return obj.$$meta ? Opal.own_instance_methods(obj.$$meta) : []; }; Opal.receiver_methods = function(obj) { var mod = Opal.get_singleton_class(obj); var singleton_methods = Opal.own_instance_methods(mod); @@ -976,11 +969,11 @@ // and its ancestors. // // @param module [Module] // @return [Object] Opal.class_variables = function(module) { - var ancestors = Opal.ancestors(module), + var ancestors = $ancestors(module), i, length = ancestors.length, result = {}; for (i = length - 1; i >= 0; i--) { var ancestor = ancestors[i]; @@ -998,11 +991,11 @@ // // @param module [Module] // @param name [String] // @param value [Object] Opal.class_variable_set = function(module, name, value) { - var ancestors = Opal.ancestors(module), + var ancestors = $ancestors(module), i, length = ancestors.length; for (i = length - 2; i >= 0; i--) { var ancestor = ancestors[i]; @@ -1023,11 +1016,11 @@ // @param name [String] Opal.class_variable_get = function(module, name, tolerant) { if ($has_own.call(module.$$cvars, name)) return module.$$cvars[name]; - var ancestors = Opal.ancestors(module), + var ancestors = $ancestors(module), i, length = ancestors.length; for (i = 0; i < length; i++) { var ancestor = ancestors[i]; @@ -1103,11 +1096,11 @@ // // @param module [Module] the module to include // @param includer [Module] the target class to include module into // @return [null] Opal.append_features = function(module, includer) { - var module_ancestors = Opal.ancestors(module); + var module_ancestors = $ancestors(module); var iclasses = []; if (module_ancestors.indexOf(includer) !== -1) { throw Opal.ArgumentError.$new('cyclic include detected'); } @@ -1115,11 +1108,11 @@ for (var i = 0, length = module_ancestors.length; i < length; i++) { var ancestor = module_ancestors[i], iclass = create_iclass(ancestor); $prop(iclass, '$$included', true); iclasses.push(iclass); } - var includer_ancestors = Opal.ancestors(includer), + var includer_ancestors = $ancestors(includer), chain = chain_iclasses(iclasses), start_chain_after, end_chain_on; if (includer_ancestors.indexOf(module) === -1) { @@ -1208,11 +1201,11 @@ // iclass(module) // | // iclass(prepender) // | // parent - var module_ancestors = Opal.ancestors(module); + var module_ancestors = $ancestors(module); var iclasses = []; if (module_ancestors.indexOf(prepender) !== -1) { throw Opal.ArgumentError.$new('cyclic prepend detected'); } @@ -1247,11 +1240,11 @@ // dummy(prepender) -> iclass(prepender) -> previous_parent $set_proto(dummy_prepender, prepender_iclass); $set_proto(prepender_iclass, previous_parent); } - var prepender_ancestors = Opal.ancestors(prepender); + var prepender_ancestors = $ancestors(prepender); if (prepender_ancestors.indexOf(module) === -1) { // first time prepend start_chain_after = dummy_prepender; @@ -1407,11 +1400,11 @@ function own_ancestors(module) { return module.$$own_prepended_modules.concat([module]).concat(module.$$own_included_modules); } // The Array of ancestors for a given module/class - Opal.ancestors = function(module) { + function $ancestors(module) { if (!module) { return []; } if (module.$$ancestors_cache_version === Opal.const_cache_version) { return module.$$ancestors; } @@ -1421,20 +1414,21 @@ for (i = 0, mods = own_ancestors(module), length = mods.length; i < length; i++) { result.push(mods[i]); } if (module.$$super) { - for (i = 0, mods = Opal.ancestors(module.$$super), length = mods.length; i < length; i++) { + for (i = 0, mods = $ancestors(module.$$super), length = mods.length; i < length; i++) { result.push(mods[i]); } } module.$$ancestors_cache_version = Opal.const_cache_version; module.$$ancestors = result; return result; }; + Opal.ancestors = $ancestors; Opal.included_modules = function(module) { var result = [], mod = null, proto = Object.getPrototypeOf(module.$$prototype); for (; proto && Object.getPrototypeOf(proto); proto = Object.getPrototypeOf(proto)) { @@ -1512,11 +1506,11 @@ function method_missing_stub() { // Copy any given block onto the method_missing dispatcher this.$method_missing.$$p = method_missing_stub.$$p; // Set block property to null ready for the next call (stop false-positives) - delete method_missing_stub.$$p; + method_missing_stub.$$p = null; // call method missing with correct args (remove '$' prefix on method name) var args_ary = new Array(arguments.length); for(var i = 0, l = args_ary.length; i < l; i++) { args_ary[i] = arguments[i]; } @@ -1562,19 +1556,23 @@ var inspect = "`block in " + context + "'"; throw Opal.ArgumentError.$new(inspect + ': wrong number of arguments (given ' + actual + ', expected ' + expected + ')'); }; + function get_ancestors(obj) { + if (obj.hasOwnProperty('$$meta') && obj.$$meta !== null) { + return $ancestors(obj.$$meta); + } else { + return $ancestors(obj.$$class); + } + }; + // Super dispatcher Opal.find_super = function(obj, mid, current_func, defcheck, allow_stubs) { var jsid = '$' + mid, ancestors, super_method; - if (obj.hasOwnProperty('$$meta')) { - ancestors = Opal.ancestors(obj.$$meta); - } else { - ancestors = Opal.ancestors(obj.$$class); - } + ancestors = get_ancestors(obj); var current_index = ancestors.indexOf(current_func.$$owner); for (var i = current_index + 1; i < ancestors.length; i++) { var ancestor = ancestors[i], @@ -1624,34 +1622,10 @@ Opal.find_super_dispatcher = Opal.find_super; // @deprecated Opal.find_iter_super_dispatcher = Opal.find_block_super; - // Used to return as an expression. Sometimes, we can't simply return from - // a javascript function as if we were a method, as the return is used as - // an expression, or even inside a block which must "return" to the outer - // method. This helper simply throws an error which is then caught by the - // method. This approach is expensive, so it is only used when absolutely - // needed. - // - Opal.ret = function(val) { - Opal.returner.$v = val; - throw Opal.returner; - }; - - // Used to break out of a block. - Opal.brk = function(val, breaker) { - breaker.$v = val; - throw breaker; - }; - - // Builds a new unique breaker, this is to avoid multiple nested breaks to get - // in the way of each other. - Opal.new_brk = function() { - return new Error('unexpected break'); - }; - // handles yield calls for 1 yielded arg Opal.yield1 = function(block, arg) { if (typeof(block) !== "function") { throw Opal.LocalJumpError.$new("no block given"); } @@ -1704,16 +1678,13 @@ if (result) { return result; } } - else if (candidate === Opal.JS.Error) { + else if (candidate === Opal.JS.Error || candidate['$==='](exception)) { return candidate; } - else if (candidate['$==='](exception)) { - return candidate; - } } return null; }; @@ -1724,11 +1695,11 @@ if (object.$$is_number && klass.$$is_number_class) { return (klass.$$is_integer_class) ? (object % 1) === 0 : true; } - var ancestors = Opal.ancestors(object.$$is_class ? Opal.get_singleton_class(object) : (object.$$meta || object.$$class)); + var ancestors = $ancestors(object.$$is_class ? Opal.get_singleton_class(object) : (object.$$meta || object.$$class)); return ancestors.indexOf(klass) !== -1; }; // Helpers for extracting kwsplats @@ -1813,15 +1784,12 @@ // Opal.extract_kwargs = function(parameters) { var kwargs = parameters[parameters.length - 1]; if (kwargs != null && Opal.respond_to(kwargs, '$to_hash', true)) { $splice.call(parameters, parameters.length - 1); - return kwargs.$to_hash(); + return kwargs; } - else { - return Opal.hash2([], {}); - } }; // Used to get a list of rest keyword arguments. Method takes the given // keyword args, i.e. the hash literal passed to the method containing all // keyword arguemnts passed to method, as well as the used args which are @@ -1911,37 +1879,37 @@ }; Opal.refined_send = function(refinement_groups, recv, method, args, block, blockopts) { var i, j, k, ancestors, ancestor, refinements, refinement, refine_modules, refine_module, body; - if (recv.hasOwnProperty('$$meta')) { - ancestors = Opal.ancestors(recv.$$meta); - } else { - ancestors = Opal.ancestors(recv.$$class); - } + ancestors = get_ancestors(recv); // For all ancestors that there are, starting from the closest to the furthest... for (i = 0; i < ancestors.length; i++) { ancestor = Opal.id(ancestors[i]); + // For all refinement groups there are, starting from the closest scope to the furthest... for (j = 0; j < refinement_groups.length; j++) { refinements = refinement_groups[j]; + // For all refinements there are, starting from the last `using` call to the furthest... for (k = refinements.length - 1; k >= 0; k--) { refinement = refinements[k]; if (typeof refinement.$$refine_modules === 'undefined') continue; + // A single module being given as an argument of the `using` call contains multiple // refinement modules refine_modules = refinement.$$refine_modules; + // Does this module refine a given call for a given ancestor module? - if (typeof refine_modules[ancestor] !== 'undefined') { - refine_module = refine_modules[ancestor]; - // Does this module define a method we want to call? - if (typeof refine_module.$$prototype['$'+method] !== 'undefined') { - body = refine_module.$$prototype['$'+method]; - return Opal.send2(recv, body, method, args, block, blockopts); - } + if (typeof refine_modules[ancestor] === 'undefined') continue; + refine_module = refine_modules[ancestor]; + + // Does this module define a method we want to call? + if (typeof refine_module.$$prototype['$'+method] !== 'undefined') { + body = refine_module.$$prototype['$'+method]; + return Opal.send2(recv, body, method, args, block, blockopts); } } } } @@ -2010,10 +1978,12 @@ } }; // Define method on a module or class (see Opal.def). Opal.defn = function(module, jsid, body) { + $deny_frozen_access(module); + body.displayName = jsid; body.$$owner = module; var name = jsid.substr(1); @@ -2147,11 +2117,11 @@ args = new Array(arguments.length); for(i = 0, ii = arguments.length; i < ii; i++) { args[i] = arguments[i]; } - delete alias.$$p; + alias.$$p = null; return Opal.send(this, body, args, block); }; // Assign the 'length' value with defineProperty because @@ -2175,18 +2145,18 @@ return obj; }; Opal.alias_gvar = function(new_name, old_name) { - Object.defineProperty(Opal.gvars, new_name, { + Object.defineProperty($gvars, new_name, { configurable: true, enumerable: true, get: function() { - return Opal.gvars[old_name]; + return $gvars[old_name]; }, set: function(new_value) { - Opal.gvars[old_name] = new_value; + $gvars[old_name] = new_value; } }); return nil; } @@ -2428,39 +2398,41 @@ } hash = new Opal.Hash(); Opal.hash_init(hash); - if (arguments_length === 1 && arguments[0].$$is_array) { + if (arguments_length === 1) { 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()); - } + if (arguments[0].$$is_array) { + length = args.length; - key = args[i][0]; - value = args[i][1]; + for (i = 0; i < length; i++) { + if (args[i].length !== 2) { + throw Opal.ArgumentError.$new("value not of length 2: " + args[i].$inspect()); + } - Opal.hash_put(hash, key, value); - } + key = args[i][0]; + value = args[i][1]; - return hash; - } - - if (arguments_length === 1) { - args = arguments[0]; - for (key in args) { - if ($has_own.call(args, key)) { - value = args[key]; - Opal.hash_put(hash, key, value); } + + return hash; } + else { + args = arguments[0]; + for (key in args) { + if ($has_own.call(args, key)) { + value = args[key]; - return hash; + Opal.hash_put(hash, key, value); + } + } + + return hash; + } } if (arguments_length % 2 !== 0) { throw Opal.ArgumentError.$new("odd number of arguments for Hash"); } @@ -2500,35 +2472,94 @@ range.excl = exc; return range; }; + var reserved_ivar_names = [ + // properties + "constructor", "displayName", "__count__", "__noSuchMethod__", + "__parent__", "__proto__", + // methods + "hasOwnProperty", "valueOf" + ]; + // Get the ivar name for a given name. // Mostly adds a trailing $ to reserved names. // Opal.ivar = function(name) { - if ( - // properties - name === "constructor" || - name === "displayName" || - name === "__count__" || - name === "__noSuchMethod__" || - name === "__parent__" || - name === "__proto__" || - - // methods - name === "hasOwnProperty" || - name === "valueOf" - ) - { - return name + "$"; + if (reserved_ivar_names.indexOf(name) !== -1) { + name += "$"; } return name; }; + // Support for #freeze + // ------------------- + // helper that can be used from methods + function $deny_frozen_access(obj) { + if (obj.$$frozen) { + throw Opal.FrozenError.$new("can't modify frozen " + (obj.$class()) + ": " + (obj), Opal.hash2(["receiver"], {"receiver": obj})); + } + }; + Opal.deny_frozen_access = $deny_frozen_access; + + // common #freeze runtime support + Opal.freeze = function(obj) { + $prop(obj, "$$frozen", true); + + // set $$id + if (!obj.hasOwnProperty('$$id')) { $prop(obj, '$$id', $uid()); } + + if (obj.hasOwnProperty('$$meta')) { + // freeze $$meta if it has already been set + obj.$$meta.$freeze(); + } else { + // ensure $$meta can be set lazily, $$meta is frozen when set in runtime.js + $prop(obj, '$$meta', null); + } + + // $$comparable is used internally and set multiple times + // defining it before sealing ensures it can be modified later on + if (!obj.hasOwnProperty('$$comparable')) { $prop(obj, '$$comparable', null); } + + // seal the Object + Object.seal(obj); + + return obj; + }; + + // freze props, make setters of instance variables throw FrozenError + Opal.freeze_props = function(obj) { + var prop, prop_type, desc; + + for(prop in obj) { + prop_type = typeof(prop); + + // prop_type "object" here is a String(), skip $ props + if ((prop_type === "string" || prop_type === "object") && prop[0] === '$') { + continue; + } + + desc = Object.getOwnPropertyDescriptor(obj, prop); + if (desc && desc.enumerable && desc.writable) { + // create closure to retain current value as cv + // for Opal 2.0 let for cv should do the trick, instead of a function + (function() { + // set v to undefined, as if the property is not set + var cv = obj[prop]; + Object.defineProperty(obj, prop, { + get: function() { return cv; }, + set: function(_val) { $deny_frozen_access(obj); }, + enumerable: true + }); + })(); + } + } + }; + // Regexps // ------- // Escape Regexp special chars letting the resulting string be used to build // a new Regexp. @@ -2558,25 +2589,27 @@ // Create a global multiline Regexp from a RegExp object and cache the result // on the object itself ($$gm or $$g attribute). // Opal.global_multiline_regexp = function(pattern) { - var result; + var result, flags; + + // RegExp already has the global and multiline flag + if (pattern.global && pattern.multiline) return pattern; + + flags = 'gm' + (pattern.ignoreCase ? 'i' : ''); if (pattern.multiline) { - if (pattern.global) { - return pattern; // RegExp already has the global and multiline flag - } // we are using the $$g attribute because the Regexp is already multiline - if (pattern.$$g != null) { - result = pattern.$$g; - } else { - result = pattern.$$g = new RegExp(pattern.source, 'gm' + (pattern.ignoreCase ? 'i' : '')); + if (pattern.$$g == null) { + pattern.$$g = new RegExp(pattern.source, flags); } - } else if (pattern.$$gm != null) { - result = pattern.$$gm; + result = pattern.$$g; } else { - result = pattern.$$gm = new RegExp(pattern.source, 'gm' + (pattern.ignoreCase ? 'i' : '')); + if (pattern.$$gm == null) { + pattern.$$gm = new RegExp(pattern.source, flags); + } + result = pattern.$$gm; } result.lastIndex = null; // reset lastIndex property return result; }; @@ -2648,13 +2681,11 @@ Opal.loaded_features.push(path); Opal.require_table[path] = true; } }; - Opal.load = function(path) { - path = Opal.normalize(path); - + Opal.load_normalized = function(path) { Opal.loaded([path]); var module = Opal.modules[path]; if (module) { @@ -2682,18 +2713,24 @@ } return true; }; + Opal.load = function(path) { + path = Opal.normalize(path); + + return Opal.load_normalized(path); + }; + Opal.require = function(path) { path = Opal.normalize(path); if (Opal.require_table[path]) { return false; } - return Opal.load(path); + return Opal.load_normalized(path); }; // Strings // ------- @@ -2773,46 +2810,41 @@ } } // Operator helpers // ---------------- - Opal.rb_plus = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l + r : l['$+'](r); } - Opal.rb_minus = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l - r : l['$-'](r); } - Opal.rb_times = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l * r : l['$*'](r); } - Opal.rb_divide = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l / r : l['$/'](r); } - Opal.rb_lt = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l < r : l['$<'](r); } - Opal.rb_gt = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l > r : l['$>'](r); } - Opal.rb_le = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l <= r : l['$<='](r); } - Opal.rb_ge = function(l,r) { return (typeof(l) === 'number' && typeof(r) === 'number') ? l >= r : l['$>='](r); } + function are_both_numbers(l,r) { return typeof(l) === 'number' && typeof(r) === 'number' } + + Opal.rb_plus = function(l,r) { return are_both_numbers(l,r) ? l + r : l['$+'](r); } + Opal.rb_minus = function(l,r) { return are_both_numbers(l,r) ? l - r : l['$-'](r); } + Opal.rb_times = function(l,r) { return are_both_numbers(l,r) ? l * r : l['$*'](r); } + Opal.rb_divide = function(l,r) { return are_both_numbers(l,r) ? l / r : l['$/'](r); } + Opal.rb_lt = function(l,r) { return are_both_numbers(l,r) ? l < r : l['$<'](r); } + Opal.rb_gt = function(l,r) { return are_both_numbers(l,r) ? l > r : l['$>'](r); } + Opal.rb_le = function(l,r) { return are_both_numbers(l,r) ? l <= r : l['$<='](r); } + Opal.rb_ge = function(l,r) { return are_both_numbers(l,r) ? l >= r : l['$>='](r); } + // Optimized helpers for calls like $truthy((a)['$==='](b)) -> $eqeqeq(a, b) + function are_both_numbers_or_strings(lhs, rhs) { + return (typeof lhs === 'number' && typeof rhs === 'number') || + (typeof lhs === 'string' && typeof rhs === 'string'); + } + function $eqeq(lhs, rhs) { - if ((typeof lhs === 'number' && typeof rhs === 'number') || - (typeof lhs === 'string' && typeof rhs === 'string')) { - return lhs === rhs; - } - return $truthy((lhs)['$=='](rhs)); + return are_both_numbers_or_strings(lhs,rhs) ? lhs === rhs : $truthy((lhs)['$=='](rhs)); }; Opal.eqeq = $eqeq; - Opal.eqeqeq = function(lhs, rhs) { - if ((typeof lhs === 'number' && typeof rhs === 'number') || - (typeof lhs === 'string' && typeof rhs === 'string')) { - return lhs === rhs; - } - return $truthy((lhs)['$==='](rhs)); + return are_both_numbers_or_strings(lhs,rhs) ? lhs === rhs : $truthy((lhs)['$==='](rhs)); }; Opal.neqeq = function(lhs, rhs) { - if ((typeof lhs === 'number' && typeof rhs === 'number') || - (typeof lhs === 'string' && typeof rhs === 'string')) { - return lhs !== rhs; - } - return $truthy((lhs)['$!='](rhs)); + return are_both_numbers_or_strings(lhs,rhs) ? lhs !== rhs : $truthy((lhs)['$!='](rhs)); }; Opal.not = function(arg) { - if (true === arg) return false; if (undefined === arg || null === arg || false === arg || nil === arg) return true; + if (true === arg || arg['$!'].$$pristine) return false; return $truthy(arg['$!']()); } // Shortcuts - optimized function generators for simple kinds of functions function $return_val(arg) { @@ -2825,41 +2857,61 @@ Opal.return_self = function() { return this; } Opal.return_ivar = function(ivar) { return function() { - if (this[ivar] == null) this[ivar] = nil; + if (this[ivar] == null) { return nil; } return this[ivar]; } } Opal.assign_ivar = function(ivar) { return function(val) { + $deny_frozen_access(this); return this[ivar] = val; } } Opal.assign_ivar_val = function(ivar, static_val) { return function() { + $deny_frozen_access(this); return this[ivar] = static_val; } } + // Primitives for handling parameters + Opal.ensure_kwargs = function(kwargs) { + if (kwargs == null) { + return Opal.hash2([], {}); + } else if (kwargs.$$is_hash) { + return kwargs; + } else { + throw Opal.ArgumentError.$new('expected kwargs'); + } + } + + Opal.get_kwarg = function(kwargs, key) { + if (!$has_own.call(kwargs.$$smap, key)) { + throw Opal.ArgumentError.$new('missing keyword: '+key); + } + return kwargs.$$smap[key]; + } + // Initialization // -------------- - Opal.BasicObject = BasicObject = Opal.allocate_class('BasicObject', null); - Opal.Object = _Object = Opal.allocate_class('Object', Opal.BasicObject); - Opal.Module = Module = Opal.allocate_class('Module', Opal.Object); - Opal.Class = Class = Opal.allocate_class('Class', Opal.Module); - Opal.Opal = _Opal = Opal.allocate_module('Opal'); - Opal.Kernel = Kernel = Opal.allocate_module('Kernel'); + Opal.BasicObject = BasicObject = $allocate_class('BasicObject', null); + Opal.Object = _Object = $allocate_class('Object', Opal.BasicObject); + Opal.Module = Module = $allocate_class('Module', Opal.Object); + Opal.Class = Class = $allocate_class('Class', Opal.Module); + Opal.Opal = _Opal = $allocate_module('Opal'); + Opal.Kernel = Kernel = $allocate_module('Kernel'); $set_proto(Opal.BasicObject, Opal.Class.$$prototype); $set_proto(Opal.Object, Opal.Class.$$prototype); $set_proto(Opal.Module, Opal.Class.$$prototype); $set_proto(Opal.Class, Opal.Class.$$prototype); // BasicObject can reach itself, avoid const_set to skip the $$base_module logic - BasicObject.$$const["BasicObject"] = BasicObject; + BasicObject.$$const.BasicObject = BasicObject; // Assign basic constants $const_set(_Object, "BasicObject", BasicObject); $const_set(_Object, "Object", _Object); $const_set(_Object, "Module", Module); @@ -2897,21 +2949,38 @@ // Foward calls to define_method on the top object to Object function top_define_method() { var args = Opal.slice.call(arguments); var block = top_define_method.$$p; - delete top_define_method.$$p; + top_define_method.$$p = null; return Opal.send(_Object, 'define_method', args, block) }; // Nil - Opal.NilClass = Opal.allocate_class('NilClass', Opal.Object); + Opal.NilClass = $allocate_class('NilClass', Opal.Object); $const_set(_Object, 'NilClass', Opal.NilClass); nil = Opal.nil = new Opal.NilClass(); nil.$$id = nil_id; nil.call = nil.apply = function() { throw Opal.LocalJumpError.$new('no block given'); }; + nil.$$frozen = true; + nil.$$comparable = false; + Object.seal(nil); - // Errors - Opal.breaker = new Error('unexpected break (old)'); - Opal.returner = new Error('unexpected return'); + Opal.thrower = function(type) { + var thrower = new Error('unexpected '+type); + thrower.$thrower_type = type; + thrower.$throw = function(value) { + if (value == null) value = nil; + thrower.$v = value; + throw thrower; + }; + return thrower; + }; + + Opal.t_eval_return = Opal.thrower("return"); + TypeError.$$super = Error; + + // If enable-file-source-embed compiler option is enabled, each module loaded will add its + // sources to this object + Opal.file_sources = {}; }).call(this);