yann300
10 years ago
91 changed files with 2119 additions and 1895 deletions
@ -1,966 +0,0 @@ |
|||
/*! |
|||
* @overview es6-promise - a tiny implementation of Promises/A+. |
|||
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) |
|||
* @license Licensed under MIT license |
|||
* See https://raw.githubusercontent.com/jakearchibald/es6-promise/master/LICENSE
|
|||
* @version 2.0.0 |
|||
*/ |
|||
|
|||
(function() { |
|||
"use strict"; |
|||
|
|||
function $$utils$$objectOrFunction(x) { |
|||
return typeof x === 'function' || (typeof x === 'object' && x !== null); |
|||
} |
|||
|
|||
function $$utils$$isFunction(x) { |
|||
return typeof x === 'function'; |
|||
} |
|||
|
|||
function $$utils$$isMaybeThenable(x) { |
|||
return typeof x === 'object' && x !== null; |
|||
} |
|||
|
|||
var $$utils$$_isArray; |
|||
|
|||
if (!Array.isArray) { |
|||
$$utils$$_isArray = function (x) { |
|||
return Object.prototype.toString.call(x) === '[object Array]'; |
|||
}; |
|||
} else { |
|||
$$utils$$_isArray = Array.isArray; |
|||
} |
|||
|
|||
var $$utils$$isArray = $$utils$$_isArray; |
|||
var $$utils$$now = Date.now || function() { return new Date().getTime(); }; |
|||
function $$utils$$F() { } |
|||
|
|||
var $$utils$$o_create = (Object.create || function (o) { |
|||
if (arguments.length > 1) { |
|||
throw new Error('Second argument not supported'); |
|||
} |
|||
if (typeof o !== 'object') { |
|||
throw new TypeError('Argument must be an object'); |
|||
} |
|||
$$utils$$F.prototype = o; |
|||
return new $$utils$$F(); |
|||
}); |
|||
|
|||
var $$asap$$len = 0; |
|||
|
|||
var $$asap$$default = function asap(callback, arg) { |
|||
$$asap$$queue[$$asap$$len] = callback; |
|||
$$asap$$queue[$$asap$$len + 1] = arg; |
|||
$$asap$$len += 2; |
|||
if ($$asap$$len === 2) { |
|||
// If len is 1, that means that we need to schedule an async flush.
|
|||
// If additional callbacks are queued before the queue is flushed, they
|
|||
// will be processed by this flush that we are scheduling.
|
|||
$$asap$$scheduleFlush(); |
|||
} |
|||
}; |
|||
|
|||
var $$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {}; |
|||
var $$asap$$BrowserMutationObserver = $$asap$$browserGlobal.MutationObserver || $$asap$$browserGlobal.WebKitMutationObserver; |
|||
|
|||
// test for web worker but not in IE10
|
|||
var $$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && |
|||
typeof importScripts !== 'undefined' && |
|||
typeof MessageChannel !== 'undefined'; |
|||
|
|||
// node
|
|||
function $$asap$$useNextTick() { |
|||
return function() { |
|||
process.nextTick($$asap$$flush); |
|||
}; |
|||
} |
|||
|
|||
function $$asap$$useMutationObserver() { |
|||
var iterations = 0; |
|||
var observer = new $$asap$$BrowserMutationObserver($$asap$$flush); |
|||
var node = document.createTextNode(''); |
|||
observer.observe(node, { characterData: true }); |
|||
|
|||
return function() { |
|||
node.data = (iterations = ++iterations % 2); |
|||
}; |
|||
} |
|||
|
|||
// web worker
|
|||
function $$asap$$useMessageChannel() { |
|||
var channel = new MessageChannel(); |
|||
channel.port1.onmessage = $$asap$$flush; |
|||
return function () { |
|||
channel.port2.postMessage(0); |
|||
}; |
|||
} |
|||
|
|||
function $$asap$$useSetTimeout() { |
|||
return function() { |
|||
setTimeout($$asap$$flush, 1); |
|||
}; |
|||
} |
|||
|
|||
var $$asap$$queue = new Array(1000); |
|||
|
|||
function $$asap$$flush() { |
|||
for (var i = 0; i < $$asap$$len; i+=2) { |
|||
var callback = $$asap$$queue[i]; |
|||
var arg = $$asap$$queue[i+1]; |
|||
|
|||
callback(arg); |
|||
|
|||
$$asap$$queue[i] = undefined; |
|||
$$asap$$queue[i+1] = undefined; |
|||
} |
|||
|
|||
$$asap$$len = 0; |
|||
} |
|||
|
|||
var $$asap$$scheduleFlush; |
|||
|
|||
// Decide what async method to use to triggering processing of queued callbacks:
|
|||
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { |
|||
$$asap$$scheduleFlush = $$asap$$useNextTick(); |
|||
} else if ($$asap$$BrowserMutationObserver) { |
|||
$$asap$$scheduleFlush = $$asap$$useMutationObserver(); |
|||
} else if ($$asap$$isWorker) { |
|||
$$asap$$scheduleFlush = $$asap$$useMessageChannel(); |
|||
} else { |
|||
$$asap$$scheduleFlush = $$asap$$useSetTimeout(); |
|||
} |
|||
|
|||
function $$$internal$$noop() {} |
|||
var $$$internal$$PENDING = void 0; |
|||
var $$$internal$$FULFILLED = 1; |
|||
var $$$internal$$REJECTED = 2; |
|||
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject(); |
|||
|
|||
function $$$internal$$selfFullfillment() { |
|||
return new TypeError("You cannot resolve a promise with itself"); |
|||
} |
|||
|
|||
function $$$internal$$cannotReturnOwn() { |
|||
return new TypeError('A promises callback cannot return that same promise.') |
|||
} |
|||
|
|||
function $$$internal$$getThen(promise) { |
|||
try { |
|||
return promise.then; |
|||
} catch(error) { |
|||
$$$internal$$GET_THEN_ERROR.error = error; |
|||
return $$$internal$$GET_THEN_ERROR; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { |
|||
try { |
|||
then.call(value, fulfillmentHandler, rejectionHandler); |
|||
} catch(e) { |
|||
return e; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$handleForeignThenable(promise, thenable, then) { |
|||
$$asap$$default(function(promise) { |
|||
var sealed = false; |
|||
var error = $$$internal$$tryThen(then, thenable, function(value) { |
|||
if (sealed) { return; } |
|||
sealed = true; |
|||
if (thenable !== value) { |
|||
$$$internal$$resolve(promise, value); |
|||
} else { |
|||
$$$internal$$fulfill(promise, value); |
|||
} |
|||
}, function(reason) { |
|||
if (sealed) { return; } |
|||
sealed = true; |
|||
|
|||
$$$internal$$reject(promise, reason); |
|||
}, 'Settle: ' + (promise._label || ' unknown promise')); |
|||
|
|||
if (!sealed && error) { |
|||
sealed = true; |
|||
$$$internal$$reject(promise, error); |
|||
} |
|||
}, promise); |
|||
} |
|||
|
|||
function $$$internal$$handleOwnThenable(promise, thenable) { |
|||
if (thenable._state === $$$internal$$FULFILLED) { |
|||
$$$internal$$fulfill(promise, thenable._result); |
|||
} else if (promise._state === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, thenable._result); |
|||
} else { |
|||
$$$internal$$subscribe(thenable, undefined, function(value) { |
|||
$$$internal$$resolve(promise, value); |
|||
}, function(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$handleMaybeThenable(promise, maybeThenable) { |
|||
if (maybeThenable.constructor === promise.constructor) { |
|||
$$$internal$$handleOwnThenable(promise, maybeThenable); |
|||
} else { |
|||
var then = $$$internal$$getThen(maybeThenable); |
|||
|
|||
if (then === $$$internal$$GET_THEN_ERROR) { |
|||
$$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error); |
|||
} else if (then === undefined) { |
|||
$$$internal$$fulfill(promise, maybeThenable); |
|||
} else if ($$utils$$isFunction(then)) { |
|||
$$$internal$$handleForeignThenable(promise, maybeThenable, then); |
|||
} else { |
|||
$$$internal$$fulfill(promise, maybeThenable); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$resolve(promise, value) { |
|||
if (promise === value) { |
|||
$$$internal$$reject(promise, $$$internal$$selfFullfillment()); |
|||
} else if ($$utils$$objectOrFunction(value)) { |
|||
$$$internal$$handleMaybeThenable(promise, value); |
|||
} else { |
|||
$$$internal$$fulfill(promise, value); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$publishRejection(promise) { |
|||
if (promise._onerror) { |
|||
promise._onerror(promise._result); |
|||
} |
|||
|
|||
$$$internal$$publish(promise); |
|||
} |
|||
|
|||
function $$$internal$$fulfill(promise, value) { |
|||
if (promise._state !== $$$internal$$PENDING) { return; } |
|||
|
|||
promise._result = value; |
|||
promise._state = $$$internal$$FULFILLED; |
|||
|
|||
if (promise._subscribers.length === 0) { |
|||
} else { |
|||
$$asap$$default($$$internal$$publish, promise); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$reject(promise, reason) { |
|||
if (promise._state !== $$$internal$$PENDING) { return; } |
|||
promise._state = $$$internal$$REJECTED; |
|||
promise._result = reason; |
|||
|
|||
$$asap$$default($$$internal$$publishRejection, promise); |
|||
} |
|||
|
|||
function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) { |
|||
var subscribers = parent._subscribers; |
|||
var length = subscribers.length; |
|||
|
|||
parent._onerror = null; |
|||
|
|||
subscribers[length] = child; |
|||
subscribers[length + $$$internal$$FULFILLED] = onFulfillment; |
|||
subscribers[length + $$$internal$$REJECTED] = onRejection; |
|||
|
|||
if (length === 0 && parent._state) { |
|||
$$asap$$default($$$internal$$publish, parent); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$publish(promise) { |
|||
var subscribers = promise._subscribers; |
|||
var settled = promise._state; |
|||
|
|||
if (subscribers.length === 0) { return; } |
|||
|
|||
var child, callback, detail = promise._result; |
|||
|
|||
for (var i = 0; i < subscribers.length; i += 3) { |
|||
child = subscribers[i]; |
|||
callback = subscribers[i + settled]; |
|||
|
|||
if (child) { |
|||
$$$internal$$invokeCallback(settled, child, callback, detail); |
|||
} else { |
|||
callback(detail); |
|||
} |
|||
} |
|||
|
|||
promise._subscribers.length = 0; |
|||
} |
|||
|
|||
function $$$internal$$ErrorObject() { |
|||
this.error = null; |
|||
} |
|||
|
|||
var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject(); |
|||
|
|||
function $$$internal$$tryCatch(callback, detail) { |
|||
try { |
|||
return callback(detail); |
|||
} catch(e) { |
|||
$$$internal$$TRY_CATCH_ERROR.error = e; |
|||
return $$$internal$$TRY_CATCH_ERROR; |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$invokeCallback(settled, promise, callback, detail) { |
|||
var hasCallback = $$utils$$isFunction(callback), |
|||
value, error, succeeded, failed; |
|||
|
|||
if (hasCallback) { |
|||
value = $$$internal$$tryCatch(callback, detail); |
|||
|
|||
if (value === $$$internal$$TRY_CATCH_ERROR) { |
|||
failed = true; |
|||
error = value.error; |
|||
value = null; |
|||
} else { |
|||
succeeded = true; |
|||
} |
|||
|
|||
if (promise === value) { |
|||
$$$internal$$reject(promise, $$$internal$$cannotReturnOwn()); |
|||
return; |
|||
} |
|||
|
|||
} else { |
|||
value = detail; |
|||
succeeded = true; |
|||
} |
|||
|
|||
if (promise._state !== $$$internal$$PENDING) { |
|||
// noop
|
|||
} else if (hasCallback && succeeded) { |
|||
$$$internal$$resolve(promise, value); |
|||
} else if (failed) { |
|||
$$$internal$$reject(promise, error); |
|||
} else if (settled === $$$internal$$FULFILLED) { |
|||
$$$internal$$fulfill(promise, value); |
|||
} else if (settled === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, value); |
|||
} |
|||
} |
|||
|
|||
function $$$internal$$initializePromise(promise, resolver) { |
|||
try { |
|||
resolver(function resolvePromise(value){ |
|||
$$$internal$$resolve(promise, value); |
|||
}, function rejectPromise(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
}); |
|||
} catch(e) { |
|||
$$$internal$$reject(promise, e); |
|||
} |
|||
} |
|||
|
|||
function $$$enumerator$$makeSettledResult(state, position, value) { |
|||
if (state === $$$internal$$FULFILLED) { |
|||
return { |
|||
state: 'fulfilled', |
|||
value: value |
|||
}; |
|||
} else { |
|||
return { |
|||
state: 'rejected', |
|||
reason: value |
|||
}; |
|||
} |
|||
} |
|||
|
|||
function $$$enumerator$$Enumerator(Constructor, input, abortOnReject, label) { |
|||
this._instanceConstructor = Constructor; |
|||
this.promise = new Constructor($$$internal$$noop, label); |
|||
this._abortOnReject = abortOnReject; |
|||
|
|||
if (this._validateInput(input)) { |
|||
this._input = input; |
|||
this.length = input.length; |
|||
this._remaining = input.length; |
|||
|
|||
this._init(); |
|||
|
|||
if (this.length === 0) { |
|||
$$$internal$$fulfill(this.promise, this._result); |
|||
} else { |
|||
this.length = this.length || 0; |
|||
this._enumerate(); |
|||
if (this._remaining === 0) { |
|||
$$$internal$$fulfill(this.promise, this._result); |
|||
} |
|||
} |
|||
} else { |
|||
$$$internal$$reject(this.promise, this._validationError()); |
|||
} |
|||
} |
|||
|
|||
$$$enumerator$$Enumerator.prototype._validateInput = function(input) { |
|||
return $$utils$$isArray(input); |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._validationError = function() { |
|||
return new Error('Array Methods must be provided an Array'); |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._init = function() { |
|||
this._result = new Array(this.length); |
|||
}; |
|||
|
|||
var $$$enumerator$$default = $$$enumerator$$Enumerator; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._enumerate = function() { |
|||
var length = this.length; |
|||
var promise = this.promise; |
|||
var input = this._input; |
|||
|
|||
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { |
|||
this._eachEntry(input[i], i); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { |
|||
var c = this._instanceConstructor; |
|||
if ($$utils$$isMaybeThenable(entry)) { |
|||
if (entry.constructor === c && entry._state !== $$$internal$$PENDING) { |
|||
entry._onerror = null; |
|||
this._settledAt(entry._state, i, entry._result); |
|||
} else { |
|||
this._willSettleAt(c.resolve(entry), i); |
|||
} |
|||
} else { |
|||
this._remaining--; |
|||
this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { |
|||
var promise = this.promise; |
|||
|
|||
if (promise._state === $$$internal$$PENDING) { |
|||
this._remaining--; |
|||
|
|||
if (this._abortOnReject && state === $$$internal$$REJECTED) { |
|||
$$$internal$$reject(promise, value); |
|||
} else { |
|||
this._result[i] = this._makeResult(state, i, value); |
|||
} |
|||
} |
|||
|
|||
if (this._remaining === 0) { |
|||
$$$internal$$fulfill(promise, this._result); |
|||
} |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) { |
|||
return value; |
|||
}; |
|||
|
|||
$$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { |
|||
var enumerator = this; |
|||
|
|||
$$$internal$$subscribe(promise, undefined, function(value) { |
|||
enumerator._settledAt($$$internal$$FULFILLED, i, value); |
|||
}, function(reason) { |
|||
enumerator._settledAt($$$internal$$REJECTED, i, reason); |
|||
}); |
|||
}; |
|||
|
|||
var $$promise$all$$default = function all(entries, label) { |
|||
return new $$$enumerator$$default(this, entries, true /* abort on reject */, label).promise; |
|||
}; |
|||
|
|||
var $$promise$race$$default = function race(entries, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
|
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
|
|||
if (!$$utils$$isArray(entries)) { |
|||
$$$internal$$reject(promise, new TypeError('You must pass an array to race.')); |
|||
return promise; |
|||
} |
|||
|
|||
var length = entries.length; |
|||
|
|||
function onFulfillment(value) { |
|||
$$$internal$$resolve(promise, value); |
|||
} |
|||
|
|||
function onRejection(reason) { |
|||
$$$internal$$reject(promise, reason); |
|||
} |
|||
|
|||
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) { |
|||
$$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection); |
|||
} |
|||
|
|||
return promise; |
|||
}; |
|||
|
|||
var $$promise$resolve$$default = function resolve(object, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
|
|||
if (object && typeof object === 'object' && object.constructor === Constructor) { |
|||
return object; |
|||
} |
|||
|
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
$$$internal$$resolve(promise, object); |
|||
return promise; |
|||
}; |
|||
|
|||
var $$promise$reject$$default = function reject(reason, label) { |
|||
/*jshint validthis:true */ |
|||
var Constructor = this; |
|||
var promise = new Constructor($$$internal$$noop, label); |
|||
$$$internal$$reject(promise, reason); |
|||
return promise; |
|||
}; |
|||
|
|||
var $$es6$promise$promise$$counter = 0; |
|||
|
|||
function $$es6$promise$promise$$needsResolver() { |
|||
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); |
|||
} |
|||
|
|||
function $$es6$promise$promise$$needsNew() { |
|||
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); |
|||
} |
|||
|
|||
var $$es6$promise$promise$$default = $$es6$promise$promise$$Promise; |
|||
|
|||
/** |
|||
Promise objects represent the eventual result of an asynchronous operation. The |
|||
primary way of interacting with a promise is through its `then` method, which |
|||
registers callbacks to receive either a promise’s eventual value or the reason |
|||
why the promise cannot be fulfilled. |
|||
|
|||
Terminology |
|||
----------- |
|||
|
|||
- `promise` is an object or function with a `then` method whose behavior conforms to this specification. |
|||
- `thenable` is an object or function that defines a `then` method. |
|||
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise). |
|||
- `exception` is a value that is thrown using the throw statement. |
|||
- `reason` is a value that indicates why a promise was rejected. |
|||
- `settled` the final resting state of a promise, fulfilled or rejected. |
|||
|
|||
A promise can be in one of three states: pending, fulfilled, or rejected. |
|||
|
|||
Promises that are fulfilled have a fulfillment value and are in the fulfilled |
|||
state. Promises that are rejected have a rejection reason and are in the |
|||
rejected state. A fulfillment value is never a thenable. |
|||
|
|||
Promises can also be said to *resolve* a value. If this value is also a |
|||
promise, then the original promise's settled state will match the value's |
|||
settled state. So a promise that *resolves* a promise that rejects will |
|||
itself reject, and a promise that *resolves* a promise that fulfills will |
|||
itself fulfill. |
|||
|
|||
|
|||
Basic Usage: |
|||
------------ |
|||
|
|||
```js
|
|||
var promise = new Promise(function(resolve, reject) { |
|||
// on success
|
|||
resolve(value); |
|||
|
|||
// on failure
|
|||
reject(reason); |
|||
}); |
|||
|
|||
promise.then(function(value) { |
|||
// on fulfillment
|
|||
}, function(reason) { |
|||
// on rejection
|
|||
}); |
|||
``` |
|||
|
|||
Advanced Usage: |
|||
--------------- |
|||
|
|||
Promises shine when abstracting away asynchronous interactions such as |
|||
`XMLHttpRequest`s. |
|||
|
|||
```js
|
|||
function getJSON(url) { |
|||
return new Promise(function(resolve, reject){ |
|||
var xhr = new XMLHttpRequest(); |
|||
|
|||
xhr.open('GET', url); |
|||
xhr.onreadystatechange = handler; |
|||
xhr.responseType = 'json'; |
|||
xhr.setRequestHeader('Accept', 'application/json'); |
|||
xhr.send(); |
|||
|
|||
function handler() { |
|||
if (this.readyState === this.DONE) { |
|||
if (this.status === 200) { |
|||
resolve(this.response); |
|||
} else { |
|||
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); |
|||
} |
|||
} |
|||
}; |
|||
}); |
|||
} |
|||
|
|||
getJSON('/posts.json').then(function(json) { |
|||
// on fulfillment
|
|||
}, function(reason) { |
|||
// on rejection
|
|||
}); |
|||
``` |
|||
|
|||
Unlike callbacks, promises are great composable primitives. |
|||
|
|||
```js
|
|||
Promise.all([ |
|||
getJSON('/posts'), |
|||
getJSON('/comments') |
|||
]).then(function(values){ |
|||
values[0] // => postsJSON
|
|||
values[1] // => commentsJSON
|
|||
|
|||
return values; |
|||
}); |
|||
``` |
|||
|
|||
@class Promise |
|||
@param {function} resolver |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@constructor |
|||
*/ |
|||
function $$es6$promise$promise$$Promise(resolver, label) { |
|||
this._id = $$es6$promise$promise$$counter++; |
|||
this._label = label; |
|||
this._state = undefined; |
|||
this._result = undefined; |
|||
this._subscribers = []; |
|||
|
|||
if ($$$internal$$noop !== resolver) { |
|||
if (!$$utils$$isFunction(resolver)) { |
|||
$$es6$promise$promise$$needsResolver(); |
|||
} |
|||
|
|||
if (!(this instanceof $$es6$promise$promise$$Promise)) { |
|||
$$es6$promise$promise$$needsNew(); |
|||
} |
|||
|
|||
$$$internal$$initializePromise(this, resolver); |
|||
} |
|||
} |
|||
|
|||
$$es6$promise$promise$$Promise.all = $$promise$all$$default; |
|||
$$es6$promise$promise$$Promise.race = $$promise$race$$default; |
|||
$$es6$promise$promise$$Promise.resolve = $$promise$resolve$$default; |
|||
$$es6$promise$promise$$Promise.reject = $$promise$reject$$default; |
|||
|
|||
$$es6$promise$promise$$Promise.prototype = { |
|||
constructor: $$es6$promise$promise$$Promise, |
|||
|
|||
/** |
|||
The primary way of interacting with a promise is through its `then` method, |
|||
which registers callbacks to receive either a promise's eventual value or the |
|||
reason why the promise cannot be fulfilled. |
|||
|
|||
```js
|
|||
findUser().then(function(user){ |
|||
// user is available
|
|||
}, function(reason){ |
|||
// user is unavailable, and you are given the reason why
|
|||
}); |
|||
``` |
|||
|
|||
Chaining |
|||
-------- |
|||
|
|||
The return value of `then` is itself a promise. This second, 'downstream' |
|||
promise is resolved with the return value of the first promise's fulfillment |
|||
or rejection handler, or rejected if the handler throws an exception. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return user.name; |
|||
}, function (reason) { |
|||
return 'default name'; |
|||
}).then(function (userName) { |
|||
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
|
|||
// will be `'default name'`
|
|||
}); |
|||
|
|||
findUser().then(function (user) { |
|||
throw new Error('Found user, but still unhappy'); |
|||
}, function (reason) { |
|||
throw new Error('`findUser` rejected and we're unhappy'); |
|||
}).then(function (value) { |
|||
// never reached
|
|||
}, function (reason) { |
|||
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
|
|||
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
|
|||
}); |
|||
``` |
|||
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
throw new PedagogicalException('Upstream error'); |
|||
}).then(function (value) { |
|||
// never reached
|
|||
}).then(function (value) { |
|||
// never reached
|
|||
}, function (reason) { |
|||
// The `PedgagocialException` is propagated all the way down to here
|
|||
}); |
|||
``` |
|||
|
|||
Assimilation |
|||
------------ |
|||
|
|||
Sometimes the value you want to propagate to a downstream promise can only be |
|||
retrieved asynchronously. This can be achieved by returning a promise in the |
|||
fulfillment or rejection handler. The downstream promise will then be pending |
|||
until the returned promise is settled. This is called *assimilation*. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return findCommentsByAuthor(user); |
|||
}).then(function (comments) { |
|||
// The user's comments are now available
|
|||
}); |
|||
``` |
|||
|
|||
If the assimliated promise rejects, then the downstream promise will also reject. |
|||
|
|||
```js
|
|||
findUser().then(function (user) { |
|||
return findCommentsByAuthor(user); |
|||
}).then(function (comments) { |
|||
// If `findCommentsByAuthor` fulfills, we'll have the value here
|
|||
}, function (reason) { |
|||
// If `findCommentsByAuthor` rejects, we'll have the reason here
|
|||
}); |
|||
``` |
|||
|
|||
Simple Example |
|||
-------------- |
|||
|
|||
Synchronous Example |
|||
|
|||
```javascript
|
|||
var result; |
|||
|
|||
try { |
|||
result = findResult(); |
|||
// success
|
|||
} catch(reason) { |
|||
// failure
|
|||
} |
|||
``` |
|||
|
|||
Errback Example |
|||
|
|||
```js
|
|||
findResult(function(result, err){ |
|||
if (err) { |
|||
// failure
|
|||
} else { |
|||
// success
|
|||
} |
|||
}); |
|||
``` |
|||
|
|||
Promise Example; |
|||
|
|||
```javascript
|
|||
findResult().then(function(result){ |
|||
// success
|
|||
}, function(reason){ |
|||
// failure
|
|||
}); |
|||
``` |
|||
|
|||
Advanced Example |
|||
-------------- |
|||
|
|||
Synchronous Example |
|||
|
|||
```javascript
|
|||
var author, books; |
|||
|
|||
try { |
|||
author = findAuthor(); |
|||
books = findBooksByAuthor(author); |
|||
// success
|
|||
} catch(reason) { |
|||
// failure
|
|||
} |
|||
``` |
|||
|
|||
Errback Example |
|||
|
|||
```js
|
|||
|
|||
function foundBooks(books) { |
|||
|
|||
} |
|||
|
|||
function failure(reason) { |
|||
|
|||
} |
|||
|
|||
findAuthor(function(author, err){ |
|||
if (err) { |
|||
failure(err); |
|||
// failure
|
|||
} else { |
|||
try { |
|||
findBoooksByAuthor(author, function(books, err) { |
|||
if (err) { |
|||
failure(err); |
|||
} else { |
|||
try { |
|||
foundBooks(books); |
|||
} catch(reason) { |
|||
failure(reason); |
|||
} |
|||
} |
|||
}); |
|||
} catch(error) { |
|||
failure(err); |
|||
} |
|||
// success
|
|||
} |
|||
}); |
|||
``` |
|||
|
|||
Promise Example; |
|||
|
|||
```javascript
|
|||
findAuthor(). |
|||
then(findBooksByAuthor). |
|||
then(function(books){ |
|||
// found books
|
|||
}).catch(function(reason){ |
|||
// something went wrong
|
|||
}); |
|||
``` |
|||
|
|||
@method then |
|||
@param {Function} onFulfilled |
|||
@param {Function} onRejected |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@return {Promise} |
|||
*/ |
|||
then: function(onFulfillment, onRejection, label) { |
|||
var parent = this; |
|||
var state = parent._state; |
|||
|
|||
if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) { |
|||
return this; |
|||
} |
|||
|
|||
parent._onerror = null; |
|||
|
|||
var child = new this.constructor($$$internal$$noop, label); |
|||
var result = parent._result; |
|||
|
|||
if (state) { |
|||
var callback = arguments[state - 1]; |
|||
$$asap$$default(function(){ |
|||
$$$internal$$invokeCallback(state, child, callback, result); |
|||
}); |
|||
} else { |
|||
$$$internal$$subscribe(parent, child, onFulfillment, onRejection); |
|||
} |
|||
|
|||
return child; |
|||
}, |
|||
|
|||
/** |
|||
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same |
|||
as the catch block of a try/catch statement. |
|||
|
|||
```js
|
|||
function findAuthor(){ |
|||
throw new Error('couldn't find that author'); |
|||
} |
|||
|
|||
// synchronous
|
|||
try { |
|||
findAuthor(); |
|||
} catch(reason) { |
|||
// something went wrong
|
|||
} |
|||
|
|||
// async with promises
|
|||
findAuthor().catch(function(reason){ |
|||
// something went wrong
|
|||
}); |
|||
``` |
|||
|
|||
@method catch |
|||
@param {Function} onRejection |
|||
@param {String} label optional string for labeling the promise. |
|||
Useful for tooling. |
|||
@return {Promise} |
|||
*/ |
|||
'catch': function(onRejection, label) { |
|||
return this.then(null, onRejection, label); |
|||
} |
|||
}; |
|||
|
|||
var $$es6$promise$polyfill$$default = function polyfill() { |
|||
var local; |
|||
|
|||
if (typeof global !== 'undefined') { |
|||
local = global; |
|||
} else if (typeof window !== 'undefined' && window.document) { |
|||
local = window; |
|||
} else { |
|||
local = self; |
|||
} |
|||
|
|||
var es6PromiseSupport = |
|||
"Promise" in local && |
|||
// Some of these methods are missing from
|
|||
// Firefox/Chrome experimental implementations
|
|||
"resolve" in local.Promise && |
|||
"reject" in local.Promise && |
|||
"all" in local.Promise && |
|||
"race" in local.Promise && |
|||
// Older version of the spec had a resolver object
|
|||
// as the arg rather than a function
|
|||
(function() { |
|||
var resolve; |
|||
new local.Promise(function(r) { resolve = r; }); |
|||
return $$utils$$isFunction(resolve); |
|||
}()); |
|||
|
|||
if (!es6PromiseSupport) { |
|||
local.Promise = $$es6$promise$promise$$default; |
|||
} |
|||
}; |
|||
|
|||
var es6$promise$umd$$ES6Promise = { |
|||
Promise: $$es6$promise$promise$$default, |
|||
polyfill: $$es6$promise$polyfill$$default |
|||
}; |
|||
|
|||
/* global define:true module:true window: true */ |
|||
if (typeof define === 'function' && define['amd']) { |
|||
define(function() { return es6$promise$umd$$ES6Promise; }); |
|||
} else if (typeof module !== 'undefined' && module['exports']) { |
|||
module['exports'] = es6$promise$umd$$ES6Promise; |
|||
} else if (typeof this !== 'undefined') { |
|||
this['ES6Promise'] = es6$promise$umd$$ES6Promise; |
|||
} |
|||
}).call(this); |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,16 +1,12 @@ |
|||
#!/usr/bin/env node
|
|||
|
|||
require('es6-promise').polyfill(); |
|||
|
|||
var web3 = require("../index.js"); |
|||
|
|||
web3.setProvider(new web3.providers.HttpRpcProvider('http://localhost:8080')); |
|||
web3.setProvider(new web3.providers.HttpSyncProvider('http://localhost:8080')); |
|||
|
|||
var coinbase = web3.eth.coinbase; |
|||
console.log(coinbase); |
|||
|
|||
var balance = web3.eth.balanceAt(coinbase); |
|||
console.log(balance); |
|||
|
|||
web3.eth.coinbase.then(function(result){ |
|||
console.log(result); |
|||
return web3.eth.balanceAt(result); |
|||
}).then(function(balance){ |
|||
console.log(web3.toDecimal(balance)); |
|||
}).catch(function(err){ |
|||
console.log(err); |
|||
}); |
@ -0,0 +1,18 @@ |
|||
var addressName = {"0x12378912345789": "Gav", "0x57835893478594739854": "Jeff"}; |
|||
var nameAddress = {}; |
|||
|
|||
for (var prop in addressName) { |
|||
if (addressName.hasOwnProperty(prop)) { |
|||
nameAddress[addressName[prop]] = prop; |
|||
} |
|||
} |
|||
|
|||
var local = { |
|||
addressBook:{ |
|||
byName: addressName, |
|||
byAddress: nameAddress |
|||
} |
|||
}; |
|||
|
|||
if (typeof(module) !== "undefined") |
|||
module.exports = local; |
@ -1,105 +0,0 @@ |
|||
|
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Callgraph of functions inside a contract. |
|||
*/ |
|||
|
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/CallGraph.h> |
|||
|
|||
using namespace std; |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
void CallGraph::addNode(ASTNode const& _node) |
|||
{ |
|||
if (!m_nodesSeen.count(&_node)) |
|||
{ |
|||
m_workQueue.push(&_node); |
|||
m_nodesSeen.insert(&_node); |
|||
} |
|||
} |
|||
|
|||
set<FunctionDefinition const*> const& CallGraph::getCalls() |
|||
{ |
|||
computeCallGraph(); |
|||
return m_functionsSeen; |
|||
} |
|||
|
|||
void CallGraph::computeCallGraph() |
|||
{ |
|||
while (!m_workQueue.empty()) |
|||
{ |
|||
m_workQueue.front()->accept(*this); |
|||
m_workQueue.pop(); |
|||
} |
|||
} |
|||
|
|||
bool CallGraph::visit(Identifier const& _identifier) |
|||
{ |
|||
if (auto fun = dynamic_cast<FunctionDefinition const*>(_identifier.getReferencedDeclaration())) |
|||
{ |
|||
if (m_functionOverrideResolver) |
|||
fun = (*m_functionOverrideResolver)(fun->getName()); |
|||
solAssert(fun, "Error finding override for function " + fun->getName()); |
|||
addNode(*fun); |
|||
} |
|||
if (auto modifier = dynamic_cast<ModifierDefinition const*>(_identifier.getReferencedDeclaration())) |
|||
{ |
|||
if (m_modifierOverrideResolver) |
|||
modifier = (*m_modifierOverrideResolver)(modifier->getName()); |
|||
solAssert(modifier, "Error finding override for modifier " + modifier->getName()); |
|||
addNode(*modifier); |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
bool CallGraph::visit(FunctionDefinition const& _function) |
|||
{ |
|||
m_functionsSeen.insert(&_function); |
|||
return true; |
|||
} |
|||
|
|||
bool CallGraph::visit(MemberAccess const& _memberAccess) |
|||
{ |
|||
// used for "BaseContract.baseContractFunction"
|
|||
if (_memberAccess.getExpression().getType()->getCategory() == Type::Category::TYPE) |
|||
{ |
|||
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.getExpression().getType()); |
|||
if (type.getMembers().getMemberType(_memberAccess.getMemberName())) |
|||
{ |
|||
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*type.getActualType()) |
|||
.getContractDefinition(); |
|||
for (ASTPointer<FunctionDefinition> const& function: contract.getDefinedFunctions()) |
|||
if (function->getName() == _memberAccess.getMemberName()) |
|||
{ |
|||
addNode(*function); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,69 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Callgraph of functions inside a contract. |
|||
*/ |
|||
|
|||
#include <set> |
|||
#include <queue> |
|||
#include <functional> |
|||
#include <boost/range/iterator_range.hpp> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
/**
|
|||
* Can be used to compute the graph of calls (or rather references) between functions of the same |
|||
* contract. Current functionality is limited to computing all functions that are directly |
|||
* or indirectly called by some functions. |
|||
*/ |
|||
class CallGraph: private ASTConstVisitor |
|||
{ |
|||
public: |
|||
using FunctionOverrideResolver = std::function<FunctionDefinition const*(std::string const&)>; |
|||
using ModifierOverrideResolver = std::function<ModifierDefinition const*(std::string const&)>; |
|||
|
|||
CallGraph(FunctionOverrideResolver const& _functionOverrideResolver, |
|||
ModifierOverrideResolver const& _modifierOverrideResolver): |
|||
m_functionOverrideResolver(&_functionOverrideResolver), |
|||
m_modifierOverrideResolver(&_modifierOverrideResolver) {} |
|||
|
|||
void addNode(ASTNode const& _node); |
|||
|
|||
std::set<FunctionDefinition const*> const& getCalls(); |
|||
|
|||
private: |
|||
virtual bool visit(FunctionDefinition const& _function) override; |
|||
virtual bool visit(Identifier const& _identifier) override; |
|||
virtual bool visit(MemberAccess const& _memberAccess) override; |
|||
|
|||
void computeCallGraph(); |
|||
|
|||
FunctionOverrideResolver const* m_functionOverrideResolver; |
|||
ModifierOverrideResolver const* m_modifierOverrideResolver; |
|||
std::set<ASTNode const*> m_nodesSeen; |
|||
std::set<FunctionDefinition const*> m_functionsSeen; |
|||
std::queue<ASTNode const*> m_workQueue; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,46 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CorsHttpServer.cpp
|
|||
* @author Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include "CorsHttpServer.h" |
|||
|
|||
namespace jsonrpc |
|||
{ |
|||
|
|||
int HttpServer::callback(struct mg_connection*) |
|||
{ |
|||
return 0; |
|||
} |
|||
|
|||
bool CorsHttpServer::SendResponse(std::string const& _response, void* _addInfo) |
|||
{ |
|||
struct mg_connection* conn = (struct mg_connection*) _addInfo; |
|||
if (mg_printf(conn, "HTTP/1.1 200 OK\r\n" |
|||
"Content-Type: application/json\r\n" |
|||
"Content-Length: %d\r\n" |
|||
"Access-Control-Allow-Origin: *\r\n" |
|||
"Access-Control-Allow-Headers: Content-Type\r\n" |
|||
"\r\n" |
|||
"%s",(int)_response.length(), _response.c_str()) > 0) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
} |
@ -1,35 +0,0 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file CorsHttpServer.h
|
|||
* @author Marek Kotewicz <marek@ethdev.com> |
|||
* @date 2014 |
|||
*/ |
|||
|
|||
#include <jsonrpccpp/server/connectors/httpserver.h> |
|||
|
|||
namespace jsonrpc |
|||
{ |
|||
|
|||
class CorsHttpServer: public HttpServer |
|||
{ |
|||
public: |
|||
using HttpServer::HttpServer; |
|||
bool virtual SendResponse(std::string const& _response, void* _addInfo = NULL); |
|||
}; |
|||
|
|||
} |
|||
|
@ -0,0 +1,57 @@ |
|||
FROM ubuntu:14.04 |
|||
|
|||
ENV DEBIAN_FRONTEND noninteractive |
|||
RUN apt-get update |
|||
RUN apt-get upgrade -y |
|||
|
|||
# Ethereum dependencies |
|||
RUN apt-get install -qy build-essential git cmake libcurl4-openssl-dev wget |
|||
RUN apt-get install -qy automake libtool yasm scons |
|||
|
|||
RUN useradd -ms /bin/bash user |
|||
USER user |
|||
WORKDIR /home/user |
|||
|
|||
# Emscripten SDK |
|||
RUN wget -c https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz |
|||
RUN tar xzf emsdk-portable.tar.gz |
|||
WORKDIR /home/user/emsdk_portable |
|||
RUN ./emsdk update && ./emsdk install latest && ./emsdk activate latest |
|||
ENV PATH $PATH:/home/user/emsdk_portable:/home/user/emsdk_portable/clang/fastcomp/build_master_64/bin:/home/user/emsdk_portable/emscripten/master |
|||
|
|||
USER root |
|||
RUN apt-get install -qy nodejs |
|||
USER user |
|||
RUN sed -i "s/NODE_JS = 'node'/NODE_JS = 'nodejs'/g" ~/.emscripten |
|||
|
|||
# CryptoPP |
|||
WORKDIR /home/user |
|||
RUN git clone https://github.com/mmoss/cryptopp.git |
|||
WORKDIR /home/user/cryptopp |
|||
RUN emcmake cmake -DCRYPTOPP_LIBRARY_TYPE=STATIC -DCRYPTOPP_RUNTIME_TYPE=STATIC && emmake make -j 4 |
|||
RUN ln -s . src/cryptopp |
|||
|
|||
# Boost |
|||
WORKDIR /home/user |
|||
RUN wget 'http://downloads.sourceforge.net/project/boost/boost/1.57.0/boost_1_57_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fboost%2Ffiles%2Fboost%2F1.57.0%2F&ts=1421887207&use_mirror=cznic' -O boost_1_57_0.tar.bz2 |
|||
RUN tar xjf boost_1_57_0.tar.bz2 |
|||
WORKDIR /home/user/boost_1_57_0 |
|||
RUN ./bootstrap.sh --with-libraries=thread,system,regex |
|||
RUN sed -i 's/using gcc ;/using gcc : : \/home\/user\/emsdk_portable\/emscripten\/master\/em++ ;/g' ./project-config.jam |
|||
RUN sed -i 's/$(archiver\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\/emar/g' ./tools/build/src/tools/gcc.jam |
|||
RUN sed -i 's/$(ranlib\[1\])/\/home\/user\/emsdk_portable\/emscripten\/master\/emranlib/g' ./tools/build/src/tools/gcc.jam |
|||
RUN ./b2 link=static variant=release threading=single runtime-link=static thread system regex |
|||
|
|||
|
|||
# Build soljs |
|||
WORKDIR /home/user |
|||
ADD https://api.github.com/repos/ethereum/cpp-ethereum/git/refs/heads/develop unused.txt |
|||
RUN git clone --depth=1 https://github.com/ethereum/cpp-ethereum |
|||
WORKDIR /home/user/cpp-ethereum |
|||
ADD https://api.github.com/repos/chriseth/cpp-ethereum/git/refs/heads/solidity-js unused2.txt |
|||
RUN git remote add -f solidityjs https://github.com/chriseth/cpp-ethereum |
|||
RUN git merge solidityjs/solidity-js |
|||
RUN emcmake cmake -DETH_STATIC=1 -DONLY_SOLIDITY=1 -DHEADLESS=1 -DCMAKE_CXX_COMPILER=/home/user/emsdk_portable/emscripten/master/em++ -DCMAKE_C_COMPILER=/home/user/emsdk_portable/emscripten/master/emcc |
|||
RUN emmake make -j 6 soljs |
|||
|
|||
ENTRYPOINT cat soljs/soljs.js |
@ -0,0 +1,219 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file transaction.cpp
|
|||
* @author Dmitrii Khokhlov <winsvega@mail.ru> |
|||
* @date 2015 |
|||
* Transaaction test functions. |
|||
*/ |
|||
|
|||
#include "TestHelper.h" |
|||
|
|||
using namespace std; |
|||
using namespace json_spirit; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
namespace dev { namespace test { |
|||
|
|||
Transaction createTransactionFromFields(mObject& _tObj) |
|||
{ |
|||
BOOST_REQUIRE(_tObj.count("data") > 0); |
|||
BOOST_REQUIRE(_tObj.count("value") > 0); |
|||
BOOST_REQUIRE(_tObj.count("gasPrice") > 0); |
|||
BOOST_REQUIRE(_tObj.count("gasLimit") > 0); |
|||
BOOST_REQUIRE(_tObj.count("nonce")> 0); |
|||
BOOST_REQUIRE(_tObj.count("to") > 0); |
|||
|
|||
BOOST_REQUIRE(_tObj.count("v") > 0); |
|||
BOOST_REQUIRE(_tObj.count("r") > 0); |
|||
BOOST_REQUIRE(_tObj.count("s") > 0); |
|||
|
|||
//Construct Rlp of the given transaction
|
|||
RLPStream rlpStream; |
|||
rlpStream.appendList(9); |
|||
rlpStream << bigint(_tObj["nonce"].get_str()) << bigint(_tObj["gasPrice"].get_str()) << bigint(_tObj["gasLimit"].get_str()); |
|||
if (_tObj["to"].get_str().empty()) |
|||
rlpStream << ""; |
|||
else |
|||
rlpStream << Address(_tObj["to"].get_str()); |
|||
rlpStream << bigint(_tObj["value"].get_str()) << importData(_tObj); |
|||
rlpStream << bigint(_tObj["v"].get_str()) << bigint(_tObj["r"].get_str()) << bigint(_tObj["s"].get_str()); |
|||
|
|||
return Transaction(rlpStream.out(), CheckSignature::Sender); |
|||
} |
|||
|
|||
void doTransactionTests(json_spirit::mValue& _v, bool _fillin) |
|||
{ |
|||
for (auto& i: _v.get_obj()) |
|||
{ |
|||
cerr << i.first << endl; |
|||
mObject& o = i.second.get_obj(); |
|||
|
|||
if (_fillin == false) |
|||
{ |
|||
BOOST_REQUIRE(o.count("rlp") > 0); |
|||
bytes rlpReaded = importByteArray(o["rlp"].get_str()); |
|||
Transaction txFromRlp; |
|||
|
|||
try |
|||
{ |
|||
txFromRlp = Transaction(rlpReaded, CheckSignature::Sender); |
|||
if (!txFromRlp.signature().isValid()) |
|||
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); |
|||
} |
|||
catch(...) |
|||
{ |
|||
BOOST_CHECK_MESSAGE(o.count("transaction") == 0, "A transction object should not be defined because the RLP is invalid!"); |
|||
return; |
|||
} |
|||
|
|||
BOOST_REQUIRE(o.count("transaction") > 0); |
|||
|
|||
mObject tObj = o["transaction"].get_obj(); |
|||
Transaction txFromFields = createTransactionFromFields(tObj); |
|||
|
|||
//Check the fields restored from RLP to original fields
|
|||
BOOST_CHECK_MESSAGE(txFromFields.data() == txFromRlp.data(), "Data in given RLP not matching the Transaction data!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.value() == txFromRlp.value(), "Value in given RLP not matching the Transaction value!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.gasPrice() == txFromRlp.gasPrice(), "GasPrice in given RLP not matching the Transaction gasPrice!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.gas() == txFromRlp.gas(),"Gas in given RLP not matching the Transaction gas!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.nonce() == txFromRlp.nonce(),"Nonce in given RLP not matching the Transaction nonce!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.receiveAddress() == txFromRlp.receiveAddress(), "Receive address in given RLP not matching the Transaction 'to' address!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields.sender() == txFromRlp.sender(), "Transaction sender address in given RLP not matching the Transaction 'vrs' signature!"); |
|||
BOOST_CHECK_MESSAGE(txFromFields == txFromRlp, "However, txFromFields != txFromRlp!"); |
|||
BOOST_REQUIRE (o.count("sender") > 0); |
|||
|
|||
Address addressReaded = Address(o["sender"].get_str()); |
|||
BOOST_CHECK_MESSAGE(txFromFields.sender() == addressReaded || txFromRlp.sender() == addressReaded, "Signature address of sender does not match given sender address!"); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_REQUIRE(o.count("transaction") > 0); |
|||
mObject tObj = o["transaction"].get_obj(); |
|||
|
|||
//Construct Rlp of the given transaction
|
|||
RLPStream rlpStream; |
|||
rlpStream.appendList(tObj.size()); |
|||
|
|||
if (tObj.count("nonce") > 0) |
|||
rlpStream << bigint(tObj["nonce"].get_str()); |
|||
|
|||
if (tObj.count("gasPrice") > 0) |
|||
rlpStream << bigint(tObj["gasPrice"].get_str()); |
|||
|
|||
if (tObj.count("gasLimit") > 0) |
|||
rlpStream << bigint(tObj["gasLimit"].get_str()); |
|||
|
|||
if (tObj.count("to") > 0) |
|||
{ |
|||
if (tObj["to"].get_str().empty()) |
|||
rlpStream << ""; |
|||
else |
|||
rlpStream << Address(tObj["to"].get_str()); |
|||
} |
|||
|
|||
if (tObj.count("value") > 0) |
|||
rlpStream << bigint(tObj["value"].get_str()); |
|||
|
|||
if (tObj.count("data") > 0) |
|||
rlpStream << importData(tObj); |
|||
|
|||
if (tObj.count("v") > 0) |
|||
rlpStream << bigint(tObj["v"].get_str()); |
|||
|
|||
if (tObj.count("r") > 0) |
|||
rlpStream << bigint(tObj["r"].get_str()); |
|||
|
|||
if (tObj.count("s") > 0) |
|||
rlpStream << bigint(tObj["s"].get_str()); |
|||
|
|||
if (tObj.count("extrafield") > 0) |
|||
rlpStream << bigint(tObj["extrafield"].get_str()); |
|||
|
|||
o["rlp"] = "0x" + toHex(rlpStream.out()); |
|||
|
|||
try |
|||
{ |
|||
Transaction txFromFields(rlpStream.out(), CheckSignature::Sender); |
|||
if (!txFromFields.signature().isValid()) |
|||
BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("transaction from RLP signature is invalid") ); |
|||
|
|||
o["sender"] = toString(txFromFields.sender()); |
|||
} |
|||
catch(...) |
|||
{ |
|||
o.erase(o.find("transaction")); |
|||
} |
|||
} |
|||
}//for
|
|||
}//doTransactionTests
|
|||
|
|||
} }// Namespace Close
|
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE(TransactionTests) |
|||
|
|||
BOOST_AUTO_TEST_CASE(TransactionTest) |
|||
{ |
|||
dev::test::executeTests("ttTransactionTest", "/TransactionTests", dev::test::doTransactionTests); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(tt10mbDataField) |
|||
{ |
|||
dev::test::executeTests("tt10mbDataField", "/TransactionTests", dev::test::doTransactionTests); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(ttCreateTest) |
|||
{ |
|||
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) |
|||
{ |
|||
string arg = boost::unit_test::framework::master_test_suite().argv[i]; |
|||
if (arg == "--createtest") |
|||
{ |
|||
if (boost::unit_test::framework::master_test_suite().argc <= i + 2) |
|||
{ |
|||
cnote << "usage: ./testeth --createtest <PathToConstructor> <PathToDestiny>\n"; |
|||
return; |
|||
} |
|||
try |
|||
{ |
|||
cnote << "Populating tests..."; |
|||
json_spirit::mValue v; |
|||
string s = asString(dev::contents(boost::unit_test::framework::master_test_suite().argv[i + 1])); |
|||
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Content of " + (string)boost::unit_test::framework::master_test_suite().argv[i + 1] + " is empty."); |
|||
json_spirit::read_string(s, v); |
|||
dev::test::doTransactionTests(v, true); |
|||
writeFile(boost::unit_test::framework::master_test_suite().argv[i + 2], asBytes(json_spirit::write_string(v, true))); |
|||
} |
|||
catch (Exception const& _e) |
|||
{ |
|||
BOOST_ERROR("Failed transaction test with Exception: " << diagnostic_information(_e)); |
|||
} |
|||
catch (std::exception const& _e) |
|||
{ |
|||
BOOST_ERROR("Failed transaction test with Exception: " << _e.what()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(userDefinedFileTT) |
|||
{ |
|||
dev::test::userDefinedTest("--ttTest", dev::test::doTransactionTests); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
File diff suppressed because one or more lines are too long
@ -0,0 +1,290 @@ |
|||
{ |
|||
"RightVRSTest" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "0x5544", |
|||
"gasLimit" : "2000", |
|||
"gasPrice" : "1", |
|||
"nonce" : "3", |
|||
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", |
|||
"value" : "10", |
|||
"v" : "28", |
|||
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", |
|||
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" |
|||
|
|||
} |
|||
}, |
|||
|
|||
"WrongVRSTestVl27" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "2000", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", |
|||
"value" : "10", |
|||
"v" : "26", |
|||
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", |
|||
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" |
|||
} |
|||
}, |
|||
|
|||
"WrongVRSTestVge31" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "2000", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", |
|||
"value" : "10", |
|||
"v" : "31", |
|||
"r" : "0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a", |
|||
"s" : "0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3" |
|||
} |
|||
}, |
|||
|
|||
"SenderTest" : { |
|||
"//" : "sender a94f5374fce5edbc8e2a8697c15331677e6ebf0b", |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "10", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "secretkey 45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithTooManyRLPElements" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "10", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804", |
|||
"extrafield" : "128472387293" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithTooFewRLPElements" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithHihghValue" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "115792089237316195423570985008687907853269984665640564039457584007913129639935", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
|
|||
"TransactionWithHihghValueOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "115792089237316195423570985008687907853269984665640564039457584007913129639936", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithSvalueOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithRvalueOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithNonceOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "1", |
|||
"nonce" : "115792089237316195423570985008687907853269984665640564039457584007913129639936", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithGasPriceOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "850", |
|||
"gasPrice" : "115792089237316195423570985008687907853269984665640564039457584007913129639936", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"TransactionWithGasLimitOverflow" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "115792089237316195423570985008687907853269984665640564039457584007913129639936", |
|||
"gasPrice" : "123", |
|||
"nonce" : "0", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"RLPElementsWithZeros" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "0x0000011222333", |
|||
"gasLimit" : "1000", |
|||
"gasPrice" : "00123", |
|||
"nonce" : "0054", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "00000011", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"RLPWrongHexElements" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "0x0000000012", |
|||
"gasLimit" : "1000", |
|||
"gasPrice" : "123", |
|||
"nonce" : "54", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "11", |
|||
"v" : "27", |
|||
"r" : "0x0048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0x00efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"EmptyTransaction" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "", |
|||
"gasPrice" : "", |
|||
"nonce" : "", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87", |
|||
"value" : "", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"AddressMore20" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "", |
|||
"gasPrice" : "", |
|||
"nonce" : "", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d871f", |
|||
"value" : "", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"AddressLess20" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "", |
|||
"gasPrice" : "", |
|||
"nonce" : "", |
|||
"to" : "095e7baea6a6c7c4c2dfeb977efac326af552d", |
|||
"value" : "", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
}, |
|||
|
|||
"AddressMore20WithFirstZeros" : { |
|||
"transaction" : |
|||
{ |
|||
"data" : "", |
|||
"gasLimit" : "", |
|||
"gasPrice" : "", |
|||
"nonce" : "", |
|||
"to" : "0x00000000000000000000000095e7baea6a6c7c4c2dfeb977efac326af552d", |
|||
"value" : "", |
|||
"v" : "27", |
|||
"r" : "0x48b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353", |
|||
"s" : "0xefffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804" |
|||
} |
|||
} |
|||
|
|||
} |
Loading…
Reference in new issue