diff --git a/features.txt b/features.txt
index 41323de4edf37433a32b660f70b4a3db4d1c93a0..66ad482cfbe4a90aefc16a10215e09c3ec007419 100644
--- a/features.txt
+++ b/features.txt
@@ -7,6 +7,10 @@
 #
 # https://github.com/tc39/process-document
 
+# Promise.prototype.finally
+# https://github.com/tc39/proposal-promise-finally
+Promise.prototype.finally
+
 # Async Iteration and Generators
 # https://github.com/tc39/proposal-async-iteration
 async-iteration
diff --git a/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js b/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js
new file mode 100644
index 0000000000000000000000000000000000000000..d7b808611ced878210d05e34606ee57ba02cd42b
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/invokes-then-with-function.js
@@ -0,0 +1,53 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally invokes `then` method
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+var target = new Promise(function () {});
+var returnValue = {};
+var callCount = 0;
+var thisValue = null;
+var argCount = null;
+var firstArg = null;
+var secondArg = null;
+
+target.then = function(a, b) {
+  callCount += 1;
+
+  thisValue = this;
+  argCount = arguments.length;
+  firstArg = a;
+  secondArg = b;
+
+  return returnValue;
+};
+
+var originalFinallyHandler = function () {};
+
+var result = Promise.prototype.finally.call(target, originalFinallyHandler, 2, 3);
+
+assert.sameValue(callCount, 1, 'Invokes `then` method exactly once');
+assert.sameValue(
+  thisValue,
+  target,
+  'Invokes `then` method with the instance as the `this` value'
+);
+assert.sameValue(argCount, 2, 'Invokes `then` method with exactly two single arguments');
+assert.sameValue(
+  typeof firstArg,
+  'function',
+  'Invokes `then` method with a function as the first argument'
+);
+assert.notSameValue(firstArg, originalFinallyHandler, 'Invokes `then` method with a different fulfillment handler');
+assert.sameValue(
+  typeof secondArg,
+  'function',
+  'Invokes `then` method with a function as the second argument'
+);
+assert.notSameValue(secondArg, originalFinallyHandler, 'Invokes `then` method with a different fulfillment handler');
+
+assert.sameValue(result, returnValue, 'Returns the result of the invocation of `then`');
diff --git a/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js b/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js
new file mode 100644
index 0000000000000000000000000000000000000000..1193119fbeded1f98bce84153a05bc99133ac31d
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/invokes-then-with-non-function.js
@@ -0,0 +1,49 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally invokes `then` method
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+var target = new Promise(function () {});
+var returnValue = {};
+var callCount = 0;
+var thisValue = null;
+var argCount = null;
+var firstArg = null;
+var secondArg = null;
+var result = null;
+
+target.then = function(a, b) {
+  callCount += 1;
+
+  thisValue = this;
+  argCount = arguments.length;
+  firstArg = a;
+  secondArg = b;
+
+  return returnValue;
+};
+
+result = Promise.prototype.finally.call(target, 1, 2, 3);
+
+assert.sameValue(callCount, 1, 'Invokes `then` method exactly once');
+assert.sameValue(
+  thisValue,
+  target,
+  'Invokes `then` method with the instance as the `this` value'
+);
+assert.sameValue(argCount, 2, 'Invokes `then` method with exactly two single arguments');
+assert.sameValue(
+  firstArg,
+  1,
+  'Invokes `then` method with the provided non-callable first argument'
+);
+assert.sameValue(
+  secondArg,
+  1,
+  'Invokes `then` method with the provided non-callable first argument'
+);
+assert.sameValue(result, returnValue, 'Returns the result of the invocation of `then`');
diff --git a/test/built-ins/Promise/prototype/finally/is-a-function.js b/test/built-ins/Promise/prototype/finally/is-a-function.js
new file mode 100644
index 0000000000000000000000000000000000000000..5630fc3288c35bf3009a2b08f5ef0b3989264995
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/is-a-function.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally is a function
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+assert.sameValue(
+  Promise.prototype.finally instanceof Function,
+  true,
+  'Expected Promise.prototype.finally to be instanceof Function'
+);
+
+assert.sameValue(
+  typeof Promise.prototype.finally,
+  'function',
+  'Expected Promise.prototype.finally to be a function'
+);
diff --git a/test/built-ins/Promise/prototype/finally/is-a-method.js b/test/built-ins/Promise/prototype/finally/is-a-method.js
new file mode 100644
index 0000000000000000000000000000000000000000..bc35f4242ad6314f3a5ccc4b3e2d549c064c2d31
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/is-a-method.js
@@ -0,0 +1,16 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally is a method on a Promise
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+var p = Promise.resolve(3);
+
+assert.sameValue(
+  p.finally,
+  Promise.prototype.finally,
+  'Expected the `finally` method on a Promise to be `Promise.prototype.finally`'
+);
diff --git a/test/built-ins/Promise/prototype/finally/length.js b/test/built-ins/Promise/prototype/finally/length.js
new file mode 100644
index 0000000000000000000000000000000000000000..a6010957d057a009e4ebd890d2a13de48e4ed7cd
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/length.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally `length` property
+esid: sec-promise.prototype.finally
+info: >
+    ES6 Section 17:
+    Every built-in Function object, including constructors, has a length
+    property whose value is an integer. Unless otherwise specified, this value
+    is equal to the largest number of named arguments shown in the subclause
+    headings for the function description, including optional parameters.
+
+    [...]
+
+    Unless otherwise specified, the length property of a built-in Function
+    object has the attributes { [[Writable]]: false, [[Enumerable]]: false,
+    [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Promise.prototype.finally]
+---*/
+
+verifyProperty(Promise.prototype.finally, "length", {
+  value: 1,
+  enumerable: false,
+  configurable: true,
+  writable: false,
+});
diff --git a/test/built-ins/Promise/prototype/finally/name.js b/test/built-ins/Promise/prototype/finally/name.js
new file mode 100644
index 0000000000000000000000000000000000000000..f2040e033e73018f3f1a76976e720ad117ba9624
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/name.js
@@ -0,0 +1,28 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally `name` property
+esid: sec-promise.prototype.finally
+info: >
+    ES Section 17:
+
+    Every built-in Function object, including constructors, that is not
+    identified as an anonymous function has a name property whose value is a
+    String. Unless otherwise specified, this value is the name that is given to
+    the function in this specification.
+
+    [...]
+
+    Unless otherwise specified, the name property of a built-in Function
+    object, if it exists, has the attributes { [[Writable]]: false,
+    [[Enumerable]]: false, [[Configurable]]: true }.
+includes: [propertyHelper.js]
+features: [Promise.prototype.finally]
+---*/
+
+assert.sameValue(Promise.prototype.finally.name, 'finally');
+
+verifyNotEnumerable(Promise.prototype.finally, 'name');
+verifyNotWritable(Promise.prototype.finally, 'name');
+verifyConfigurable(Promise.prototype.finally, 'name');
diff --git a/test/built-ins/Promise/prototype/finally/prop-desc.js b/test/built-ins/Promise/prototype/finally/prop-desc.js
new file mode 100644
index 0000000000000000000000000000000000000000..21c5b815954bc1638fb89d4dfef7a33240987d1b
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/prop-desc.js
@@ -0,0 +1,19 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: Promise.prototype.finally property descriptor
+esid: sec-promise.prototype.finally
+info: >
+    Every other data property described in clauses 18 through 26 and in Annex
+    B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false,
+    [[Configurable]]: true } unless otherwise specified.
+includes: [propertyHelper.js]
+features: [Promise.prototype.finally]
+---*/
+
+assert.sameValue(typeof Promise.prototype.finally, 'function');
+
+verifyNotEnumerable(Promise.prototype, 'finally');
+verifyWritable(Promise.prototype, 'finally');
+verifyConfigurable(Promise.prototype, 'finally');
diff --git a/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js b/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js
new file mode 100644
index 0000000000000000000000000000000000000000..c8ed29340c6ea8013ed9edb0c037de6d9cc94df6
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/rejected-observable-then-calls.js
@@ -0,0 +1,46 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally observably calls .then
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+flags: [async]
+---*/
+
+var initialThenCount = 0;
+var noReason = {};
+var no = Promise.reject(noReason);
+no.then = function () {
+  initialThenCount += 1;
+  return Promise.prototype.then.apply(this, arguments);
+};
+
+var onFinallyThenCount = 0;
+var yesValue = {};
+var yes = Promise.resolve(yesValue);
+yes.then = function () {
+  onFinallyThenCount += 1;
+  return Promise.prototype.then.apply(this, arguments);
+};
+
+var finallyCalled = false;
+var catchCalled = false;
+
+no.catch(function (e) {
+  assert.sameValue(e, noReason);
+  throw e;
+}).finally(function () {
+  finallyCalled = true;
+  return yes;
+}).catch(function (e) {
+  catchCalled = true;
+  assert.sameValue(e, noReason);
+}).then(function () {
+  assert.sameValue(finallyCalled, true, 'initial finally was called');
+  assert.sameValue(initialThenCount, 1, 'initial finally invokes .then once');
+
+  assert.sameValue(catchCalled, true, 'catch was called');
+  assert.sameValue(onFinallyThenCount, 1, 'onFinally return promise has .then invoked once');
+  $DONE();
+}).catch($ERROR);
diff --git a/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js b/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js
new file mode 100644
index 0000000000000000000000000000000000000000..cb31bcd40225291596ee7ea67d9d7dade6ffbccf
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/rejection-reason-no-fulfill.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally on a rejected promise can not convert to a fulfillment
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+flags: [async]
+---*/
+
+var original = {};
+var replacement = {};
+
+var p = Promise.reject(original);
+
+p.finally(function () {
+  assert.sameValue(arguments.length, 0, 'onFinally receives zero args');
+  return replacement;
+}).then(function () {
+  $ERROR('promise is rejected pre-finally; onFulfill should not be called');
+}).catch(function (reason) {
+  assert.sameValue(reason, original, 'onFinally can not override the rejection value by returning');
+}).then($DONE).catch($ERROR);
diff --git a/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js b/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js
new file mode 100644
index 0000000000000000000000000000000000000000..013854d104d61de07908652e39aeaa1fcef2792d
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/rejection-reason-override-with-throw.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally on a rejected promise can override the rejection reason
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+flags: [async]
+---*/
+
+var original = {};
+var thrown = {};
+
+var p = Promise.reject(original);
+
+p.finally(function () {
+  assert.sameValue(arguments.length, 0, 'onFinally receives zero args');
+  throw thrown;
+}).then(function () {
+  $ERROR('promise is rejected; onFulfill should not be called');
+}).catch(function (reason) {
+  assert.sameValue(reason, thrown, 'onFinally can override the rejection reason by throwing');
+}).then($DONE).catch($ERROR);
diff --git a/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js b/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js
new file mode 100644
index 0000000000000000000000000000000000000000..9535063405fa47142e90f1ec81261b6cc20c34e2
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/resolution-value-no-override.js
@@ -0,0 +1,20 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally on a fulfilled promise can not override the resolution value
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+flags: [async]
+---*/
+
+var obj = {};
+
+var p = Promise.resolve(obj);
+
+p.finally(function () {
+  assert.sameValue(arguments.length, 0, 'onFinally receives zero args');
+  return {};
+}).then(function (x) {
+  assert.sameValue(x, obj, 'onFinally can not override the resolution value');
+}).then($DONE).catch($ERROR);
diff --git a/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js b/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c92a7035995d5d9e1a3503e54aae75a12b9e977
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/resolved-observable-then-calls.js
@@ -0,0 +1,46 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: finally observably calls .then
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+flags: [async]
+---*/
+
+var initialThenCount = 0;
+var yesValue = {};
+var yes = Promise.resolve(yesValue);
+yes.then = function () {
+  initialThenCount += 1;
+  return Promise.prototype.then.apply(this, arguments);
+};
+
+var onFinallyThenCount = 0;
+var noReason = {};
+var no = Promise.reject(noReason);
+no.then = function () {
+  onFinallyThenCount += 1;
+  return Promise.prototype.then.apply(this, arguments);
+};
+
+var finallyCalled = false;
+var catchCalled = false;
+
+yes.then(function (x) {
+  assert.sameValue(x, yesValue);
+  return x;
+}).finally(function () {
+  finallyCalled = true;
+  return no;
+}).catch(function (e) {
+  catchCalled = true;
+  assert.sameValue(e, noReason);
+}).then(function () {
+  assert.sameValue(finallyCalled, true, 'initial finally was called');
+  assert.sameValue(initialThenCount, 1, 'initial finally invokes .then once');
+
+  assert.sameValue(catchCalled, true, 'catch was called');
+  assert.sameValue(onFinallyThenCount, 1, 'onFinally return promise has .then invoked once');
+  $DONE();
+}).catch($ERROR);
diff --git a/test/built-ins/Promise/prototype/finally/this-value-non-object.js b/test/built-ins/Promise/prototype/finally/this-value-non-object.js
new file mode 100644
index 0000000000000000000000000000000000000000..1e8467b9b86b2d9a72c8e4c0f919f783ca3b37f9
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/this-value-non-object.js
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: >
+  Promise.prototype.finally called with a non-object-coercible `this` value
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(undefined);
+}, 'undefined');
+
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(null);
+}, 'null');
diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js b/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js
new file mode 100644
index 0000000000000000000000000000000000000000..cdeccdadd31806985d76bafd8854df7f3eee4c17
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/this-value-then-not-callable.js
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: >
+  Promise.prototype.finally called with a `this` value that does not define a
+  callable `then` property
+esid: sec-promise.prototype.finally
+features: [Symbol, Promise.prototype.finally]
+---*/
+
+var symbol = Symbol();
+
+var thrower = function () { throw new Test262Error('this should never happen'); };
+
+var p = new Promise(function () {});
+
+p.then = undefined;
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'undefined');
+
+p.then = null;
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'null');
+
+p.then = 1;
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'number');
+
+p.then = '';
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'string');
+
+p.then = true;
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'boolean');
+
+p.then = symbol;
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'symbol');
+
+p.then = {};
+assert.throws(TypeError, function() {
+  Promise.prototype.finally.call(p, thrower);
+}, 'ordinary object');
diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js b/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js
new file mode 100644
index 0000000000000000000000000000000000000000..d8afd2612c7efdc6cdb27e6230114d42e709d1ac
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/this-value-then-poisoned.js
@@ -0,0 +1,24 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: >
+  Promise.prototype.finally called with a `this` value whose `then` property is
+  an accessor property that returns an abrupt completion
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+var poisonedThen = Object.defineProperty(new Promise(function () {}), 'then', {
+  get: function() {
+    throw new Test262Error();
+  }
+});
+
+assert.throws(Test262Error, function() {
+  Promise.prototype.finally.call(poisonedThen);
+});
+
+assert.throws(Test262Error, function() {
+  poisonedThen.finally();
+});
diff --git a/test/built-ins/Promise/prototype/finally/this-value-then-throws.js b/test/built-ins/Promise/prototype/finally/this-value-then-throws.js
new file mode 100644
index 0000000000000000000000000000000000000000..7fff8d51aa22a015a199e0e816ce54c4c5646a28
--- /dev/null
+++ b/test/built-ins/Promise/prototype/finally/this-value-then-throws.js
@@ -0,0 +1,23 @@
+// Copyright (C) 2017 Jordan Harband. All rights reserved.
+// This code is governed by the BSD license found in the LICENSE file.
+/*---
+author: Jordan Harband
+description: >
+  Promise.prototype.finally called with a `this` value that defines a `then`
+  method which returns an abrupt completion.
+esid: sec-promise.prototype.finally
+features: [Promise.prototype.finally]
+---*/
+
+var thrower = new Promise(function () {});
+thrower.then = function() {
+  throw new Test262Error();
+};
+
+assert.throws(Test262Error, function() {
+  Promise.prototype.finally.call(thrower);
+});
+
+assert.throws(Test262Error, function() {
+  thrower.finally();
+});