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 // --------------