Polyfill promises in the JS shell.

This commit is contained in:
Michael Bebenita 2015-05-16 17:19:09 -07:00
Родитель d008938e55
Коммит ff9c561342
9 изменённых файлов: 575 добавлений и 30 удалений

Просмотреть файл

@ -1,6 +1,5 @@
module J2ME {
declare var util, config;
declare var Promise;
import BytecodeStream = Bytecode.BytecodeStream;
import BlockMap = Bytecode.BlockMap;

Просмотреть файл

@ -85,29 +85,11 @@ if (files.length !== 1) {
}
var callbacks = [];
var window = {
setTimeout: function(callback) {
callbacks.push(callback);
},
setZeroTimeout: function(callback) {
callbacks.push(callback);
},
addEventListener: function() {
},
crypto: {
getRandomValues: function() {
},
},
};
var navigator = {
language: "en-US",
};
function Promise() {
// ...
}
var document = {
documentElement: {
classList: {
@ -146,6 +128,32 @@ var document = {
},
};
var microTaskQueue = null;
var window = {
addEventListener: function() {
},
crypto: {
getRandomValues: function() {
},
},
};
window.setZeroTimeout = window.setTimeout = function (fn, interval) {
var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : [];
var task = microTaskQueue.scheduleInterval(fn, args, interval, false);
return task.id;
};
window.setInterval = function (fn, interval) {
var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : [];
var task = microTaskQueue.scheduleInterval(fn, args, interval, true);
return task.id;
};
window.clearTimeout = function (id) {
microTaskQueue.remove(id);
};
var Event = function() {
}
@ -160,13 +168,15 @@ try {
if (profileTimeline) {
load("bld/shumway.js");
}
load("libs/relooper.js", "libs/native.js", "bld/j2me.js","libs/zipfile.js", "blackBox.js",
load("polyfill/promise.js", "libs/relooper.js", "libs/native.js", "bld/j2me.js","libs/zipfile.js", "blackBox.js",
"libs/encoding.js", "util.js", "libs/jarstore.js",
"native.js", "midp/midp.js",
"libs/long.js", "midp/crypto.js", "libs/forge/md5.js", "libs/forge/util.js"
// "bld/classes.jar.js"
);
microTaskQueue = new J2ME.Shell.MicroTasksQueue();
// load("bld/classes.jar.js");
// load("bld/program.jar.js");
// load("bld/tests.jar.js");
@ -205,9 +215,12 @@ try {
start = dateNow();
var runtime = jvm.startIsolate0(files[0], config.args);
while (callbacks.length) {
(callbacks.shift())();
}
// Pump Event Queue
microTaskQueue.run(0, 0, false, function () {
return true;
});
print("Time: " + (dateNow() - start).toFixed(4) + " ms");
print("Bytecodes: " + J2ME.bytecodeCount);
J2ME.interpreterCounter.traceSorted(new J2ME.IndentingWriter(false, function (x) {

4
nat.ts
Просмотреть файл

@ -23,7 +23,7 @@ module J2ME {
var asyncImplStringAsync = "Async";
export function asyncImplOld(returnKind: string, promise) {
export function asyncImplOld(returnKind: string, promise: Promise<any>) {
return asyncImpl(kindCharacterToKind(returnKind), promise);
}
@ -36,7 +36,7 @@ module J2ME {
*
* |onRejected| is called with a java.lang.Exception object.
*/
export function asyncImpl(returnKind: Kind, promise) {
export function asyncImpl(returnKind: Kind, promise: Promise<any>) {
var ctx = $.ctx;
promise.then(function onFulfilled(l: any, h?: number) {

325
polyfill/promise.js Normal file
Просмотреть файл

@ -0,0 +1,325 @@
// Polyfill for Promises
(function PromiseClosure() {
/*jshint -W061 */
var global = Function("return this")();
if (global.Promise) {
// Promises existing in the DOM/Worker, checking presence of all/resolve
if (typeof global.Promise.all !== 'function') {
global.Promise.all = function (iterable) {
var count = 0, results = [], resolve, reject;
var promise = new global.Promise(function (resolve_, reject_) {
resolve = resolve_;
reject = reject_;
});
iterable.forEach(function (p, i) {
count++;
p.then(function (result) {
results[i] = result;
count--;
if (count === 0) {
resolve(results);
}
}, reject);
});
if (count === 0) {
resolve(results);
}
return promise;
};
}
if (typeof global.Promise.resolve !== 'function') {
global.Promise.resolve = function (x) {
return new global.Promise(function (resolve) { resolve(x); });
};
}
return;
}
function getDeferred(C) {
if (typeof C !== 'function') {
throw new TypeError('Invalid deferred constructor');
}
var resolver = createDeferredConstructionFunctions();
var promise = new C(resolver);
var resolve = resolver.resolve;
if (typeof resolve !== 'function') {
throw new TypeError('Invalid resolve construction function');
}
var reject = resolver.reject;
if (typeof reject !== 'function') {
throw new TypeError('Invalid reject construction function');
}
return {promise: promise, resolve: resolve, reject: reject};
}
function updateDeferredFromPotentialThenable(x, deferred) {
if (typeof x !== 'object' || x === null) {
return false;
}
try {
var then = x.then;
if (typeof then !== 'function') {
return false;
}
var thenCallResult = then.call(x, deferred.resolve, deferred.reject);
} catch (e) {
var reject = deferred.reject;
reject(e);
}
return true;
}
function isPromise(x) {
return typeof x === 'object' && x !== null &&
typeof x.promiseStatus !== 'undefined';
}
function rejectPromise(promise, reason) {
if (promise.promiseStatus !== 'unresolved') {
return;
}
var reactions = promise.rejectReactions;
promise.result = reason;
promise.resolveReactions = undefined;
promise.rejectReactions = undefined;
promise.promiseStatus = 'has-rejection';
triggerPromiseReactions(reactions, reason);
}
function resolvePromise(promise, resolution) {
if (promise.promiseStatus !== 'unresolved') {
return;
}
var reactions = promise.resolveReactions;
promise.result = resolution;
promise.resolveReactions = undefined;
promise.rejectReactions = undefined;
promise.promiseStatus = 'has-resolution';
triggerPromiseReactions(reactions, resolution);
}
function triggerPromiseReactions(reactions, argument) {
for (var i = 0; i < reactions.length; i++) {
queueMicrotask({reaction: reactions[i], argument: argument});
}
}
function queueMicrotask(task) {
if (microtasksQueue.length === 0) {
setTimeout(handleMicrotasksQueue, 0);
}
microtasksQueue.push(task);
}
function executePromiseReaction(reaction, argument) {
var deferred = reaction.deferred;
var handler = reaction.handler;
var handlerResult, updateResult;
try {
handlerResult = handler(argument);
} catch (e) {
var reject = deferred.reject;
return reject(e);
}
if (handlerResult === deferred.promise) {
var reject = deferred.reject;
return reject(new TypeError('Self resolution'));
}
try {
updateResult = updateDeferredFromPotentialThenable(handlerResult,
deferred);
if (!updateResult) {
var resolve = deferred.resolve;
return resolve(handlerResult);
}
} catch (e) {
var reject = deferred.reject;
return reject(e);
}
}
var microtasksQueue = [];
function handleMicrotasksQueue() {
while (microtasksQueue.length > 0) {
var task = microtasksQueue[0];
try {
executePromiseReaction(task.reaction, task.argument);
} catch (e) {
// unhandler onFulfillment/onRejection exception
if (typeof (Promise).onerror === 'function') {
(Promise).onerror(e);
}
}
microtasksQueue.shift();
}
}
function throwerFunction(e) {
throw e;
}
function identityFunction(x) {
return x;
}
function createRejectPromiseFunction(promise) {
return function (reason) {
rejectPromise(promise, reason);
};
}
function createResolvePromiseFunction(promise) {
return function (resolution) {
resolvePromise(promise, resolution);
};
}
function createDeferredConstructionFunctions() {
var fn = function (resolve, reject) {
fn.resolve = resolve;
fn.reject = reject;
};
return fn;
}
function createPromiseResolutionHandlerFunctions(promise,
fulfillmentHandler, rejectionHandler) {
return function (x) {
if (x === promise) {
return rejectionHandler(new TypeError('Self resolution'));
}
var cstr = promise.promiseConstructor;
if (isPromise(x)) {
var xConstructor = x.promiseConstructor;
if (xConstructor === cstr) {
return x.then(fulfillmentHandler, rejectionHandler);
}
}
var deferred = getDeferred(cstr);
var updateResult = updateDeferredFromPotentialThenable(x, deferred);
if (updateResult) {
var deferredPromise = deferred.promise;
return deferredPromise.then(fulfillmentHandler, rejectionHandler);
}
return fulfillmentHandler(x);
};
}
function createPromiseAllCountdownFunction(index, values, deferred,
countdownHolder) {
return function (x) {
values[index] = x;
countdownHolder.countdown--;
if (countdownHolder.countdown === 0) {
deferred.resolve(values);
}
};
}
function Promise(resolver) {
if (typeof resolver !== 'function') {
throw new TypeError('resolver is not a function');
}
var promise = this;
if (typeof promise !== 'object') {
throw new TypeError('Promise to initialize is not an object');
}
promise.promiseStatus = 'unresolved';
promise.resolveReactions = [];
promise.rejectReactions = [];
promise.result = undefined;
var resolve = createResolvePromiseFunction(promise);
var reject = createRejectPromiseFunction(promise);
try {
var result = resolver(resolve, reject);
} catch (e) {
rejectPromise(promise, e);
}
promise.promiseConstructor = Promise;
return promise;
}
(Promise).all = function (iterable) {
var deferred = getDeferred(this);
var values = [];
var countdownHolder = {countdown: 0};
var index = 0;
iterable.forEach(function (nextValue) {
var nextPromise = this.cast(nextValue);
var fn = createPromiseAllCountdownFunction(index, values,
deferred, countdownHolder);
nextPromise.then(fn, deferred.reject);
index++;
countdownHolder.countdown++;
}, this);
if (index === 0) {
deferred.resolve(values);
}
return deferred.promise;
};
(Promise).cast = function (x) {
if (isPromise(x)) {
return x;
}
var deferred = getDeferred(this);
deferred.resolve(x);
return deferred.promise;
};
(Promise).reject = function (r) {
var deferred = getDeferred(this);
var rejectResult = deferred.reject(r);
return deferred.promise;
};
(Promise).resolve = function (x) {
var deferred = getDeferred(this);
var rejectResult = deferred.resolve(x);
return deferred.promise;
};
Promise.prototype = {
'catch': function (onRejected) {
this.then(undefined, onRejected);
},
then: function (onFulfilled, onRejected) {
var promise = this;
if (!isPromise(promise)) {
throw new TypeError('this is not a Promises');
}
var cstr = promise.promiseConstructor;
var deferred = getDeferred(cstr);
var rejectionHandler = typeof onRejected === 'function' ? onRejected :
throwerFunction;
var fulfillmentHandler = typeof onFulfilled === 'function' ? onFulfilled :
identityFunction;
var resolutionHandler = createPromiseResolutionHandlerFunctions(promise,
fulfillmentHandler, rejectionHandler);
var resolveReaction = {deferred: deferred, handler: resolutionHandler};
var rejectReaction = {deferred: deferred, handler: rejectionHandler};
switch (promise.promiseStatus) {
case 'unresolved':
promise.resolveReactions.push(resolveReaction);
promise.rejectReactions.push(rejectReaction);
break;
case 'has-resolution':
var resolution = promise.result;
queueMicrotask({reaction: resolveReaction, argument: resolution});
break;
case 'has-rejection':
var rejection = promise.result;
queueMicrotask({reaction: rejectReaction, argument: rejection});
break;
}
return deferred.promise;
}
};
global.Promise = Promise;
})();

Просмотреть файл

@ -1,6 +1,7 @@
// Basics
///<reference path='tools/lib.d.ts' />
///<reference path='tools/es6-promises.d.ts' />
///<reference path='config.ts' />
///<reference path='utilities.ts' />
@ -14,6 +15,7 @@
///<reference path='types.ts' />
///<reference path='vm/classRegistry.ts' />
///<reference path='bindings.ts' />
///<reference path='nat.ts' />
///<reference path='int.ts' />
///<reference path='scheduler.ts' />
///<reference path='vm/runtime.ts' />

Просмотреть файл

@ -1,6 +1,7 @@
// Basics
///<reference path='tools/lib.d.ts' />
///<reference path='tools/es6-promises.d.ts' />
///<reference path='config.ts' />
///<reference path='utilities.ts' />

73
tools/es6-promises.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,73 @@
// Type definitions for es6-promise
// Project: https://github.com/jakearchibald/ES6-Promise
// Definitions by: François de Campredon <https://github.com/fdecampredon/>, vvakame <https://github.com/vvakame>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
interface Thenable<R> {
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Thenable<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Thenable<U>;
}
declare class Promise<R> implements Thenable<R> {
/**
* If you call resolve in the body of the callback passed to the constructor,
* your promise is fulfilled with result object passed to resolve.
* If you call reject your promise is rejected with the object passed to resolve.
* For consistency and debugging (eg stack traces), obj should be an instanceof Error.
* Any errors thrown in the constructor callback will be implicitly passed to reject().
*/
constructor(callback: (resolve : (value?: R | Thenable<R>) => void, reject: (error?: any) => void) => void);
/**
* onFulfilled is called when/if "promise" resolves. onRejected is called when/if "promise" rejects.
* Both are optional, if either/both are omitted the next onFulfilled/onRejected in the chain is called.
* Both callbacks have a single parameter , the fulfillment value or rejection reason.
* "then" returns a new promise equivalent to the value you return from onFulfilled/onRejected after being passed through Promise.resolve.
* If an error is thrown in the callback, the returned promise rejects with that error.
*
* @param onFulfilled called when/if "promise" resolves
* @param onRejected called when/if "promise" rejects
*/
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
then<U>(onFulfilled?: (value: R) => U | Thenable<U>, onRejected?: (error: any) => void): Promise<U>;
/**
* Sugar for promise.then(undefined, onRejected)
*
* @param onRejected called when/if "promise" rejects
*/
catch<U>(onRejected?: (error: any) => U | Thenable<U>): Promise<U>;
}
declare module Promise {
/**
* Make a new promise from the thenable.
* A thenable is promise-like in as far as it has a "then" method.
*/
function resolve<R>(value?: R | Thenable<R>): Promise<R>;
/**
* Make a promise that rejects to obj. For consistency and debugging (eg stack traces), obj should be an instanceof Error
*/
function reject(error: any): Promise<any>;
/**
* Make a promise that fulfills when every item in the array fulfills, and rejects if (and when) any item rejects.
* the array passed to all can be a mixture of promise-like objects and other objects.
* The fulfillment value is an array (in order) of fulfillment values. The rejection value is the first rejection value.
*/
function all<R>(promises: (R | Thenable<R>)[]): Promise<R[]>;
/**
* Make a Promise that fulfills when any item fulfills, and rejects if any item rejects.
*/
function race<R>(promises: (R | Thenable<R>)[]): Promise<R>;
}
declare module 'es6-promise' {
var foo: typeof Promise; // Temp variable to reference Promise in local context
module rsvp {
export var Promise: typeof foo;
}
export = rsvp;
}

Просмотреть файл

@ -927,3 +927,139 @@ module J2ME {
return this.indexOf(str, this.length - str.length) !== -1;
});
})();
module J2ME.Shell {
export class MicroTask {
runAt: number;
constructor(public id: number, public fn: () => any, public args: any[],
public interval: number, public repeat: boolean) {
}
}
var RealDate = Date;
var fakeTime = 1428107694580; // 3-Apr-2015
var jsGlobal = (function() { return this || (1, eval)('this//# sourceURL=jsGlobal-getter'); })();
/**
* This should only be called if you need fake time.
*/
export function installTimeWarper() {
// Go back in time.
fakeTime = 1428107694580; // 3-Apr-2015
// Overload
jsGlobal.Date = function (yearOrTimevalue, month, date, hour, minute, second, millisecond) {
switch (arguments.length) {
case 0: return new RealDate(fakeTime); break;
case 1: return new RealDate(yearOrTimevalue); break;
case 2: return new RealDate(yearOrTimevalue, month); break;
case 3: return new RealDate(yearOrTimevalue, month, date); break;
case 4: return new RealDate(yearOrTimevalue, month, date, hour); break;
case 5: return new RealDate(yearOrTimevalue, month, date, hour, minute); break;
case 6: return new RealDate(yearOrTimevalue, month, date, hour, minute, second); break;
default: return new RealDate(yearOrTimevalue, month, date, hour, minute, second, millisecond); break;
}
}
// Make date now deterministic.
jsGlobal.Date.now = function () {
return fakeTime += 10; // Advance time.
}
}
export class MicroTasksQueue {
private tasks: MicroTask[] = [];
private nextId: number = 1;
private time: number = 1388556000000; // 1-Jan-2014
private stopped: boolean = true;
constructor() {
}
public get isEmpty(): boolean {
return this.tasks.length === 0;
}
public scheduleInterval(fn: () => any, args: any[], interval: number, repeat: boolean) {
var MIN_INTERVAL = 4;
interval = Math.round((interval || 0)/10) * 10;
if (interval < MIN_INTERVAL) {
interval = MIN_INTERVAL;
}
var taskId = this.nextId++;
var task = new MicroTask(taskId, fn, args, interval, repeat);
this.enqueue(task);
return task;
}
public enqueue(task: MicroTask) {
var tasks = this.tasks;
task.runAt = this.time + task.interval;
var i = tasks.length;
while (i > 0 && tasks[i - 1].runAt > task.runAt) {
i--;
}
if (i === tasks.length) {
tasks.push(task);
} else {
tasks.splice(i, 0, task);
}
}
public dequeue(): MicroTask {
var task = this.tasks.shift();
this.time = task.runAt;
return task;
}
public remove(id: number) {
var tasks = this.tasks;
for (var i = 0; i < tasks.length; i++) {
if (tasks[i].id === id) {
tasks.splice(i, 1);
return;
}
}
}
public clear() {
this.tasks.length = 0;
}
/**
* Runs micro tasks for a certain |duration| and |count| whichever comes first. Optionally,
* if the |clear| option is specified, the micro task queue is cleared even if not all the
* tasks have been executed.
*
* If a |preCallback| function is specified, only continue execution if |preCallback()| returns true.
*/
run(duration: number = 0, count: number = 0, clear: boolean = false, preCallback: Function = null) {
this.stopped = false;
var executedTasks = 0;
var stopAt = Date.now() + duration;
while (!this.isEmpty && !this.stopped) {
if (duration > 0 && Date.now() >= stopAt) {
break;
}
if (count > 0 && executedTasks >= count) {
break;
}
var task = this.dequeue();
if (preCallback && !preCallback(task)) {
return;
}
task.fn.apply(null, task.args);
executedTasks ++;
}
if (clear) {
this.clear();
}
this.stopped = true;
}
stop() {
this.stopped = true;
}
}
}

Просмотреть файл

@ -21,13 +21,9 @@ declare var Long: {
fromNumber(value: number);
}
interface Promise {
catch(onRejected: { (reason: any): any; }): Promise;
}
interface CompiledMethodCache {
get(key: string): { key: string; source: string; referencedClasses: string[]; };
put(obj: { key: string; source: string; referencedClasses: string[]; }): Promise;
put(obj: { key: string; source: string; referencedClasses: string[]; }): Promise<any>;
}
interface AOTMetaData {