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;