opal/corelib/runtime.js in opal-1.2.0 vs opal/corelib/runtime.js in opal-1.3.0.alpha1
- old
+ new
@@ -63,10 +63,11 @@
// 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
+ experimental_features_severity: 'warning',// warning, ignore
enable_stack_trace: true // true, false
};
// Minify common function calls
var $has_own = Object.hasOwnProperty;
@@ -109,11 +110,18 @@
Opal.exceptions = [];
// @private
// Pops an exception from the stack and updates `$!`.
Opal.pop_exception = function() {
- Opal.gvars["!"] = Opal.exceptions.pop() || nil;
+ var exception = Opal.exceptions.pop();
+ if (exception) {
+ Opal.gvars["!"] = exception;
+ Opal.gvars["@"] = exception.$backtrace();
+ }
+ else {
+ Opal.gvars["!"] = Opal.gvars["@"] = nil;
+ }
};
// Inspect any kind of object, including non Ruby ones
Opal.inspect = function(obj) {
if (obj === undefined) {
@@ -207,11 +215,27 @@
} else {
return Opal.send(obj, obj['$respond_to?'], [jsid.substr(1), include_all]);
}
}
+ // TracePoint support
+ // ------------------
+ //
+ // Support for `TracePoint.trace(:class) do ... end`
+ Opal.trace_class = false;
+ Opal.tracers_for_class = [];
+ function invoke_tracers_for_class(klass_or_module) {
+ var i, ii, tracer;
+
+ for(i = 0, ii = Opal.tracers_for_class.length; i < ii; i++) {
+ tracer = Opal.tracers_for_class[i];
+ tracer.trace_object = klass_or_module;
+ tracer.block.$call(tracer);
+ }
+ }
+
// Constants
// ---------
//
// For future reference:
// - The Rails autoloading guide (http://guides.rubyonrails.org/v5.0/autoloading_and_reloading_constants.html)
@@ -563,10 +587,13 @@
if (klass) {
if (superclass) {
// Make sure existing class has same superclass
ensureSuperclassMatch(klass, superclass);
}
+
+ if (Opal.trace_class) { invoke_tracers_for_class(klass); }
+
return klass;
}
// Class doesn't exist, create a new one with given superclass...
@@ -586,10 +613,12 @@
if (bridged) {
Opal.bridge(bridged, klass);
}
+ if (Opal.trace_class) { invoke_tracers_for_class(klass); }
+
return klass;
};
// Define new module (or return existing module). The given `scope` is basically
// the current `self` value the `module` statement was defined in. If this is
@@ -663,17 +692,22 @@
}
module = find_existing_module(scope, name);
if (module) {
+
+ if (Opal.trace_class) { invoke_tracers_for_class(module); }
+
return module;
}
// Module doesnt exist, create a new one...
module = Opal.allocate_module(name);
Opal.const_set(scope, name, module);
+ if (Opal.trace_class) { invoke_tracers_for_class(module); }
+
return module;
};
// Return the singleton class for the passed object.
//
@@ -1356,13 +1390,14 @@
//
// @param stubs [Array] an array of method stubs to add
// @return [undefined]
Opal.add_stubs = function(stubs) {
var proto = Opal.BasicObject.$$prototype;
+ var stub, existing_method;
for (var i = 0, length = stubs.length; i < length; i++) {
- var stub = stubs[i], existing_method = proto[stub];
+ stub = stubs[i], existing_method = proto[stub];
if (existing_method == null || existing_method.$$stub) {
Opal.add_stub_for(proto, stub);
}
}
@@ -1373,23 +1408,21 @@
//
// @param prototype [Prototype] the target prototype
// @param stub [String] stub name to add (e.g. "$foo")
// @return [undefined]
Opal.add_stub_for = function(prototype, stub) {
- var method_missing_stub = Opal.stub_for(stub);
- $defineProperty(prototype, stub, method_missing_stub);
+ // Opal.stub_for(stub) is the method_missing_stub
+ $defineProperty(prototype, stub, Opal.stub_for(stub));
};
// Generate the method_missing stub for a given method name.
//
// @param method_name [String] The js-name of the method to stub (e.g. "$foo")
// @return [undefined]
Opal.stub_for = function(method_name) {
function method_missing_stub() {
- /* jshint validthis: true */
-
// Copy any given block onto the method_missing dispatcher
this.$method_missing.$$p = method_missing_stub.$$p;
// Set block property to null ready for the next call (stop false-positives)
method_missing_stub.$$p = null;
@@ -1441,11 +1474,11 @@
throw Opal.ArgumentError.$new(inspect + ': wrong number of arguments (' + actual + ' for ' + expected + ')');
};
// Super dispatcher
- Opal.find_super_dispatcher = function(obj, mid, current_func, defcheck, allow_stubs) {
+ 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 {
@@ -1475,11 +1508,11 @@
return (super_method.$$stub && !allow_stubs) ? null : super_method;
};
// Iter dispatcher for super in a block
- Opal.find_iter_super_dispatcher = function(obj, jsid, current_func, defcheck, implicit) {
+ Opal.find_block_super = function(obj, jsid, current_func, defcheck, implicit) {
var call_jsid = jsid;
if (!current_func) {
throw Opal.RuntimeError.$new("super called outside of method");
}
@@ -1493,10 +1526,16 @@
}
return Opal.find_super_dispatcher(obj, call_jsid, current_func, defcheck);
};
+ // @deprecated
+ 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
@@ -1771,10 +1810,48 @@
if (typeof block === 'function') body.$$p = block;
return body.apply(recv, args);
};
+ Opal.refined_send = function(refinement_groups, recv, method, args, block) {
+ 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);
+ }
+
+ // 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);
+ }
+ }
+ }
+ }
+ }
+
+ return Opal.send(recv, method, args, block);
+ };
+
Opal.lambda = function(block) {
block.$$is_lambda = true;
return block;
};
@@ -1913,13 +1990,20 @@
}
Opal.alias = function(obj, name, old) {
var id = '$' + name,
old_id = '$' + old,
- body = obj.$$prototype['$' + old],
+ body,
alias;
+ // Aliasing on main means aliasing on Object...
+ if (typeof obj.$$prototype === 'undefined') {
+ obj = Opal.Object;
+ }
+
+ body = obj.$$prototype['$' + old];
+
// When running inside #instance_eval the alias refers to class methods.
if (obj.$$eval) {
return Opal.alias(Opal.get_singleton_class(obj), name, old);
}
@@ -1980,10 +2064,24 @@
Opal.defn(obj, id, alias);
return obj;
};
+ Opal.alias_gvar = function(new_name, old_name) {
+ Object.defineProperty(Opal.gvars, new_name, {
+ configurable: true,
+ enumerable: true,
+ get: function() {
+ return Opal.gvars[old_name];
+ },
+ set: function(new_value) {
+ Opal.gvars[old_name] = new_value;
+ }
+ });
+ return nil;
+ }
+
Opal.alias_native = function(obj, name, native_name) {
var id = '$' + name,
body = obj.$$prototype[native_name];
if (typeof(body) !== "function" || body.$$stub) {
@@ -2083,21 +2181,27 @@
}
}
};
Opal.hash_delete = function(hash, key) {
- var i, keys = hash.$$keys, length = keys.length, value;
+ var i, keys = hash.$$keys, length = keys.length, value, key_tmp;
if (key.$$is_string) {
if (typeof key !== "string") key = key.valueOf();
if (!$has_own.call(hash.$$smap, key)) {
return;
}
for (i = 0; i < length; i++) {
- if (keys[i] === key) {
+ key_tmp = keys[i];
+
+ if (key_tmp.$$is_string && typeof key_tmp !== "string") {
+ key_tmp = key_tmp.valueOf();
+ }
+
+ if (key_tmp === key) {
keys.splice(i, 1);
break;
}
}
@@ -2442,11 +2546,16 @@
Opal.loaded([path]);
var module = Opal.modules[path];
if (module) {
- module(Opal);
+ var retval = module(Opal);
+ if (typeof Promise !== 'undefined' && retval instanceof Promise) {
+ // A special case of require having an async top:
+ // We will need to await it.
+ return retval.then(function() { return true; });
+ }
}
else {
var severity = Opal.config.missing_require_severity;
var message = 'cannot load such file -- ' + path;
@@ -2519,9 +2628,40 @@
// @returns a String object with the internal encoding set to Binary
Opal.binary = function(str) {
var dup = new String(str);
return Opal.set_encoding(dup, "binary", "internal_encoding");
+ }
+
+ Opal.last_promise = null;
+ Opal.promise_unhandled_exception = false;
+
+ // Run a block of code, but if it returns a Promise, don't run the next
+ // one, but queue it.
+ Opal.queue = function(proc) {
+ if (Opal.last_promise) {
+ // The async path is taken only if anything before returned a
+ // Promise(V2).
+ Opal.last_promise = Opal.last_promise.then(function() {
+ if (!Opal.promise_unhandled_exception) return proc(Opal);
+ })['catch'](function(error) {
+ if (Opal.respond_to(error, '$full_message')) {
+ error = error.$full_message();
+ }
+ console.error(error);
+ // Abort further execution
+ Opal.promise_unhandled_exception = true;
+ Opal.exit(1);
+ });
+ return Opal.last_promise;
+ }
+ else {
+ var ret = proc(Opal);
+ if (typeof Promise === 'function' && typeof ret === 'object' && ret instanceof Promise) {
+ Opal.last_promise = ret;
+ }
+ return ret;
+ }
}
// Initialization
// --------------