opal/corelib/runtime.js in opal-1.8.0.alpha1 vs opal/corelib/runtime.js in opal-1.8.0.beta1

- old
+ new

@@ -117,18 +117,20 @@ // keeps track of exceptions for $! Opal.exceptions = []; // @private // Pops an exception from the stack and updates `$!`. - Opal.pop_exception = function() { + Opal.pop_exception = function(rescued_exception) { var exception = Opal.exceptions.pop(); - if (exception) { + if (exception === rescued_exception) { + // Current $! is raised in the rescue block, so we don't update it + } + else if (exception) { $gvars["!"] = exception; - $gvars["@"] = exception.$backtrace(); } else { - $gvars["!"] = $gvars["@"] = nil; + $gvars["!"] = nil; } }; // A helper function for raising things, that gracefully degrades if necessary // functionality is not yet loaded. @@ -150,10 +152,18 @@ else { throw klass.$new(message); } } + // Reuse the same object for performance/memory sake + var prop_options = { + value: undefined, + enumerable: false, + configurable: true, + writable: true + }; + function $prop(object, name, initialValue) { if (typeof(object) === "string") { // Special case for: // s = "string" // def s.m; end @@ -161,16 +171,12 @@ // + compiles to JS primitive // + allows method definition directly on instances // numbers, true, false and null do not support it. object[name] = initialValue; } else { - Object.defineProperty(object, name, { - value: initialValue, - enumerable: false, - configurable: true, - writable: true - }); + prop_options.value = initialValue; + Object.defineProperty(object, name, prop_options); } } Opal.prop = $prop; @@ -605,10 +611,11 @@ $prop(klass, '$$own_included_modules', []); $prop(klass, '$$own_prepended_modules', []); $prop(klass, '$$ancestors', []); $prop(klass, '$$ancestors_cache_version', null); $prop(klass, '$$subclasses', []); + $prop(klass, '$$cloned_from', []); $prop(klass.$$prototype, '$$class', klass); // By default if there are no singleton class methods // __proto__ is Class.prototype @@ -772,10 +779,11 @@ $prop(module, '$$iclasses', []); $prop(module, '$$own_included_modules', []); $prop(module, '$$own_prepended_modules', []); $prop(module, '$$ancestors', [module]); $prop(module, '$$ancestors_cache_version', null); + $prop(module, '$$cloned_from', []); $set_proto(module, Opal.Module.prototype); return module; }; @@ -1604,19 +1612,26 @@ } }; // Super dispatcher Opal.find_super = function(obj, mid, current_func, defcheck, allow_stubs) { - var jsid = $jsid(mid), ancestors, super_method; + var jsid = $jsid(mid), ancestors, ancestor, super_method, method_owner, current_index = -1, i; ancestors = get_ancestors(obj); + method_owner = current_func.$$owner; - var current_index = ancestors.indexOf(current_func.$$owner); + for (i = 0; i < ancestors.length; i++) { + ancestor = ancestors[i]; + if (ancestor === method_owner || ancestor.$$cloned_from.indexOf(method_owner) !== -1) { + current_index = i; + break; + } + } - for (var i = current_index + 1; i < ancestors.length; i++) { - var ancestor = ancestors[i], - proto = ancestor.$$prototype; + for (i = current_index + 1; i < ancestors.length; i++) { + ancestor = ancestors[i]; + var proto = ancestor.$$prototype; if (proto.hasOwnProperty('$$dummy')) { proto = proto.$$define_methods_on; } @@ -1660,27 +1675,45 @@ Opal.find_super_dispatcher = Opal.find_super; // @deprecated Opal.find_iter_super_dispatcher = Opal.find_block_super; + function call_lambda(block, arg, ret) { + try { + block(arg); + } catch (e) { + if (e === ret) { + return ret.$v; + } + throw e; + } + } + // handles yield calls for 1 yielded arg Opal.yield1 = function(block, arg) { if (typeof(block) !== "function") { $raise(Opal.LocalJumpError, "no block given"); } var has_mlhs = block.$$has_top_level_mlhs_arg, - has_trailing_comma = block.$$has_trailing_comma_in_args; + has_trailing_comma = block.$$has_trailing_comma_in_args, + is_returning_lambda = block.$$is_lambda && block.$$ret; if (block.length > 1 || ((has_mlhs || has_trailing_comma) && block.length === 1)) { arg = Opal.to_ary(arg); } if ((block.length > 1 || (has_trailing_comma && block.length === 1)) && arg.$$is_array) { + if (is_returning_lambda) { + return call_lambda(block.apply.bind(block, null), arg, block.$$ret); + } return block.apply(null, arg); } else { + if (is_returning_lambda) { + return call_lambda(block, arg, block.$$ret); + } return block(arg); } }; // handles yield for > 1 yielded arg @@ -1689,14 +1722,17 @@ $raise(Opal.LocalJumpError, "no block given"); } if (block.length > 1 && args.length === 1) { if (args[0].$$is_array) { - return block.apply(null, args[0]); + args = args[0]; } } + if (block.$$is_lambda && block.$$ret) { + return call_lambda(block.apply.bind(block, null), args, block.$$ret); + } 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. @@ -1852,13 +1888,18 @@ Object.assign(block, blockopts); } } // Optimization for a costly operation of prepending '$' to method names - var jsid_cache = {} + var jsid_cache = new Map(); function $jsid(name) { - return jsid_cache[name] || (jsid_cache[name] = '$' + name); + var jsid = jsid_cache.get(name); + if (!jsid) { + jsid = '$' + name; + jsid_cache.set(name, jsid); + } + return jsid; } Opal.jsid = $jsid; function $prepend(first, second) { if (!second.$$is_array) second = $slice(second); @@ -2164,38 +2205,44 @@ // to keep the max depth at 1. if (body.$$alias_of) body = body.$$alias_of; // We need a wrapper because otherwise properties // would be overwritten on the original body. - alias = function() { - var block = alias.$$p, i, ii; + alias = Opal.wrapMethodBody(body); - alias.$$p = null; + // Try to make the browser pick the right name + alias.displayName = name; + alias.$$alias_of = body; + alias.$$alias_name = name; + Opal.defn(obj, id, alias); + + return obj; + }; + + Opal.wrapMethodBody = function(body) { + var wrapped = function() { + var block = wrapped.$$p; + + wrapped.$$p = null; + return Opal.send(this, body, arguments, block); }; // Assign the 'length' value with defineProperty because // in strict mode the property is not writable. // It doesn't work in older browsers (like Chrome 38), where // an exception is thrown breaking Opal altogether. try { - Object.defineProperty(alias, 'length', { value: body.length }); + Object.defineProperty(wrapped, 'length', { value: body.length }); } catch (e) {} - // Try to make the browser pick the right name - alias.displayName = name; + wrapped.$$arity = body.$$arity == null ? body.length : body.$$arity; + wrapped.$$parameters = body.$$parameters; + wrapped.$$source_location = body.$$source_location; - alias.$$arity = body.$$arity == null ? body.length : 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; + return wrapped; }; Opal.alias_gvar = function(new_name, old_name) { Object.defineProperty($gvars, new_name, { configurable: true, @@ -2262,11 +2309,11 @@ var objects = keys.get(key_hash), object; for (var i=0; i<objects.length; i++) { - object = objects[0]; + object = objects[i]; if (key === object || key['$eql?'](object)) { hash.set(object, value); return; } } @@ -2920,10 +2967,24 @@ } } return array; } + // Opal32-checksum algorithm for #hash + // ----------------------------------- + Opal.opal32_init = $return_val(0x4f70616c); + + function $opal32_ror(n, d) { + return (n << d)|(n >>> (32 - d)); + }; + + Opal.opal32_add = function(hash, next) { + hash ^= next; + hash = $opal32_ror(hash, 1); + return hash; + }; + // Initialization // -------------- Opal.BasicObject = BasicObject = $allocate_class('BasicObject', null); Opal.Object = _Object = $allocate_class('Object', Opal.BasicObject); Opal.Module = Module = $allocate_class('Module', Opal.Object); @@ -2991,18 +3052,39 @@ nil.$$frozen = true; nil.$$comparable = false; Object.seal(nil); Opal.thrower = function(type) { - var thrower = { message: 'unexpected '+type }; - thrower.$thrower_type = type; - thrower.$throw = function(value) { - if (value == null) value = nil; - thrower.$v = value; - throw thrower; - }; + var thrower = { + $thrower_type: type, + $throw: function(value, called_from_lambda) { + if (value == null) value = nil; + if (this.is_orphan && !called_from_lambda) { + $raise(Opal.LocalJumpError, 'unexpected ' + type, value, type.$to_sym()); + } + this.$v = value; + throw this; + }, + is_orphan: false + } return thrower; }; + + // Define a "$@" global variable, which would compute and return a backtrace on demand. + Object.defineProperty($gvars, "@", { + enumerable: true, + configurable: true, + get: function() { + if ($truthy($gvars["!"])) return $gvars["!"].$backtrace(); + return nil; + }, + set: function(bt) { + if ($truthy($gvars["!"])) + $gvars["!"].$set_backtrace(bt); + else + $raise(Opal.ArgumentError, "$! not set"); + } + }); Opal.t_eval_return = Opal.thrower("return"); TypeError.$$super = Error;