Upgrade V8 to version 4.9.385.28
https://chromium.googlesource.com/v8/v8/+/4.9.385.28
FPIIM-449
Change-Id: I4b2e74289d4bf3667f2f3dc8aa2e541f63e26eb4
diff --git a/src/js/OWNERS b/src/js/OWNERS
new file mode 100644
index 0000000..f7002c7
--- /dev/null
+++ b/src/js/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+adamk@chromium.org
+bmeurer@chromium.org
+cbruni@chromium.org
+ishell@chromium.org
+jkummerow@chromium.org
+littledan@chromium.org
+rossberg@chromium.org
+verwaest@chromium.org
+yangguo@chromium.org
diff --git a/src/js/array-iterator.js b/src/js/array-iterator.js
new file mode 100644
index 0000000..2609ebd
--- /dev/null
+++ b/src/js/array-iterator.js
@@ -0,0 +1,154 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -----------------------------------------------------------------------
+// Imports
+
+var arrayIterationKindSymbol =
+ utils.ImportNow("array_iteration_kind_symbol");
+var arrayIteratorNextIndexSymbol =
+ utils.ImportNow("array_iterator_next_symbol");
+var arrayIteratorObjectSymbol =
+ utils.ImportNow("array_iterator_object_symbol");
+var GlobalArray = global.Array;
+var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var GlobalTypedArray = global.Uint8Array.__proto__;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+})
+
+// -----------------------------------------------------------------------
+
+function ArrayIterator() {}
+
+
+// TODO(wingo): Update section numbers when ES6 has stabilized. The
+// section numbers below are already out of date as of the May 2014
+// draft.
+
+
+// 15.4.5.1 CreateArrayIterator Abstract Operation
+function CreateArrayIterator(array, kind) {
+ var object = TO_OBJECT(array);
+ var iterator = new ArrayIterator;
+ SET_PRIVATE(iterator, arrayIteratorObjectSymbol, object);
+ SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, 0);
+ SET_PRIVATE(iterator, arrayIterationKindSymbol, kind);
+ return iterator;
+}
+
+
+// 22.1.5.2.2 %ArrayIteratorPrototype%[@@iterator]
+function ArrayIteratorIterator() {
+ return this;
+}
+
+
+// ES6 section 22.1.5.2.1 %ArrayIteratorPrototype%.next( )
+function ArrayIteratorNext() {
+ var iterator = this;
+ var value = UNDEFINED;
+ var done = true;
+
+ if (!IS_RECEIVER(iterator) ||
+ !HAS_DEFINED_PRIVATE(iterator, arrayIteratorNextIndexSymbol)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Array Iterator.prototype.next', this);
+ }
+
+ var array = GET_PRIVATE(iterator, arrayIteratorObjectSymbol);
+ if (!IS_UNDEFINED(array)) {
+ var index = GET_PRIVATE(iterator, arrayIteratorNextIndexSymbol);
+ var itemKind = GET_PRIVATE(iterator, arrayIterationKindSymbol);
+ var length = TO_UINT32(array.length);
+
+ // "sparse" is never used.
+
+ if (index >= length) {
+ SET_PRIVATE(iterator, arrayIteratorObjectSymbol, UNDEFINED);
+ } else {
+ SET_PRIVATE(iterator, arrayIteratorNextIndexSymbol, index + 1);
+
+ if (itemKind == ITERATOR_KIND_VALUES) {
+ value = array[index];
+ } else if (itemKind == ITERATOR_KIND_ENTRIES) {
+ value = [index, array[index]];
+ } else {
+ value = index;
+ }
+ done = false;
+ }
+ }
+
+ return %_CreateIterResultObject(value, done);
+}
+
+
+function ArrayEntries() {
+ return CreateArrayIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function ArrayValues() {
+ return CreateArrayIterator(this, ITERATOR_KIND_VALUES);
+}
+
+
+function ArrayKeys() {
+ return CreateArrayIterator(this, ITERATOR_KIND_KEYS);
+}
+
+
+%FunctionSetPrototype(ArrayIterator, {__proto__: IteratorPrototype});
+%FunctionSetInstanceClassName(ArrayIterator, 'Array Iterator');
+
+utils.InstallFunctions(ArrayIterator.prototype, DONT_ENUM, [
+ 'next', ArrayIteratorNext
+]);
+utils.SetFunctionName(ArrayIteratorIterator, iteratorSymbol);
+%AddNamedProperty(ArrayIterator.prototype, iteratorSymbol,
+ ArrayIteratorIterator, DONT_ENUM);
+%AddNamedProperty(ArrayIterator.prototype, toStringTagSymbol,
+ "Array Iterator", READ_ONLY | DONT_ENUM);
+
+utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
+ // No 'values' since it breaks webcompat: http://crbug.com/409858
+ 'entries', ArrayEntries,
+ 'keys', ArrayKeys
+]);
+
+// TODO(adam): Remove this call once 'values' is in the above
+// InstallFunctions block, as it'll be redundant.
+utils.SetFunctionName(ArrayValues, 'values');
+
+%AddNamedProperty(GlobalArray.prototype, iteratorSymbol, ArrayValues,
+ DONT_ENUM);
+
+%AddNamedProperty(GlobalTypedArray.prototype,
+ 'entries', ArrayEntries, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype, 'values', ArrayValues, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype, 'keys', ArrayKeys, DONT_ENUM);
+%AddNamedProperty(GlobalTypedArray.prototype,
+ iteratorSymbol, ArrayValues, DONT_ENUM);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.ArrayValues = ArrayValues;
+});
+
+%InstallToContext(["array_values_iterator", ArrayValues]);
+
+})
diff --git a/src/js/array.js b/src/js/array.js
new file mode 100644
index 0000000..f9cf161
--- /dev/null
+++ b/src/js/array.js
@@ -0,0 +1,1983 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var AddIndexedProperty;
+var FLAG_harmony_tolength;
+var FLAG_harmony_species;
+var GetIterator;
+var GetMethod;
+var GlobalArray = global.Array;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var ObjectDefineProperty;
+var ObjectHasOwnProperty;
+var ObjectToString = utils.ImportNow("object_to_string");
+var ObserveBeginPerformSplice;
+var ObserveEndPerformSplice;
+var ObserveEnqueueSpliceRecord;
+var SameValueZero;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
+
+utils.Import(function(from) {
+ AddIndexedProperty = from.AddIndexedProperty;
+ GetIterator = from.GetIterator;
+ GetMethod = from.GetMethod;
+ MakeTypeError = from.MakeTypeError;
+ MaxSimple = from.MaxSimple;
+ MinSimple = from.MinSimple;
+ ObjectDefineProperty = from.ObjectDefineProperty;
+ ObjectHasOwnProperty = from.ObjectHasOwnProperty;
+ ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
+ ObserveEndPerformSplice = from.ObserveEndPerformSplice;
+ ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
+ SameValueZero = from.SameValueZero;
+});
+
+utils.ImportFromExperimental(function(from) {
+ FLAG_harmony_tolength = from.FLAG_harmony_tolength;
+ FLAG_harmony_species = from.FLAG_harmony_species;
+});
+
+// -------------------------------------------------------------------
+
+
+function ArraySpeciesCreate(array, length) {
+ var constructor;
+ if (FLAG_harmony_species) {
+ constructor = %ArraySpeciesConstructor(array);
+ } else {
+ constructor = GlobalArray;
+ }
+ return new constructor(length);
+}
+
+
+function DefineIndexedProperty(array, i, value) {
+ if (FLAG_harmony_species) {
+ var result = ObjectDefineProperty(array, i, {
+ value: value, writable: true, configurable: true, enumerable: true
+ });
+ if (!result) throw MakeTypeError(kStrictCannotAssign, i);
+ } else {
+ AddIndexedProperty(array, i, value);
+ }
+}
+
+
+// Global list of arrays visited during toString, toLocaleString and
+// join invocations.
+var visited_arrays = new InternalArray();
+
+
+// Gets a sorted array of array keys. Useful for operations on sparse
+// arrays. Dupes have not been removed.
+function GetSortedArrayKeys(array, indices) {
+ var keys = new InternalArray();
+ if (IS_NUMBER(indices)) {
+ // It's an interval
+ var limit = indices;
+ for (var i = 0; i < limit; ++i) {
+ var e = array[i];
+ if (!IS_UNDEFINED(e) || i in array) {
+ keys.push(i);
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ var e = array[key];
+ if (!IS_UNDEFINED(e) || key in array) {
+ keys.push(key);
+ }
+ }
+ }
+ keys.sort(function(a, b) { return a - b; });
+ }
+ return keys;
+}
+
+
+function SparseJoinWithSeparatorJS(array, len, convert, separator) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var totalLength = 0;
+ var elements = new InternalArray(keys.length * 2);
+ var previousKey = -1;
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if (key != previousKey) { // keys may contain duplicates.
+ var e = array[key];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i * 2] = key;
+ elements[i * 2 + 1] = e;
+ previousKey = key;
+ }
+ }
+ return %SparseJoinWithSeparator(elements, len, separator);
+}
+
+
+// Optimized for sparse arrays if separator is ''.
+function SparseJoin(array, len, convert) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var last_key = -1;
+ var keys_length = keys.length;
+
+ var elements = new InternalArray(keys_length);
+ var elements_length = 0;
+
+ for (var i = 0; i < keys_length; i++) {
+ var key = keys[i];
+ if (key != last_key) {
+ var e = array[key];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[elements_length++] = e;
+ last_key = key;
+ }
+ }
+ return %StringBuilderConcat(elements, elements_length, '');
+}
+
+
+function UseSparseVariant(array, length, is_array, touched) {
+ // Only use the sparse variant on arrays that are likely to be sparse and the
+ // number of elements touched in the operation is relatively small compared to
+ // the overall size of the array.
+ if (!is_array || length < 1000 || %IsObserved(array) ||
+ %HasComplexElements(array)) {
+ return false;
+ }
+ if (!%_IsSmi(length)) {
+ return true;
+ }
+ var elements_threshold = length >> 2; // No more than 75% holes
+ var estimated_elements = %EstimateNumberOfElements(array);
+ return (estimated_elements < elements_threshold) &&
+ (touched > estimated_elements * 4);
+}
+
+
+function Join(array, length, separator, convert) {
+ if (length == 0) return '';
+
+ var is_array = IS_ARRAY(array);
+
+ if (is_array) {
+ // If the array is cyclic, return the empty string for already
+ // visited arrays.
+ if (!%PushIfAbsent(visited_arrays, array)) return '';
+ }
+
+ // Attempt to convert the elements.
+ try {
+ if (UseSparseVariant(array, length, is_array, length)) {
+ %NormalizeElements(array);
+ if (separator.length == 0) {
+ return SparseJoin(array, length, convert);
+ } else {
+ return SparseJoinWithSeparatorJS(array, length, convert, separator);
+ }
+ }
+
+ // Fast case for one-element arrays.
+ if (length == 1) {
+ var e = array[0];
+ if (IS_STRING(e)) return e;
+ return convert(e);
+ }
+
+ // Construct an array for the elements.
+ var elements = new InternalArray(length);
+
+ // We pull the empty separator check outside the loop for speed!
+ if (separator.length == 0) {
+ var elements_length = 0;
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[elements_length++] = e;
+ }
+ elements.length = elements_length;
+ var result = %_FastOneByteArrayJoin(elements, '');
+ if (!IS_UNDEFINED(result)) return result;
+ return %StringBuilderConcat(elements, elements_length, '');
+ }
+ // Non-empty separator case.
+ // If the first element is a number then use the heuristic that the
+ // remaining elements are also likely to be numbers.
+ if (!IS_NUMBER(array[0])) {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (!IS_STRING(e)) e = convert(e);
+ elements[i] = e;
+ }
+ } else {
+ for (var i = 0; i < length; i++) {
+ var e = array[i];
+ if (IS_NUMBER(e)) {
+ e = %_NumberToString(e);
+ } else if (!IS_STRING(e)) {
+ e = convert(e);
+ }
+ elements[i] = e;
+ }
+ }
+ var result = %_FastOneByteArrayJoin(elements, separator);
+ if (!IS_UNDEFINED(result)) return result;
+
+ return %StringBuilderJoin(elements, length, separator);
+ } finally {
+ // Make sure to remove the last element of the visited array no
+ // matter what happens.
+ if (is_array) visited_arrays.length = visited_arrays.length - 1;
+ }
+}
+
+
+function ConvertToString(x) {
+ if (IS_NULL_OR_UNDEFINED(x)) {
+ return '';
+ } else {
+ return TO_STRING(x);
+ }
+}
+
+
+function ConvertToLocaleString(e) {
+ if (IS_NULL_OR_UNDEFINED(e)) {
+ return '';
+ } else {
+ return TO_STRING(e.toLocaleString());
+ }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SparseSlice(array, start_i, del_count, len, deleted_elements) {
+ // Move deleted elements to a new array (the return value from splice).
+ var indices = %GetArrayKeys(array, start_i + del_count);
+ if (IS_NUMBER(indices)) {
+ var limit = indices;
+ for (var i = start_i; i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ DefineIndexedProperty(deleted_elements, i - start_i, current);
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ if (key >= start_i) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ DefineIndexedProperty(deleted_elements, key - start_i, current);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+// This function implements the optimized splice implementation that can use
+// special array operations to handle sparse arrays in a sensible fashion.
+function SparseMove(array, start_i, del_count, len, num_additional_args) {
+ // Bail out if no moving is necessary.
+ if (num_additional_args === del_count) return;
+ // Move data to new array.
+ var new_array = new InternalArray(
+ // Clamp array length to 2^32-1 to avoid early RangeError.
+ MinSimple(len - del_count + num_additional_args, 0xffffffff));
+ var big_indices;
+ var indices = %GetArrayKeys(array, len);
+ if (IS_NUMBER(indices)) {
+ var limit = indices;
+ for (var i = 0; i < start_i && i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ new_array[i] = current;
+ }
+ }
+ for (var i = start_i + del_count; i < limit; ++i) {
+ var current = array[i];
+ if (!IS_UNDEFINED(current) || i in array) {
+ new_array[i - del_count + num_additional_args] = current;
+ }
+ }
+ } else {
+ var length = indices.length;
+ for (var k = 0; k < length; ++k) {
+ var key = indices[k];
+ if (!IS_UNDEFINED(key)) {
+ if (key < start_i) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ new_array[key] = current;
+ }
+ } else if (key >= start_i + del_count) {
+ var current = array[key];
+ if (!IS_UNDEFINED(current) || key in array) {
+ var new_key = key - del_count + num_additional_args;
+ new_array[new_key] = current;
+ if (new_key > 0xfffffffe) {
+ big_indices = big_indices || new InternalArray();
+ big_indices.push(new_key);
+ }
+ }
+ }
+ }
+ }
+ }
+ // Move contents of new_array into this array
+ %MoveArrayContents(new_array, array);
+ // Add any moved values that aren't elements anymore.
+ if (!IS_UNDEFINED(big_indices)) {
+ var length = big_indices.length;
+ for (var i = 0; i < length; ++i) {
+ var key = big_indices[i];
+ array[key] = new_array[key];
+ }
+ }
+}
+
+
+// This is part of the old simple-minded splice. We are using it either
+// because the receiver is not an array (so we have no choice) or because we
+// know we are not deleting or moving a lot of elements.
+function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < del_count; i++) {
+ var index = start_i + i;
+ if (HAS_INDEX(array, index, is_array)) {
+ var current = array[index];
+ DefineIndexedProperty(deleted_elements, i, current);
+ }
+ }
+}
+
+
+function SimpleMove(array, start_i, del_count, len, num_additional_args) {
+ var is_array = IS_ARRAY(array);
+ if (num_additional_args !== del_count) {
+ // Move the existing elements after the elements to be deleted
+ // to the right position in the resulting array.
+ if (num_additional_args > del_count) {
+ for (var i = len - del_count; i > start_i; i--) {
+ var from_index = i + del_count - 1;
+ var to_index = i + num_additional_args - 1;
+ if (HAS_INDEX(array, from_index, is_array)) {
+ array[to_index] = array[from_index];
+ } else {
+ delete array[to_index];
+ }
+ }
+ } else {
+ for (var i = start_i; i < len - del_count; i++) {
+ var from_index = i + del_count;
+ var to_index = i + num_additional_args;
+ if (HAS_INDEX(array, from_index, is_array)) {
+ array[to_index] = array[from_index];
+ } else {
+ delete array[to_index];
+ }
+ }
+ for (var i = len; i > len - del_count + num_additional_args; i--) {
+ delete array[i - 1];
+ }
+ }
+ }
+}
+
+
+// -------------------------------------------------------------------
+
+
+function ArrayToString() {
+ var array;
+ var func;
+ if (IS_ARRAY(this)) {
+ func = this.join;
+ if (func === ArrayJoin) {
+ return Join(this, this.length, ',', ConvertToString);
+ }
+ array = this;
+ } else {
+ array = TO_OBJECT(this);
+ func = array.join;
+ }
+ if (!IS_CALLABLE(func)) {
+ return %_Call(ObjectToString, array);
+ }
+ return %_Call(func, array);
+}
+
+
+function InnerArrayToLocaleString(array, length) {
+ var len = TO_LENGTH_OR_UINT32(length);
+ if (len === 0) return "";
+ return Join(array, len, ',', ConvertToLocaleString);
+}
+
+
+function ArrayToLocaleString() {
+ var array = TO_OBJECT(this);
+ var arrayLen = array.length;
+ return InnerArrayToLocaleString(array, arrayLen);
+}
+
+
+function InnerArrayJoin(separator, array, length) {
+ if (IS_UNDEFINED(separator)) {
+ separator = ',';
+ } else {
+ separator = TO_STRING(separator);
+ }
+
+ var result = %_FastOneByteArrayJoin(array, separator);
+ if (!IS_UNDEFINED(result)) return result;
+
+ // Fast case for one-element arrays.
+ if (length === 1) {
+ var e = array[0];
+ if (IS_NULL_OR_UNDEFINED(e)) return '';
+ return TO_STRING(e);
+ }
+
+ return Join(array, length, separator, ConvertToString);
+}
+
+
+function ArrayJoin(separator) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.join");
+
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+
+ return InnerArrayJoin(separator, array, length);
+}
+
+
+function ObservedArrayPop(n) {
+ n--;
+ var value = this[n];
+
+ try {
+ ObserveBeginPerformSplice(this);
+ delete this[n];
+ this.length = n;
+ } finally {
+ ObserveEndPerformSplice(this);
+ ObserveEnqueueSpliceRecord(this, n, [value], 0);
+ }
+
+ return value;
+}
+
+
+// Removes the last element from the array and returns it. See
+// ECMA-262, section 15.4.4.6.
+function ArrayPop() {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.pop");
+
+ var array = TO_OBJECT(this);
+ var n = TO_LENGTH_OR_UINT32(array.length);
+ if (n == 0) {
+ array.length = n;
+ return;
+ }
+
+ if (%IsObserved(array))
+ return ObservedArrayPop.call(array, n);
+
+ n--;
+ var value = array[n];
+ %DeleteProperty_Strict(array, n);
+ array.length = n;
+ return value;
+}
+
+
+function ObservedArrayPush() {
+ var n = TO_LENGTH_OR_UINT32(this.length);
+ var m = %_ArgumentsLength();
+
+ try {
+ ObserveBeginPerformSplice(this);
+ for (var i = 0; i < m; i++) {
+ this[i+n] = %_Arguments(i);
+ }
+ var new_length = n + m;
+ this.length = new_length;
+ } finally {
+ ObserveEndPerformSplice(this);
+ ObserveEnqueueSpliceRecord(this, n, [], m);
+ }
+
+ return new_length;
+}
+
+
+// Appends the arguments to the end of the array and returns the new
+// length of the array. See ECMA-262, section 15.4.4.7.
+function ArrayPush() {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.push");
+
+ if (%IsObserved(this))
+ return ObservedArrayPush.apply(this, arguments);
+
+ var array = TO_OBJECT(this);
+ var n = TO_LENGTH_OR_UINT32(array.length);
+ var m = %_ArgumentsLength();
+
+ // It appears that there is no enforced, absolute limit on the number of
+ // arguments, but it would surely blow the stack to use 2**30 or more.
+ // To avoid integer overflow, do the comparison to the max safe integer
+ // after subtracting 2**30 from both sides. (2**31 would seem like a
+ // natural value, but it is negative in JS, and 2**32 is 1.)
+ if (m > (1 << 30) || (n - (1 << 30)) + m > kMaxSafeInteger - (1 << 30)) {
+ throw MakeTypeError(kPushPastSafeLength, m, n);
+ }
+
+ for (var i = 0; i < m; i++) {
+ array[i+n] = %_Arguments(i);
+ }
+
+ var new_length = n + m;
+ array.length = new_length;
+ return new_length;
+}
+
+
+// For implementing reverse() on large, sparse arrays.
+function SparseReverse(array, len) {
+ var keys = GetSortedArrayKeys(array, %GetArrayKeys(array, len));
+ var high_counter = keys.length - 1;
+ var low_counter = 0;
+ while (low_counter <= high_counter) {
+ var i = keys[low_counter];
+ var j = keys[high_counter];
+
+ var j_complement = len - j - 1;
+ var low, high;
+
+ if (j_complement <= i) {
+ high = j;
+ while (keys[--high_counter] == j) { }
+ low = j_complement;
+ }
+ if (j_complement >= i) {
+ low = i;
+ while (keys[++low_counter] == i) { }
+ high = len - i - 1;
+ }
+
+ var current_i = array[low];
+ if (!IS_UNDEFINED(current_i) || low in array) {
+ var current_j = array[high];
+ if (!IS_UNDEFINED(current_j) || high in array) {
+ array[low] = current_j;
+ array[high] = current_i;
+ } else {
+ array[high] = current_i;
+ delete array[low];
+ }
+ } else {
+ var current_j = array[high];
+ if (!IS_UNDEFINED(current_j) || high in array) {
+ array[low] = current_j;
+ delete array[high];
+ }
+ }
+ }
+}
+
+function PackedArrayReverse(array, len) {
+ var j = len - 1;
+ for (var i = 0; i < j; i++, j--) {
+ var current_i = array[i];
+ var current_j = array[j];
+ array[i] = current_j;
+ array[j] = current_i;
+ }
+ return array;
+}
+
+
+function GenericArrayReverse(array, len) {
+ var j = len - 1;
+ for (var i = 0; i < j; i++, j--) {
+ if (i in array) {
+ var current_i = array[i];
+ if (j in array) {
+ var current_j = array[j];
+ array[i] = current_j;
+ array[j] = current_i;
+ } else {
+ array[j] = current_i;
+ delete array[i];
+ }
+ } else {
+ if (j in array) {
+ var current_j = array[j];
+ array[i] = current_j;
+ delete array[j];
+ }
+ }
+ }
+ return array;
+}
+
+
+function ArrayReverse() {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reverse");
+
+ var array = TO_OBJECT(this);
+ var len = TO_LENGTH_OR_UINT32(array.length);
+ var isArray = IS_ARRAY(array);
+
+ if (UseSparseVariant(array, len, isArray, len)) {
+ %NormalizeElements(array);
+ SparseReverse(array, len);
+ return array;
+ } else if (isArray && %_HasFastPackedElements(array)) {
+ return PackedArrayReverse(array, len);
+ } else {
+ return GenericArrayReverse(array, len);
+ }
+}
+
+
+function ObservedArrayShift(len) {
+ var first = this[0];
+
+ try {
+ ObserveBeginPerformSplice(this);
+ SimpleMove(this, 0, 1, len, 0);
+ this.length = len - 1;
+ } finally {
+ ObserveEndPerformSplice(this);
+ ObserveEnqueueSpliceRecord(this, 0, [first], 0);
+ }
+
+ return first;
+}
+
+
+function ArrayShift() {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.shift");
+
+ var array = TO_OBJECT(this);
+ var len = TO_LENGTH_OR_UINT32(array.length);
+
+ if (len === 0) {
+ array.length = 0;
+ return;
+ }
+
+ if (%object_is_sealed(array)) throw MakeTypeError(kArrayFunctionsOnSealed);
+
+ if (%IsObserved(array))
+ return ObservedArrayShift.call(array, len);
+
+ var first = array[0];
+
+ if (UseSparseVariant(array, len, IS_ARRAY(array), len)) {
+ SparseMove(array, 0, 1, len, 0);
+ } else {
+ SimpleMove(array, 0, 1, len, 0);
+ }
+
+ array.length = len - 1;
+
+ return first;
+}
+
+
+function ObservedArrayUnshift() {
+ var len = TO_LENGTH_OR_UINT32(this.length);
+ var num_arguments = %_ArgumentsLength();
+
+ try {
+ ObserveBeginPerformSplice(this);
+ SimpleMove(this, 0, 0, len, num_arguments);
+ for (var i = 0; i < num_arguments; i++) {
+ this[i] = %_Arguments(i);
+ }
+ var new_length = len + num_arguments;
+ this.length = new_length;
+ } finally {
+ ObserveEndPerformSplice(this);
+ ObserveEnqueueSpliceRecord(this, 0, [], num_arguments);
+ }
+
+ return new_length;
+}
+
+
+function ArrayUnshift(arg1) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.unshift");
+
+ if (%IsObserved(this))
+ return ObservedArrayUnshift.apply(this, arguments);
+
+ var array = TO_OBJECT(this);
+ var len = TO_LENGTH_OR_UINT32(array.length);
+ var num_arguments = %_ArgumentsLength();
+
+ if (len > 0 && UseSparseVariant(array, len, IS_ARRAY(array), len) &&
+ !%object_is_sealed(array)) {
+ SparseMove(array, 0, 0, len, num_arguments);
+ } else {
+ SimpleMove(array, 0, 0, len, num_arguments);
+ }
+
+ for (var i = 0; i < num_arguments; i++) {
+ array[i] = %_Arguments(i);
+ }
+
+ var new_length = len + num_arguments;
+ array.length = new_length;
+ return new_length;
+}
+
+
+function ArraySlice(start, end) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.slice");
+
+ var array = TO_OBJECT(this);
+ var len = TO_LENGTH_OR_UINT32(array.length);
+ var start_i = TO_INTEGER(start);
+ var end_i = len;
+
+ if (!IS_UNDEFINED(end)) end_i = TO_INTEGER(end);
+
+ if (start_i < 0) {
+ start_i += len;
+ if (start_i < 0) start_i = 0;
+ } else {
+ if (start_i > len) start_i = len;
+ }
+
+ if (end_i < 0) {
+ end_i += len;
+ if (end_i < 0) end_i = 0;
+ } else {
+ if (end_i > len) end_i = len;
+ }
+
+ var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
+
+ if (end_i < start_i) return result;
+
+ if (UseSparseVariant(array, len, IS_ARRAY(array), end_i - start_i)) {
+ %NormalizeElements(array);
+ %NormalizeElements(result);
+ SparseSlice(array, start_i, end_i - start_i, len, result);
+ } else {
+ SimpleSlice(array, start_i, end_i - start_i, len, result);
+ }
+
+ result.length = end_i - start_i;
+
+ return result;
+}
+
+
+function ComputeSpliceStartIndex(start_i, len) {
+ if (start_i < 0) {
+ start_i += len;
+ return start_i < 0 ? 0 : start_i;
+ }
+
+ return start_i > len ? len : start_i;
+}
+
+
+function ComputeSpliceDeleteCount(delete_count, num_arguments, len, start_i) {
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given as a request to delete all the elements from the start.
+ // And it differs from the case of undefined delete count.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ var del_count = 0;
+ if (num_arguments == 1)
+ return len - start_i;
+
+ del_count = TO_INTEGER(delete_count);
+ if (del_count < 0)
+ return 0;
+
+ if (del_count > len - start_i)
+ return len - start_i;
+
+ return del_count;
+}
+
+
+function ObservedArraySplice(start, delete_count) {
+ var num_arguments = %_ArgumentsLength();
+ var len = TO_LENGTH_OR_UINT32(this.length);
+ var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+ var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+ start_i);
+ var deleted_elements = [];
+ deleted_elements.length = del_count;
+ var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+ try {
+ ObserveBeginPerformSplice(this);
+
+ SimpleSlice(this, start_i, del_count, len, deleted_elements);
+ SimpleMove(this, start_i, del_count, len, num_elements_to_add);
+
+ // Insert the arguments into the resulting array in
+ // place of the deleted elements.
+ var i = start_i;
+ var arguments_index = 2;
+ var arguments_length = %_ArgumentsLength();
+ while (arguments_index < arguments_length) {
+ this[i++] = %_Arguments(arguments_index++);
+ }
+ this.length = len - del_count + num_elements_to_add;
+
+ } finally {
+ ObserveEndPerformSplice(this);
+ if (deleted_elements.length || num_elements_to_add) {
+ ObserveEnqueueSpliceRecord(this,
+ start_i,
+ deleted_elements.slice(),
+ num_elements_to_add);
+ }
+ }
+
+ // Return the deleted elements.
+ return deleted_elements;
+}
+
+
+function ArraySplice(start, delete_count) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.splice");
+
+ if (%IsObserved(this))
+ return ObservedArraySplice.apply(this, arguments);
+
+ var num_arguments = %_ArgumentsLength();
+ var array = TO_OBJECT(this);
+ var len = TO_LENGTH_OR_UINT32(array.length);
+ var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
+ var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
+ start_i);
+ var deleted_elements = ArraySpeciesCreate(array, del_count);
+ deleted_elements.length = del_count;
+ var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
+
+ if (del_count != num_elements_to_add && %object_is_sealed(array)) {
+ throw MakeTypeError(kArrayFunctionsOnSealed);
+ } else if (del_count > 0 && %object_is_frozen(array)) {
+ throw MakeTypeError(kArrayFunctionsOnFrozen);
+ }
+
+ var changed_elements = del_count;
+ if (num_elements_to_add != del_count) {
+ // If the slice needs to do a actually move elements after the insertion
+ // point, then include those in the estimate of changed elements.
+ changed_elements += len - start_i - del_count;
+ }
+ if (UseSparseVariant(array, len, IS_ARRAY(array), changed_elements)) {
+ %NormalizeElements(array);
+ %NormalizeElements(deleted_elements);
+ SparseSlice(array, start_i, del_count, len, deleted_elements);
+ SparseMove(array, start_i, del_count, len, num_elements_to_add);
+ } else {
+ SimpleSlice(array, start_i, del_count, len, deleted_elements);
+ SimpleMove(array, start_i, del_count, len, num_elements_to_add);
+ }
+
+ // Insert the arguments into the resulting array in
+ // place of the deleted elements.
+ var i = start_i;
+ var arguments_index = 2;
+ var arguments_length = %_ArgumentsLength();
+ while (arguments_index < arguments_length) {
+ array[i++] = %_Arguments(arguments_index++);
+ }
+ array.length = len - del_count + num_elements_to_add;
+
+ // Return the deleted elements.
+ return deleted_elements;
+}
+
+
+function InnerArraySort(array, length, comparefn) {
+ // In-place QuickSort algorithm.
+ // For short (length <= 22) arrays, insertion sort is used for efficiency.
+
+ if (!IS_CALLABLE(comparefn)) {
+ comparefn = function (x, y) {
+ if (x === y) return 0;
+ if (%_IsSmi(x) && %_IsSmi(y)) {
+ return %SmiLexicographicCompare(x, y);
+ }
+ x = TO_STRING(x);
+ y = TO_STRING(y);
+ if (x == y) return 0;
+ else return x < y ? -1 : 1;
+ };
+ }
+ var InsertionSort = function InsertionSort(a, from, to) {
+ for (var i = from + 1; i < to; i++) {
+ var element = a[i];
+ for (var j = i - 1; j >= from; j--) {
+ var tmp = a[j];
+ var order = comparefn(tmp, element);
+ if (order > 0) {
+ a[j + 1] = tmp;
+ } else {
+ break;
+ }
+ }
+ a[j + 1] = element;
+ }
+ };
+
+ var GetThirdIndex = function(a, from, to) {
+ var t_array = new InternalArray();
+ // Use both 'from' and 'to' to determine the pivot candidates.
+ var increment = 200 + ((to - from) & 15);
+ var j = 0;
+ from += 1;
+ to -= 1;
+ for (var i = from; i < to; i += increment) {
+ t_array[j] = [i, a[i]];
+ j++;
+ }
+ t_array.sort(function(a, b) {
+ return comparefn(a[1], b[1]);
+ });
+ var third_index = t_array[t_array.length >> 1][0];
+ return third_index;
+ }
+
+ var QuickSort = function QuickSort(a, from, to) {
+ var third_index = 0;
+ while (true) {
+ // Insertion sort is faster for short arrays.
+ if (to - from <= 10) {
+ InsertionSort(a, from, to);
+ return;
+ }
+ if (to - from > 1000) {
+ third_index = GetThirdIndex(a, from, to);
+ } else {
+ third_index = from + ((to - from) >> 1);
+ }
+ // Find a pivot as the median of first, last and middle element.
+ var v0 = a[from];
+ var v1 = a[to - 1];
+ var v2 = a[third_index];
+ var c01 = comparefn(v0, v1);
+ if (c01 > 0) {
+ // v1 < v0, so swap them.
+ var tmp = v0;
+ v0 = v1;
+ v1 = tmp;
+ } // v0 <= v1.
+ var c02 = comparefn(v0, v2);
+ if (c02 >= 0) {
+ // v2 <= v0 <= v1.
+ var tmp = v0;
+ v0 = v2;
+ v2 = v1;
+ v1 = tmp;
+ } else {
+ // v0 <= v1 && v0 < v2
+ var c12 = comparefn(v1, v2);
+ if (c12 > 0) {
+ // v0 <= v2 < v1
+ var tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ }
+ // v0 <= v1 <= v2
+ a[from] = v0;
+ a[to - 1] = v2;
+ var pivot = v1;
+ var low_end = from + 1; // Upper bound of elements lower than pivot.
+ var high_start = to - 1; // Lower bound of elements greater than pivot.
+ a[third_index] = a[low_end];
+ a[low_end] = pivot;
+
+ // From low_end to i are elements equal to pivot.
+ // From i to high_start are elements that haven't been compared yet.
+ partition: for (var i = low_end + 1; i < high_start; i++) {
+ var element = a[i];
+ var order = comparefn(element, pivot);
+ if (order < 0) {
+ a[i] = a[low_end];
+ a[low_end] = element;
+ low_end++;
+ } else if (order > 0) {
+ do {
+ high_start--;
+ if (high_start == i) break partition;
+ var top_elem = a[high_start];
+ order = comparefn(top_elem, pivot);
+ } while (order > 0);
+ a[i] = a[high_start];
+ a[high_start] = element;
+ if (order < 0) {
+ element = a[i];
+ a[i] = a[low_end];
+ a[low_end] = element;
+ low_end++;
+ }
+ }
+ }
+ if (to - high_start < low_end - from) {
+ QuickSort(a, high_start, to);
+ to = low_end;
+ } else {
+ QuickSort(a, from, low_end);
+ from = high_start;
+ }
+ }
+ };
+
+ // Copy elements in the range 0..length from obj's prototype chain
+ // to obj itself, if obj has holes. Return one more than the maximal index
+ // of a prototype property.
+ var CopyFromPrototype = function CopyFromPrototype(obj, length) {
+ var max = 0;
+ for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
+ var indices = %GetArrayKeys(proto, length);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ var proto_length = indices;
+ for (var i = 0; i < proto_length; i++) {
+ if (!HAS_OWN_PROPERTY(obj, i) && HAS_OWN_PROPERTY(proto, i)) {
+ obj[i] = proto[i];
+ if (i >= max) { max = i + 1; }
+ }
+ }
+ } else {
+ for (var i = 0; i < indices.length; i++) {
+ var index = indices[i];
+ if (!IS_UNDEFINED(index) && !HAS_OWN_PROPERTY(obj, index)
+ && HAS_OWN_PROPERTY(proto, index)) {
+ obj[index] = proto[index];
+ if (index >= max) { max = index + 1; }
+ }
+ }
+ }
+ }
+ return max;
+ };
+
+ // Set a value of "undefined" on all indices in the range from..to
+ // where a prototype of obj has an element. I.e., shadow all prototype
+ // elements in that range.
+ var ShadowPrototypeElements = function(obj, from, to) {
+ for (var proto = %_GetPrototype(obj); proto; proto = %_GetPrototype(proto)) {
+ var indices = %GetArrayKeys(proto, to);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ var proto_length = indices;
+ for (var i = from; i < proto_length; i++) {
+ if (HAS_OWN_PROPERTY(proto, i)) {
+ obj[i] = UNDEFINED;
+ }
+ }
+ } else {
+ for (var i = 0; i < indices.length; i++) {
+ var index = indices[i];
+ if (!IS_UNDEFINED(index) && from <= index &&
+ HAS_OWN_PROPERTY(proto, index)) {
+ obj[index] = UNDEFINED;
+ }
+ }
+ }
+ }
+ };
+
+ var SafeRemoveArrayHoles = function SafeRemoveArrayHoles(obj) {
+ // Copy defined elements from the end to fill in all holes and undefineds
+ // in the beginning of the array. Write undefineds and holes at the end
+ // after loop is finished.
+ var first_undefined = 0;
+ var last_defined = length - 1;
+ var num_holes = 0;
+ while (first_undefined < last_defined) {
+ // Find first undefined element.
+ while (first_undefined < last_defined &&
+ !IS_UNDEFINED(obj[first_undefined])) {
+ first_undefined++;
+ }
+ // Maintain the invariant num_holes = the number of holes in the original
+ // array with indices <= first_undefined or > last_defined.
+ if (!HAS_OWN_PROPERTY(obj, first_undefined)) {
+ num_holes++;
+ }
+
+ // Find last defined element.
+ while (first_undefined < last_defined &&
+ IS_UNDEFINED(obj[last_defined])) {
+ if (!HAS_OWN_PROPERTY(obj, last_defined)) {
+ num_holes++;
+ }
+ last_defined--;
+ }
+ if (first_undefined < last_defined) {
+ // Fill in hole or undefined.
+ obj[first_undefined] = obj[last_defined];
+ obj[last_defined] = UNDEFINED;
+ }
+ }
+ // If there were any undefineds in the entire array, first_undefined
+ // points to one past the last defined element. Make this true if
+ // there were no undefineds, as well, so that first_undefined == number
+ // of defined elements.
+ if (!IS_UNDEFINED(obj[first_undefined])) first_undefined++;
+ // Fill in the undefineds and the holes. There may be a hole where
+ // an undefined should be and vice versa.
+ var i;
+ for (i = first_undefined; i < length - num_holes; i++) {
+ obj[i] = UNDEFINED;
+ }
+ for (i = length - num_holes; i < length; i++) {
+ // For compatability with Webkit, do not expose elements in the prototype.
+ if (i in %_GetPrototype(obj)) {
+ obj[i] = UNDEFINED;
+ } else {
+ delete obj[i];
+ }
+ }
+
+ // Return the number of defined elements.
+ return first_undefined;
+ };
+
+ if (length < 2) return array;
+
+ var is_array = IS_ARRAY(array);
+ var max_prototype_element;
+ if (!is_array) {
+ // For compatibility with JSC, we also sort elements inherited from
+ // the prototype chain on non-Array objects.
+ // We do this by copying them to this object and sorting only
+ // own elements. This is not very efficient, but sorting with
+ // inherited elements happens very, very rarely, if at all.
+ // The specification allows "implementation dependent" behavior
+ // if an element on the prototype chain has an element that
+ // might interact with sorting.
+ max_prototype_element = CopyFromPrototype(array, length);
+ }
+
+ // %RemoveArrayHoles returns -1 if fast removal is not supported.
+ var num_non_undefined = %RemoveArrayHoles(array, length);
+
+ if (num_non_undefined == -1) {
+ // The array is observed, or there were indexed accessors in the array.
+ // Move array holes and undefineds to the end using a Javascript function
+ // that is safe in the presence of accessors and is observable.
+ num_non_undefined = SafeRemoveArrayHoles(array);
+ }
+
+ QuickSort(array, 0, num_non_undefined);
+
+ if (!is_array && (num_non_undefined + 1 < max_prototype_element)) {
+ // For compatibility with JSC, we shadow any elements in the prototype
+ // chain that has become exposed by sort moving a hole to its position.
+ ShadowPrototypeElements(array, num_non_undefined, max_prototype_element);
+ }
+
+ return array;
+}
+
+
+function ArraySort(comparefn) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.sort");
+
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ return InnerArraySort(array, length, comparefn);
+}
+
+
+// The following functions cannot be made efficient on sparse arrays while
+// preserving the semantics, since the calls to the receiver function can add
+// or delete elements from the array.
+function InnerArrayFilter(f, receiver, array, length, result) {
+ var result_length = 0;
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ if (%_Call(f, receiver, element, i, array)) {
+ DefineIndexedProperty(result, result_length, element);
+ result_length++;
+ }
+ }
+ }
+ return result;
+}
+
+
+
+function ArrayFilter(f, receiver) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+ var result = ArraySpeciesCreate(array, 0);
+ return InnerArrayFilter(f, receiver, array, length, result);
+}
+
+
+function InnerArrayForEach(f, receiver, array, length) {
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ %_Call(f, receiver, element, i, array);
+ }
+ }
+}
+
+
+function ArrayForEach(f, receiver) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.forEach");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ InnerArrayForEach(f, receiver, array, length);
+}
+
+
+function InnerArraySome(f, receiver, array, length) {
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ if (%_Call(f, receiver, element, i, array)) return true;
+ }
+ }
+ return false;
+}
+
+
+// Executes the function once for each element present in the
+// array until it finds one where callback returns true.
+function ArraySome(f, receiver) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.some");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ return InnerArraySome(f, receiver, array, length);
+}
+
+
+function InnerArrayEvery(f, receiver, array, length) {
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ if (!%_Call(f, receiver, element, i, array)) return false;
+ }
+ }
+ return true;
+}
+
+function ArrayEvery(f, receiver) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.every");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ return InnerArrayEvery(f, receiver, array, length);
+}
+
+
+function ArrayMap(f, receiver) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+ var result = ArraySpeciesCreate(array, length);
+ var is_array = IS_ARRAY(array);
+ for (var i = 0; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ DefineIndexedProperty(result, i, %_Call(f, receiver, element, i, array));
+ }
+ }
+ return result;
+}
+
+
+// For .indexOf, we don't need to pass in the number of arguments
+// at the callsite since ToInteger(undefined) == 0; however, for
+// .lastIndexOf, we need to pass it, since the behavior for passing
+// undefined is 0 but for not including the argument is length-1.
+function InnerArrayIndexOf(array, element, index, length) {
+ if (length == 0) return -1;
+ if (IS_UNDEFINED(index)) {
+ index = 0;
+ } else {
+ index = TO_INTEGER(index);
+ // If index is negative, index from the end of the array.
+ if (index < 0) {
+ index = length + index;
+ // If index is still negative, search the entire array.
+ if (index < 0) index = 0;
+ }
+ }
+ var min = index;
+ var max = length;
+ if (UseSparseVariant(array, length, IS_ARRAY(array), max - min)) {
+ %NormalizeElements(array);
+ var indices = %GetArrayKeys(array, length);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ max = indices; // Capped by length already.
+ // Fall through to loop below.
+ } else {
+ if (indices.length == 0) return -1;
+ // Get all the keys in sorted order.
+ var sortedKeys = GetSortedArrayKeys(array, indices);
+ var n = sortedKeys.length;
+ var i = 0;
+ while (i < n && sortedKeys[i] < index) i++;
+ while (i < n) {
+ var key = sortedKeys[i];
+ if (!IS_UNDEFINED(key) && array[key] === element) return key;
+ i++;
+ }
+ return -1;
+ }
+ }
+ // Lookup through the array.
+ if (!IS_UNDEFINED(element)) {
+ for (var i = min; i < max; i++) {
+ if (array[i] === element) return i;
+ }
+ return -1;
+ }
+ // Lookup through the array.
+ for (var i = min; i < max; i++) {
+ if (IS_UNDEFINED(array[i]) && i in array) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+function ArrayIndexOf(element, index) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.indexOf");
+
+ var length = TO_LENGTH_OR_UINT32(this.length);
+ return InnerArrayIndexOf(this, element, index, length);
+}
+
+
+function InnerArrayLastIndexOf(array, element, index, length, argumentsLength) {
+ if (length == 0) return -1;
+ if (argumentsLength < 2) {
+ index = length - 1;
+ } else {
+ index = TO_INTEGER(index);
+ // If index is negative, index from end of the array.
+ if (index < 0) index += length;
+ // If index is still negative, do not search the array.
+ if (index < 0) return -1;
+ else if (index >= length) index = length - 1;
+ }
+ var min = 0;
+ var max = index;
+ if (UseSparseVariant(array, length, IS_ARRAY(array), index)) {
+ %NormalizeElements(array);
+ var indices = %GetArrayKeys(array, index + 1);
+ if (IS_NUMBER(indices)) {
+ // It's an interval.
+ max = indices; // Capped by index already.
+ // Fall through to loop below.
+ } else {
+ if (indices.length == 0) return -1;
+ // Get all the keys in sorted order.
+ var sortedKeys = GetSortedArrayKeys(array, indices);
+ var i = sortedKeys.length - 1;
+ while (i >= 0) {
+ var key = sortedKeys[i];
+ if (!IS_UNDEFINED(key) && array[key] === element) return key;
+ i--;
+ }
+ return -1;
+ }
+ }
+ // Lookup through the array.
+ if (!IS_UNDEFINED(element)) {
+ for (var i = max; i >= min; i--) {
+ if (array[i] === element) return i;
+ }
+ return -1;
+ }
+ for (var i = max; i >= min; i--) {
+ if (IS_UNDEFINED(array[i]) && i in array) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+function ArrayLastIndexOf(element, index) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.lastIndexOf");
+
+ var length = TO_LENGTH_OR_UINT32(this.length);
+ return InnerArrayLastIndexOf(this, element, index, length,
+ %_ArgumentsLength());
+}
+
+
+function InnerArrayReduce(callback, current, array, length, argumentsLength) {
+ if (!IS_CALLABLE(callback)) {
+ throw MakeTypeError(kCalledNonCallable, callback);
+ }
+
+ var is_array = IS_ARRAY(array);
+ var i = 0;
+ find_initial: if (argumentsLength < 2) {
+ for (; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ current = array[i++];
+ break find_initial;
+ }
+ }
+ throw MakeTypeError(kReduceNoInitial);
+ }
+
+ for (; i < length; i++) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ current = callback(current, element, i, array);
+ }
+ }
+ return current;
+}
+
+
+function ArrayReduce(callback, current) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduce");
+
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping and side effects are visible.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ return InnerArrayReduce(callback, current, array, length,
+ %_ArgumentsLength());
+}
+
+
+function InnerArrayReduceRight(callback, current, array, length,
+ argumentsLength) {
+ if (!IS_CALLABLE(callback)) {
+ throw MakeTypeError(kCalledNonCallable, callback);
+ }
+
+ var is_array = IS_ARRAY(array);
+ var i = length - 1;
+ find_initial: if (argumentsLength < 2) {
+ for (; i >= 0; i--) {
+ if (HAS_INDEX(array, i, is_array)) {
+ current = array[i--];
+ break find_initial;
+ }
+ }
+ throw MakeTypeError(kReduceNoInitial);
+ }
+
+ for (; i >= 0; i--) {
+ if (HAS_INDEX(array, i, is_array)) {
+ var element = array[i];
+ current = callback(current, element, i, array);
+ }
+ }
+ return current;
+}
+
+
+function ArrayReduceRight(callback, current) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.reduceRight");
+
+ // Pull out the length so that side effects are visible before the
+ // callback function is checked.
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+ return InnerArrayReduceRight(callback, current, array, length,
+ %_ArgumentsLength());
+}
+
+
+function InnerArrayCopyWithin(target, start, end, array, length) {
+ target = TO_INTEGER(target);
+ var to;
+ if (target < 0) {
+ to = MaxSimple(length + target, 0);
+ } else {
+ to = MinSimple(target, length);
+ }
+
+ start = TO_INTEGER(start);
+ var from;
+ if (start < 0) {
+ from = MaxSimple(length + start, 0);
+ } else {
+ from = MinSimple(start, length);
+ }
+
+ end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
+ var final;
+ if (end < 0) {
+ final = MaxSimple(length + end, 0);
+ } else {
+ final = MinSimple(end, length);
+ }
+
+ var count = MinSimple(final - from, length - to);
+ var direction = 1;
+ if (from < to && to < (from + count)) {
+ direction = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ }
+
+ while (count > 0) {
+ if (from in array) {
+ array[to] = array[from];
+ } else {
+ delete array[to];
+ }
+ from = from + direction;
+ to = to + direction;
+ count--;
+ }
+
+ return array;
+}
+
+
+// ES6 draft 03-17-15, section 22.1.3.3
+function ArrayCopyWithin(target, start, end) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.copyWithin");
+
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH(array.length);
+
+ return InnerArrayCopyWithin(target, start, end, array, length);
+}
+
+
+function InnerArrayFind(predicate, thisArg, array, length) {
+ if (!IS_CALLABLE(predicate)) {
+ throw MakeTypeError(kCalledNonCallable, predicate);
+ }
+
+ for (var i = 0; i < length; i++) {
+ var element = array[i];
+ if (%_Call(predicate, thisArg, element, i, array)) {
+ return element;
+ }
+ }
+
+ return;
+}
+
+
+// ES6 draft 07-15-13, section 15.4.3.23
+function ArrayFind(predicate, thisArg) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.find");
+
+ var array = TO_OBJECT(this);
+ var length = TO_INTEGER(array.length);
+
+ return InnerArrayFind(predicate, thisArg, array, length);
+}
+
+
+function InnerArrayFindIndex(predicate, thisArg, array, length) {
+ if (!IS_CALLABLE(predicate)) {
+ throw MakeTypeError(kCalledNonCallable, predicate);
+ }
+
+ for (var i = 0; i < length; i++) {
+ var element = array[i];
+ if (%_Call(predicate, thisArg, element, i, array)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+// ES6 draft 07-15-13, section 15.4.3.24
+function ArrayFindIndex(predicate, thisArg) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.findIndex");
+
+ var array = TO_OBJECT(this);
+ var length = TO_INTEGER(array.length);
+
+ return InnerArrayFindIndex(predicate, thisArg, array, length);
+}
+
+
+// ES6, draft 04-05-14, section 22.1.3.6
+function InnerArrayFill(value, start, end, array, length) {
+ var i = IS_UNDEFINED(start) ? 0 : TO_INTEGER(start);
+ var end = IS_UNDEFINED(end) ? length : TO_INTEGER(end);
+
+ if (i < 0) {
+ i += length;
+ if (i < 0) i = 0;
+ } else {
+ if (i > length) i = length;
+ }
+
+ if (end < 0) {
+ end += length;
+ if (end < 0) end = 0;
+ } else {
+ if (end > length) end = length;
+ }
+
+ if ((end - i) > 0 && %object_is_frozen(array)) {
+ throw MakeTypeError(kArrayFunctionsOnFrozen);
+ }
+
+ for (; i < end; i++)
+ array[i] = value;
+ return array;
+}
+
+
+// ES6, draft 04-05-14, section 22.1.3.6
+function ArrayFill(value, start, end) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.fill");
+
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH_OR_UINT32(array.length);
+
+ return InnerArrayFill(value, start, end, array, length);
+}
+
+
+function InnerArrayIncludes(searchElement, fromIndex, array, length) {
+ if (length === 0) {
+ return false;
+ }
+
+ var n = TO_INTEGER(fromIndex);
+
+ var k;
+ if (n >= 0) {
+ k = n;
+ } else {
+ k = length + n;
+ if (k < 0) {
+ k = 0;
+ }
+ }
+
+ while (k < length) {
+ var elementK = array[k];
+ if (SameValueZero(searchElement, elementK)) {
+ return true;
+ }
+
+ ++k;
+ }
+
+ return false;
+}
+
+
+// ES2016 draft, section 22.1.3.11
+function ArrayIncludes(searchElement, fromIndex) {
+ CHECK_OBJECT_COERCIBLE(this, "Array.prototype.includes");
+
+ var array = TO_OBJECT(this);
+ var length = TO_LENGTH(array.length);
+
+ return InnerArrayIncludes(searchElement, fromIndex, array, length);
+}
+
+
+function AddArrayElement(constructor, array, i, value) {
+ if (constructor === GlobalArray) {
+ AddIndexedProperty(array, i, value);
+ } else {
+ ObjectDefineProperty(array, i, {
+ value: value, writable: true, configurable: true, enumerable: true
+ });
+ }
+}
+
+
+// ES6, draft 10-14-14, section 22.1.2.1
+function ArrayFrom(arrayLike, mapfn, receiver) {
+ var items = TO_OBJECT(arrayLike);
+ var mapping = !IS_UNDEFINED(mapfn);
+
+ if (mapping) {
+ if (!IS_CALLABLE(mapfn)) {
+ throw MakeTypeError(kCalledNonCallable, mapfn);
+ }
+ }
+
+ var iterable = GetMethod(items, iteratorSymbol);
+ var k;
+ var result;
+ var mappedValue;
+ var nextValue;
+
+ if (!IS_UNDEFINED(iterable)) {
+ result = %IsConstructor(this) ? new this() : [];
+
+ var iterator = GetIterator(items, iterable);
+
+ k = 0;
+ while (true) {
+ var next = iterator.next();
+
+ if (!IS_RECEIVER(next)) {
+ throw MakeTypeError(kIteratorResultNotAnObject, next);
+ }
+
+ if (next.done) {
+ result.length = k;
+ return result;
+ }
+
+ nextValue = next.value;
+ if (mapping) {
+ mappedValue = %_Call(mapfn, receiver, nextValue, k);
+ } else {
+ mappedValue = nextValue;
+ }
+ AddArrayElement(this, result, k, mappedValue);
+ k++;
+ }
+ } else {
+ var len = TO_LENGTH(items.length);
+ result = %IsConstructor(this) ? new this(len) : new GlobalArray(len);
+
+ for (k = 0; k < len; ++k) {
+ nextValue = items[k];
+ if (mapping) {
+ mappedValue = %_Call(mapfn, receiver, nextValue, k);
+ } else {
+ mappedValue = nextValue;
+ }
+ AddArrayElement(this, result, k, mappedValue);
+ }
+
+ result.length = k;
+ return result;
+ }
+}
+
+
+// ES6, draft 05-22-14, section 22.1.2.3
+function ArrayOf() {
+ var length = %_ArgumentsLength();
+ var constructor = this;
+ // TODO: Implement IsConstructor (ES6 section 7.2.5)
+ var array = %IsConstructor(constructor) ? new constructor(length) : [];
+ for (var i = 0; i < length; i++) {
+ AddArrayElement(constructor, array, i, %_Arguments(i));
+ }
+ array.length = length;
+ return array;
+}
+
+// -------------------------------------------------------------------
+
+// Set up non-enumerable constructor property on the Array.prototype
+// object.
+%AddNamedProperty(GlobalArray.prototype, "constructor", GlobalArray,
+ DONT_ENUM);
+
+// Set up unscopable properties on the Array.prototype object.
+var unscopables = {
+ __proto__: null,
+ copyWithin: true,
+ entries: true,
+ fill: true,
+ find: true,
+ findIndex: true,
+ keys: true,
+};
+
+%AddNamedProperty(GlobalArray.prototype, unscopablesSymbol, unscopables,
+ DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(ArrayFrom, 1);
+
+// Set up non-enumerable functions on the Array object.
+utils.InstallFunctions(GlobalArray, DONT_ENUM, [
+ "from", ArrayFrom,
+ "of", ArrayOf
+]);
+
+var specialFunctions = %SpecialArrayFunctions();
+
+var getFunction = function(name, jsBuiltin, len) {
+ var f = jsBuiltin;
+ if (specialFunctions.hasOwnProperty(name)) {
+ f = specialFunctions[name];
+ }
+ if (!IS_UNDEFINED(len)) {
+ %FunctionSetLength(f, len);
+ }
+ return f;
+};
+
+// Set up non-enumerable functions of the Array.prototype object and
+// set their names.
+// Manipulate the length of some of the functions to meet
+// expectations set by ECMA-262 or Mozilla.
+utils.InstallFunctions(GlobalArray.prototype, DONT_ENUM, [
+ "toString", getFunction("toString", ArrayToString),
+ "toLocaleString", getFunction("toLocaleString", ArrayToLocaleString),
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush, 1),
+ "reverse", getFunction("reverse", ArrayReverse),
+ "shift", getFunction("shift", ArrayShift),
+ "unshift", getFunction("unshift", ArrayUnshift, 1),
+ "slice", getFunction("slice", ArraySlice, 2),
+ "splice", getFunction("splice", ArraySplice, 2),
+ "sort", getFunction("sort", ArraySort),
+ "filter", getFunction("filter", ArrayFilter, 1),
+ "forEach", getFunction("forEach", ArrayForEach, 1),
+ "some", getFunction("some", ArraySome, 1),
+ "every", getFunction("every", ArrayEvery, 1),
+ "map", getFunction("map", ArrayMap, 1),
+ "indexOf", getFunction("indexOf", ArrayIndexOf, 1),
+ "lastIndexOf", getFunction("lastIndexOf", ArrayLastIndexOf, 1),
+ "reduce", getFunction("reduce", ArrayReduce, 1),
+ "reduceRight", getFunction("reduceRight", ArrayReduceRight, 1),
+ "copyWithin", getFunction("copyWithin", ArrayCopyWithin, 2),
+ "find", getFunction("find", ArrayFind, 1),
+ "findIndex", getFunction("findIndex", ArrayFindIndex, 1),
+ "fill", getFunction("fill", ArrayFill, 1),
+ "includes", getFunction("includes", ArrayIncludes, 1),
+]);
+
+%FinishArrayPrototypeSetup(GlobalArray.prototype);
+
+// The internal Array prototype doesn't need to be fancy, since it's never
+// exposed to user code.
+// Adding only the functions that are actually used.
+utils.SetUpLockedPrototype(InternalArray, GlobalArray(), [
+ "indexOf", getFunction("indexOf", ArrayIndexOf),
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush),
+ "shift", getFunction("shift", ArrayShift),
+ "sort", getFunction("sort", ArraySort),
+ "splice", getFunction("splice", ArraySplice)
+]);
+
+utils.SetUpLockedPrototype(InternalPackedArray, GlobalArray(), [
+ "join", getFunction("join", ArrayJoin),
+ "pop", getFunction("pop", ArrayPop),
+ "push", getFunction("push", ArrayPush),
+ "shift", getFunction("shift", ArrayShift)
+]);
+
+// V8 extras get a separate copy of InternalPackedArray. We give them the basic
+// manipulation methods.
+utils.SetUpLockedPrototype(extrasUtils.InternalPackedArray, GlobalArray(), [
+ "push", getFunction("push", ArrayPush),
+ "pop", getFunction("pop", ArrayPop),
+ "shift", getFunction("shift", ArrayShift),
+ "unshift", getFunction("unshift", ArrayUnshift),
+ "splice", getFunction("splice", ArraySplice),
+ "slice", getFunction("slice", ArraySlice)
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.ArrayFrom = ArrayFrom;
+ to.ArrayIndexOf = ArrayIndexOf;
+ to.ArrayJoin = ArrayJoin;
+ to.ArrayPush = ArrayPush;
+ to.ArrayToString = ArrayToString;
+ to.InnerArrayCopyWithin = InnerArrayCopyWithin;
+ to.InnerArrayEvery = InnerArrayEvery;
+ to.InnerArrayFill = InnerArrayFill;
+ to.InnerArrayFilter = InnerArrayFilter;
+ to.InnerArrayFind = InnerArrayFind;
+ to.InnerArrayFindIndex = InnerArrayFindIndex;
+ to.InnerArrayForEach = InnerArrayForEach;
+ to.InnerArrayIncludes = InnerArrayIncludes;
+ to.InnerArrayIndexOf = InnerArrayIndexOf;
+ to.InnerArrayJoin = InnerArrayJoin;
+ to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
+ to.InnerArrayReduce = InnerArrayReduce;
+ to.InnerArrayReduceRight = InnerArrayReduceRight;
+ to.InnerArraySome = InnerArraySome;
+ to.InnerArraySort = InnerArraySort;
+ to.InnerArrayToLocaleString = InnerArrayToLocaleString;
+ to.PackedArrayReverse = PackedArrayReverse;
+});
+
+%InstallToContext([
+ "array_pop", ArrayPop,
+ "array_push", ArrayPush,
+ "array_shift", ArrayShift,
+ "array_splice", ArraySplice,
+ "array_slice", ArraySlice,
+ "array_unshift", ArrayUnshift,
+]);
+
+});
diff --git a/src/js/arraybuffer.js b/src/js/arraybuffer.js
new file mode 100644
index 0000000..f0273c7
--- /dev/null
+++ b/src/js/arraybuffer.js
@@ -0,0 +1,92 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalArrayBuffer = global.ArrayBuffer;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var SpeciesConstructor;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+ MaxSimple = from.MaxSimple;
+ MinSimple = from.MinSimple;
+ SpeciesConstructor = from.SpeciesConstructor;
+});
+
+// -------------------------------------------------------------------
+
+function ArrayBufferGetByteLen() {
+ if (!IS_ARRAYBUFFER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'ArrayBuffer.prototype.byteLength', this);
+ }
+ return %_ArrayBufferGetByteLength(this);
+}
+
+// ES6 Draft 15.13.5.5.3
+function ArrayBufferSlice(start, end) {
+ if (!IS_ARRAYBUFFER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'ArrayBuffer.prototype.slice', this);
+ }
+
+ var relativeStart = TO_INTEGER(start);
+ if (!IS_UNDEFINED(end)) {
+ end = TO_INTEGER(end);
+ }
+ var first;
+ var byte_length = %_ArrayBufferGetByteLength(this);
+ if (relativeStart < 0) {
+ first = MaxSimple(byte_length + relativeStart, 0);
+ } else {
+ first = MinSimple(relativeStart, byte_length);
+ }
+ var relativeEnd = IS_UNDEFINED(end) ? byte_length : end;
+ var fin;
+ if (relativeEnd < 0) {
+ fin = MaxSimple(byte_length + relativeEnd, 0);
+ } else {
+ fin = MinSimple(relativeEnd, byte_length);
+ }
+
+ if (fin < first) {
+ fin = first;
+ }
+ var newLen = fin - first;
+ var constructor = SpeciesConstructor(this, GlobalArrayBuffer, true);
+ var result = new constructor(newLen);
+ if (!IS_ARRAYBUFFER(result)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'ArrayBuffer.prototype.slice', result);
+ }
+ // TODO(littledan): Check for a detached ArrayBuffer
+ if (result === this) {
+ throw MakeTypeError(kArrayBufferSpeciesThis);
+ }
+ if (%_ArrayBufferGetByteLength(result) < newLen) {
+ throw MakeTypeError(kArrayBufferTooShort);
+ }
+
+ %ArrayBufferSliceImpl(this, result, first, newLen);
+ return result;
+}
+
+utils.InstallGetter(GlobalArrayBuffer.prototype, "byteLength",
+ ArrayBufferGetByteLen);
+
+utils.InstallFunctions(GlobalArrayBuffer.prototype, DONT_ENUM, [
+ "slice", ArrayBufferSlice
+]);
+
+})
diff --git a/src/js/collection-iterator.js b/src/js/collection-iterator.js
new file mode 100644
index 0000000..621d726
--- /dev/null
+++ b/src/js/collection-iterator.js
@@ -0,0 +1,183 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var GlobalSet = global.Set;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var MapIterator = utils.ImportNow("MapIterator");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var SetIterator = utils.ImportNow("SetIterator");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function SetIteratorConstructor(set, kind) {
+ %SetIteratorInitialize(this, set, kind);
+}
+
+
+function SetIteratorNextJS() {
+ if (!IS_SET_ITERATOR(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set Iterator.prototype.next', this);
+ }
+
+ var value_array = [UNDEFINED, UNDEFINED];
+ var result = %_CreateIterResultObject(value_array, false);
+ switch (%SetIteratorNext(this, value_array)) {
+ case 0:
+ result.value = UNDEFINED;
+ result.done = true;
+ break;
+ case ITERATOR_KIND_VALUES:
+ result.value = value_array[0];
+ break;
+ case ITERATOR_KIND_ENTRIES:
+ value_array[1] = value_array[0];
+ break;
+ }
+
+ return result;
+}
+
+
+function SetEntries() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.entries', this);
+ }
+ return new SetIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function SetValues() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.values', this);
+ }
+ return new SetIterator(this, ITERATOR_KIND_VALUES);
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(SetIterator, SetIteratorConstructor);
+%FunctionSetInstanceClassName(SetIterator, 'Set Iterator');
+utils.InstallFunctions(SetIterator.prototype, DONT_ENUM, [
+ 'next', SetIteratorNextJS
+]);
+
+%AddNamedProperty(SetIterator.prototype, toStringTagSymbol,
+ "Set Iterator", READ_ONLY | DONT_ENUM);
+
+utils.InstallFunctions(GlobalSet.prototype, DONT_ENUM, [
+ 'entries', SetEntries,
+ 'keys', SetValues,
+ 'values', SetValues
+]);
+
+%AddNamedProperty(GlobalSet.prototype, iteratorSymbol, SetValues, DONT_ENUM);
+
+// -------------------------------------------------------------------
+
+function MapIteratorConstructor(map, kind) {
+ %MapIteratorInitialize(this, map, kind);
+}
+
+
+function MapIteratorNextJS() {
+ if (!IS_MAP_ITERATOR(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map Iterator.prototype.next', this);
+ }
+
+ var value_array = [UNDEFINED, UNDEFINED];
+ var result = %_CreateIterResultObject(value_array, false);
+ switch (%MapIteratorNext(this, value_array)) {
+ case 0:
+ result.value = UNDEFINED;
+ result.done = true;
+ break;
+ case ITERATOR_KIND_KEYS:
+ result.value = value_array[0];
+ break;
+ case ITERATOR_KIND_VALUES:
+ result.value = value_array[1];
+ break;
+ // ITERATOR_KIND_ENTRIES does not need any processing.
+ }
+
+ return result;
+}
+
+
+function MapEntries() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.entries', this);
+ }
+ return new MapIterator(this, ITERATOR_KIND_ENTRIES);
+}
+
+
+function MapKeys() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.keys', this);
+ }
+ return new MapIterator(this, ITERATOR_KIND_KEYS);
+}
+
+
+function MapValues() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.values', this);
+ }
+ return new MapIterator(this, ITERATOR_KIND_VALUES);
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(MapIterator, MapIteratorConstructor);
+%FunctionSetInstanceClassName(MapIterator, 'Map Iterator');
+utils.InstallFunctions(MapIterator.prototype, DONT_ENUM, [
+ 'next', MapIteratorNextJS
+]);
+
+%AddNamedProperty(MapIterator.prototype, toStringTagSymbol,
+ "Map Iterator", READ_ONLY | DONT_ENUM);
+
+
+utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
+ 'entries', MapEntries,
+ 'keys', MapKeys,
+ 'values', MapValues
+]);
+
+%AddNamedProperty(GlobalMap.prototype, iteratorSymbol, MapEntries, DONT_ENUM);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.MapEntries = MapEntries;
+ to.MapIteratorNext = MapIteratorNextJS;
+ to.SetIteratorNext = SetIteratorNextJS;
+ to.SetValues = SetValues;
+});
+
+})
diff --git a/src/js/collection.js b/src/js/collection.js
new file mode 100644
index 0000000..0d7195d
--- /dev/null
+++ b/src/js/collection.js
@@ -0,0 +1,478 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var GlobalObject = global.Object;
+var GlobalSet = global.Set;
+var hashCodeSymbol = utils.ImportNow("hash_code_symbol");
+var IntRandom;
+var MakeTypeError;
+var MapIterator;
+var NumberIsNaN;
+var SetIterator;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ IntRandom = from.IntRandom;
+ MakeTypeError = from.MakeTypeError;
+ MapIterator = from.MapIterator;
+ NumberIsNaN = from.NumberIsNaN;
+ SetIterator = from.SetIterator;
+});
+
+// -------------------------------------------------------------------
+
+function HashToEntry(table, hash, numBuckets) {
+ var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+ return ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+}
+%SetForceInlineFlag(HashToEntry);
+
+
+function SetFindEntry(table, numBuckets, key, hash) {
+ var entry = HashToEntry(table, hash, numBuckets);
+ if (entry === NOT_FOUND) return entry;
+ var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) return entry;
+ var keyIsNaN = NumberIsNaN(key);
+ while (true) {
+ if (keyIsNaN && NumberIsNaN(candidate)) {
+ return entry;
+ }
+ entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets);
+ if (entry === NOT_FOUND) return entry;
+ candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) return entry;
+ }
+ return NOT_FOUND;
+}
+%SetForceInlineFlag(SetFindEntry);
+
+
+function MapFindEntry(table, numBuckets, key, hash) {
+ var entry = HashToEntry(table, hash, numBuckets);
+ if (entry === NOT_FOUND) return entry;
+ var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) return entry;
+ var keyIsNaN = NumberIsNaN(key);
+ while (true) {
+ if (keyIsNaN && NumberIsNaN(candidate)) {
+ return entry;
+ }
+ entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets);
+ if (entry === NOT_FOUND) return entry;
+ candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) return entry;
+ }
+ return NOT_FOUND;
+}
+%SetForceInlineFlag(MapFindEntry);
+
+
+function ComputeIntegerHash(key, seed) {
+ var hash = key;
+ hash = hash ^ seed;
+ hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
+ hash = hash ^ (hash >>> 12);
+ hash = hash + (hash << 2);
+ hash = hash ^ (hash >>> 4);
+ hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11);
+ hash = hash ^ (hash >>> 16);
+ return hash & 0x3fffffff;
+}
+%SetForceInlineFlag(ComputeIntegerHash);
+
+function GetExistingHash(key) {
+ if (%_IsSmi(key)) {
+ return ComputeIntegerHash(key, 0);
+ }
+ if (IS_STRING(key)) {
+ var field = %_StringGetRawHashField(key);
+ if ((field & 1 /* Name::kHashNotComputedMask */) === 0) {
+ return field >>> 2 /* Name::kHashShift */;
+ }
+ } else if (IS_RECEIVER(key) && !IS_PROXY(key) && !IS_GLOBAL(key)) {
+ var hash = GET_PRIVATE(key, hashCodeSymbol);
+ return hash;
+ }
+ return %GenericHash(key);
+}
+%SetForceInlineFlag(GetExistingHash);
+
+
+function GetHash(key) {
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) {
+ hash = IntRandom() | 0;
+ if (hash === 0) hash = 1;
+ SET_PRIVATE(key, hashCodeSymbol, hash);
+ }
+ return hash;
+}
+%SetForceInlineFlag(GetHash);
+
+
+// -------------------------------------------------------------------
+// Harmony Set
+
+function SetConstructor(iterable) {
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNotFunction, "Set");
+ }
+
+ %_SetInitialize(this);
+
+ if (!IS_NULL_OR_UNDEFINED(iterable)) {
+ var adder = this.add;
+ if (!IS_CALLABLE(adder)) {
+ throw MakeTypeError(kPropertyNotFunction, adder, 'add', this);
+ }
+
+ for (var value of iterable) {
+ %_Call(adder, this, value);
+ }
+ }
+}
+
+
+function SetAdd(key) {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver, 'Set.prototype.add', this);
+ }
+ // Normalize -0 to +0 as required by the spec.
+ // Even though we use SameValueZero as the comparison for the keys we don't
+ // want to ever store -0 as the key since the key is directly exposed when
+ // doing iteration.
+ if (key === 0) {
+ key = 0;
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetHash(key);
+ if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
+
+ var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+ var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+ var capacity = numBuckets << 1;
+ if ((nof + nod) >= capacity) {
+ // Need to grow, bail out to runtime.
+ %SetGrow(this);
+ // Re-load state from the grown backing store.
+ table = %_JSCollectionGetTable(this);
+ numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+ nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+ }
+ var entry = nof + nod;
+ var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
+ var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+ var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+ ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
+ ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
+ FIXED_ARRAY_SET(table, index, key);
+ FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry);
+ return this;
+}
+
+
+function SetHas(key) {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver, 'Set.prototype.has', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return false;
+ return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
+}
+
+
+function SetDelete(key) {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.delete', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return false;
+ var entry = SetFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return false;
+
+ var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
+ var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
+ var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
+ FIXED_ARRAY_SET(table, index, %_TheHole());
+ ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
+ ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
+ if (nof < (numBuckets >>> 1)) %SetShrink(this);
+ return true;
+}
+
+
+function SetGetSize() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.size', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+}
+
+
+function SetClearJS() {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.clear', this);
+ }
+ %_SetClear(this);
+}
+
+
+function SetForEach(f, receiver) {
+ if (!IS_SET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Set.prototype.forEach', this);
+ }
+
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+ var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
+ var key;
+ var value_array = [UNDEFINED];
+ while (%SetIteratorNext(iterator, value_array)) {
+ key = value_array[0];
+ %_Call(f, receiver, key, key, this);
+ }
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalSet, SetConstructor);
+%FunctionSetLength(GlobalSet, 0);
+%FunctionSetPrototype(GlobalSet, new GlobalObject());
+%AddNamedProperty(GlobalSet.prototype, "constructor", GlobalSet, DONT_ENUM);
+%AddNamedProperty(GlobalSet.prototype, toStringTagSymbol, "Set",
+ DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(SetForEach, 1);
+
+// Set up the non-enumerable functions on the Set prototype object.
+utils.InstallGetter(GlobalSet.prototype, "size", SetGetSize);
+utils.InstallFunctions(GlobalSet.prototype, DONT_ENUM, [
+ "add", SetAdd,
+ "has", SetHas,
+ "delete", SetDelete,
+ "clear", SetClearJS,
+ "forEach", SetForEach
+]);
+
+
+// -------------------------------------------------------------------
+// Harmony Map
+
+function MapConstructor(iterable) {
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNotFunction, "Map");
+ }
+
+ %_MapInitialize(this);
+
+ if (!IS_NULL_OR_UNDEFINED(iterable)) {
+ var adder = this.set;
+ if (!IS_CALLABLE(adder)) {
+ throw MakeTypeError(kPropertyNotFunction, adder, 'set', this);
+ }
+
+ for (var nextItem of iterable) {
+ if (!IS_RECEIVER(nextItem)) {
+ throw MakeTypeError(kIteratorValueNotAnObject, nextItem);
+ }
+ %_Call(adder, this, nextItem[0], nextItem[1]);
+ }
+ }
+}
+
+
+function MapGet(key) {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.get', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return UNDEFINED;
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return UNDEFINED;
+ return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets);
+}
+
+
+function MapSet(key, value) {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.set', this);
+ }
+ // Normalize -0 to +0 as required by the spec.
+ // Even though we use SameValueZero as the comparison for the keys we don't
+ // want to ever store -0 as the key since the key is directly exposed when
+ // doing iteration.
+ if (key === 0) {
+ key = 0;
+ }
+
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetHash(key);
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry !== NOT_FOUND) {
+ var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ FIXED_ARRAY_SET(table, existingIndex + 1, value);
+ return this;
+ }
+
+ var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+ var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+ var capacity = numBuckets << 1;
+ if ((nof + nod) >= capacity) {
+ // Need to grow, bail out to runtime.
+ %MapGrow(this);
+ // Re-load state from the grown backing store.
+ table = %_JSCollectionGetTable(this);
+ numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+ nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
+ }
+ entry = nof + nod;
+ var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
+ var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
+ ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
+ ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
+ FIXED_ARRAY_SET(table, index, key);
+ FIXED_ARRAY_SET(table, index + 1, value);
+ FIXED_ARRAY_SET(table, index + 2, chainEntry);
+ return this;
+}
+
+
+function MapHas(key) {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.has', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetHash(key);
+ return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
+}
+
+
+function MapDelete(key) {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.delete', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
+ var hash = GetHash(key);
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return false;
+
+ var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
+ var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
+ var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ FIXED_ARRAY_SET(table, index, %_TheHole());
+ FIXED_ARRAY_SET(table, index + 1, %_TheHole());
+ ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
+ ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
+ if (nof < (numBuckets >>> 1)) %MapShrink(this);
+ return true;
+}
+
+
+function MapGetSize() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.size', this);
+ }
+ var table = %_JSCollectionGetTable(this);
+ return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
+}
+
+
+function MapClearJS() {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.clear', this);
+ }
+ %_MapClear(this);
+}
+
+
+function MapForEach(f, receiver) {
+ if (!IS_MAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'Map.prototype.forEach', this);
+ }
+
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+
+ var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
+ var value_array = [UNDEFINED, UNDEFINED];
+ while (%MapIteratorNext(iterator, value_array)) {
+ %_Call(f, receiver, value_array[1], value_array[0], this);
+ }
+}
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalMap, MapConstructor);
+%FunctionSetLength(GlobalMap, 0);
+%FunctionSetPrototype(GlobalMap, new GlobalObject());
+%AddNamedProperty(GlobalMap.prototype, "constructor", GlobalMap, DONT_ENUM);
+%AddNamedProperty(
+ GlobalMap.prototype, toStringTagSymbol, "Map", DONT_ENUM | READ_ONLY);
+
+%FunctionSetLength(MapForEach, 1);
+
+// Set up the non-enumerable functions on the Map prototype object.
+utils.InstallGetter(GlobalMap.prototype, "size", MapGetSize);
+utils.InstallFunctions(GlobalMap.prototype, DONT_ENUM, [
+ "get", MapGet,
+ "set", MapSet,
+ "has", MapHas,
+ "delete", MapDelete,
+ "clear", MapClearJS,
+ "forEach", MapForEach
+]);
+
+// -----------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+ "map_get", MapGet,
+ "map_set", MapSet,
+ "map_has", MapHas,
+ "map_delete", MapDelete,
+ "set_add", SetAdd,
+ "set_has", SetHas,
+ "set_delete", SetDelete,
+]);
+
+utils.Export(function(to) {
+ to.GetExistingHash = GetExistingHash;
+ to.GetHash = GetHash;
+});
+
+})
diff --git a/src/js/generator.js b/src/js/generator.js
new file mode 100644
index 0000000..7f43656
--- /dev/null
+++ b/src/js/generator.js
@@ -0,0 +1,104 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GeneratorFunctionPrototype = utils.ImportNow("GeneratorFunctionPrototype");
+var GeneratorFunction = utils.ImportNow("GeneratorFunction");
+var GlobalFunction = global.Function;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// ----------------------------------------------------------------------------
+
+// Generator functions and objects are specified by ES6, sections 15.19.3 and
+// 15.19.4.
+
+function GeneratorObjectNext(value) {
+ if (!IS_GENERATOR(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ '[Generator].prototype.next', this);
+ }
+
+ var continuation = %GeneratorGetContinuation(this);
+ if (continuation > 0) {
+ // Generator is suspended.
+ DEBUG_PREPARE_STEP_IN_IF_STEPPING(this);
+ try {
+ return %_GeneratorNext(this, value);
+ } catch (e) {
+ %GeneratorClose(this);
+ throw e;
+ }
+ } else if (continuation == 0) {
+ // Generator is already closed.
+ return { value: void 0, done: true };
+ } else {
+ // Generator is running.
+ throw MakeTypeError(kGeneratorRunning);
+ }
+}
+
+
+function GeneratorObjectThrow(exn) {
+ if (!IS_GENERATOR(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ '[Generator].prototype.throw', this);
+ }
+
+ var continuation = %GeneratorGetContinuation(this);
+ if (continuation > 0) {
+ // Generator is suspended.
+ try {
+ return %_GeneratorThrow(this, exn);
+ } catch (e) {
+ %GeneratorClose(this);
+ throw e;
+ }
+ } else if (continuation == 0) {
+ // Generator is already closed.
+ throw exn;
+ } else {
+ // Generator is running.
+ throw MakeTypeError(kGeneratorRunning);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+// Both Runtime_GeneratorNext and Runtime_GeneratorThrow are supported by
+// neither Crankshaft nor TurboFan, disable optimization of wrappers here.
+%NeverOptimizeFunction(GeneratorObjectNext);
+%NeverOptimizeFunction(GeneratorObjectThrow);
+
+// Set up non-enumerable functions on the generator prototype object.
+var GeneratorObjectPrototype = GeneratorFunctionPrototype.prototype;
+utils.InstallFunctions(GeneratorObjectPrototype,
+ DONT_ENUM,
+ ["next", GeneratorObjectNext,
+ "throw", GeneratorObjectThrow]);
+
+%AddNamedProperty(GeneratorObjectPrototype, "constructor",
+ GeneratorFunctionPrototype, DONT_ENUM | READ_ONLY);
+%AddNamedProperty(GeneratorObjectPrototype,
+ toStringTagSymbol, "Generator", DONT_ENUM | READ_ONLY);
+%InternalSetPrototype(GeneratorFunctionPrototype, GlobalFunction.prototype);
+%AddNamedProperty(GeneratorFunctionPrototype,
+ toStringTagSymbol, "GeneratorFunction", DONT_ENUM | READ_ONLY);
+%AddNamedProperty(GeneratorFunctionPrototype, "constructor",
+ GeneratorFunction, DONT_ENUM | READ_ONLY);
+%InternalSetPrototype(GeneratorFunction, GlobalFunction);
+
+})
diff --git a/src/js/harmony-atomics.js b/src/js/harmony-atomics.js
new file mode 100644
index 0000000..b861a2a
--- /dev/null
+++ b/src/js/harmony-atomics.js
@@ -0,0 +1,215 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var MakeTypeError;
+var MaxSimple;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+ MaxSimple = from.MaxSimple;
+});
+
+// -------------------------------------------------------------------
+
+
+function CheckSharedIntegerTypedArray(ia) {
+ if (!%IsSharedIntegerTypedArray(ia)) {
+ throw MakeTypeError(kNotIntegerSharedTypedArray, ia);
+ }
+}
+
+function CheckSharedInteger32TypedArray(ia) {
+ CheckSharedIntegerTypedArray(ia);
+ if (!%IsSharedInteger32TypedArray(ia)) {
+ throw MakeTypeError(kNotInt32SharedTypedArray, ia);
+ }
+}
+
+//-------------------------------------------------------------------
+
+function AtomicsCompareExchangeJS(sta, index, oldValue, newValue) {
+ CheckSharedIntegerTypedArray(sta);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+ return UNDEFINED;
+ }
+ oldValue = TO_NUMBER(oldValue);
+ newValue = TO_NUMBER(newValue);
+ return %_AtomicsCompareExchange(sta, index, oldValue, newValue);
+}
+
+function AtomicsLoadJS(sta, index) {
+ CheckSharedIntegerTypedArray(sta);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+ return UNDEFINED;
+ }
+ return %_AtomicsLoad(sta, index);
+}
+
+function AtomicsStoreJS(sta, index, value) {
+ CheckSharedIntegerTypedArray(sta);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(sta)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsStore(sta, index, value);
+}
+
+function AtomicsAddJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsAdd(ia, index, value);
+}
+
+function AtomicsSubJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsSub(ia, index, value);
+}
+
+function AtomicsAndJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsAnd(ia, index, value);
+}
+
+function AtomicsOrJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsOr(ia, index, value);
+}
+
+function AtomicsXorJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsXor(ia, index, value);
+}
+
+function AtomicsExchangeJS(ia, index, value) {
+ CheckSharedIntegerTypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ value = TO_NUMBER(value);
+ return %_AtomicsExchange(ia, index, value);
+}
+
+function AtomicsIsLockFreeJS(size) {
+ return %_AtomicsIsLockFree(size);
+}
+
+// Futexes
+
+function AtomicsFutexWaitJS(ia, index, value, timeout) {
+ CheckSharedInteger32TypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ if (IS_UNDEFINED(timeout)) {
+ timeout = INFINITY;
+ } else {
+ timeout = TO_NUMBER(timeout);
+ if (NUMBER_IS_NAN(timeout)) {
+ timeout = INFINITY;
+ } else {
+ timeout = MaxSimple(0, timeout);
+ }
+ }
+ return %AtomicsFutexWait(ia, index, value, timeout);
+}
+
+function AtomicsFutexWakeJS(ia, index, count) {
+ CheckSharedInteger32TypedArray(ia);
+ index = TO_INTEGER(index);
+ if (index < 0 || index >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ count = MaxSimple(0, TO_INTEGER(count));
+ return %AtomicsFutexWake(ia, index, count);
+}
+
+function AtomicsFutexWakeOrRequeueJS(ia, index1, count, value, index2) {
+ CheckSharedInteger32TypedArray(ia);
+ index1 = TO_INTEGER(index1);
+ count = MaxSimple(0, TO_INTEGER(count));
+ value = TO_INT32(value);
+ index2 = TO_INTEGER(index2);
+ if (index1 < 0 || index1 >= %_TypedArrayGetLength(ia) ||
+ index2 < 0 || index2 >= %_TypedArrayGetLength(ia)) {
+ return UNDEFINED;
+ }
+ return %AtomicsFutexWakeOrRequeue(ia, index1, count, value, index2);
+}
+
+// -------------------------------------------------------------------
+
+function AtomicsConstructor() {}
+
+var Atomics = new AtomicsConstructor();
+
+%InternalSetPrototype(Atomics, GlobalObject.prototype);
+%AddNamedProperty(global, "Atomics", Atomics, DONT_ENUM);
+%FunctionSetInstanceClassName(AtomicsConstructor, 'Atomics');
+
+%AddNamedProperty(Atomics, toStringTagSymbol, "Atomics", READ_ONLY | DONT_ENUM);
+
+// These must match the values in src/futex-emulation.h
+utils.InstallConstants(Atomics, [
+ "OK", 0,
+ "NOTEQUAL", -1,
+ "TIMEDOUT", -2,
+]);
+
+utils.InstallFunctions(Atomics, DONT_ENUM, [
+ "compareExchange", AtomicsCompareExchangeJS,
+ "load", AtomicsLoadJS,
+ "store", AtomicsStoreJS,
+ "add", AtomicsAddJS,
+ "sub", AtomicsSubJS,
+ "and", AtomicsAndJS,
+ "or", AtomicsOrJS,
+ "xor", AtomicsXorJS,
+ "exchange", AtomicsExchangeJS,
+ "isLockFree", AtomicsIsLockFreeJS,
+ "futexWait", AtomicsFutexWaitJS,
+ "futexWake", AtomicsFutexWakeJS,
+ "futexWakeOrRequeue", AtomicsFutexWakeOrRequeueJS,
+]);
+
+})
diff --git a/src/js/harmony-object-observe.js b/src/js/harmony-object-observe.js
new file mode 100644
index 0000000..95dd298
--- /dev/null
+++ b/src/js/harmony-object-observe.js
@@ -0,0 +1,17 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var ObserveArrayMethods = utils.ImportNow("ObserveArrayMethods");
+var ObserveObjectMethods = utils.ImportNow("ObserveObjectMethods");;
+
+utils.InstallFunctions(global.Object, DONT_ENUM, ObserveObjectMethods);
+utils.InstallFunctions(global.Array, DONT_ENUM, ObserveArrayMethods);
+
+})
diff --git a/src/js/harmony-reflect.js b/src/js/harmony-reflect.js
new file mode 100644
index 0000000..dcadad5
--- /dev/null
+++ b/src/js/harmony-reflect.js
@@ -0,0 +1,37 @@
+// Copyright 2013-2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalReflect = global.Reflect;
+var MakeTypeError;
+var ReflectApply = utils.ImportNow("reflect_apply");
+var ReflectConstruct = utils.ImportNow("reflect_construct");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function ReflectEnumerate(obj) {
+ if (!IS_RECEIVER(obj))
+ throw MakeTypeError(kCalledOnNonObject, "Reflect.enumerate")
+ return (function* () { for (var x in obj) yield x })();
+}
+
+utils.InstallFunctions(GlobalReflect, DONT_ENUM, [
+ "apply", ReflectApply,
+ "construct", ReflectConstruct,
+ "enumerate", ReflectEnumerate
+]);
+
+})
diff --git a/src/js/harmony-regexp.js b/src/js/harmony-regexp.js
new file mode 100644
index 0000000..f76ef86
--- /dev/null
+++ b/src/js/harmony-regexp.js
@@ -0,0 +1,60 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype = GlobalRegExp.prototype;
+var MakeTypeError;
+var regExpFlagsSymbol = utils.ImportNow("regexp_flags_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// ES6 draft 12-06-13, section 21.2.5.3
+// + https://bugs.ecmascript.org/show_bug.cgi?id=3423
+function RegExpGetFlags() {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(
+ kRegExpNonObject, "RegExp.prototype.flags", TO_STRING(this));
+ }
+ var result = '';
+ if (this.global) result += 'g';
+ if (this.ignoreCase) result += 'i';
+ if (this.multiline) result += 'm';
+ if (this.unicode) result += 'u';
+ if (this.sticky) result += 'y';
+ return result;
+}
+
+// ES6 21.2.5.12.
+function RegExpGetSticky() {
+ if (!IS_REGEXP(this)) {
+ // Compat fix: RegExp.prototype.sticky == undefined; UseCounter tracks it
+ // TODO(littledan): Remove this workaround or standardize it
+ if (this === GlobalRegExpPrototype) {
+ %IncrementUseCounter(kRegExpPrototypeStickyGetter);
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.sticky");
+ }
+ return !!REGEXP_STICKY(this);
+}
+%FunctionSetName(RegExpGetSticky, "RegExp.prototype.sticky");
+%SetNativeFlag(RegExpGetSticky);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'flags', RegExpGetFlags);
+utils.InstallGetter(GlobalRegExp.prototype, 'sticky', RegExpGetSticky);
+
+})
diff --git a/src/js/harmony-sharedarraybuffer.js b/src/js/harmony-sharedarraybuffer.js
new file mode 100644
index 0000000..10ceb70
--- /dev/null
+++ b/src/js/harmony-sharedarraybuffer.js
@@ -0,0 +1,31 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalSharedArrayBuffer = global.SharedArrayBuffer;
+var MakeTypeError;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+})
+
+// -------------------------------------------------------------------
+
+function SharedArrayBufferGetByteLen() {
+ if (!IS_SHAREDARRAYBUFFER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'SharedArrayBuffer.prototype.byteLength', this);
+ }
+ return %_ArrayBufferGetByteLength(this);
+}
+
+utils.InstallGetter(GlobalSharedArrayBuffer.prototype, "byteLength",
+ SharedArrayBufferGetByteLen);
+
+})
diff --git a/src/js/harmony-simd.js b/src/js/harmony-simd.js
new file mode 100644
index 0000000..4df2f43
--- /dev/null
+++ b/src/js/harmony-simd.js
@@ -0,0 +1,941 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalSIMD = global.SIMD;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+macro SIMD_FLOAT_TYPES(FUNCTION)
+FUNCTION(Float32x4, float32x4, 4)
+endmacro
+
+macro SIMD_INT_TYPES(FUNCTION)
+FUNCTION(Int32x4, int32x4, 4)
+FUNCTION(Int16x8, int16x8, 8)
+FUNCTION(Int8x16, int8x16, 16)
+endmacro
+
+macro SIMD_UINT_TYPES(FUNCTION)
+FUNCTION(Uint32x4, uint32x4, 4)
+FUNCTION(Uint16x8, uint16x8, 8)
+FUNCTION(Uint8x16, uint8x16, 16)
+endmacro
+
+macro SIMD_BOOL_TYPES(FUNCTION)
+FUNCTION(Bool32x4, bool32x4, 4)
+FUNCTION(Bool16x8, bool16x8, 8)
+FUNCTION(Bool8x16, bool8x16, 16)
+endmacro
+
+macro SIMD_ALL_TYPES(FUNCTION)
+SIMD_FLOAT_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+SIMD_BOOL_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_GLOBALS(NAME, TYPE, LANES)
+var GlobalNAME = GlobalSIMD.NAME;
+endmacro
+
+SIMD_ALL_TYPES(DECLARE_GLOBALS)
+
+macro DECLARE_COMMON_FUNCTIONS(NAME, TYPE, LANES)
+function NAMECheckJS(a) {
+ return %NAMECheck(a);
+}
+
+function NAMEToString() {
+ var value = %_ValueOf(this);
+ if (typeof(value) !== 'TYPE') {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "NAME.prototype.toString", this);
+ }
+ var str = "SIMD.NAME(";
+ str += %NAMEExtractLane(value, 0);
+ for (var i = 1; i < LANES; i++) {
+ str += ", " + %NAMEExtractLane(value, i);
+ }
+ return str + ")";
+}
+
+function NAMEToLocaleString() {
+ var value = %_ValueOf(this);
+ if (typeof(value) !== 'TYPE') {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "NAME.prototype.toLocaleString", this);
+ }
+ var str = "SIMD.NAME(";
+ str += %NAMEExtractLane(value, 0).toLocaleString();
+ for (var i = 1; i < LANES; i++) {
+ str += ", " + %NAMEExtractLane(value, i).toLocaleString();
+ }
+ return str + ")";
+}
+
+function NAMEValueOf() {
+ var value = %_ValueOf(this);
+ if (typeof(value) !== 'TYPE') {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "NAME.prototype.valueOf", this);
+ }
+ return value;
+}
+
+function NAMEExtractLaneJS(instance, lane) {
+ return %NAMEExtractLane(instance, lane);
+}
+endmacro
+
+SIMD_ALL_TYPES(DECLARE_COMMON_FUNCTIONS)
+
+macro DECLARE_SHIFT_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEShiftLeftByScalarJS(instance, shift) {
+ return %NAMEShiftLeftByScalar(instance, shift);
+}
+
+function NAMEShiftRightByScalarJS(instance, shift) {
+ return %NAMEShiftRightByScalar(instance, shift);
+}
+endmacro
+
+SIMD_INT_TYPES(DECLARE_SHIFT_FUNCTIONS)
+SIMD_UINT_TYPES(DECLARE_SHIFT_FUNCTIONS)
+
+macro SIMD_SMALL_INT_TYPES(FUNCTION)
+FUNCTION(Int16x8)
+FUNCTION(Int8x16)
+FUNCTION(Uint8x16)
+FUNCTION(Uint16x8)
+endmacro
+
+macro DECLARE_SMALL_INT_FUNCTIONS(NAME)
+function NAMEAddSaturateJS(a, b) {
+ return %NAMEAddSaturate(a, b);
+}
+
+function NAMESubSaturateJS(a, b) {
+ return %NAMESubSaturate(a, b);
+}
+endmacro
+
+SIMD_SMALL_INT_TYPES(DECLARE_SMALL_INT_FUNCTIONS)
+
+macro DECLARE_SIGNED_FUNCTIONS(NAME, TYPE, LANES)
+function NAMENegJS(a) {
+ return %NAMENeg(a);
+}
+endmacro
+
+SIMD_FLOAT_TYPES(DECLARE_SIGNED_FUNCTIONS)
+SIMD_INT_TYPES(DECLARE_SIGNED_FUNCTIONS)
+
+macro DECLARE_BOOL_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEReplaceLaneJS(instance, lane, value) {
+ return %NAMEReplaceLane(instance, lane, value);
+}
+
+function NAMEAnyTrueJS(s) {
+ return %NAMEAnyTrue(s);
+}
+
+function NAMEAllTrueJS(s) {
+ return %NAMEAllTrue(s);
+}
+endmacro
+
+SIMD_BOOL_TYPES(DECLARE_BOOL_FUNCTIONS)
+
+macro SIMD_NUMERIC_TYPES(FUNCTION)
+SIMD_FLOAT_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_NUMERIC_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEReplaceLaneJS(instance, lane, value) {
+ return %NAMEReplaceLane(instance, lane, TO_NUMBER(value));
+}
+
+function NAMESelectJS(selector, a, b) {
+ return %NAMESelect(selector, a, b);
+}
+
+function NAMEAddJS(a, b) {
+ return %NAMEAdd(a, b);
+}
+
+function NAMESubJS(a, b) {
+ return %NAMESub(a, b);
+}
+
+function NAMEMulJS(a, b) {
+ return %NAMEMul(a, b);
+}
+
+function NAMEMinJS(a, b) {
+ return %NAMEMin(a, b);
+}
+
+function NAMEMaxJS(a, b) {
+ return %NAMEMax(a, b);
+}
+
+function NAMEEqualJS(a, b) {
+ return %NAMEEqual(a, b);
+}
+
+function NAMENotEqualJS(a, b) {
+ return %NAMENotEqual(a, b);
+}
+
+function NAMELessThanJS(a, b) {
+ return %NAMELessThan(a, b);
+}
+
+function NAMELessThanOrEqualJS(a, b) {
+ return %NAMELessThanOrEqual(a, b);
+}
+
+function NAMEGreaterThanJS(a, b) {
+ return %NAMEGreaterThan(a, b);
+}
+
+function NAMEGreaterThanOrEqualJS(a, b) {
+ return %NAMEGreaterThanOrEqual(a, b);
+}
+
+function NAMELoadJS(tarray, index) {
+ return %NAMELoad(tarray, index);
+}
+
+function NAMEStoreJS(tarray, index, a) {
+ return %NAMEStore(tarray, index, a);
+}
+endmacro
+
+SIMD_NUMERIC_TYPES(DECLARE_NUMERIC_FUNCTIONS)
+
+macro SIMD_LOGICAL_TYPES(FUNCTION)
+SIMD_INT_TYPES(FUNCTION)
+SIMD_UINT_TYPES(FUNCTION)
+SIMD_BOOL_TYPES(FUNCTION)
+endmacro
+
+macro DECLARE_LOGICAL_FUNCTIONS(NAME, TYPE, LANES)
+function NAMEAndJS(a, b) {
+ return %NAMEAnd(a, b);
+}
+
+function NAMEOrJS(a, b) {
+ return %NAMEOr(a, b);
+}
+
+function NAMEXorJS(a, b) {
+ return %NAMEXor(a, b);
+}
+
+function NAMENotJS(a) {
+ return %NAMENot(a);
+}
+endmacro
+
+SIMD_LOGICAL_TYPES(DECLARE_LOGICAL_FUNCTIONS)
+
+macro SIMD_FROM_TYPES(FUNCTION)
+FUNCTION(Float32x4, Int32x4)
+FUNCTION(Float32x4, Uint32x4)
+FUNCTION(Int32x4, Float32x4)
+FUNCTION(Int32x4, Uint32x4)
+FUNCTION(Uint32x4, Float32x4)
+FUNCTION(Uint32x4, Int32x4)
+FUNCTION(Int16x8, Uint16x8)
+FUNCTION(Uint16x8, Int16x8)
+FUNCTION(Int8x16, Uint8x16)
+FUNCTION(Uint8x16, Int8x16)
+endmacro
+
+macro DECLARE_FROM_FUNCTIONS(TO, FROM)
+function TOFromFROMJS(a) {
+ return %TOFromFROM(a);
+}
+endmacro
+
+SIMD_FROM_TYPES(DECLARE_FROM_FUNCTIONS)
+
+macro SIMD_FROM_BITS_TYPES(FUNCTION)
+FUNCTION(Float32x4, Int32x4)
+FUNCTION(Float32x4, Uint32x4)
+FUNCTION(Float32x4, Int16x8)
+FUNCTION(Float32x4, Uint16x8)
+FUNCTION(Float32x4, Int8x16)
+FUNCTION(Float32x4, Uint8x16)
+FUNCTION(Int32x4, Float32x4)
+FUNCTION(Int32x4, Uint32x4)
+FUNCTION(Int32x4, Int16x8)
+FUNCTION(Int32x4, Uint16x8)
+FUNCTION(Int32x4, Int8x16)
+FUNCTION(Int32x4, Uint8x16)
+FUNCTION(Uint32x4, Float32x4)
+FUNCTION(Uint32x4, Int32x4)
+FUNCTION(Uint32x4, Int16x8)
+FUNCTION(Uint32x4, Uint16x8)
+FUNCTION(Uint32x4, Int8x16)
+FUNCTION(Uint32x4, Uint8x16)
+FUNCTION(Int16x8, Float32x4)
+FUNCTION(Int16x8, Int32x4)
+FUNCTION(Int16x8, Uint32x4)
+FUNCTION(Int16x8, Uint16x8)
+FUNCTION(Int16x8, Int8x16)
+FUNCTION(Int16x8, Uint8x16)
+FUNCTION(Uint16x8, Float32x4)
+FUNCTION(Uint16x8, Int32x4)
+FUNCTION(Uint16x8, Uint32x4)
+FUNCTION(Uint16x8, Int16x8)
+FUNCTION(Uint16x8, Int8x16)
+FUNCTION(Uint16x8, Uint8x16)
+FUNCTION(Int8x16, Float32x4)
+FUNCTION(Int8x16, Int32x4)
+FUNCTION(Int8x16, Uint32x4)
+FUNCTION(Int8x16, Int16x8)
+FUNCTION(Int8x16, Uint16x8)
+FUNCTION(Int8x16, Uint8x16)
+FUNCTION(Uint8x16, Float32x4)
+FUNCTION(Uint8x16, Int32x4)
+FUNCTION(Uint8x16, Uint32x4)
+FUNCTION(Uint8x16, Int16x8)
+FUNCTION(Uint8x16, Uint16x8)
+FUNCTION(Uint8x16, Int8x16)
+endmacro
+
+macro DECLARE_FROM_BITS_FUNCTIONS(TO, FROM)
+function TOFromFROMBitsJS(a) {
+ return %TOFromFROMBits(a);
+}
+endmacro
+
+SIMD_FROM_BITS_TYPES(DECLARE_FROM_BITS_FUNCTIONS)
+
+
+macro SIMD_LOADN_STOREN_TYPES(FUNCTION)
+FUNCTION(Float32x4, 1)
+FUNCTION(Float32x4, 2)
+FUNCTION(Float32x4, 3)
+FUNCTION(Int32x4, 1)
+FUNCTION(Int32x4, 2)
+FUNCTION(Int32x4, 3)
+FUNCTION(Uint32x4, 1)
+FUNCTION(Uint32x4, 2)
+FUNCTION(Uint32x4, 3)
+endmacro
+
+macro DECLARE_LOADN_STOREN_FUNCTIONS(NAME, COUNT)
+function NAMELoadCOUNTJS(tarray, index) {
+ return %NAMELoadCOUNT(tarray, index);
+}
+
+function NAMEStoreCOUNTJS(tarray, index, a) {
+ return %NAMEStoreCOUNT(tarray, index, a);
+}
+endmacro
+
+SIMD_LOADN_STOREN_TYPES(DECLARE_LOADN_STOREN_FUNCTIONS)
+
+//-------------------------------------------------------------------
+
+macro SIMD_X4_TYPES(FUNCTION)
+FUNCTION(Float32x4)
+FUNCTION(Int32x4)
+FUNCTION(Uint32x4)
+FUNCTION(Bool32x4)
+endmacro
+
+macro DECLARE_X4_FUNCTIONS(NAME)
+function NAMESplat(s) {
+ return %CreateNAME(s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3) {
+ return %NAMESwizzle(a, c0, c1, c2, c3);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3) {
+ return %NAMEShuffle(a, b, c0, c1, c2, c3);
+}
+endmacro
+
+SIMD_X4_TYPES(DECLARE_X4_FUNCTIONS)
+
+macro SIMD_X8_TYPES(FUNCTION)
+FUNCTION(Int16x8)
+FUNCTION(Uint16x8)
+FUNCTION(Bool16x8)
+endmacro
+
+macro DECLARE_X8_FUNCTIONS(NAME)
+function NAMESplat(s) {
+ return %CreateNAME(s, s, s, s, s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3, c4, c5, c6, c7) {
+ return %NAMESwizzle(a, c0, c1, c2, c3, c4, c5, c6, c7);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3, c4, c5, c6, c7) {
+ return %NAMEShuffle(a, b, c0, c1, c2, c3, c4, c5, c6, c7);
+}
+endmacro
+
+SIMD_X8_TYPES(DECLARE_X8_FUNCTIONS)
+
+macro SIMD_X16_TYPES(FUNCTION)
+FUNCTION(Int8x16)
+FUNCTION(Uint8x16)
+FUNCTION(Bool8x16)
+endmacro
+
+macro DECLARE_X16_FUNCTIONS(NAME)
+function NAMESplat(s) {
+ return %CreateNAME(s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s);
+}
+
+function NAMESwizzleJS(a, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+ c12, c13, c14, c15) {
+ return %NAMESwizzle(a, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+ c12, c13, c14, c15);
+}
+
+function NAMEShuffleJS(a, b, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+ c11, c12, c13, c14, c15) {
+ return %NAMEShuffle(a, b, c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10,
+ c11, c12, c13, c14, c15);
+}
+endmacro
+
+SIMD_X16_TYPES(DECLARE_X16_FUNCTIONS)
+
+//-------------------------------------------------------------------
+
+function Float32x4Constructor(c0, c1, c2, c3) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Float32x4");
+ }
+ return %CreateFloat32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Int32x4Constructor(c0, c1, c2, c3) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Int32x4");
+ }
+ return %CreateInt32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Uint32x4Constructor(c0, c1, c2, c3) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Uint32x4");
+ }
+ return %CreateUint32x4(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3));
+}
+
+
+function Bool32x4Constructor(c0, c1, c2, c3) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Bool32x4");
+ }
+ return %CreateBool32x4(c0, c1, c2, c3);
+}
+
+
+function Int16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Int16x8");
+ }
+ return %CreateInt16x8(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3),
+ TO_NUMBER(c4), TO_NUMBER(c5),
+ TO_NUMBER(c6), TO_NUMBER(c7));
+}
+
+
+function Uint16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Uint16x8");
+ }
+ return %CreateUint16x8(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3),
+ TO_NUMBER(c4), TO_NUMBER(c5),
+ TO_NUMBER(c6), TO_NUMBER(c7));
+}
+
+
+function Bool16x8Constructor(c0, c1, c2, c3, c4, c5, c6, c7) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Bool16x8");
+ }
+ return %CreateBool16x8(c0, c1, c2, c3, c4, c5, c6, c7);
+}
+
+
+function Int8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+ c12, c13, c14, c15) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Int8x16");
+ }
+ return %CreateInt8x16(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3),
+ TO_NUMBER(c4), TO_NUMBER(c5),
+ TO_NUMBER(c6), TO_NUMBER(c7),
+ TO_NUMBER(c8), TO_NUMBER(c9),
+ TO_NUMBER(c10), TO_NUMBER(c11),
+ TO_NUMBER(c12), TO_NUMBER(c13),
+ TO_NUMBER(c14), TO_NUMBER(c15));
+}
+
+
+function Uint8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+ c12, c13, c14, c15) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Uint8x16");
+ }
+ return %CreateUint8x16(TO_NUMBER(c0), TO_NUMBER(c1),
+ TO_NUMBER(c2), TO_NUMBER(c3),
+ TO_NUMBER(c4), TO_NUMBER(c5),
+ TO_NUMBER(c6), TO_NUMBER(c7),
+ TO_NUMBER(c8), TO_NUMBER(c9),
+ TO_NUMBER(c10), TO_NUMBER(c11),
+ TO_NUMBER(c12), TO_NUMBER(c13),
+ TO_NUMBER(c14), TO_NUMBER(c15));
+}
+
+
+function Bool8x16Constructor(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11,
+ c12, c13, c14, c15) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kNotConstructor, "Bool8x16");
+ }
+ return %CreateBool8x16(c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,
+ c13, c14, c15);
+}
+
+
+function Float32x4AbsJS(a) {
+ return %Float32x4Abs(a);
+}
+
+
+function Float32x4SqrtJS(a) {
+ return %Float32x4Sqrt(a);
+}
+
+
+function Float32x4RecipApproxJS(a) {
+ return %Float32x4RecipApprox(a);
+}
+
+
+function Float32x4RecipSqrtApproxJS(a) {
+ return %Float32x4RecipSqrtApprox(a);
+}
+
+
+function Float32x4DivJS(a, b) {
+ return %Float32x4Div(a, b);
+}
+
+
+function Float32x4MinNumJS(a, b) {
+ return %Float32x4MinNum(a, b);
+}
+
+
+function Float32x4MaxNumJS(a, b) {
+ return %Float32x4MaxNum(a, b);
+}
+
+
+%AddNamedProperty(GlobalSIMD, toStringTagSymbol, 'SIMD', READ_ONLY | DONT_ENUM);
+
+macro SETUP_SIMD_TYPE(NAME, TYPE, LANES)
+%SetCode(GlobalNAME, NAMEConstructor);
+%FunctionSetPrototype(GlobalNAME, {});
+%AddNamedProperty(GlobalNAME.prototype, 'constructor', GlobalNAME,
+ DONT_ENUM);
+%AddNamedProperty(GlobalNAME.prototype, toStringTagSymbol, 'NAME',
+ DONT_ENUM | READ_ONLY);
+utils.InstallFunctions(GlobalNAME.prototype, DONT_ENUM, [
+ 'toLocaleString', NAMEToLocaleString,
+ 'toString', NAMEToString,
+ 'valueOf', NAMEValueOf,
+]);
+endmacro
+
+SIMD_ALL_TYPES(SETUP_SIMD_TYPE)
+
+//-------------------------------------------------------------------
+
+utils.InstallFunctions(GlobalFloat32x4, DONT_ENUM, [
+ 'splat', Float32x4Splat,
+ 'check', Float32x4CheckJS,
+ 'extractLane', Float32x4ExtractLaneJS,
+ 'replaceLane', Float32x4ReplaceLaneJS,
+ 'neg', Float32x4NegJS,
+ 'abs', Float32x4AbsJS,
+ 'sqrt', Float32x4SqrtJS,
+ 'reciprocalApproximation', Float32x4RecipApproxJS,
+ 'reciprocalSqrtApproximation', Float32x4RecipSqrtApproxJS,
+ 'add', Float32x4AddJS,
+ 'sub', Float32x4SubJS,
+ 'mul', Float32x4MulJS,
+ 'div', Float32x4DivJS,
+ 'min', Float32x4MinJS,
+ 'max', Float32x4MaxJS,
+ 'minNum', Float32x4MinNumJS,
+ 'maxNum', Float32x4MaxNumJS,
+ 'lessThan', Float32x4LessThanJS,
+ 'lessThanOrEqual', Float32x4LessThanOrEqualJS,
+ 'greaterThan', Float32x4GreaterThanJS,
+ 'greaterThanOrEqual', Float32x4GreaterThanOrEqualJS,
+ 'equal', Float32x4EqualJS,
+ 'notEqual', Float32x4NotEqualJS,
+ 'select', Float32x4SelectJS,
+ 'swizzle', Float32x4SwizzleJS,
+ 'shuffle', Float32x4ShuffleJS,
+ 'fromInt32x4', Float32x4FromInt32x4JS,
+ 'fromUint32x4', Float32x4FromUint32x4JS,
+ 'fromInt32x4Bits', Float32x4FromInt32x4BitsJS,
+ 'fromUint32x4Bits', Float32x4FromUint32x4BitsJS,
+ 'fromInt16x8Bits', Float32x4FromInt16x8BitsJS,
+ 'fromUint16x8Bits', Float32x4FromUint16x8BitsJS,
+ 'fromInt8x16Bits', Float32x4FromInt8x16BitsJS,
+ 'fromUint8x16Bits', Float32x4FromUint8x16BitsJS,
+ 'load', Float32x4LoadJS,
+ 'load1', Float32x4Load1JS,
+ 'load2', Float32x4Load2JS,
+ 'load3', Float32x4Load3JS,
+ 'store', Float32x4StoreJS,
+ 'store1', Float32x4Store1JS,
+ 'store2', Float32x4Store2JS,
+ 'store3', Float32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalInt32x4, DONT_ENUM, [
+ 'splat', Int32x4Splat,
+ 'check', Int32x4CheckJS,
+ 'extractLane', Int32x4ExtractLaneJS,
+ 'replaceLane', Int32x4ReplaceLaneJS,
+ 'neg', Int32x4NegJS,
+ 'add', Int32x4AddJS,
+ 'sub', Int32x4SubJS,
+ 'mul', Int32x4MulJS,
+ 'min', Int32x4MinJS,
+ 'max', Int32x4MaxJS,
+ 'and', Int32x4AndJS,
+ 'or', Int32x4OrJS,
+ 'xor', Int32x4XorJS,
+ 'not', Int32x4NotJS,
+ 'shiftLeftByScalar', Int32x4ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Int32x4ShiftRightByScalarJS,
+ 'lessThan', Int32x4LessThanJS,
+ 'lessThanOrEqual', Int32x4LessThanOrEqualJS,
+ 'greaterThan', Int32x4GreaterThanJS,
+ 'greaterThanOrEqual', Int32x4GreaterThanOrEqualJS,
+ 'equal', Int32x4EqualJS,
+ 'notEqual', Int32x4NotEqualJS,
+ 'select', Int32x4SelectJS,
+ 'swizzle', Int32x4SwizzleJS,
+ 'shuffle', Int32x4ShuffleJS,
+ 'fromFloat32x4', Int32x4FromFloat32x4JS,
+ 'fromUint32x4', Int32x4FromUint32x4JS,
+ 'fromFloat32x4Bits', Int32x4FromFloat32x4BitsJS,
+ 'fromUint32x4Bits', Int32x4FromUint32x4BitsJS,
+ 'fromInt16x8Bits', Int32x4FromInt16x8BitsJS,
+ 'fromUint16x8Bits', Int32x4FromUint16x8BitsJS,
+ 'fromInt8x16Bits', Int32x4FromInt8x16BitsJS,
+ 'fromUint8x16Bits', Int32x4FromUint8x16BitsJS,
+ 'load', Int32x4LoadJS,
+ 'load1', Int32x4Load1JS,
+ 'load2', Int32x4Load2JS,
+ 'load3', Int32x4Load3JS,
+ 'store', Int32x4StoreJS,
+ 'store1', Int32x4Store1JS,
+ 'store2', Int32x4Store2JS,
+ 'store3', Int32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalUint32x4, DONT_ENUM, [
+ 'splat', Uint32x4Splat,
+ 'check', Uint32x4CheckJS,
+ 'extractLane', Uint32x4ExtractLaneJS,
+ 'replaceLane', Uint32x4ReplaceLaneJS,
+ 'add', Uint32x4AddJS,
+ 'sub', Uint32x4SubJS,
+ 'mul', Uint32x4MulJS,
+ 'min', Uint32x4MinJS,
+ 'max', Uint32x4MaxJS,
+ 'and', Uint32x4AndJS,
+ 'or', Uint32x4OrJS,
+ 'xor', Uint32x4XorJS,
+ 'not', Uint32x4NotJS,
+ 'shiftLeftByScalar', Uint32x4ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Uint32x4ShiftRightByScalarJS,
+ 'lessThan', Uint32x4LessThanJS,
+ 'lessThanOrEqual', Uint32x4LessThanOrEqualJS,
+ 'greaterThan', Uint32x4GreaterThanJS,
+ 'greaterThanOrEqual', Uint32x4GreaterThanOrEqualJS,
+ 'equal', Uint32x4EqualJS,
+ 'notEqual', Uint32x4NotEqualJS,
+ 'select', Uint32x4SelectJS,
+ 'swizzle', Uint32x4SwizzleJS,
+ 'shuffle', Uint32x4ShuffleJS,
+ 'fromFloat32x4', Uint32x4FromFloat32x4JS,
+ 'fromInt32x4', Uint32x4FromInt32x4JS,
+ 'fromFloat32x4Bits', Uint32x4FromFloat32x4BitsJS,
+ 'fromInt32x4Bits', Uint32x4FromInt32x4BitsJS,
+ 'fromInt16x8Bits', Uint32x4FromInt16x8BitsJS,
+ 'fromUint16x8Bits', Uint32x4FromUint16x8BitsJS,
+ 'fromInt8x16Bits', Uint32x4FromInt8x16BitsJS,
+ 'fromUint8x16Bits', Uint32x4FromUint8x16BitsJS,
+ 'load', Uint32x4LoadJS,
+ 'load1', Uint32x4Load1JS,
+ 'load2', Uint32x4Load2JS,
+ 'load3', Uint32x4Load3JS,
+ 'store', Uint32x4StoreJS,
+ 'store1', Uint32x4Store1JS,
+ 'store2', Uint32x4Store2JS,
+ 'store3', Uint32x4Store3JS,
+]);
+
+utils.InstallFunctions(GlobalBool32x4, DONT_ENUM, [
+ 'splat', Bool32x4Splat,
+ 'check', Bool32x4CheckJS,
+ 'extractLane', Bool32x4ExtractLaneJS,
+ 'replaceLane', Bool32x4ReplaceLaneJS,
+ 'and', Bool32x4AndJS,
+ 'or', Bool32x4OrJS,
+ 'xor', Bool32x4XorJS,
+ 'not', Bool32x4NotJS,
+ 'anyTrue', Bool32x4AnyTrueJS,
+ 'allTrue', Bool32x4AllTrueJS,
+ 'swizzle', Bool32x4SwizzleJS,
+ 'shuffle', Bool32x4ShuffleJS,
+]);
+
+utils.InstallFunctions(GlobalInt16x8, DONT_ENUM, [
+ 'splat', Int16x8Splat,
+ 'check', Int16x8CheckJS,
+ 'extractLane', Int16x8ExtractLaneJS,
+ 'replaceLane', Int16x8ReplaceLaneJS,
+ 'neg', Int16x8NegJS,
+ 'add', Int16x8AddJS,
+ 'sub', Int16x8SubJS,
+ 'addSaturate', Int16x8AddSaturateJS,
+ 'subSaturate', Int16x8SubSaturateJS,
+ 'mul', Int16x8MulJS,
+ 'min', Int16x8MinJS,
+ 'max', Int16x8MaxJS,
+ 'and', Int16x8AndJS,
+ 'or', Int16x8OrJS,
+ 'xor', Int16x8XorJS,
+ 'not', Int16x8NotJS,
+ 'shiftLeftByScalar', Int16x8ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Int16x8ShiftRightByScalarJS,
+ 'lessThan', Int16x8LessThanJS,
+ 'lessThanOrEqual', Int16x8LessThanOrEqualJS,
+ 'greaterThan', Int16x8GreaterThanJS,
+ 'greaterThanOrEqual', Int16x8GreaterThanOrEqualJS,
+ 'equal', Int16x8EqualJS,
+ 'notEqual', Int16x8NotEqualJS,
+ 'select', Int16x8SelectJS,
+ 'swizzle', Int16x8SwizzleJS,
+ 'shuffle', Int16x8ShuffleJS,
+ 'fromUint16x8', Int16x8FromUint16x8JS,
+ 'fromFloat32x4Bits', Int16x8FromFloat32x4BitsJS,
+ 'fromInt32x4Bits', Int16x8FromInt32x4BitsJS,
+ 'fromUint32x4Bits', Int16x8FromUint32x4BitsJS,
+ 'fromUint16x8Bits', Int16x8FromUint16x8BitsJS,
+ 'fromInt8x16Bits', Int16x8FromInt8x16BitsJS,
+ 'fromUint8x16Bits', Int16x8FromUint8x16BitsJS,
+ 'load', Int16x8LoadJS,
+ 'store', Int16x8StoreJS,
+]);
+
+utils.InstallFunctions(GlobalUint16x8, DONT_ENUM, [
+ 'splat', Uint16x8Splat,
+ 'check', Uint16x8CheckJS,
+ 'extractLane', Uint16x8ExtractLaneJS,
+ 'replaceLane', Uint16x8ReplaceLaneJS,
+ 'add', Uint16x8AddJS,
+ 'sub', Uint16x8SubJS,
+ 'addSaturate', Uint16x8AddSaturateJS,
+ 'subSaturate', Uint16x8SubSaturateJS,
+ 'mul', Uint16x8MulJS,
+ 'min', Uint16x8MinJS,
+ 'max', Uint16x8MaxJS,
+ 'and', Uint16x8AndJS,
+ 'or', Uint16x8OrJS,
+ 'xor', Uint16x8XorJS,
+ 'not', Uint16x8NotJS,
+ 'shiftLeftByScalar', Uint16x8ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Uint16x8ShiftRightByScalarJS,
+ 'lessThan', Uint16x8LessThanJS,
+ 'lessThanOrEqual', Uint16x8LessThanOrEqualJS,
+ 'greaterThan', Uint16x8GreaterThanJS,
+ 'greaterThanOrEqual', Uint16x8GreaterThanOrEqualJS,
+ 'equal', Uint16x8EqualJS,
+ 'notEqual', Uint16x8NotEqualJS,
+ 'select', Uint16x8SelectJS,
+ 'swizzle', Uint16x8SwizzleJS,
+ 'shuffle', Uint16x8ShuffleJS,
+ 'fromInt16x8', Uint16x8FromInt16x8JS,
+ 'fromFloat32x4Bits', Uint16x8FromFloat32x4BitsJS,
+ 'fromInt32x4Bits', Uint16x8FromInt32x4BitsJS,
+ 'fromUint32x4Bits', Uint16x8FromUint32x4BitsJS,
+ 'fromInt16x8Bits', Uint16x8FromInt16x8BitsJS,
+ 'fromInt8x16Bits', Uint16x8FromInt8x16BitsJS,
+ 'fromUint8x16Bits', Uint16x8FromUint8x16BitsJS,
+ 'load', Uint16x8LoadJS,
+ 'store', Uint16x8StoreJS,
+]);
+
+utils.InstallFunctions(GlobalBool16x8, DONT_ENUM, [
+ 'splat', Bool16x8Splat,
+ 'check', Bool16x8CheckJS,
+ 'extractLane', Bool16x8ExtractLaneJS,
+ 'replaceLane', Bool16x8ReplaceLaneJS,
+ 'and', Bool16x8AndJS,
+ 'or', Bool16x8OrJS,
+ 'xor', Bool16x8XorJS,
+ 'not', Bool16x8NotJS,
+ 'anyTrue', Bool16x8AnyTrueJS,
+ 'allTrue', Bool16x8AllTrueJS,
+ 'swizzle', Bool16x8SwizzleJS,
+ 'shuffle', Bool16x8ShuffleJS,
+]);
+
+utils.InstallFunctions(GlobalInt8x16, DONT_ENUM, [
+ 'splat', Int8x16Splat,
+ 'check', Int8x16CheckJS,
+ 'extractLane', Int8x16ExtractLaneJS,
+ 'replaceLane', Int8x16ReplaceLaneJS,
+ 'neg', Int8x16NegJS,
+ 'add', Int8x16AddJS,
+ 'sub', Int8x16SubJS,
+ 'addSaturate', Int8x16AddSaturateJS,
+ 'subSaturate', Int8x16SubSaturateJS,
+ 'mul', Int8x16MulJS,
+ 'min', Int8x16MinJS,
+ 'max', Int8x16MaxJS,
+ 'and', Int8x16AndJS,
+ 'or', Int8x16OrJS,
+ 'xor', Int8x16XorJS,
+ 'not', Int8x16NotJS,
+ 'shiftLeftByScalar', Int8x16ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Int8x16ShiftRightByScalarJS,
+ 'lessThan', Int8x16LessThanJS,
+ 'lessThanOrEqual', Int8x16LessThanOrEqualJS,
+ 'greaterThan', Int8x16GreaterThanJS,
+ 'greaterThanOrEqual', Int8x16GreaterThanOrEqualJS,
+ 'equal', Int8x16EqualJS,
+ 'notEqual', Int8x16NotEqualJS,
+ 'select', Int8x16SelectJS,
+ 'swizzle', Int8x16SwizzleJS,
+ 'shuffle', Int8x16ShuffleJS,
+ 'fromUint8x16', Int8x16FromUint8x16JS,
+ 'fromFloat32x4Bits', Int8x16FromFloat32x4BitsJS,
+ 'fromInt32x4Bits', Int8x16FromInt32x4BitsJS,
+ 'fromUint32x4Bits', Int8x16FromUint32x4BitsJS,
+ 'fromInt16x8Bits', Int8x16FromInt16x8BitsJS,
+ 'fromUint16x8Bits', Int8x16FromUint16x8BitsJS,
+ 'fromUint8x16Bits', Int8x16FromUint8x16BitsJS,
+ 'load', Int8x16LoadJS,
+ 'store', Int8x16StoreJS,
+]);
+
+utils.InstallFunctions(GlobalUint8x16, DONT_ENUM, [
+ 'splat', Uint8x16Splat,
+ 'check', Uint8x16CheckJS,
+ 'extractLane', Uint8x16ExtractLaneJS,
+ 'replaceLane', Uint8x16ReplaceLaneJS,
+ 'add', Uint8x16AddJS,
+ 'sub', Uint8x16SubJS,
+ 'addSaturate', Uint8x16AddSaturateJS,
+ 'subSaturate', Uint8x16SubSaturateJS,
+ 'mul', Uint8x16MulJS,
+ 'min', Uint8x16MinJS,
+ 'max', Uint8x16MaxJS,
+ 'and', Uint8x16AndJS,
+ 'or', Uint8x16OrJS,
+ 'xor', Uint8x16XorJS,
+ 'not', Uint8x16NotJS,
+ 'shiftLeftByScalar', Uint8x16ShiftLeftByScalarJS,
+ 'shiftRightByScalar', Uint8x16ShiftRightByScalarJS,
+ 'lessThan', Uint8x16LessThanJS,
+ 'lessThanOrEqual', Uint8x16LessThanOrEqualJS,
+ 'greaterThan', Uint8x16GreaterThanJS,
+ 'greaterThanOrEqual', Uint8x16GreaterThanOrEqualJS,
+ 'equal', Uint8x16EqualJS,
+ 'notEqual', Uint8x16NotEqualJS,
+ 'select', Uint8x16SelectJS,
+ 'swizzle', Uint8x16SwizzleJS,
+ 'shuffle', Uint8x16ShuffleJS,
+ 'fromInt8x16', Uint8x16FromInt8x16JS,
+ 'fromFloat32x4Bits', Uint8x16FromFloat32x4BitsJS,
+ 'fromInt32x4Bits', Uint8x16FromInt32x4BitsJS,
+ 'fromUint32x4Bits', Uint8x16FromUint32x4BitsJS,
+ 'fromInt16x8Bits', Uint8x16FromInt16x8BitsJS,
+ 'fromUint16x8Bits', Uint8x16FromUint16x8BitsJS,
+ 'fromInt8x16Bits', Uint8x16FromInt8x16BitsJS,
+ 'load', Uint8x16LoadJS,
+ 'store', Uint8x16StoreJS,
+]);
+
+utils.InstallFunctions(GlobalBool8x16, DONT_ENUM, [
+ 'splat', Bool8x16Splat,
+ 'check', Bool8x16CheckJS,
+ 'extractLane', Bool8x16ExtractLaneJS,
+ 'replaceLane', Bool8x16ReplaceLaneJS,
+ 'and', Bool8x16AndJS,
+ 'or', Bool8x16OrJS,
+ 'xor', Bool8x16XorJS,
+ 'not', Bool8x16NotJS,
+ 'anyTrue', Bool8x16AnyTrueJS,
+ 'allTrue', Bool8x16AllTrueJS,
+ 'swizzle', Bool8x16SwizzleJS,
+ 'shuffle', Bool8x16ShuffleJS,
+]);
+
+utils.Export(function(to) {
+ to.Float32x4ToString = Float32x4ToString;
+ to.Int32x4ToString = Int32x4ToString;
+ to.Uint32x4ToString = Uint32x4ToString;
+ to.Bool32x4ToString = Bool32x4ToString;
+ to.Int16x8ToString = Int16x8ToString;
+ to.Uint16x8ToString = Uint16x8ToString;
+ to.Bool16x8ToString = Bool16x8ToString;
+ to.Int8x16ToString = Int8x16ToString;
+ to.Uint8x16ToString = Uint8x16ToString;
+ to.Bool8x16ToString = Bool8x16ToString;
+});
+
+})
diff --git a/src/js/harmony-species.js b/src/js/harmony-species.js
new file mode 100644
index 0000000..426ac46
--- /dev/null
+++ b/src/js/harmony-species.js
@@ -0,0 +1,60 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalArray = global.Array;
+// It is important that this file is run after src/js/typedarray.js,
+// otherwise GlobalTypedArray would be Object, and we would break
+// old versions of Zepto.
+var GlobalTypedArray = global.Uint8Array.__proto__;
+var GlobalMap = global.Map;
+var GlobalSet = global.Set;
+var GlobalArrayBuffer = global.ArrayBuffer;
+var GlobalPromise = global.Promise;
+var GlobalRegExp = global.RegExp;
+var speciesSymbol = utils.ImportNow("species_symbol");
+
+function ArraySpecies() {
+ return this;
+}
+
+function TypedArraySpecies() {
+ return this;
+}
+
+function MapSpecies() {
+ return this;
+}
+
+function SetSpecies() {
+ return this;
+}
+
+function ArrayBufferSpecies() {
+ return this;
+}
+
+function PromiseSpecies() {
+ return this;
+}
+
+function RegExpSpecies() {
+ return this;
+}
+
+utils.InstallGetter(GlobalArray, speciesSymbol, ArraySpecies, DONT_ENUM);
+utils.InstallGetter(GlobalTypedArray, speciesSymbol, TypedArraySpecies, DONT_ENUM);
+utils.InstallGetter(GlobalMap, speciesSymbol, MapSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalSet, speciesSymbol, SetSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalArrayBuffer, speciesSymbol, ArrayBufferSpecies,
+ DONT_ENUM);
+utils.InstallGetter(GlobalPromise, speciesSymbol, PromiseSpecies, DONT_ENUM);
+utils.InstallGetter(GlobalRegExp, speciesSymbol, RegExpSpecies, DONT_ENUM);
+
+});
diff --git a/src/js/harmony-unicode-regexps.js b/src/js/harmony-unicode-regexps.js
new file mode 100644
index 0000000..b24bbdf
--- /dev/null
+++ b/src/js/harmony-unicode-regexps.js
@@ -0,0 +1,41 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+'use strict';
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype = GlobalRegExp.prototype;
+var MakeTypeError;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// ES6 21.2.5.15.
+function RegExpGetUnicode() {
+ if (!IS_REGEXP(this)) {
+ // TODO(littledan): Remove this RegExp compat workaround
+ if (this === GlobalRegExpPrototype) {
+ %IncrementUseCounter(kRegExpPrototypeUnicodeGetter);
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.unicode");
+ }
+ return !!REGEXP_UNICODE(this);
+}
+%FunctionSetName(RegExpGetUnicode, "RegExp.prototype.unicode");
+%SetNativeFlag(RegExpGetUnicode);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'unicode', RegExpGetUnicode);
+
+})
diff --git a/src/js/i18n.js b/src/js/i18n.js
new file mode 100644
index 0000000..7e00fcd
--- /dev/null
+++ b/src/js/i18n.js
@@ -0,0 +1,2197 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ECMAScript 402 API implementation.
+
+/**
+ * Intl object is a single object that has some named properties,
+ * all of which are constructors.
+ */
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayIndexOf;
+var ArrayJoin;
+var ArrayPush;
+var IsFinite;
+var IsNaN;
+var GlobalBoolean = global.Boolean;
+var GlobalDate = global.Date;
+var GlobalNumber = global.Number;
+var GlobalRegExp = global.RegExp;
+var GlobalString = global.String;
+var MakeError;
+var MakeRangeError;
+var MakeTypeError;
+var MathFloor;
+var ObjectDefineProperties = utils.ImportNow("ObjectDefineProperties");
+var ObjectDefineProperty = utils.ImportNow("ObjectDefineProperty");
+var patternSymbol = utils.ImportNow("intl_pattern_symbol");
+var RegExpTest;
+var resolvedSymbol = utils.ImportNow("intl_resolved_symbol");
+var StringIndexOf;
+var StringLastIndexOf;
+var StringMatch;
+var StringReplace;
+var StringSplit;
+var StringSubstr;
+var StringSubstring;
+
+utils.Import(function(from) {
+ ArrayIndexOf = from.ArrayIndexOf;
+ ArrayJoin = from.ArrayJoin;
+ ArrayPush = from.ArrayPush;
+ IsFinite = from.IsFinite;
+ IsNaN = from.IsNaN;
+ MakeError = from.MakeError;
+ MakeRangeError = from.MakeRangeError;
+ MakeTypeError = from.MakeTypeError;
+ MathFloor = from.MathFloor;
+ RegExpTest = from.RegExpTest;
+ StringIndexOf = from.StringIndexOf;
+ StringLastIndexOf = from.StringLastIndexOf;
+ StringMatch = from.StringMatch;
+ StringReplace = from.StringReplace;
+ StringSplit = from.StringSplit;
+ StringSubstr = from.StringSubstr;
+ StringSubstring = from.StringSubstring;
+});
+
+// -------------------------------------------------------------------
+
+var Intl = {};
+
+%AddNamedProperty(global, "Intl", Intl, DONT_ENUM);
+
+/**
+ * Caches available locales for each service.
+ */
+var AVAILABLE_LOCALES = {
+ 'collator': UNDEFINED,
+ 'numberformat': UNDEFINED,
+ 'dateformat': UNDEFINED,
+ 'breakiterator': UNDEFINED
+};
+
+/**
+ * Caches default ICU locale.
+ */
+var DEFAULT_ICU_LOCALE = UNDEFINED;
+
+/**
+ * Unicode extension regular expression.
+ */
+var UNICODE_EXTENSION_RE = UNDEFINED;
+
+function GetUnicodeExtensionRE() {
+ if (IS_UNDEFINED(UNDEFINED)) {
+ UNICODE_EXTENSION_RE = new GlobalRegExp('-u(-[a-z0-9]{2,8})+', 'g');
+ }
+ return UNICODE_EXTENSION_RE;
+}
+
+/**
+ * Matches any Unicode extension.
+ */
+var ANY_EXTENSION_RE = UNDEFINED;
+
+function GetAnyExtensionRE() {
+ if (IS_UNDEFINED(ANY_EXTENSION_RE)) {
+ ANY_EXTENSION_RE = new GlobalRegExp('-[a-z0-9]{1}-.*', 'g');
+ }
+ return ANY_EXTENSION_RE;
+}
+
+/**
+ * Replace quoted text (single quote, anything but the quote and quote again).
+ */
+var QUOTED_STRING_RE = UNDEFINED;
+
+function GetQuotedStringRE() {
+ if (IS_UNDEFINED(QUOTED_STRING_RE)) {
+ QUOTED_STRING_RE = new GlobalRegExp("'[^']+'", 'g');
+ }
+ return QUOTED_STRING_RE;
+}
+
+/**
+ * Matches valid service name.
+ */
+var SERVICE_RE = UNDEFINED;
+
+function GetServiceRE() {
+ if (IS_UNDEFINED(SERVICE_RE)) {
+ SERVICE_RE =
+ new GlobalRegExp('^(collator|numberformat|dateformat|breakiterator)$');
+ }
+ return SERVICE_RE;
+}
+
+/**
+ * Validates a language tag against bcp47 spec.
+ * Actual value is assigned on first run.
+ */
+var LANGUAGE_TAG_RE = UNDEFINED;
+
+function GetLanguageTagRE() {
+ if (IS_UNDEFINED(LANGUAGE_TAG_RE)) {
+ BuildLanguageTagREs();
+ }
+ return LANGUAGE_TAG_RE;
+}
+
+/**
+ * Helps find duplicate variants in the language tag.
+ */
+var LANGUAGE_VARIANT_RE = UNDEFINED;
+
+function GetLanguageVariantRE() {
+ if (IS_UNDEFINED(LANGUAGE_VARIANT_RE)) {
+ BuildLanguageTagREs();
+ }
+ return LANGUAGE_VARIANT_RE;
+}
+
+/**
+ * Helps find duplicate singletons in the language tag.
+ */
+var LANGUAGE_SINGLETON_RE = UNDEFINED;
+
+function GetLanguageSingletonRE() {
+ if (IS_UNDEFINED(LANGUAGE_SINGLETON_RE)) {
+ BuildLanguageTagREs();
+ }
+ return LANGUAGE_SINGLETON_RE;
+}
+
+/**
+ * Matches valid IANA time zone names.
+ */
+var TIMEZONE_NAME_CHECK_RE = UNDEFINED;
+
+function GetTimezoneNameCheckRE() {
+ if (IS_UNDEFINED(TIMEZONE_NAME_CHECK_RE)) {
+ TIMEZONE_NAME_CHECK_RE = new GlobalRegExp(
+ '^([A-Za-z]+)/([A-Za-z_-]+)((?:\/[A-Za-z_-]+)+)*$');
+ }
+ return TIMEZONE_NAME_CHECK_RE;
+}
+
+/**
+ * Matches valid location parts of IANA time zone names.
+ */
+var TIMEZONE_NAME_LOCATION_PART_RE = UNDEFINED;
+
+function GetTimezoneNameLocationPartRE() {
+ if (IS_UNDEFINED(TIMEZONE_NAME_LOCATION_PART_RE)) {
+ TIMEZONE_NAME_LOCATION_PART_RE =
+ new GlobalRegExp('^([A-Za-z]+)((?:[_-][A-Za-z]+)+)*$');
+ }
+ return TIMEZONE_NAME_LOCATION_PART_RE;
+}
+
+/**
+ * Adds bound method to the prototype of the given object.
+ */
+function addBoundMethod(obj, methodName, implementation, length) {
+ %CheckIsBootstrapping();
+ function getter() {
+ if (!%IsInitializedIntlObject(this)) {
+ throw MakeTypeError(kMethodCalledOnWrongObject, methodName);
+ }
+ var internalName = '__bound' + methodName + '__';
+ if (IS_UNDEFINED(this[internalName])) {
+ var that = this;
+ var boundMethod;
+ if (IS_UNDEFINED(length) || length === 2) {
+ boundMethod = function(x, y) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+ return implementation(that, x, y);
+ }
+ } else if (length === 1) {
+ boundMethod = function(x) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+ return implementation(that, x);
+ }
+ } else {
+ boundMethod = function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+ // DateTimeFormat.format needs to be 0 arg method, but can stil
+ // receive optional dateValue param. If one was provided, pass it
+ // along.
+ if (%_ArgumentsLength() > 0) {
+ return implementation(that, %_Arguments(0));
+ } else {
+ return implementation(that);
+ }
+ }
+ }
+ %FunctionSetName(boundMethod, internalName);
+ %FunctionRemovePrototype(boundMethod);
+ %SetNativeFlag(boundMethod);
+ this[internalName] = boundMethod;
+ }
+ return this[internalName];
+ }
+
+ %FunctionSetName(getter, methodName);
+ %FunctionRemovePrototype(getter);
+ %SetNativeFlag(getter);
+
+ ObjectDefineProperty(obj.prototype, methodName, {
+ get: getter,
+ enumerable: false,
+ configurable: true
+ });
+}
+
+
+/**
+ * Returns an intersection of locales and service supported locales.
+ * Parameter locales is treated as a priority list.
+ */
+function supportedLocalesOf(service, locales, options) {
+ if (IS_NULL(%_Call(StringMatch, service, GetServiceRE()))) {
+ throw MakeError(kWrongServiceType, service);
+ }
+
+ // Provide defaults if matcher was not specified.
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ } else {
+ options = TO_OBJECT(options);
+ }
+
+ var matcher = options.localeMatcher;
+ if (!IS_UNDEFINED(matcher)) {
+ matcher = GlobalString(matcher);
+ if (matcher !== 'lookup' && matcher !== 'best fit') {
+ throw MakeRangeError(kLocaleMatcher, matcher);
+ }
+ } else {
+ matcher = 'best fit';
+ }
+
+ var requestedLocales = initializeLocaleList(locales);
+
+ // Cache these, they don't ever change per service.
+ if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ // Use either best fit or lookup algorithm to match locales.
+ if (matcher === 'best fit') {
+ return initializeLocaleList(bestFitSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+ }
+
+ return initializeLocaleList(lookupSupportedLocalesOf(
+ requestedLocales, AVAILABLE_LOCALES[service]));
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the BCP 47 Lookup algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function lookupSupportedLocalesOf(requestedLocales, availableLocales) {
+ var matchedLocales = [];
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove -u- extension.
+ var locale = %_Call(StringReplace,
+ requestedLocales[i],
+ GetUnicodeExtensionRE(),
+ '');
+ do {
+ if (!IS_UNDEFINED(availableLocales[locale])) {
+ // Push requested locale not the resolved one.
+ %_Call(ArrayPush, matchedLocales, requestedLocales[i]);
+ break;
+ }
+ // Truncate locale if possible, if not break.
+ var pos = %_Call(StringLastIndexOf, locale, '-');
+ if (pos === -1) {
+ break;
+ }
+ locale = %_Call(StringSubstring, locale, 0, pos);
+ } while (true);
+ }
+
+ return matchedLocales;
+}
+
+
+/**
+ * Returns the subset of the provided BCP 47 language priority list for which
+ * this service has a matching locale when using the implementation
+ * dependent algorithm.
+ * Locales appear in the same order in the returned list as in the input list.
+ */
+function bestFitSupportedLocalesOf(requestedLocales, availableLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupSupportedLocalesOf(requestedLocales, availableLocales);
+}
+
+
+/**
+ * Returns a getOption function that extracts property value for given
+ * options object. If property is missing it returns defaultValue. If value
+ * is out of range for that property it throws RangeError.
+ */
+function getGetOption(options, caller) {
+ if (IS_UNDEFINED(options)) throw MakeError(kDefaultOptionsMissing, caller);
+
+ var getOption = function getOption(property, type, values, defaultValue) {
+ if (!IS_UNDEFINED(options[property])) {
+ var value = options[property];
+ switch (type) {
+ case 'boolean':
+ value = GlobalBoolean(value);
+ break;
+ case 'string':
+ value = GlobalString(value);
+ break;
+ case 'number':
+ value = GlobalNumber(value);
+ break;
+ default:
+ throw MakeError(kWrongValueType);
+ }
+
+ if (!IS_UNDEFINED(values) && %_Call(ArrayIndexOf, values, value) === -1) {
+ throw MakeRangeError(kValueOutOfRange, value, caller, property);
+ }
+
+ return value;
+ }
+
+ return defaultValue;
+ }
+
+ return getOption;
+}
+
+
+/**
+ * Compares a BCP 47 language priority list requestedLocales against the locales
+ * in availableLocales and determines the best available language to meet the
+ * request. Two algorithms are available to match the locales: the Lookup
+ * algorithm described in RFC 4647 section 3.4, and an implementation dependent
+ * best-fit algorithm. Independent of the locale matching algorithm, options
+ * specified through Unicode locale extension sequences are negotiated
+ * separately, taking the caller's relevant extension keys and locale data as
+ * well as client-provided options into consideration. Returns an object with
+ * a locale property whose value is the language tag of the selected locale,
+ * and properties for each key in relevantExtensionKeys providing the selected
+ * value for that key.
+ */
+function resolveLocale(service, requestedLocales, options) {
+ requestedLocales = initializeLocaleList(requestedLocales);
+
+ var getOption = getGetOption(options, service);
+ var matcher = getOption('localeMatcher', 'string',
+ ['lookup', 'best fit'], 'best fit');
+ var resolved;
+ if (matcher === 'lookup') {
+ resolved = lookupMatcher(service, requestedLocales);
+ } else {
+ resolved = bestFitMatcher(service, requestedLocales);
+ }
+
+ return resolved;
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using basic
+ * lookup algorithm.
+ */
+function lookupMatcher(service, requestedLocales) {
+ if (IS_NULL(%_Call(StringMatch, service, GetServiceRE()))) {
+ throw MakeError(kWrongServiceType, service);
+ }
+
+ // Cache these, they don't ever change per service.
+ if (IS_UNDEFINED(AVAILABLE_LOCALES[service])) {
+ AVAILABLE_LOCALES[service] = getAvailableLocalesOf(service);
+ }
+
+ for (var i = 0; i < requestedLocales.length; ++i) {
+ // Remove all extensions.
+ var locale = %_Call(StringReplace, requestedLocales[i],
+ GetAnyExtensionRE(), '');
+ do {
+ if (!IS_UNDEFINED(AVAILABLE_LOCALES[service][locale])) {
+ // Return the resolved locale and extension.
+ var extensionMatch =
+ %_Call(StringMatch, requestedLocales[i], GetUnicodeExtensionRE());
+ var extension = IS_NULL(extensionMatch) ? '' : extensionMatch[0];
+ return {'locale': locale, 'extension': extension, 'position': i};
+ }
+ // Truncate locale if possible.
+ var pos = %_Call(StringLastIndexOf, locale, '-');
+ if (pos === -1) {
+ break;
+ }
+ locale = %_Call(StringSubstring, locale, 0, pos);
+ } while (true);
+ }
+
+ // Didn't find a match, return default.
+ if (IS_UNDEFINED(DEFAULT_ICU_LOCALE)) {
+ DEFAULT_ICU_LOCALE = %GetDefaultICULocale();
+ }
+
+ return {'locale': DEFAULT_ICU_LOCALE, 'extension': '', 'position': -1};
+}
+
+
+/**
+ * Returns best matched supported locale and extension info using
+ * implementation dependend algorithm.
+ */
+function bestFitMatcher(service, requestedLocales) {
+ // TODO(cira): implement better best fit algorithm.
+ return lookupMatcher(service, requestedLocales);
+}
+
+
+/**
+ * Parses Unicode extension into key - value map.
+ * Returns empty object if the extension string is invalid.
+ * We are not concerned with the validity of the values at this point.
+ */
+function parseExtension(extension) {
+ var extensionSplit = %_Call(StringSplit, extension, '-');
+
+ // Assume ['', 'u', ...] input, but don't throw.
+ if (extensionSplit.length <= 2 ||
+ (extensionSplit[0] !== '' && extensionSplit[1] !== 'u')) {
+ return {};
+ }
+
+ // Key is {2}alphanum, value is {3,8}alphanum.
+ // Some keys may not have explicit values (booleans).
+ var extensionMap = {};
+ var previousKey = UNDEFINED;
+ for (var i = 2; i < extensionSplit.length; ++i) {
+ var length = extensionSplit[i].length;
+ var element = extensionSplit[i];
+ if (length === 2) {
+ extensionMap[element] = UNDEFINED;
+ previousKey = element;
+ } else if (length >= 3 && length <=8 && !IS_UNDEFINED(previousKey)) {
+ extensionMap[previousKey] = element;
+ previousKey = UNDEFINED;
+ } else {
+ // There is a value that's too long, or that doesn't have a key.
+ return {};
+ }
+ }
+
+ return extensionMap;
+}
+
+
+/**
+ * Populates internalOptions object with boolean key-value pairs
+ * from extensionMap and options.
+ * Returns filtered extension (number and date format constructors use
+ * Unicode extensions for passing parameters to ICU).
+ * It's used for extension-option pairs only, e.g. kn-normalization, but not
+ * for 'sensitivity' since it doesn't have extension equivalent.
+ * Extensions like nu and ca don't have options equivalent, so we place
+ * undefined in the map.property to denote that.
+ */
+function setOptions(inOptions, extensionMap, keyValues, getOption, outOptions) {
+ var extension = '';
+
+ var updateExtension = function updateExtension(key, value) {
+ return '-' + key + '-' + GlobalString(value);
+ }
+
+ var updateProperty = function updateProperty(property, type, value) {
+ if (type === 'boolean' && (typeof value === 'string')) {
+ value = (value === 'true') ? true : false;
+ }
+
+ if (!IS_UNDEFINED(property)) {
+ defineWEProperty(outOptions, property, value);
+ }
+ }
+
+ for (var key in keyValues) {
+ if (%HasOwnProperty(keyValues, key)) {
+ var value = UNDEFINED;
+ var map = keyValues[key];
+ if (!IS_UNDEFINED(map.property)) {
+ // This may return true if user specifies numeric: 'false', since
+ // Boolean('nonempty') === true.
+ value = getOption(map.property, map.type, map.values);
+ }
+ if (!IS_UNDEFINED(value)) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ continue;
+ }
+ // User options didn't have it, check Unicode extension.
+ // Here we want to convert strings 'true', 'false' into proper Boolean
+ // values (not a user error).
+ if (%HasOwnProperty(extensionMap, key)) {
+ value = extensionMap[key];
+ if (!IS_UNDEFINED(value)) {
+ updateProperty(map.property, map.type, value);
+ extension += updateExtension(key, value);
+ } else if (map.type === 'boolean') {
+ // Boolean keys are allowed not to have values in Unicode extension.
+ // Those default to true.
+ updateProperty(map.property, map.type, true);
+ extension += updateExtension(key, true);
+ }
+ }
+ }
+ }
+
+ return extension === ''? '' : '-u' + extension;
+}
+
+
+/**
+ * Converts all OwnProperties into
+ * configurable: false, writable: false, enumerable: true.
+ */
+function freezeArray(array) {
+ var l = array.length;
+ for (var i = 0; i < l; i++) {
+ if (i in array) {
+ ObjectDefineProperty(array, i, {value: array[i],
+ configurable: false,
+ writable: false,
+ enumerable: true});
+ }
+ }
+
+ ObjectDefineProperty(array, 'length', {value: l, writable: false});
+ return array;
+}
+
+
+/**
+ * It's sometimes desireable to leave user requested locale instead of ICU
+ * supported one (zh-TW is equivalent to zh-Hant-TW, so we should keep shorter
+ * one, if that was what user requested).
+ * This function returns user specified tag if its maximized form matches ICU
+ * resolved locale. If not we return ICU result.
+ */
+function getOptimalLanguageTag(original, resolved) {
+ // Returns Array<Object>, where each object has maximized and base properties.
+ // Maximized: zh -> zh-Hans-CN
+ // Base: zh-CN-u-ca-gregory -> zh-CN
+ // Take care of grandfathered or simple cases.
+ if (original === resolved) {
+ return original;
+ }
+
+ var locales = %GetLanguageTagVariants([original, resolved]);
+ if (locales[0].maximized !== locales[1].maximized) {
+ return resolved;
+ }
+
+ // Preserve extensions of resolved locale, but swap base tags with original.
+ var resolvedBase = new GlobalRegExp('^' + locales[1].base);
+ return %_Call(StringReplace, resolved, resolvedBase, locales[0].base);
+}
+
+
+/**
+ * Returns an Object that contains all of supported locales for a given
+ * service.
+ * In addition to the supported locales we add xx-ZZ locale for each xx-Yyyy-ZZ
+ * that is supported. This is required by the spec.
+ */
+function getAvailableLocalesOf(service) {
+ var available = %AvailableLocalesOf(service);
+
+ for (var i in available) {
+ if (%HasOwnProperty(available, i)) {
+ var parts =
+ %_Call(StringMatch, i, /^([a-z]{2,3})-([A-Z][a-z]{3})-([A-Z]{2})$/);
+ if (parts !== null) {
+ // Build xx-ZZ. We don't care about the actual value,
+ // as long it's not undefined.
+ available[parts[1] + '-' + parts[3]] = null;
+ }
+ }
+ }
+
+ return available;
+}
+
+
+/**
+ * Defines a property and sets writable and enumerable to true.
+ * Configurable is false by default.
+ */
+function defineWEProperty(object, property, value) {
+ ObjectDefineProperty(object, property,
+ {value: value, writable: true, enumerable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets configurable descriptor to false.
+ */
+function addWEPropertyIfDefined(object, property, value) {
+ if (!IS_UNDEFINED(value)) {
+ defineWEProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Defines a property and sets writable, enumerable and configurable to true.
+ */
+function defineWECProperty(object, property, value) {
+ ObjectDefineProperty(object, property, {value: value,
+ writable: true,
+ enumerable: true,
+ configurable: true});
+}
+
+
+/**
+ * Adds property to an object if the value is not undefined.
+ * Sets all descriptors to true.
+ */
+function addWECPropertyIfDefined(object, property, value) {
+ if (!IS_UNDEFINED(value)) {
+ defineWECProperty(object, property, value);
+ }
+}
+
+
+/**
+ * Returns titlecased word, aMeRricA -> America.
+ */
+function toTitleCaseWord(word) {
+ return %StringToUpperCase(%_Call(StringSubstr, word, 0, 1)) +
+ %StringToLowerCase(%_Call(StringSubstr, word, 1));
+}
+
+/**
+ * Returns titlecased location, bueNos_airES -> Buenos_Aires
+ * or ho_cHi_minH -> Ho_Chi_Minh. It is locale-agnostic and only
+ * deals with ASCII only characters.
+ * 'of', 'au' and 'es' are special-cased and lowercased.
+ */
+function toTitleCaseTimezoneLocation(location) {
+ var match = %_Call(StringMatch, location, GetTimezoneNameLocationPartRE());
+ if (IS_NULL(match)) throw MakeRangeError(kExpectedLocation, location);
+
+ var result = toTitleCaseWord(match[1]);
+ if (!IS_UNDEFINED(match[2]) && 2 < match.length) {
+ // The first character is a separator, '_' or '-'.
+ // None of IANA zone names has both '_' and '-'.
+ var separator = %_Call(StringSubstring, match[2], 0, 1);
+ var parts = %_Call(StringSplit, match[2], separator);
+ for (var i = 1; i < parts.length; i++) {
+ var part = parts[i]
+ var lowercasedPart = %StringToLowerCase(part);
+ result = result + separator +
+ ((lowercasedPart !== 'es' &&
+ lowercasedPart !== 'of' && lowercasedPart !== 'au') ?
+ toTitleCaseWord(part) : lowercasedPart);
+ }
+ }
+ return result;
+}
+
+/**
+ * Canonicalizes the language tag, or throws in case the tag is invalid.
+ */
+function canonicalizeLanguageTag(localeID) {
+ // null is typeof 'object' so we have to do extra check.
+ if (typeof localeID !== 'string' && typeof localeID !== 'object' ||
+ IS_NULL(localeID)) {
+ throw MakeTypeError(kLanguageID);
+ }
+
+ var localeString = GlobalString(localeID);
+
+ if (isValidLanguageTag(localeString) === false) {
+ throw MakeRangeError(kInvalidLanguageTag, localeString);
+ }
+
+ // This call will strip -kn but not -kn-true extensions.
+ // ICU bug filled - http://bugs.icu-project.org/trac/ticket/9265.
+ // TODO(cira): check if -u-kn-true-kc-true-kh-true still throws after
+ // upgrade to ICU 4.9.
+ var tag = %CanonicalizeLanguageTag(localeString);
+ if (tag === 'invalid-tag') {
+ throw MakeRangeError(kInvalidLanguageTag, localeString);
+ }
+
+ return tag;
+}
+
+
+/**
+ * Returns an array where all locales are canonicalized and duplicates removed.
+ * Throws on locales that are not well formed BCP47 tags.
+ */
+function initializeLocaleList(locales) {
+ var seen = [];
+ if (IS_UNDEFINED(locales)) {
+ // Constructor is called without arguments.
+ seen = [];
+ } else {
+ // We allow single string localeID.
+ if (typeof locales === 'string') {
+ %_Call(ArrayPush, seen, canonicalizeLanguageTag(locales));
+ return freezeArray(seen);
+ }
+
+ var o = TO_OBJECT(locales);
+ var len = TO_UINT32(o.length);
+
+ for (var k = 0; k < len; k++) {
+ if (k in o) {
+ var value = o[k];
+
+ var tag = canonicalizeLanguageTag(value);
+
+ if (%_Call(ArrayIndexOf, seen, tag) === -1) {
+ %_Call(ArrayPush, seen, tag);
+ }
+ }
+ }
+ }
+
+ return freezeArray(seen);
+}
+
+
+/**
+ * Validates the language tag. Section 2.2.9 of the bcp47 spec
+ * defines a valid tag.
+ *
+ * ICU is too permissible and lets invalid tags, like
+ * hant-cmn-cn, through.
+ *
+ * Returns false if the language tag is invalid.
+ */
+function isValidLanguageTag(locale) {
+ // Check if it's well-formed, including grandfadered tags.
+ if (!%_Call(RegExpTest, GetLanguageTagRE(), locale)) {
+ return false;
+ }
+
+ // Just return if it's a x- form. It's all private.
+ if (%_Call(StringIndexOf, locale, 'x-') === 0) {
+ return true;
+ }
+
+ // Check if there are any duplicate variants or singletons (extensions).
+
+ // Remove private use section.
+ locale = %_Call(StringSplit, locale, /-x-/)[0];
+
+ // Skip language since it can match variant regex, so we start from 1.
+ // We are matching i-klingon here, but that's ok, since i-klingon-klingon
+ // is not valid and would fail LANGUAGE_TAG_RE test.
+ var variants = [];
+ var extensions = [];
+ var parts = %_Call(StringSplit, locale, /-/);
+ for (var i = 1; i < parts.length; i++) {
+ var value = parts[i];
+ if (%_Call(RegExpTest, GetLanguageVariantRE(), value) &&
+ extensions.length === 0) {
+ if (%_Call(ArrayIndexOf, variants, value) === -1) {
+ %_Call(ArrayPush, variants, value);
+ } else {
+ return false;
+ }
+ }
+
+ if (%_Call(RegExpTest, GetLanguageSingletonRE(), value)) {
+ if (%_Call(ArrayIndexOf, extensions, value) === -1) {
+ %_Call(ArrayPush, extensions, value);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+
+/**
+ * Builds a regular expresion that validates the language tag
+ * against bcp47 spec.
+ * Uses http://tools.ietf.org/html/bcp47, section 2.1, ABNF.
+ * Runs on load and initializes the global REs.
+ */
+function BuildLanguageTagREs() {
+ var alpha = '[a-zA-Z]';
+ var digit = '[0-9]';
+ var alphanum = '(' + alpha + '|' + digit + ')';
+ var regular = '(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|' +
+ 'zh-min|zh-min-nan|zh-xiang)';
+ var irregular = '(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|' +
+ 'i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|' +
+ 'i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)';
+ var grandfathered = '(' + irregular + '|' + regular + ')';
+ var privateUse = '(x(-' + alphanum + '{1,8})+)';
+
+ var singleton = '(' + digit + '|[A-WY-Za-wy-z])';
+ LANGUAGE_SINGLETON_RE = new GlobalRegExp('^' + singleton + '$', 'i');
+
+ var extension = '(' + singleton + '(-' + alphanum + '{2,8})+)';
+
+ var variant = '(' + alphanum + '{5,8}|(' + digit + alphanum + '{3}))';
+ LANGUAGE_VARIANT_RE = new GlobalRegExp('^' + variant + '$', 'i');
+
+ var region = '(' + alpha + '{2}|' + digit + '{3})';
+ var script = '(' + alpha + '{4})';
+ var extLang = '(' + alpha + '{3}(-' + alpha + '{3}){0,2})';
+ var language = '(' + alpha + '{2,3}(-' + extLang + ')?|' + alpha + '{4}|' +
+ alpha + '{5,8})';
+ var langTag = language + '(-' + script + ')?(-' + region + ')?(-' +
+ variant + ')*(-' + extension + ')*(-' + privateUse + ')?';
+
+ var languageTag =
+ '^(' + langTag + '|' + privateUse + '|' + grandfathered + ')$';
+ LANGUAGE_TAG_RE = new GlobalRegExp(languageTag, 'i');
+}
+
+var resolvedAccessor = {
+ get() {
+ %IncrementUseCounter(kIntlResolved);
+ return this[resolvedSymbol];
+ },
+ set(value) {
+ this[resolvedSymbol] = value;
+ }
+};
+
+/**
+ * Initializes the given object so it's a valid Collator instance.
+ * Useful for subclassing.
+ */
+function initializeCollator(collator, locales, options) {
+ if (%IsInitializedIntlObject(collator)) {
+ throw MakeTypeError(kReinitializeIntl, "Collator");
+ }
+
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'collator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'usage', getOption(
+ 'usage', 'string', ['sort', 'search'], 'sort'));
+
+ var sensitivity = getOption('sensitivity', 'string',
+ ['base', 'accent', 'case', 'variant']);
+ if (IS_UNDEFINED(sensitivity) && internalOptions.usage === 'sort') {
+ sensitivity = 'variant';
+ }
+ defineWEProperty(internalOptions, 'sensitivity', sensitivity);
+
+ defineWEProperty(internalOptions, 'ignorePunctuation', getOption(
+ 'ignorePunctuation', 'boolean', UNDEFINED, false));
+
+ var locale = resolveLocale('collator', locales, options);
+
+ // ICU can't take kb, kc... parameters through localeID, so we need to pass
+ // them as options.
+ // One exception is -co- which has to be part of the extension, but only for
+ // usage: sort, and its value can't be 'standard' or 'search'.
+ var extensionMap = parseExtension(locale.extension);
+
+ /**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a collator.
+ */
+ var COLLATOR_KEY_MAP = {
+ 'kn': {'property': 'numeric', 'type': 'boolean'},
+ 'kf': {'property': 'caseFirst', 'type': 'string',
+ 'values': ['false', 'lower', 'upper']}
+ };
+
+ setOptions(
+ options, extensionMap, COLLATOR_KEY_MAP, getOption, internalOptions);
+
+ var collation = 'default';
+ var extension = '';
+ if (%HasOwnProperty(extensionMap, 'co') && internalOptions.usage === 'sort') {
+
+ /**
+ * Allowed -u-co- values. List taken from:
+ * http://unicode.org/repos/cldr/trunk/common/bcp47/collation.xml
+ */
+ var ALLOWED_CO_VALUES = [
+ 'big5han', 'dict', 'direct', 'ducet', 'gb2312', 'phonebk', 'phonetic',
+ 'pinyin', 'reformed', 'searchjl', 'stroke', 'trad', 'unihan', 'zhuyin'
+ ];
+
+ if (%_Call(ArrayIndexOf, ALLOWED_CO_VALUES, extensionMap.co) !== -1) {
+ extension = '-u-co-' + extensionMap.co;
+ // ICU can't tell us what the collation is, so save user's input.
+ collation = extensionMap.co;
+ }
+ } else if (internalOptions.usage === 'search') {
+ extension = '-u-co-search';
+ }
+ defineWEProperty(internalOptions, 'collation', collation);
+
+ var requestedLocale = locale.locale + extension;
+
+ // We define all properties C++ code may produce, to prevent security
+ // problems. If malicious user decides to redefine Object.prototype.locale
+ // we can't just use plain x.locale = 'us' or in C++ Set("locale", "us").
+ // ObjectDefineProperties will either succeed defining or throw an error.
+ var resolved = ObjectDefineProperties({}, {
+ caseFirst: {writable: true},
+ collation: {value: internalOptions.collation, writable: true},
+ ignorePunctuation: {writable: true},
+ locale: {writable: true},
+ numeric: {writable: true},
+ requestedLocale: {value: requestedLocale, writable: true},
+ sensitivity: {writable: true},
+ strength: {writable: true},
+ usage: {value: internalOptions.usage, writable: true}
+ });
+
+ var internalCollator = %CreateCollator(requestedLocale,
+ internalOptions,
+ resolved);
+
+ // Writable, configurable and enumerable are set to false by default.
+ %MarkAsInitializedIntlObjectOfType(collator, 'collator', internalCollator);
+ collator[resolvedSymbol] = resolved;
+ ObjectDefineProperty(collator, 'resolved', resolvedAccessor);
+
+ return collator;
+}
+
+
+/**
+ * Constructs Intl.Collator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'Collator', function() {
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.Collator(locales, options);
+ }
+
+ return initializeCollator(TO_OBJECT(this), locales, options);
+ },
+ DONT_ENUM
+);
+
+
+/**
+ * Collator resolvedOptions method.
+ */
+%AddNamedProperty(Intl.Collator.prototype, 'resolvedOptions', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (!%IsInitializedIntlObjectOfType(this, 'collator')) {
+ throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "Collator");
+ }
+
+ var coll = this;
+ var locale = getOptimalLanguageTag(coll[resolvedSymbol].requestedLocale,
+ coll[resolvedSymbol].locale);
+
+ return {
+ locale: locale,
+ usage: coll[resolvedSymbol].usage,
+ sensitivity: coll[resolvedSymbol].sensitivity,
+ ignorePunctuation: coll[resolvedSymbol].ignorePunctuation,
+ numeric: coll[resolvedSymbol].numeric,
+ caseFirst: coll[resolvedSymbol].caseFirst,
+ collation: coll[resolvedSymbol].collation
+ };
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.prototype.resolvedOptions, 'resolvedOptions');
+%FunctionRemovePrototype(Intl.Collator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.Collator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.Collator, 'supportedLocalesOf', function(locales) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ return supportedLocalesOf('collator', locales, %_Arguments(1));
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.Collator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.Collator.supportedLocalesOf);
+%SetNativeFlag(Intl.Collator.supportedLocalesOf);
+
+
+/**
+ * When the compare method is called with two arguments x and y, it returns a
+ * Number other than NaN that represents the result of a locale-sensitive
+ * String comparison of x with y.
+ * The result is intended to order String values in the sort order specified
+ * by the effective locale and collation options computed during construction
+ * of this Collator object, and will be negative, zero, or positive, depending
+ * on whether x comes before y in the sort order, the Strings are equal under
+ * the sort order, or x comes after y in the sort order, respectively.
+ */
+function compare(collator, x, y) {
+ return %InternalCompare(%GetImplFromInitializedIntlObject(collator),
+ GlobalString(x), GlobalString(y));
+};
+
+
+addBoundMethod(Intl.Collator, 'compare', compare, 2);
+
+/**
+ * Verifies that the input is a well-formed ISO 4217 currency code.
+ * Don't uppercase to test. It could convert invalid code into a valid one.
+ * For example \u00DFP (Eszett+P) becomes SSP.
+ */
+function isWellFormedCurrencyCode(currency) {
+ return typeof currency == "string" &&
+ currency.length == 3 &&
+ %_Call(StringMatch, currency, /[^A-Za-z]/) == null;
+}
+
+
+/**
+ * Returns the valid digit count for a property, or throws RangeError on
+ * a value out of the range.
+ */
+function getNumberOption(options, property, min, max, fallback) {
+ var value = options[property];
+ if (!IS_UNDEFINED(value)) {
+ value = GlobalNumber(value);
+ if (IsNaN(value) || value < min || value > max) {
+ throw MakeRangeError(kPropertyValueOutOfRange, property);
+ }
+ return MathFloor(value);
+ }
+
+ return fallback;
+}
+
+var patternAccessor = {
+ get() {
+ %IncrementUseCounter(kIntlPattern);
+ return this[patternSymbol];
+ },
+ set(value) {
+ this[patternSymbol] = value;
+ }
+};
+
+/**
+ * Initializes the given object so it's a valid NumberFormat instance.
+ * Useful for subclassing.
+ */
+function initializeNumberFormat(numberFormat, locales, options) {
+ if (%IsInitializedIntlObject(numberFormat)) {
+ throw MakeTypeError(kReinitializeIntl, "NumberFormat");
+ }
+
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'numberformat');
+
+ var locale = resolveLocale('numberformat', locales, options);
+
+ var internalOptions = {};
+ defineWEProperty(internalOptions, 'style', getOption(
+ 'style', 'string', ['decimal', 'percent', 'currency'], 'decimal'));
+
+ var currency = getOption('currency', 'string');
+ if (!IS_UNDEFINED(currency) && !isWellFormedCurrencyCode(currency)) {
+ throw MakeRangeError(kInvalidCurrencyCode, currency);
+ }
+
+ if (internalOptions.style === 'currency' && IS_UNDEFINED(currency)) {
+ throw MakeTypeError(kCurrencyCode);
+ }
+
+ var currencyDisplay = getOption(
+ 'currencyDisplay', 'string', ['code', 'symbol', 'name'], 'symbol');
+ if (internalOptions.style === 'currency') {
+ defineWEProperty(internalOptions, 'currency', %StringToUpperCase(currency));
+ defineWEProperty(internalOptions, 'currencyDisplay', currencyDisplay);
+ }
+
+ // Digit ranges.
+ var mnid = getNumberOption(options, 'minimumIntegerDigits', 1, 21, 1);
+ defineWEProperty(internalOptions, 'minimumIntegerDigits', mnid);
+
+ var mnfd = options['minimumFractionDigits'];
+ var mxfd = options['maximumFractionDigits'];
+ if (!IS_UNDEFINED(mnfd) || internalOptions.style !== 'currency') {
+ mnfd = getNumberOption(options, 'minimumFractionDigits', 0, 20, 0);
+ defineWEProperty(internalOptions, 'minimumFractionDigits', mnfd);
+ }
+
+ if (!IS_UNDEFINED(mxfd) || internalOptions.style !== 'currency') {
+ var min_mxfd = internalOptions.style === 'percent' ? 0 : 3;
+ mnfd = IS_UNDEFINED(mnfd) ? 0 : mnfd;
+ var fallback_limit = (mnfd > min_mxfd) ? mnfd : min_mxfd;
+ mxfd = getNumberOption(options, 'maximumFractionDigits', mnfd, 20, fallback_limit);
+ defineWEProperty(internalOptions, 'maximumFractionDigits', mxfd);
+ }
+
+ var mnsd = options['minimumSignificantDigits'];
+ var mxsd = options['maximumSignificantDigits'];
+ if (!IS_UNDEFINED(mnsd) || !IS_UNDEFINED(mxsd)) {
+ mnsd = getNumberOption(options, 'minimumSignificantDigits', 1, 21, 0);
+ defineWEProperty(internalOptions, 'minimumSignificantDigits', mnsd);
+
+ mxsd = getNumberOption(options, 'maximumSignificantDigits', mnsd, 21, 21);
+ defineWEProperty(internalOptions, 'maximumSignificantDigits', mxsd);
+ }
+
+ // Grouping.
+ defineWEProperty(internalOptions, 'useGrouping', getOption(
+ 'useGrouping', 'boolean', UNDEFINED, true));
+
+ // ICU prefers options to be passed using -u- extension key/values for
+ // number format, so we need to build that.
+ var extensionMap = parseExtension(locale.extension);
+
+ /**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a number format.
+ */
+ var NUMBER_FORMAT_KEY_MAP = {
+ 'nu': {'property': UNDEFINED, 'type': 'string'}
+ };
+
+ var extension = setOptions(options, extensionMap, NUMBER_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = ObjectDefineProperties({}, {
+ currency: {writable: true},
+ currencyDisplay: {writable: true},
+ locale: {writable: true},
+ maximumFractionDigits: {writable: true},
+ minimumFractionDigits: {writable: true},
+ minimumIntegerDigits: {writable: true},
+ numberingSystem: {writable: true},
+ pattern: patternAccessor,
+ requestedLocale: {value: requestedLocale, writable: true},
+ style: {value: internalOptions.style, writable: true},
+ useGrouping: {writable: true}
+ });
+ if (%HasOwnProperty(internalOptions, 'minimumSignificantDigits')) {
+ defineWEProperty(resolved, 'minimumSignificantDigits', UNDEFINED);
+ }
+ if (%HasOwnProperty(internalOptions, 'maximumSignificantDigits')) {
+ defineWEProperty(resolved, 'maximumSignificantDigits', UNDEFINED);
+ }
+ var formatter = %CreateNumberFormat(requestedLocale,
+ internalOptions,
+ resolved);
+
+ if (internalOptions.style === 'currency') {
+ ObjectDefineProperty(resolved, 'currencyDisplay', {value: currencyDisplay,
+ writable: true});
+ }
+
+ %MarkAsInitializedIntlObjectOfType(numberFormat, 'numberformat', formatter);
+ numberFormat[resolvedSymbol] = resolved;
+ ObjectDefineProperty(numberFormat, 'resolved', resolvedAccessor);
+
+ return numberFormat;
+}
+
+
+/**
+ * Constructs Intl.NumberFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'NumberFormat', function() {
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.NumberFormat(locales, options);
+ }
+
+ return initializeNumberFormat(TO_OBJECT(this), locales, options);
+ },
+ DONT_ENUM
+);
+
+
+/**
+ * NumberFormat resolvedOptions method.
+ */
+%AddNamedProperty(Intl.NumberFormat.prototype, 'resolvedOptions', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (!%IsInitializedIntlObjectOfType(this, 'numberformat')) {
+ throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "NumberFormat");
+ }
+
+ var format = this;
+ var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
+ format[resolvedSymbol].locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format[resolvedSymbol].numberingSystem,
+ style: format[resolvedSymbol].style,
+ useGrouping: format[resolvedSymbol].useGrouping,
+ minimumIntegerDigits: format[resolvedSymbol].minimumIntegerDigits,
+ minimumFractionDigits: format[resolvedSymbol].minimumFractionDigits,
+ maximumFractionDigits: format[resolvedSymbol].maximumFractionDigits,
+ };
+
+ if (result.style === 'currency') {
+ defineWECProperty(result, 'currency', format[resolvedSymbol].currency);
+ defineWECProperty(result, 'currencyDisplay',
+ format[resolvedSymbol].currencyDisplay);
+ }
+
+ if (%HasOwnProperty(format[resolvedSymbol], 'minimumSignificantDigits')) {
+ defineWECProperty(result, 'minimumSignificantDigits',
+ format[resolvedSymbol].minimumSignificantDigits);
+ }
+
+ if (%HasOwnProperty(format[resolvedSymbol], 'maximumSignificantDigits')) {
+ defineWECProperty(result, 'maximumSignificantDigits',
+ format[resolvedSymbol].maximumSignificantDigits);
+ }
+
+ return result;
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.NumberFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.NumberFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.NumberFormat, 'supportedLocalesOf', function(locales) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ return supportedLocalesOf('numberformat', locales, %_Arguments(1));
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.NumberFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.NumberFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.NumberFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(value)
+ * according to the effective locale and the formatting options of this
+ * NumberFormat.
+ */
+function formatNumber(formatter, value) {
+ // Spec treats -0 and +0 as 0.
+ var number = TO_NUMBER(value) + 0;
+
+ return %InternalNumberFormat(%GetImplFromInitializedIntlObject(formatter),
+ number);
+}
+
+
+/**
+ * Returns a Number that represents string value that was passed in.
+ */
+function parseNumber(formatter, value) {
+ return %InternalNumberParse(%GetImplFromInitializedIntlObject(formatter),
+ GlobalString(value));
+}
+
+
+addBoundMethod(Intl.NumberFormat, 'format', formatNumber, 1);
+addBoundMethod(Intl.NumberFormat, 'v8Parse', parseNumber, 1);
+
+/**
+ * Returns a string that matches LDML representation of the options object.
+ */
+function toLDMLString(options) {
+ var getOption = getGetOption(options, 'dateformat');
+
+ var ldmlString = '';
+
+ var option = getOption('weekday', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'EEEEE', short: 'EEE', long: 'EEEE'});
+
+ option = getOption('era', 'string', ['narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(
+ option, {narrow: 'GGGGG', short: 'GGG', long: 'GGGG'});
+
+ option = getOption('year', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'yy', 'numeric': 'y'});
+
+ option = getOption('month', 'string',
+ ['2-digit', 'numeric', 'narrow', 'short', 'long']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'MM', 'numeric': 'M',
+ 'narrow': 'MMMMM', 'short': 'MMM', 'long': 'MMMM'});
+
+ option = getOption('day', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(
+ option, {'2-digit': 'dd', 'numeric': 'd'});
+
+ var hr12 = getOption('hour12', 'boolean');
+ option = getOption('hour', 'string', ['2-digit', 'numeric']);
+ if (IS_UNDEFINED(hr12)) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'jj', 'numeric': 'j'});
+ } else if (hr12 === true) {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'hh', 'numeric': 'h'});
+ } else {
+ ldmlString += appendToLDMLString(option, {'2-digit': 'HH', 'numeric': 'H'});
+ }
+
+ option = getOption('minute', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'mm', 'numeric': 'm'});
+
+ option = getOption('second', 'string', ['2-digit', 'numeric']);
+ ldmlString += appendToLDMLString(option, {'2-digit': 'ss', 'numeric': 's'});
+
+ option = getOption('timeZoneName', 'string', ['short', 'long']);
+ ldmlString += appendToLDMLString(option, {short: 'z', long: 'zzzz'});
+
+ return ldmlString;
+}
+
+
+/**
+ * Returns either LDML equivalent of the current option or empty string.
+ */
+function appendToLDMLString(option, pairs) {
+ if (!IS_UNDEFINED(option)) {
+ return pairs[option];
+ } else {
+ return '';
+ }
+}
+
+
+/**
+ * Returns object that matches LDML representation of the date.
+ */
+function fromLDMLString(ldmlString) {
+ // First remove '' quoted text, so we lose 'Uhr' strings.
+ ldmlString = %_Call(StringReplace, ldmlString, GetQuotedStringRE(), '');
+
+ var options = {};
+ var match = %_Call(StringMatch, ldmlString, /E{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'weekday', match, {EEEEE: 'narrow', EEE: 'short', EEEE: 'long'});
+
+ match = %_Call(StringMatch, ldmlString, /G{3,5}/g);
+ options = appendToDateTimeObject(
+ options, 'era', match, {GGGGG: 'narrow', GGG: 'short', GGGG: 'long'});
+
+ match = %_Call(StringMatch, ldmlString, /y{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'year', match, {y: 'numeric', yy: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /M{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {MM: '2-digit',
+ M: 'numeric', MMMMM: 'narrow', MMM: 'short', MMMM: 'long'});
+
+ // Sometimes we get L instead of M for month - standalone name.
+ match = %_Call(StringMatch, ldmlString, /L{1,5}/g);
+ options = appendToDateTimeObject(options, 'month', match, {LL: '2-digit',
+ L: 'numeric', LLLLL: 'narrow', LLL: 'short', LLLL: 'long'});
+
+ match = %_Call(StringMatch, ldmlString, /d{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'day', match, {d: 'numeric', dd: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /h{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = true;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {h: 'numeric', hh: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /H{1,2}/g);
+ if (match !== null) {
+ options['hour12'] = false;
+ }
+ options = appendToDateTimeObject(
+ options, 'hour', match, {H: 'numeric', HH: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /m{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'minute', match, {m: 'numeric', mm: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /s{1,2}/g);
+ options = appendToDateTimeObject(
+ options, 'second', match, {s: 'numeric', ss: '2-digit'});
+
+ match = %_Call(StringMatch, ldmlString, /z|zzzz/g);
+ options = appendToDateTimeObject(
+ options, 'timeZoneName', match, {z: 'short', zzzz: 'long'});
+
+ return options;
+}
+
+
+function appendToDateTimeObject(options, option, match, pairs) {
+ if (IS_NULL(match)) {
+ if (!%HasOwnProperty(options, option)) {
+ defineWEProperty(options, option, UNDEFINED);
+ }
+ return options;
+ }
+
+ var property = match[0];
+ defineWEProperty(options, option, pairs[property]);
+
+ return options;
+}
+
+
+/**
+ * Returns options with at least default values in it.
+ */
+function toDateTimeOptions(options, required, defaults) {
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ } else {
+ options = TO_OBJECT(options);
+ }
+
+ var needsDefault = true;
+ if ((required === 'date' || required === 'any') &&
+ (!IS_UNDEFINED(options.weekday) || !IS_UNDEFINED(options.year) ||
+ !IS_UNDEFINED(options.month) || !IS_UNDEFINED(options.day))) {
+ needsDefault = false;
+ }
+
+ if ((required === 'time' || required === 'any') &&
+ (!IS_UNDEFINED(options.hour) || !IS_UNDEFINED(options.minute) ||
+ !IS_UNDEFINED(options.second))) {
+ needsDefault = false;
+ }
+
+ if (needsDefault && (defaults === 'date' || defaults === 'all')) {
+ ObjectDefineProperty(options, 'year', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ ObjectDefineProperty(options, 'month', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ ObjectDefineProperty(options, 'day', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ if (needsDefault && (defaults === 'time' || defaults === 'all')) {
+ ObjectDefineProperty(options, 'hour', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ ObjectDefineProperty(options, 'minute', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ ObjectDefineProperty(options, 'second', {value: 'numeric',
+ writable: true,
+ enumerable: true,
+ configurable: true});
+ }
+
+ return options;
+}
+
+
+/**
+ * Initializes the given object so it's a valid DateTimeFormat instance.
+ * Useful for subclassing.
+ */
+function initializeDateTimeFormat(dateFormat, locales, options) {
+
+ if (%IsInitializedIntlObject(dateFormat)) {
+ throw MakeTypeError(kReinitializeIntl, "DateTimeFormat");
+ }
+
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ }
+
+ var locale = resolveLocale('dateformat', locales, options);
+
+ options = toDateTimeOptions(options, 'any', 'date');
+
+ var getOption = getGetOption(options, 'dateformat');
+
+ // We implement only best fit algorithm, but still need to check
+ // if the formatMatcher values are in range.
+ var matcher = getOption('formatMatcher', 'string',
+ ['basic', 'best fit'], 'best fit');
+
+ // Build LDML string for the skeleton that we pass to the formatter.
+ var ldmlString = toLDMLString(options);
+
+ // Filter out supported extension keys so we know what to put in resolved
+ // section later on.
+ // We need to pass calendar and number system to the method.
+ var tz = canonicalizeTimeZoneID(options.timeZone);
+
+ // ICU prefers options to be passed using -u- extension key/values, so
+ // we need to build that.
+ var internalOptions = {};
+ var extensionMap = parseExtension(locale.extension);
+
+ /**
+ * Map of Unicode extensions to option properties, and their values and types,
+ * for a date/time format.
+ */
+ var DATETIME_FORMAT_KEY_MAP = {
+ 'ca': {'property': UNDEFINED, 'type': 'string'},
+ 'nu': {'property': UNDEFINED, 'type': 'string'}
+ };
+
+ var extension = setOptions(options, extensionMap, DATETIME_FORMAT_KEY_MAP,
+ getOption, internalOptions);
+
+ var requestedLocale = locale.locale + extension;
+ var resolved = ObjectDefineProperties({}, {
+ calendar: {writable: true},
+ day: {writable: true},
+ era: {writable: true},
+ hour12: {writable: true},
+ hour: {writable: true},
+ locale: {writable: true},
+ minute: {writable: true},
+ month: {writable: true},
+ numberingSystem: {writable: true},
+ [patternSymbol]: {writable: true},
+ pattern: patternAccessor,
+ requestedLocale: {value: requestedLocale, writable: true},
+ second: {writable: true},
+ timeZone: {writable: true},
+ timeZoneName: {writable: true},
+ tz: {value: tz, writable: true},
+ weekday: {writable: true},
+ year: {writable: true}
+ });
+
+ var formatter = %CreateDateTimeFormat(
+ requestedLocale, {skeleton: ldmlString, timeZone: tz}, resolved);
+
+ if (resolved.timeZone === "Etc/Unknown") {
+ throw MakeRangeError(kUnsupportedTimeZone, tz);
+ }
+
+ %MarkAsInitializedIntlObjectOfType(dateFormat, 'dateformat', formatter);
+ dateFormat[resolvedSymbol] = resolved;
+ ObjectDefineProperty(dateFormat, 'resolved', resolvedAccessor);
+
+ return dateFormat;
+}
+
+
+/**
+ * Constructs Intl.DateTimeFormat object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'DateTimeFormat', function() {
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.DateTimeFormat(locales, options);
+ }
+
+ return initializeDateTimeFormat(TO_OBJECT(this), locales, options);
+ },
+ DONT_ENUM
+);
+
+
+/**
+ * DateTimeFormat resolvedOptions method.
+ */
+%AddNamedProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (!%IsInitializedIntlObjectOfType(this, 'dateformat')) {
+ throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "DateTimeFormat");
+ }
+
+ /**
+ * Maps ICU calendar names into LDML type.
+ */
+ var ICU_CALENDAR_MAP = {
+ 'gregorian': 'gregory',
+ 'japanese': 'japanese',
+ 'buddhist': 'buddhist',
+ 'roc': 'roc',
+ 'persian': 'persian',
+ 'islamic-civil': 'islamicc',
+ 'islamic': 'islamic',
+ 'hebrew': 'hebrew',
+ 'chinese': 'chinese',
+ 'indian': 'indian',
+ 'coptic': 'coptic',
+ 'ethiopic': 'ethiopic',
+ 'ethiopic-amete-alem': 'ethioaa'
+ };
+
+ var format = this;
+ var fromPattern = fromLDMLString(format[resolvedSymbol][patternSymbol]);
+ var userCalendar = ICU_CALENDAR_MAP[format[resolvedSymbol].calendar];
+ if (IS_UNDEFINED(userCalendar)) {
+ // Use ICU name if we don't have a match. It shouldn't happen, but
+ // it would be too strict to throw for this.
+ userCalendar = format[resolvedSymbol].calendar;
+ }
+
+ var locale = getOptimalLanguageTag(format[resolvedSymbol].requestedLocale,
+ format[resolvedSymbol].locale);
+
+ var result = {
+ locale: locale,
+ numberingSystem: format[resolvedSymbol].numberingSystem,
+ calendar: userCalendar,
+ timeZone: format[resolvedSymbol].timeZone
+ };
+
+ addWECPropertyIfDefined(result, 'timeZoneName', fromPattern.timeZoneName);
+ addWECPropertyIfDefined(result, 'era', fromPattern.era);
+ addWECPropertyIfDefined(result, 'year', fromPattern.year);
+ addWECPropertyIfDefined(result, 'month', fromPattern.month);
+ addWECPropertyIfDefined(result, 'day', fromPattern.day);
+ addWECPropertyIfDefined(result, 'weekday', fromPattern.weekday);
+ addWECPropertyIfDefined(result, 'hour12', fromPattern.hour12);
+ addWECPropertyIfDefined(result, 'hour', fromPattern.hour);
+ addWECPropertyIfDefined(result, 'minute', fromPattern.minute);
+ addWECPropertyIfDefined(result, 'second', fromPattern.second);
+
+ return result;
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.DateTimeFormat.prototype.resolvedOptions);
+%SetNativeFlag(Intl.DateTimeFormat.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.DateTimeFormat, 'supportedLocalesOf', function(locales) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ return supportedLocalesOf('dateformat', locales, %_Arguments(1));
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.DateTimeFormat.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.DateTimeFormat.supportedLocalesOf);
+%SetNativeFlag(Intl.DateTimeFormat.supportedLocalesOf);
+
+
+/**
+ * Returns a String value representing the result of calling ToNumber(date)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ */
+function formatDate(formatter, dateValue) {
+ var dateMs;
+ if (IS_UNDEFINED(dateValue)) {
+ dateMs = %DateCurrentTime();
+ } else {
+ dateMs = TO_NUMBER(dateValue);
+ }
+
+ if (!IsFinite(dateMs)) throw MakeRangeError(kDateRange);
+
+ return %InternalDateFormat(%GetImplFromInitializedIntlObject(formatter),
+ new GlobalDate(dateMs));
+}
+
+
+/**
+ * Returns a Date object representing the result of calling ToString(value)
+ * according to the effective locale and the formatting options of this
+ * DateTimeFormat.
+ * Returns undefined if date string cannot be parsed.
+ */
+function parseDate(formatter, value) {
+ return %InternalDateParse(%GetImplFromInitializedIntlObject(formatter),
+ GlobalString(value));
+}
+
+
+// 0 because date is optional argument.
+addBoundMethod(Intl.DateTimeFormat, 'format', formatDate, 0);
+addBoundMethod(Intl.DateTimeFormat, 'v8Parse', parseDate, 1);
+
+
+/**
+ * Returns canonical Area/Location(/Location) name, or throws an exception
+ * if the zone name is invalid IANA name.
+ */
+function canonicalizeTimeZoneID(tzID) {
+ // Skip undefined zones.
+ if (IS_UNDEFINED(tzID)) {
+ return tzID;
+ }
+
+ // Special case handling (UTC, GMT).
+ var upperID = %StringToUpperCase(tzID);
+ if (upperID === 'UTC' || upperID === 'GMT' ||
+ upperID === 'ETC/UTC' || upperID === 'ETC/GMT') {
+ return 'UTC';
+ }
+
+ // TODO(jshin): Add support for Etc/GMT[+-]([1-9]|1[0-2])
+
+ // We expect only _, '-' and / beside ASCII letters.
+ // All inputs should conform to Area/Location(/Location)* from now on.
+ var match = %_Call(StringMatch, tzID, GetTimezoneNameCheckRE());
+ if (IS_NULL(match)) throw MakeRangeError(kExpectedTimezoneID, tzID);
+
+ var result = toTitleCaseTimezoneLocation(match[1]) + '/' +
+ toTitleCaseTimezoneLocation(match[2]);
+
+ if (!IS_UNDEFINED(match[3]) && 3 < match.length) {
+ var locations = %_Call(StringSplit, match[3], '/');
+ // The 1st element is empty. Starts with i=1.
+ for (var i = 1; i < locations.length; i++) {
+ result = result + '/' + toTitleCaseTimezoneLocation(locations[i]);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * Initializes the given object so it's a valid BreakIterator instance.
+ * Useful for subclassing.
+ */
+function initializeBreakIterator(iterator, locales, options) {
+ if (%IsInitializedIntlObject(iterator)) {
+ throw MakeTypeError(kReinitializeIntl, "v8BreakIterator");
+ }
+
+ if (IS_UNDEFINED(options)) {
+ options = {};
+ }
+
+ var getOption = getGetOption(options, 'breakiterator');
+
+ var internalOptions = {};
+
+ defineWEProperty(internalOptions, 'type', getOption(
+ 'type', 'string', ['character', 'word', 'sentence', 'line'], 'word'));
+
+ var locale = resolveLocale('breakiterator', locales, options);
+ var resolved = ObjectDefineProperties({}, {
+ requestedLocale: {value: locale.locale, writable: true},
+ type: {value: internalOptions.type, writable: true},
+ locale: {writable: true}
+ });
+
+ var internalIterator = %CreateBreakIterator(locale.locale,
+ internalOptions,
+ resolved);
+
+ %MarkAsInitializedIntlObjectOfType(iterator, 'breakiterator',
+ internalIterator);
+ iterator[resolvedSymbol] = resolved;
+ ObjectDefineProperty(iterator, 'resolved', resolvedAccessor);
+
+ return iterator;
+}
+
+
+/**
+ * Constructs Intl.v8BreakIterator object given optional locales and options
+ * parameters.
+ *
+ * @constructor
+ */
+%AddNamedProperty(Intl, 'v8BreakIterator', function() {
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+
+ if (!this || this === Intl) {
+ // Constructor is called as a function.
+ return new Intl.v8BreakIterator(locales, options);
+ }
+
+ return initializeBreakIterator(TO_OBJECT(this), locales, options);
+ },
+ DONT_ENUM
+);
+
+
+/**
+ * BreakIterator resolvedOptions method.
+ */
+%AddNamedProperty(Intl.v8BreakIterator.prototype, 'resolvedOptions',
+ function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (!%IsInitializedIntlObjectOfType(this, 'breakiterator')) {
+ throw MakeTypeError(kResolvedOptionsCalledOnNonObject, "v8BreakIterator");
+ }
+
+ var segmenter = this;
+ var locale =
+ getOptimalLanguageTag(segmenter[resolvedSymbol].requestedLocale,
+ segmenter[resolvedSymbol].locale);
+
+ return {
+ locale: locale,
+ type: segmenter[resolvedSymbol].type
+ };
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.prototype.resolvedOptions,
+ 'resolvedOptions');
+%FunctionRemovePrototype(Intl.v8BreakIterator.prototype.resolvedOptions);
+%SetNativeFlag(Intl.v8BreakIterator.prototype.resolvedOptions);
+
+
+/**
+ * Returns the subset of the given locale list for which this locale list
+ * has a matching (possibly fallback) locale. Locales appear in the same
+ * order in the returned list as in the input list.
+ * Options are optional parameter.
+ */
+%AddNamedProperty(Intl.v8BreakIterator, 'supportedLocalesOf',
+ function(locales) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ return supportedLocalesOf('breakiterator', locales, %_Arguments(1));
+ },
+ DONT_ENUM
+);
+%FunctionSetName(Intl.v8BreakIterator.supportedLocalesOf, 'supportedLocalesOf');
+%FunctionRemovePrototype(Intl.v8BreakIterator.supportedLocalesOf);
+%SetNativeFlag(Intl.v8BreakIterator.supportedLocalesOf);
+
+
+/**
+ * Adopts text to segment using the iterator. Old text, if present,
+ * gets discarded.
+ */
+function adoptText(iterator, text) {
+ %BreakIteratorAdoptText(%GetImplFromInitializedIntlObject(iterator),
+ GlobalString(text));
+}
+
+
+/**
+ * Returns index of the first break in the string and moves current pointer.
+ */
+function first(iterator) {
+ return %BreakIteratorFirst(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns the index of the next break and moves the pointer.
+ */
+function next(iterator) {
+ return %BreakIteratorNext(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns index of the current break.
+ */
+function current(iterator) {
+ return %BreakIteratorCurrent(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+/**
+ * Returns type of the current break.
+ */
+function breakType(iterator) {
+ return %BreakIteratorBreakType(%GetImplFromInitializedIntlObject(iterator));
+}
+
+
+addBoundMethod(Intl.v8BreakIterator, 'adoptText', adoptText, 1);
+addBoundMethod(Intl.v8BreakIterator, 'first', first, 0);
+addBoundMethod(Intl.v8BreakIterator, 'next', next, 0);
+addBoundMethod(Intl.v8BreakIterator, 'current', current, 0);
+addBoundMethod(Intl.v8BreakIterator, 'breakType', breakType, 0);
+
+// Save references to Intl objects and methods we use, for added security.
+var savedObjects = {
+ 'collator': Intl.Collator,
+ 'numberformat': Intl.NumberFormat,
+ 'dateformatall': Intl.DateTimeFormat,
+ 'dateformatdate': Intl.DateTimeFormat,
+ 'dateformattime': Intl.DateTimeFormat
+};
+
+
+// Default (created with undefined locales and options parameters) collator,
+// number and date format instances. They'll be created as needed.
+var defaultObjects = {
+ 'collator': UNDEFINED,
+ 'numberformat': UNDEFINED,
+ 'dateformatall': UNDEFINED,
+ 'dateformatdate': UNDEFINED,
+ 'dateformattime': UNDEFINED,
+};
+
+
+/**
+ * Returns cached or newly created instance of a given service.
+ * We cache only default instances (where no locales or options are provided).
+ */
+function cachedOrNewService(service, locales, options, defaults) {
+ var useOptions = (IS_UNDEFINED(defaults)) ? options : defaults;
+ if (IS_UNDEFINED(locales) && IS_UNDEFINED(options)) {
+ if (IS_UNDEFINED(defaultObjects[service])) {
+ defaultObjects[service] = new savedObjects[service](locales, useOptions);
+ }
+ return defaultObjects[service];
+ }
+ return new savedObjects[service](locales, useOptions);
+}
+
+
+function OverrideFunction(object, name, f) {
+ %CheckIsBootstrapping();
+ ObjectDefineProperty(object, name, { value: f,
+ writeable: true,
+ configurable: true,
+ enumerable: false });
+ %FunctionSetName(f, name);
+ %FunctionRemovePrototype(f);
+ %SetNativeFlag(f);
+}
+
+/**
+ * Compares this and that, and returns less than 0, 0 or greater than 0 value.
+ * Overrides the built-in method.
+ */
+OverrideFunction(GlobalString.prototype, 'localeCompare', function(that) {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (IS_NULL_OR_UNDEFINED(this)) {
+ throw MakeTypeError(kMethodInvokedOnNullOrUndefined);
+ }
+
+ var locales = %_Arguments(1);
+ var options = %_Arguments(2);
+ var collator = cachedOrNewService('collator', locales, options);
+ return compare(collator, this, that);
+ }
+);
+
+
+/**
+ * Unicode normalization. This method is called with one argument that
+ * specifies the normalization form.
+ * If none is specified, "NFC" is assumed.
+ * If the form is not one of "NFC", "NFD", "NFKC", or "NFKD", then throw
+ * a RangeError Exception.
+ */
+
+OverrideFunction(GlobalString.prototype, 'normalize', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
+ var s = TO_STRING(this);
+
+ var formArg = %_Arguments(0);
+ var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
+
+ var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
+
+ var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
+ if (normalizationForm === -1) {
+ throw MakeRangeError(kNormalizationForm,
+ %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
+ }
+
+ return %StringNormalize(s, normalizationForm);
+ }
+);
+
+
+/**
+ * Formats a Number object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used.
+ */
+OverrideFunction(GlobalNumber.prototype, 'toLocaleString', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ if (!(this instanceof GlobalNumber) && typeof(this) !== 'number') {
+ throw MakeTypeError(kMethodInvokedOnWrongType, "Number");
+ }
+
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+ var numberFormat = cachedOrNewService('numberformat', locales, options);
+ return formatNumber(numberFormat, this);
+ }
+);
+
+
+/**
+ * Returns actual formatted date or fails if date parameter is invalid.
+ */
+function toLocaleDateTime(date, locales, options, required, defaults, service) {
+ if (!(date instanceof GlobalDate)) {
+ throw MakeTypeError(kMethodInvokedOnWrongType, "Date");
+ }
+
+ if (IsNaN(date)) return 'Invalid Date';
+
+ var internalOptions = toDateTimeOptions(options, required, defaults);
+
+ var dateFormat =
+ cachedOrNewService(service, locales, options, internalOptions);
+
+ return formatDate(dateFormat, date);
+}
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - both date and time are
+ * present in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleString', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+ return toLocaleDateTime(
+ this, locales, options, 'any', 'all', 'dateformatall');
+ }
+);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only date is present
+ * in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleDateString', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+ return toLocaleDateTime(
+ this, locales, options, 'date', 'date', 'dateformatdate');
+ }
+);
+
+
+/**
+ * Formats a Date object (this) using locale and options values.
+ * If locale or options are omitted, defaults are used - only time is present
+ * in the output.
+ */
+OverrideFunction(GlobalDate.prototype, 'toLocaleTimeString', function() {
+ if (!IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kOrdinaryFunctionCalledAsConstructor);
+ }
+
+ var locales = %_Arguments(0);
+ var options = %_Arguments(1);
+ return toLocaleDateTime(
+ this, locales, options, 'time', 'time', 'dateformattime');
+ }
+);
+
+})
diff --git a/src/js/iterator-prototype.js b/src/js/iterator-prototype.js
new file mode 100644
index 0000000..6f25019
--- /dev/null
+++ b/src/js/iterator-prototype.js
@@ -0,0 +1,21 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+ "use strict";
+ %CheckIsBootstrapping();
+
+ var GlobalObject = global.Object;
+ var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+ var iteratorSymbol = utils.ImportNow("iterator_symbol");
+
+ // 25.1.2.1 %IteratorPrototype% [ @@iterator ] ( )
+ function IteratorPrototypeIterator() {
+ return this;
+ }
+
+ utils.SetFunctionName(IteratorPrototypeIterator, iteratorSymbol);
+ %AddNamedProperty(IteratorPrototype, iteratorSymbol,
+ IteratorPrototypeIterator, DONT_ENUM);
+})
diff --git a/src/js/json.js b/src/js/json.js
new file mode 100644
index 0000000..b8836ea
--- /dev/null
+++ b/src/js/json.js
@@ -0,0 +1,280 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalDate = global.Date;
+var GlobalJSON = global.JSON;
+var GlobalSet = global.Set;
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var ObjectHasOwnProperty;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+ MaxSimple = from.MaxSimple;
+ MinSimple = from.MinSimple;
+ ObjectHasOwnProperty = from.ObjectHasOwnProperty;
+});
+
+// -------------------------------------------------------------------
+
+function CreateDataProperty(o, p, v) {
+ var desc = {value: v, enumerable: true, writable: true, configurable: true};
+ return %reflect_define_property(o, p, desc);
+}
+
+
+function InternalizeJSONProperty(holder, name, reviver) {
+ var val = holder[name];
+ if (IS_RECEIVER(val)) {
+ if (%is_arraylike(val)) {
+ var length = TO_LENGTH(val.length);
+ for (var i = 0; i < length; i++) {
+ var newElement =
+ InternalizeJSONProperty(val, %_NumberToString(i), reviver);
+ if (IS_UNDEFINED(newElement)) {
+ %reflect_delete_property(val, i);
+ } else {
+ CreateDataProperty(val, i, newElement);
+ }
+ }
+ } else {
+ for (var p of %object_keys(val)) {
+ var newElement = InternalizeJSONProperty(val, p, reviver);
+ if (IS_UNDEFINED(newElement)) {
+ %reflect_delete_property(val, p);
+ } else {
+ CreateDataProperty(val, p, newElement);
+ }
+ }
+ }
+ }
+ return %_Call(reviver, holder, name, val);
+}
+
+
+function JSONParse(text, reviver) {
+ var unfiltered = %ParseJson(text);
+ if (IS_CALLABLE(reviver)) {
+ return InternalizeJSONProperty({'': unfiltered}, '', reviver);
+ } else {
+ return unfiltered;
+ }
+}
+
+
+function SerializeArray(value, replacer, stack, indent, gap) {
+ if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+ var stepback = indent;
+ indent += gap;
+ var partial = new InternalArray();
+ var len = TO_LENGTH(value.length);
+ for (var i = 0; i < len; i++) {
+ var strP = JSONSerialize(%_NumberToString(i), value, replacer, stack,
+ indent, gap);
+ if (IS_UNDEFINED(strP)) {
+ strP = "null";
+ }
+ partial.push(strP);
+ }
+ var final;
+ if (gap == "") {
+ final = "[" + partial.join(",") + "]";
+ } else if (partial.length > 0) {
+ var separator = ",\n" + indent;
+ final = "[\n" + indent + partial.join(separator) + "\n" +
+ stepback + "]";
+ } else {
+ final = "[]";
+ }
+ stack.pop();
+ return final;
+}
+
+
+function SerializeObject(value, replacer, stack, indent, gap) {
+ if (!%PushIfAbsent(stack, value)) throw MakeTypeError(kCircularStructure);
+ var stepback = indent;
+ indent += gap;
+ var partial = new InternalArray();
+ if (IS_ARRAY(replacer)) {
+ var length = replacer.length;
+ for (var i = 0; i < length; i++) {
+ var p = replacer[i];
+ var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+ if (!IS_UNDEFINED(strP)) {
+ var member = %QuoteJSONString(p) + ":";
+ if (gap != "") member += " ";
+ member += strP;
+ partial.push(member);
+ }
+ }
+ } else {
+ for (var p of %object_keys(value)) {
+ var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
+ if (!IS_UNDEFINED(strP)) {
+ var member = %QuoteJSONString(p) + ":";
+ if (gap != "") member += " ";
+ member += strP;
+ partial.push(member);
+ }
+ }
+ }
+ var final;
+ if (gap == "") {
+ final = "{" + partial.join(",") + "}";
+ } else if (partial.length > 0) {
+ var separator = ",\n" + indent;
+ final = "{\n" + indent + partial.join(separator) + "\n" +
+ stepback + "}";
+ } else {
+ final = "{}";
+ }
+ stack.pop();
+ return final;
+}
+
+
+function JSONSerialize(key, holder, replacer, stack, indent, gap) {
+ var value = holder[key];
+ if (IS_RECEIVER(value)) {
+ var toJSON = value.toJSON;
+ if (IS_CALLABLE(toJSON)) {
+ value = %_Call(toJSON, value, key);
+ }
+ }
+ if (IS_CALLABLE(replacer)) {
+ value = %_Call(replacer, holder, key, value);
+ }
+ if (IS_STRING(value)) {
+ return %QuoteJSONString(value);
+ } else if (IS_NUMBER(value)) {
+ return JSON_NUMBER_TO_STRING(value);
+ } else if (IS_BOOLEAN(value)) {
+ return value ? "true" : "false";
+ } else if (IS_NULL(value)) {
+ return "null";
+ } else if (IS_RECEIVER(value) && !IS_CALLABLE(value)) {
+ // Non-callable object. If it's a primitive wrapper, it must be unwrapped.
+ if (%is_arraylike(value)) {
+ return SerializeArray(value, replacer, stack, indent, gap);
+ } else if (IS_NUMBER_WRAPPER(value)) {
+ value = TO_NUMBER(value);
+ return JSON_NUMBER_TO_STRING(value);
+ } else if (IS_STRING_WRAPPER(value)) {
+ return %QuoteJSONString(TO_STRING(value));
+ } else if (IS_BOOLEAN_WRAPPER(value)) {
+ return %_ValueOf(value) ? "true" : "false";
+ } else {
+ return SerializeObject(value, replacer, stack, indent, gap);
+ }
+ }
+ // Undefined or a callable object.
+ return UNDEFINED;
+}
+
+
+function JSONStringify(value, replacer, space) {
+ if (%_ArgumentsLength() == 1 && !IS_PROXY(value)) {
+ return %BasicJSONStringify(value);
+ }
+ if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) {
+ var property_list = new InternalArray();
+ var seen_properties = new GlobalSet();
+ var length = TO_LENGTH(replacer.length);
+ for (var i = 0; i < length; i++) {
+ var v = replacer[i];
+ var item;
+ if (IS_STRING(v)) {
+ item = v;
+ } else if (IS_NUMBER(v)) {
+ item = %_NumberToString(v);
+ } else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
+ item = TO_STRING(v);
+ } else {
+ continue;
+ }
+ if (!seen_properties.has(item)) {
+ property_list.push(item);
+ seen_properties.add(item);
+ }
+ }
+ replacer = property_list;
+ }
+ if (IS_OBJECT(space)) {
+ // Unwrap 'space' if it is wrapped
+ if (IS_NUMBER_WRAPPER(space)) {
+ space = TO_NUMBER(space);
+ } else if (IS_STRING_WRAPPER(space)) {
+ space = TO_STRING(space);
+ }
+ }
+ var gap;
+ if (IS_NUMBER(space)) {
+ space = MaxSimple(0, MinSimple(TO_INTEGER(space), 10));
+ gap = %_SubString(" ", 0, space);
+ } else if (IS_STRING(space)) {
+ if (space.length > 10) {
+ gap = %_SubString(space, 0, 10);
+ } else {
+ gap = space;
+ }
+ } else {
+ gap = "";
+ }
+ return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap);
+}
+
+// -------------------------------------------------------------------
+
+%AddNamedProperty(GlobalJSON, toStringTagSymbol, "JSON", READ_ONLY | DONT_ENUM);
+
+// Set up non-enumerable properties of the JSON object.
+utils.InstallFunctions(GlobalJSON, DONT_ENUM, [
+ "parse", JSONParse,
+ "stringify", JSONStringify
+]);
+
+// -------------------------------------------------------------------
+// Date.toJSON
+
+// 20.3.4.37 Date.prototype.toJSON ( key )
+function DateToJSON(key) {
+ var o = TO_OBJECT(this);
+ var tv = TO_PRIMITIVE_NUMBER(o);
+ if (IS_NUMBER(tv) && !NUMBER_IS_FINITE(tv)) {
+ return null;
+ }
+ return o.toISOString();
+}
+
+// Set up non-enumerable functions of the Date prototype object.
+utils.InstallFunctions(GlobalDate.prototype, DONT_ENUM, [
+ "toJSON", DateToJSON
+]);
+
+// -------------------------------------------------------------------
+// JSON Builtins
+
+function JsonSerializeAdapter(key, object) {
+ var holder = {};
+ holder[key] = object;
+ // No need to pass the actual holder since there is no replacer function.
+ return JSONSerialize(key, holder, UNDEFINED, new InternalArray(), "", "");
+}
+
+%InstallToContext(["json_serialize_adapter", JsonSerializeAdapter]);
+
+})
diff --git a/src/js/macros.py b/src/js/macros.py
new file mode 100644
index 0000000..3bcc8c1
--- /dev/null
+++ b/src/js/macros.py
@@ -0,0 +1,269 @@
+# Copyright 2006-2009 the V8 project authors. All rights reserved.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Dictionary that is passed as defines for js2c.py.
+# Used for defines that must be defined for all native JS files.
+
+define NONE = 0;
+define READ_ONLY = 1;
+define DONT_ENUM = 2;
+define DONT_DELETE = 4;
+define NEW_ONE_BYTE_STRING = true;
+define NEW_TWO_BYTE_STRING = false;
+
+# Constants used for getter and setter operations.
+define GETTER = 0;
+define SETTER = 1;
+
+# Safe maximum number of arguments to push to stack, when multiplied by
+# pointer size. Used by Function.prototype.apply(), Reflect.apply() and
+# Reflect.construct().
+define kSafeArgumentsLength = 0x800000;
+
+# 2^53 - 1
+define kMaxSafeInteger = 9007199254740991;
+
+# 2^32 - 1
+define kMaxUint32 = 4294967295;
+
+# Strict mode flags for passing to %SetProperty
+define kSloppyMode = 0;
+define kStrictMode = 1;
+
+# Native cache ids.
+define STRING_TO_REGEXP_CACHE_ID = 0;
+
+# Type query macros.
+#
+# Note: We have special support for typeof(foo) === 'bar' in the compiler.
+# It will *not* generate a runtime typeof call for the most important
+# values of 'bar'.
+macro IS_ARRAY(arg) = (%_IsArray(arg));
+macro IS_ARRAYBUFFER(arg) = (%_ClassOf(arg) === 'ArrayBuffer');
+macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
+macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean');
+macro IS_DATAVIEW(arg) = (%_ClassOf(arg) === 'DataView');
+macro IS_DATE(arg) = (%_IsDate(arg));
+macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error');
+macro IS_FUNCTION(arg) = (%_IsFunction(arg));
+macro IS_GENERATOR(arg) = (%_ClassOf(arg) === 'Generator');
+macro IS_GLOBAL(arg) = (%_ClassOf(arg) === 'global');
+macro IS_MAP(arg) = (%_ClassOf(arg) === 'Map');
+macro IS_MAP_ITERATOR(arg) = (%_ClassOf(arg) === 'Map Iterator');
+macro IS_NULL(arg) = (arg === null);
+macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
+macro IS_NUMBER(arg) = (typeof(arg) === 'number');
+macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number');
+macro IS_OBJECT(arg) = (typeof(arg) === 'object');
+macro IS_PROXY(arg) = (%_IsJSProxy(arg));
+macro IS_REGEXP(arg) = (%_IsRegExp(arg));
+macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script');
+macro IS_SET(arg) = (%_ClassOf(arg) === 'Set');
+macro IS_SET_ITERATOR(arg) = (%_ClassOf(arg) === 'Set Iterator');
+macro IS_SHAREDARRAYBUFFER(arg) = (%_ClassOf(arg) === 'SharedArrayBuffer');
+macro IS_SIMD_VALUE(arg) = (%_IsSimdValue(arg));
+macro IS_STRING(arg) = (typeof(arg) === 'string');
+macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String');
+macro IS_STRONG(arg) = (%IsStrong(arg));
+macro IS_SYMBOL(arg) = (typeof(arg) === 'symbol');
+macro IS_SYMBOL_WRAPPER(arg) = (%_ClassOf(arg) === 'Symbol');
+macro IS_UNDEFINED(arg) = (arg === (void 0));
+macro IS_WEAKMAP(arg) = (%_ClassOf(arg) === 'WeakMap');
+macro IS_WEAKSET(arg) = (%_ClassOf(arg) === 'WeakSet');
+
+# Macro for ES queries of the type: "Type(O) is Object."
+macro IS_RECEIVER(arg) = (%_IsJSReceiver(arg));
+
+# Macro for ES queries of the type: "IsCallable(O)"
+macro IS_CALLABLE(arg) = (typeof(arg) === 'function');
+
+# Macro for ES6 CheckObjectCoercible
+# Will throw a TypeError of the form "[functionName] called on null or undefined".
+macro CHECK_OBJECT_COERCIBLE(arg, functionName) = if (IS_NULL(%IS_VAR(arg)) || IS_UNDEFINED(arg)) throw MakeTypeError(kCalledOnNullOrUndefined, functionName);
+
+# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
+macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
+macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0)));
+macro TO_BOOLEAN(arg) = (!!(arg));
+macro TO_INTEGER(arg) = (%_ToInteger(arg));
+macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(arg));
+macro TO_INT32(arg) = ((arg) | 0);
+macro TO_UINT32(arg) = ((arg) >>> 0);
+macro TO_LENGTH(arg) = (%_ToLength(arg));
+macro TO_LENGTH_OR_UINT32(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_UINT32(arg));
+macro TO_LENGTH_OR_INTEGER(arg) = (FLAG_harmony_tolength ? TO_LENGTH(arg) : TO_INTEGER(arg));
+macro TO_STRING(arg) = (%_ToString(arg));
+macro TO_NUMBER(arg) = (%_ToNumber(arg));
+macro TO_OBJECT(arg) = (%_ToObject(arg));
+macro TO_PRIMITIVE(arg) = (%_ToPrimitive(arg));
+macro TO_PRIMITIVE_NUMBER(arg) = (%_ToPrimitive_Number(arg));
+macro TO_PRIMITIVE_STRING(arg) = (%_ToPrimitive_String(arg));
+macro TO_NAME(arg) = (%_ToName(arg));
+macro JSON_NUMBER_TO_STRING(arg) = ((%_IsSmi(%IS_VAR(arg)) || arg - arg == 0) ? %_NumberToString(arg) : "null");
+macro HAS_OWN_PROPERTY(arg, index) = (%_Call(ObjectHasOwnProperty, arg, index));
+macro HAS_INDEX(array, index, is_array) = ((is_array && %_HasFastPackedElements(%IS_VAR(array))) ? (index < array.length) : (index in array));
+
+# Private names.
+macro IS_PRIVATE(sym) = (%SymbolIsPrivate(sym));
+macro HAS_PRIVATE(obj, sym) = (%HasOwnProperty(obj, sym));
+macro HAS_DEFINED_PRIVATE(obj, sym) = (!IS_UNDEFINED(obj[sym]));
+macro GET_PRIVATE(obj, sym) = (obj[sym]);
+macro SET_PRIVATE(obj, sym, val) = (obj[sym] = val);
+
+# Constants. The compiler constant folds them.
+define INFINITY = (1/0);
+define UNDEFINED = (void 0);
+
+# Macros implemented in Python.
+python macro CHAR_CODE(str) = ord(str[1]);
+
+# Constants used on an array to implement the properties of the RegExp object.
+define REGEXP_NUMBER_OF_CAPTURES = 0;
+define REGEXP_FIRST_CAPTURE = 3;
+
+# Macros for internal slot access.
+macro REGEXP_GLOBAL(regexp) = (%_RegExpFlags(regexp) & 1);
+macro REGEXP_IGNORE_CASE(regexp) = (%_RegExpFlags(regexp) & 2);
+macro REGEXP_MULTILINE(regexp) = (%_RegExpFlags(regexp) & 4);
+macro REGEXP_STICKY(regexp) = (%_RegExpFlags(regexp) & 8);
+macro REGEXP_UNICODE(regexp) = (%_RegExpFlags(regexp) & 16);
+macro REGEXP_SOURCE(regexp) = (%_RegExpSource(regexp));
+
+# We can't put macros in macros so we use constants here.
+# REGEXP_NUMBER_OF_CAPTURES
+macro NUMBER_OF_CAPTURES(array) = ((array)[0]);
+
+# Last input and last subject of regexp matches.
+define LAST_SUBJECT_INDEX = 1;
+macro LAST_SUBJECT(array) = ((array)[1]);
+macro LAST_INPUT(array) = ((array)[2]);
+
+# REGEXP_FIRST_CAPTURE
+macro CAPTURE(index) = (3 + (index));
+define CAPTURE0 = 3;
+define CAPTURE1 = 4;
+
+# For the regexp capture override array. This has the same
+# format as the arguments to a function called from
+# String.prototype.replace.
+macro OVERRIDE_MATCH(override) = ((override)[0]);
+macro OVERRIDE_POS(override) = ((override)[(override).length - 2]);
+macro OVERRIDE_SUBJECT(override) = ((override)[(override).length - 1]);
+# 1-based so index of 1 returns the first capture
+macro OVERRIDE_CAPTURE(override, index) = ((override)[(index)]);
+
+# PropertyDescriptor return value indices - must match
+# PropertyDescriptorIndices in runtime-object.cc.
+define IS_ACCESSOR_INDEX = 0;
+define VALUE_INDEX = 1;
+define GETTER_INDEX = 2;
+define SETTER_INDEX = 3;
+define WRITABLE_INDEX = 4;
+define ENUMERABLE_INDEX = 5;
+define CONFIGURABLE_INDEX = 6;
+
+# For messages.js
+# Matches Script::Type from objects.h
+define TYPE_NATIVE = 0;
+define TYPE_EXTENSION = 1;
+define TYPE_NORMAL = 2;
+
+# Matches Script::CompilationType from objects.h
+define COMPILATION_TYPE_HOST = 0;
+define COMPILATION_TYPE_EVAL = 1;
+define COMPILATION_TYPE_JSON = 2;
+
+# Matches Messages::kNoLineNumberInfo from v8.h
+define kNoLineNumberInfo = 0;
+
+# Must match PropertyFilter in property-details.h
+define PROPERTY_FILTER_NONE = 0;
+define PROPERTY_FILTER_ONLY_ENUMERABLE = 2;
+define PROPERTY_FILTER_SKIP_STRINGS = 8;
+define PROPERTY_FILTER_SKIP_SYMBOLS = 16;
+
+# Use for keys, values and entries iterators.
+define ITERATOR_KIND_KEYS = 1;
+define ITERATOR_KIND_VALUES = 2;
+define ITERATOR_KIND_ENTRIES = 3;
+
+macro FIXED_ARRAY_GET(array, index) = (%_FixedArrayGet(array, (index) | 0));
+macro FIXED_ARRAY_SET(array, index, value) = (%_FixedArraySet(array, (index) | 0, value));
+# TODO(adamk): Find a more robust way to force Smi representation.
+macro FIXED_ARRAY_SET_SMI(array, index, value) = (FIXED_ARRAY_SET(array, index, (value) | 0));
+
+macro ORDERED_HASH_TABLE_BUCKET_COUNT(table) = (FIXED_ARRAY_GET(table, 0));
+macro ORDERED_HASH_TABLE_ELEMENT_COUNT(table) = (FIXED_ARRAY_GET(table, 1));
+macro ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 1, count));
+macro ORDERED_HASH_TABLE_DELETED_COUNT(table) = (FIXED_ARRAY_GET(table, 2));
+macro ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, count) = (FIXED_ARRAY_SET_SMI(table, 2, count));
+macro ORDERED_HASH_TABLE_BUCKET_AT(table, bucket) = (FIXED_ARRAY_GET(table, 3 + (bucket)));
+macro ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry) = (FIXED_ARRAY_SET(table, 3 + (bucket), entry));
+
+macro ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets) = (hash & ((numBuckets) - 1));
+
+macro ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) << 1));
+macro ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets)));
+macro ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets) + 1));
+
+macro ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) = (3 + (numBuckets) + ((entry) * 3));
+macro ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets)));
+macro ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 1));
+macro ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets) = (FIXED_ARRAY_GET(table, ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets) + 2));
+
+# Must match OrderedHashTable::kNotFound.
+define NOT_FOUND = -1;
+
+# Check whether debug is active.
+define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
+macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (%_DebugIsActive() != 0) %DebugPrepareStepInIfStepping(function);
+
+# SharedFlag equivalents
+define kNotShared = false;
+define kShared = true;
+
+# UseCounters from include/v8.h
+define kUseAsm = 0;
+define kBreakIterator = 1;
+define kLegacyConst = 2;
+define kMarkDequeOverflow = 3;
+define kStoreBufferOverflow = 4;
+define kSlotsBufferOverflow = 5;
+define kObjectObserve = 6;
+define kForcedGC = 7;
+define kSloppyMode = 8;
+define kStrictMode = 9;
+define kStrongMode = 10;
+define kRegExpPrototypeStickyGetter = 11;
+define kRegExpPrototypeToString = 12;
+define kRegExpPrototypeUnicodeGetter = 13;
+define kIntlV8Parse = 14;
+define kIntlPattern = 15;
+define kIntlResolved = 16;
+define kPromiseChain = 17;
+define kPromiseAccept = 18;
+define kPromiseDefer = 19;
diff --git a/src/js/math.js b/src/js/math.js
new file mode 100644
index 0000000..990a7e9
--- /dev/null
+++ b/src/js/math.js
@@ -0,0 +1,356 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+define kRandomBatchSize = 64;
+// The first two slots are reserved to persist PRNG state.
+define kRandomNumberStart = 2;
+
+var GlobalFloat64Array = global.Float64Array;
+var GlobalMath = global.Math;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var NaN = %GetRootNaN();
+var nextRandomIndex = kRandomBatchSize;
+var randomNumbers = UNDEFINED;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+//-------------------------------------------------------------------
+
+// ECMA 262 - 15.8.2.1
+function MathAbs(x) {
+ x = +x;
+ return (x > 0) ? x : 0 - x;
+}
+
+// ECMA 262 - 15.8.2.2
+function MathAcosJS(x) {
+ return %_MathAcos(+x);
+}
+
+// ECMA 262 - 15.8.2.3
+function MathAsinJS(x) {
+ return %_MathAsin(+x);
+}
+
+// ECMA 262 - 15.8.2.4
+function MathAtanJS(x) {
+ return %_MathAtan(+x);
+}
+
+// ECMA 262 - 15.8.2.5
+// The naming of y and x matches the spec, as does the order in which
+// ToNumber (valueOf) is called.
+function MathAtan2JS(y, x) {
+ y = +y;
+ x = +x;
+ return %_MathAtan2(y, x);
+}
+
+// ECMA 262 - 15.8.2.6
+function MathCeil(x) {
+ return -%_MathFloor(-x);
+}
+
+// ECMA 262 - 15.8.2.8
+function MathExp(x) {
+ return %MathExpRT(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.9
+function MathFloorJS(x) {
+ return %_MathFloor(+x);
+}
+
+// ECMA 262 - 15.8.2.10
+function MathLog(x) {
+ return %_MathLogRT(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.11
+function MathMax(arg1, arg2) { // length == 2
+ var length = %_ArgumentsLength();
+ if (length == 2) {
+ arg1 = TO_NUMBER(arg1);
+ arg2 = TO_NUMBER(arg2);
+ if (arg2 > arg1) return arg2;
+ if (arg1 > arg2) return arg1;
+ if (arg1 == arg2) {
+ // Make sure -0 is considered less than +0.
+ return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg2 : arg1;
+ }
+ // All comparisons failed, one of the arguments must be NaN.
+ return NaN;
+ }
+ var r = -INFINITY;
+ for (var i = 0; i < length; i++) {
+ var n = %_Arguments(i);
+ n = TO_NUMBER(n);
+ // Make sure +0 is considered greater than -0.
+ if (NUMBER_IS_NAN(n) || n > r || (r === 0 && n === 0 && %_IsMinusZero(r))) {
+ r = n;
+ }
+ }
+ return r;
+}
+
+// ECMA 262 - 15.8.2.12
+function MathMin(arg1, arg2) { // length == 2
+ var length = %_ArgumentsLength();
+ if (length == 2) {
+ arg1 = TO_NUMBER(arg1);
+ arg2 = TO_NUMBER(arg2);
+ if (arg2 > arg1) return arg1;
+ if (arg1 > arg2) return arg2;
+ if (arg1 == arg2) {
+ // Make sure -0 is considered less than +0.
+ return (arg1 === 0 && %_IsMinusZero(arg1)) ? arg1 : arg2;
+ }
+ // All comparisons failed, one of the arguments must be NaN.
+ return NaN;
+ }
+ var r = INFINITY;
+ for (var i = 0; i < length; i++) {
+ var n = %_Arguments(i);
+ n = TO_NUMBER(n);
+ // Make sure -0 is considered less than +0.
+ if (NUMBER_IS_NAN(n) || n < r || (r === 0 && n === 0 && %_IsMinusZero(n))) {
+ r = n;
+ }
+ }
+ return r;
+}
+
+// ECMA 262 - 15.8.2.13
+function MathPowJS(x, y) {
+ return %_MathPow(TO_NUMBER(x), TO_NUMBER(y));
+}
+
+// ECMA 262 - 15.8.2.14
+function MathRandom() {
+ if (nextRandomIndex >= kRandomBatchSize) {
+ randomNumbers = %GenerateRandomNumbers(randomNumbers);
+ nextRandomIndex = kRandomNumberStart;
+ }
+ return randomNumbers[nextRandomIndex++];
+}
+
+function MathRandomRaw() {
+ if (nextRandomIndex >= kRandomBatchSize) {
+ randomNumbers = %GenerateRandomNumbers(randomNumbers);
+ nextRandomIndex = kRandomNumberStart;
+ }
+ return %_DoubleLo(randomNumbers[nextRandomIndex++]) & 0x3FFFFFFF;
+}
+
+// ECMA 262 - 15.8.2.15
+function MathRound(x) {
+ return %RoundNumber(TO_NUMBER(x));
+}
+
+// ECMA 262 - 15.8.2.17
+function MathSqrtJS(x) {
+ return %_MathSqrt(+x);
+}
+
+// Non-standard extension.
+function MathImul(x, y) {
+ return %NumberImul(TO_NUMBER(x), TO_NUMBER(y));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.28.
+function MathSign(x) {
+ x = +x;
+ if (x > 0) return 1;
+ if (x < 0) return -1;
+ // -0, 0 or NaN.
+ return x;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.34.
+function MathTrunc(x) {
+ x = +x;
+ if (x > 0) return %_MathFloor(x);
+ if (x < 0) return -%_MathFloor(-x);
+ // -0, 0 or NaN.
+ return x;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.5.
+function MathAsinh(x) {
+ x = TO_NUMBER(x);
+ // Idempotent for NaN, +/-0 and +/-Infinity.
+ if (x === 0 || !NUMBER_IS_FINITE(x)) return x;
+ if (x > 0) return MathLog(x + %_MathSqrt(x * x + 1));
+ // This is to prevent numerical errors caused by large negative x.
+ return -MathLog(-x + %_MathSqrt(x * x + 1));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.3.
+function MathAcosh(x) {
+ x = TO_NUMBER(x);
+ if (x < 1) return NaN;
+ // Idempotent for NaN and +Infinity.
+ if (!NUMBER_IS_FINITE(x)) return x;
+ return MathLog(x + %_MathSqrt(x + 1) * %_MathSqrt(x - 1));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.7.
+function MathAtanh(x) {
+ x = TO_NUMBER(x);
+ // Idempotent for +/-0.
+ if (x === 0) return x;
+ // Returns NaN for NaN and +/- Infinity.
+ if (!NUMBER_IS_FINITE(x)) return NaN;
+ return 0.5 * MathLog((1 + x) / (1 - x));
+}
+
+// ES6 draft 09-27-13, section 20.2.2.17.
+function MathHypot(x, y) { // Function length is 2.
+ // We may want to introduce fast paths for two arguments and when
+ // normalization to avoid overflow is not necessary. For now, we
+ // simply assume the general case.
+ var length = %_ArgumentsLength();
+ var args = new InternalArray(length);
+ var max = 0;
+ for (var i = 0; i < length; i++) {
+ var n = %_Arguments(i);
+ n = TO_NUMBER(n);
+ if (n === INFINITY || n === -INFINITY) return INFINITY;
+ n = MathAbs(n);
+ if (n > max) max = n;
+ args[i] = n;
+ }
+
+ // Kahan summation to avoid rounding errors.
+ // Normalize the numbers to the largest one to avoid overflow.
+ if (max === 0) max = 1;
+ var sum = 0;
+ var compensation = 0;
+ for (var i = 0; i < length; i++) {
+ var n = args[i] / max;
+ var summand = n * n - compensation;
+ var preliminary = sum + summand;
+ compensation = (preliminary - sum) - summand;
+ sum = preliminary;
+ }
+ return %_MathSqrt(sum) * max;
+}
+
+// ES6 draft 09-27-13, section 20.2.2.16.
+function MathFroundJS(x) {
+ return %MathFround(TO_NUMBER(x));
+}
+
+// ES6 draft 07-18-14, section 20.2.2.11
+function MathClz32JS(x) {
+ return %_MathClz32(x >>> 0);
+}
+
+// ES6 draft 09-27-13, section 20.2.2.9.
+// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
+// Using initial approximation adapted from Kahan's cbrt and 4 iterations
+// of Newton's method.
+function MathCbrt(x) {
+ x = TO_NUMBER(x);
+ if (x == 0 || !NUMBER_IS_FINITE(x)) return x;
+ return x >= 0 ? CubeRoot(x) : -CubeRoot(-x);
+}
+
+macro NEWTON_ITERATION_CBRT(x, approx)
+ (1.0 / 3.0) * (x / (approx * approx) + 2 * approx);
+endmacro
+
+function CubeRoot(x) {
+ var approx_hi = MathFloorJS(%_DoubleHi(x) / 3) + 0x2A9F7893;
+ var approx = %_ConstructDouble(approx_hi | 0, 0);
+ approx = NEWTON_ITERATION_CBRT(x, approx);
+ approx = NEWTON_ITERATION_CBRT(x, approx);
+ approx = NEWTON_ITERATION_CBRT(x, approx);
+ return NEWTON_ITERATION_CBRT(x, approx);
+}
+
+// -------------------------------------------------------------------
+
+%AddNamedProperty(GlobalMath, toStringTagSymbol, "Math", READ_ONLY | DONT_ENUM);
+
+// Set up math constants.
+utils.InstallConstants(GlobalMath, [
+ // ECMA-262, section 15.8.1.1.
+ "E", 2.7182818284590452354,
+ // ECMA-262, section 15.8.1.2.
+ "LN10", 2.302585092994046,
+ // ECMA-262, section 15.8.1.3.
+ "LN2", 0.6931471805599453,
+ // ECMA-262, section 15.8.1.4.
+ "LOG2E", 1.4426950408889634,
+ "LOG10E", 0.4342944819032518,
+ "PI", 3.1415926535897932,
+ "SQRT1_2", 0.7071067811865476,
+ "SQRT2", 1.4142135623730951
+]);
+
+// Set up non-enumerable functions of the Math object and
+// set their names.
+utils.InstallFunctions(GlobalMath, DONT_ENUM, [
+ "random", MathRandom,
+ "abs", MathAbs,
+ "acos", MathAcosJS,
+ "asin", MathAsinJS,
+ "atan", MathAtanJS,
+ "ceil", MathCeil,
+ "exp", MathExp,
+ "floor", MathFloorJS,
+ "log", MathLog,
+ "round", MathRound,
+ "sqrt", MathSqrtJS,
+ "atan2", MathAtan2JS,
+ "pow", MathPowJS,
+ "max", MathMax,
+ "min", MathMin,
+ "imul", MathImul,
+ "sign", MathSign,
+ "trunc", MathTrunc,
+ "asinh", MathAsinh,
+ "acosh", MathAcosh,
+ "atanh", MathAtanh,
+ "hypot", MathHypot,
+ "fround", MathFroundJS,
+ "clz32", MathClz32JS,
+ "cbrt", MathCbrt
+]);
+
+%SetForceInlineFlag(MathAbs);
+%SetForceInlineFlag(MathAcosJS);
+%SetForceInlineFlag(MathAsinJS);
+%SetForceInlineFlag(MathAtanJS);
+%SetForceInlineFlag(MathAtan2JS);
+%SetForceInlineFlag(MathCeil);
+%SetForceInlineFlag(MathClz32JS);
+%SetForceInlineFlag(MathFloorJS);
+%SetForceInlineFlag(MathRandom);
+%SetForceInlineFlag(MathSign);
+%SetForceInlineFlag(MathSqrtJS);
+%SetForceInlineFlag(MathTrunc);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.MathAbs = MathAbs;
+ to.MathExp = MathExp;
+ to.MathFloor = MathFloorJS;
+ to.IntRandom = MathRandomRaw;
+ to.MathMax = MathMax;
+ to.MathMin = MathMin;
+});
+
+})
diff --git a/src/js/messages.js b/src/js/messages.js
new file mode 100644
index 0000000..feb14d3
--- /dev/null
+++ b/src/js/messages.js
@@ -0,0 +1,1014 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// -------------------------------------------------------------------
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayJoin;
+var Bool16x8ToString;
+var Bool32x4ToString;
+var Bool8x16ToString;
+var callSiteReceiverSymbol =
+ utils.ImportNow("call_site_receiver_symbol");
+var callSiteFunctionSymbol =
+ utils.ImportNow("call_site_function_symbol");
+var callSitePositionSymbol =
+ utils.ImportNow("call_site_position_symbol");
+var callSiteStrictSymbol =
+ utils.ImportNow("call_site_strict_symbol");
+var FLAG_harmony_tostring;
+var Float32x4ToString;
+var formattedStackTraceSymbol =
+ utils.ImportNow("formatted_stack_trace_symbol");
+var GlobalObject = global.Object;
+var Int16x8ToString;
+var Int32x4ToString;
+var Int8x16ToString;
+var InternalArray = utils.InternalArray;
+var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
+var ObjectDefineProperty;
+var ObjectToString = utils.ImportNow("object_to_string");
+var Script = utils.ImportNow("Script");
+var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
+var StringCharAt;
+var StringIndexOf;
+var StringSubstring;
+var SymbolToString;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var Uint16x8ToString;
+var Uint32x4ToString;
+var Uint8x16ToString;
+
+utils.Import(function(from) {
+ ArrayJoin = from.ArrayJoin;
+ Bool16x8ToString = from.Bool16x8ToString;
+ Bool32x4ToString = from.Bool32x4ToString;
+ Bool8x16ToString = from.Bool8x16ToString;
+ Float32x4ToString = from.Float32x4ToString;
+ Int16x8ToString = from.Int16x8ToString;
+ Int32x4ToString = from.Int32x4ToString;
+ Int8x16ToString = from.Int8x16ToString;
+ ObjectDefineProperty = from.ObjectDefineProperty;
+ StringCharAt = from.StringCharAt;
+ StringIndexOf = from.StringIndexOf;
+ StringSubstring = from.StringSubstring;
+ SymbolToString = from.SymbolToString;
+ Uint16x8ToString = from.Uint16x8ToString;
+ Uint32x4ToString = from.Uint32x4ToString;
+ Uint8x16ToString = from.Uint8x16ToString;
+});
+
+utils.ImportFromExperimental(function(from) {
+ FLAG_harmony_tostring = from.FLAG_harmony_tostring;
+});
+
+// -------------------------------------------------------------------
+
+var GlobalError;
+var GlobalTypeError;
+var GlobalRangeError;
+var GlobalURIError;
+var GlobalSyntaxError;
+var GlobalReferenceError;
+var GlobalEvalError;
+
+
+function NoSideEffectsObjectToString() {
+ if (IS_UNDEFINED(this)) return "[object Undefined]";
+ if (IS_NULL(this)) return "[object Null]";
+ var O = TO_OBJECT(this);
+ var builtinTag = %_ClassOf(O);
+ var tag;
+ if (FLAG_harmony_tostring) {
+ tag = %GetDataProperty(O, toStringTagSymbol);
+ if (!IS_STRING(tag)) {
+ tag = builtinTag;
+ }
+ } else {
+ tag = builtinTag;
+ }
+ return `[object ${tag}]`;
+}
+
+function IsErrorObject(obj) {
+ return HAS_PRIVATE(obj, stackTraceSymbol);
+}
+
+function NoSideEffectsErrorToString() {
+ var name = %GetDataProperty(this, "name");
+ var message = %GetDataProperty(this, "message");
+ name = IS_UNDEFINED(name) ? "Error" : NoSideEffectsToString(name);
+ message = IS_UNDEFINED(message) ? "" : NoSideEffectsToString(message);
+ if (name == "") return message;
+ if (message == "") return name;
+ return `${name}: ${message}`;
+}
+
+function NoSideEffectsToString(obj) {
+ if (IS_STRING(obj)) return obj;
+ if (IS_NUMBER(obj)) return %_NumberToString(obj);
+ if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
+ if (IS_UNDEFINED(obj)) return 'undefined';
+ if (IS_NULL(obj)) return 'null';
+ if (IS_FUNCTION(obj)) {
+ var str = %FunctionToString(obj);
+ if (str.length > 128) {
+ str = %_SubString(str, 0, 111) + "...<omitted>..." +
+ %_SubString(str, str.length - 2, str.length);
+ }
+ return str;
+ }
+ if (IS_SYMBOL(obj)) return %_Call(SymbolToString, obj);
+ if (IS_SIMD_VALUE(obj)) {
+ switch (typeof(obj)) {
+ case 'float32x4': return %_Call(Float32x4ToString, obj);
+ case 'int32x4': return %_Call(Int32x4ToString, obj);
+ case 'int16x8': return %_Call(Int16x8ToString, obj);
+ case 'int8x16': return %_Call(Int8x16ToString, obj);
+ case 'uint32x4': return %_Call(Uint32x4ToString, obj);
+ case 'uint16x8': return %_Call(Uint16x8ToString, obj);
+ case 'uint8x16': return %_Call(Uint8x16ToString, obj);
+ case 'bool32x4': return %_Call(Bool32x4ToString, obj);
+ case 'bool16x8': return %_Call(Bool16x8ToString, obj);
+ case 'bool8x16': return %_Call(Bool8x16ToString, obj);
+ }
+ }
+
+ if (IS_RECEIVER(obj)) {
+ // When internally formatting error objects, use a side-effects-free version
+ // of Error.prototype.toString independent of the actually installed
+ // toString method.
+ if (IsErrorObject(obj) ||
+ %GetDataProperty(obj, "toString") === ErrorToString) {
+ return %_Call(NoSideEffectsErrorToString, obj);
+ }
+
+ if (%GetDataProperty(obj, "toString") === ObjectToString) {
+ var constructor = %GetDataProperty(obj, "constructor");
+ if (IS_FUNCTION(constructor)) {
+ var constructor_name = %FunctionGetName(constructor);
+ if (constructor_name != "") return `#<${constructor_name}>`;
+ }
+ }
+ }
+
+ return %_Call(NoSideEffectsObjectToString, obj);
+}
+
+
+function MakeGenericError(constructor, type, arg0, arg1, arg2) {
+ var error = new constructor(FormatMessage(type, arg0, arg1, arg2));
+ error[internalErrorSymbol] = true;
+ return error;
+}
+
+
+/**
+ * Set up the Script function and constructor.
+ */
+%FunctionSetInstanceClassName(Script, 'Script');
+%AddNamedProperty(Script.prototype, 'constructor', Script,
+ DONT_ENUM | DONT_DELETE | READ_ONLY);
+%SetCode(Script, function(x) {
+ // Script objects can only be created by the VM.
+ throw MakeError(kUnsupported);
+});
+
+
+// Helper functions; called from the runtime system.
+function FormatMessage(type, arg0, arg1, arg2) {
+ var arg0 = NoSideEffectsToString(arg0);
+ var arg1 = NoSideEffectsToString(arg1);
+ var arg2 = NoSideEffectsToString(arg2);
+ try {
+ return %FormatMessageString(type, arg0, arg1, arg2);
+ } catch (e) {
+ return "<error>";
+ }
+}
+
+
+function GetLineNumber(message) {
+ var start_position = %MessageGetStartPosition(message);
+ if (start_position == -1) return kNoLineNumberInfo;
+ var script = %MessageGetScript(message);
+ var location = script.locationFromPosition(start_position, true);
+ if (location == null) return kNoLineNumberInfo;
+ return location.line + 1;
+}
+
+
+//Returns the offset of the given position within the containing line.
+function GetColumnNumber(message) {
+ var script = %MessageGetScript(message);
+ var start_position = %MessageGetStartPosition(message);
+ var location = script.locationFromPosition(start_position, true);
+ if (location == null) return -1;
+ return location.column;
+}
+
+
+// Returns the source code line containing the given source
+// position, or the empty string if the position is invalid.
+function GetSourceLine(message) {
+ var script = %MessageGetScript(message);
+ var start_position = %MessageGetStartPosition(message);
+ var location = script.locationFromPosition(start_position, true);
+ if (location == null) return "";
+ return location.sourceText();
+}
+
+
+/**
+ * Find a line number given a specific source position.
+ * @param {number} position The source position.
+ * @return {number} 0 if input too small, -1 if input too large,
+ else the line number.
+ */
+function ScriptLineFromPosition(position) {
+ var lower = 0;
+ var upper = this.lineCount() - 1;
+ var line_ends = this.line_ends;
+
+ // We'll never find invalid positions so bail right away.
+ if (position > line_ends[upper]) {
+ return -1;
+ }
+
+ // This means we don't have to safe-guard indexing line_ends[i - 1].
+ if (position <= line_ends[0]) {
+ return 0;
+ }
+
+ // Binary search to find line # from position range.
+ while (upper >= 1) {
+ var i = (lower + upper) >> 1;
+
+ if (position > line_ends[i]) {
+ lower = i + 1;
+ } else if (position <= line_ends[i - 1]) {
+ upper = i - 1;
+ } else {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Get information on a specific source position.
+ * @param {number} position The source position
+ * @param {boolean} include_resource_offset Set to true to have the resource
+ * offset added to the location
+ * @return {SourceLocation}
+ * If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromPosition(position,
+ include_resource_offset) {
+ var line = this.lineFromPosition(position);
+ if (line == -1) return null;
+
+ // Determine start, end and column.
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
+ if (end > 0 && %_Call(StringCharAt, this.source, end - 1) == '\r') {
+ end--;
+ }
+ var column = position - start;
+
+ // Adjust according to the offset within the resource.
+ if (include_resource_offset) {
+ line += this.line_offset;
+ if (line == this.line_offset) {
+ column += this.column_offset;
+ }
+ }
+
+ return new SourceLocation(this, position, line, column, start, end);
+}
+
+
+/**
+ * Get information on a specific source line and column possibly offset by a
+ * fixed source position. This function is used to find a source position from
+ * a line and column position. The fixed source position offset is typically
+ * used to find a source position in a function based on a line and column in
+ * the source for the function alone. The offset passed will then be the
+ * start position of the source for the function within the full script source.
+ * @param {number} opt_line The line within the source. Default value is 0
+ * @param {number} opt_column The column in within the line. Default value is 0
+ * @param {number} opt_offset_position The offset from the begining of the
+ * source from where the line and column calculation starts.
+ * Default value is 0
+ * @return {SourceLocation}
+ * If line is negative or not in the source null is returned.
+ */
+function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
+ // Default is the first line in the script. Lines in the script is relative
+ // to the offset within the resource.
+ var line = 0;
+ if (!IS_UNDEFINED(opt_line)) {
+ line = opt_line - this.line_offset;
+ }
+
+ // Default is first column. If on the first line add the offset within the
+ // resource.
+ var column = opt_column || 0;
+ if (line == 0) {
+ column -= this.column_offset;
+ }
+
+ var offset_position = opt_offset_position || 0;
+ if (line < 0 || column < 0 || offset_position < 0) return null;
+ if (line == 0) {
+ return this.locationFromPosition(offset_position + column, false);
+ } else {
+ // Find the line where the offset position is located.
+ var offset_line = this.lineFromPosition(offset_position);
+
+ if (offset_line == -1 || offset_line + line >= this.lineCount()) {
+ return null;
+ }
+
+ return this.locationFromPosition(
+ this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
+ }
+}
+
+
+/**
+ * Get a slice of source code from the script. The boundaries for the slice is
+ * specified in lines.
+ * @param {number} opt_from_line The first line (zero bound) in the slice.
+ * Default is 0
+ * @param {number} opt_to_column The last line (zero bound) in the slice (non
+ * inclusive). Default is the number of lines in the script
+ * @return {SourceSlice} The source slice or null of the parameters where
+ * invalid
+ */
+function ScriptSourceSlice(opt_from_line, opt_to_line) {
+ var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
+ : opt_from_line;
+ var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
+ : opt_to_line;
+
+ // Adjust according to the offset within the resource.
+ from_line -= this.line_offset;
+ to_line -= this.line_offset;
+ if (from_line < 0) from_line = 0;
+ if (to_line > this.lineCount()) to_line = this.lineCount();
+
+ // Check parameters.
+ if (from_line >= this.lineCount() ||
+ to_line < 0 ||
+ from_line > to_line) {
+ return null;
+ }
+
+ var line_ends = this.line_ends;
+ var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
+ var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
+
+ // Return a source slice with line numbers re-adjusted to the resource.
+ return new SourceSlice(this,
+ from_line + this.line_offset,
+ to_line + this.line_offset,
+ from_position, to_position);
+}
+
+
+function ScriptSourceLine(opt_line) {
+ // Default is the first line in the script. Lines in the script are relative
+ // to the offset within the resource.
+ var line = 0;
+ if (!IS_UNDEFINED(opt_line)) {
+ line = opt_line - this.line_offset;
+ }
+
+ // Check parameter.
+ if (line < 0 || this.lineCount() <= line) {
+ return null;
+ }
+
+ // Return the source line.
+ var line_ends = this.line_ends;
+ var start = line == 0 ? 0 : line_ends[line - 1] + 1;
+ var end = line_ends[line];
+ return %_Call(StringSubstring, this.source, start, end);
+}
+
+
+/**
+ * Returns the number of source lines.
+ * @return {number}
+ * Number of source lines.
+ */
+function ScriptLineCount() {
+ // Return number of source lines.
+ return this.line_ends.length;
+}
+
+
+/**
+ * Returns the position of the nth line end.
+ * @return {number}
+ * Zero-based position of the nth line end in the script.
+ */
+function ScriptLineEnd(n) {
+ return this.line_ends[n];
+}
+
+
+/**
+ * If sourceURL comment is available returns sourceURL comment contents.
+ * Otherwise, script name is returned. See
+ * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
+ * and Source Map Revision 3 proposal for details on using //# sourceURL and
+ * deprecated //@ sourceURL comment to identify scripts that don't have name.
+ *
+ * @return {?string} script name if present, value for //# sourceURL comment or
+ * deprecated //@ sourceURL comment otherwise.
+ */
+function ScriptNameOrSourceURL() {
+ if (this.source_url) return this.source_url;
+ return this.name;
+}
+
+
+utils.SetUpLockedPrototype(Script, [
+ "source",
+ "name",
+ "source_url",
+ "source_mapping_url",
+ "line_ends",
+ "line_offset",
+ "column_offset"
+ ], [
+ "lineFromPosition", ScriptLineFromPosition,
+ "locationFromPosition", ScriptLocationFromPosition,
+ "locationFromLine", ScriptLocationFromLine,
+ "sourceSlice", ScriptSourceSlice,
+ "sourceLine", ScriptSourceLine,
+ "lineCount", ScriptLineCount,
+ "nameOrSourceURL", ScriptNameOrSourceURL,
+ "lineEnd", ScriptLineEnd
+ ]
+);
+
+
+/**
+ * Class for source location. A source location is a position within some
+ * source with the following properties:
+ * script : script object for the source
+ * line : source line number
+ * column : source column within the line
+ * position : position within the source
+ * start : position of start of source context (inclusive)
+ * end : position of end of source context (not inclusive)
+ * Source text for the source context is the character interval
+ * [start, end[. In most cases end will point to a newline character.
+ * It might point just past the final position of the source if the last
+ * source line does not end with a newline character.
+ * @param {Script} script The Script object for which this is a location
+ * @param {number} position Source position for the location
+ * @param {number} line The line number for the location
+ * @param {number} column The column within the line for the location
+ * @param {number} start Source position for start of source context
+ * @param {number} end Source position for end of source context
+ * @constructor
+ */
+function SourceLocation(script, position, line, column, start, end) {
+ this.script = script;
+ this.position = position;
+ this.line = line;
+ this.column = column;
+ this.start = start;
+ this.end = end;
+}
+
+
+/**
+ * Get the source text for a SourceLocation
+ * @return {String}
+ * Source text for this location.
+ */
+function SourceLocationSourceText() {
+ return %_Call(StringSubstring, this.script.source, this.start, this.end);
+}
+
+
+utils.SetUpLockedPrototype(SourceLocation,
+ ["script", "position", "line", "column", "start", "end"],
+ ["sourceText", SourceLocationSourceText]
+);
+
+
+/**
+ * Class for a source slice. A source slice is a part of a script source with
+ * the following properties:
+ * script : script object for the source
+ * from_line : line number for the first line in the slice
+ * to_line : source line number for the last line in the slice
+ * from_position : position of the first character in the slice
+ * to_position : position of the last character in the slice
+ * The to_line and to_position are not included in the slice, that is the lines
+ * in the slice are [from_line, to_line[. Likewise the characters in the slice
+ * are [from_position, to_position[.
+ * @param {Script} script The Script object for the source slice
+ * @param {number} from_line
+ * @param {number} to_line
+ * @param {number} from_position
+ * @param {number} to_position
+ * @constructor
+ */
+function SourceSlice(script, from_line, to_line, from_position, to_position) {
+ this.script = script;
+ this.from_line = from_line;
+ this.to_line = to_line;
+ this.from_position = from_position;
+ this.to_position = to_position;
+}
+
+/**
+ * Get the source text for a SourceSlice
+ * @return {String} Source text for this slice. The last line will include
+ * the line terminating characters (if any)
+ */
+function SourceSliceSourceText() {
+ return %_Call(StringSubstring,
+ this.script.source,
+ this.from_position,
+ this.to_position);
+}
+
+utils.SetUpLockedPrototype(SourceSlice,
+ ["script", "from_line", "to_line", "from_position", "to_position"],
+ ["sourceText", SourceSliceSourceText]
+);
+
+
+function GetStackTraceLine(recv, fun, pos, isGlobal) {
+ return new CallSite(recv, fun, pos, false).toString();
+}
+
+// ----------------------------------------------------------------------------
+// Error implementation
+
+function CallSite(receiver, fun, pos, strict_mode) {
+ if (!IS_FUNCTION(fun)) {
+ throw MakeTypeError(kCallSiteExpectsFunction, typeof fun);
+ }
+
+ if (IS_UNDEFINED(new.target)) {
+ return new CallSite(receiver, fun, pos, strict_mode);
+ }
+
+ SET_PRIVATE(this, callSiteReceiverSymbol, receiver);
+ SET_PRIVATE(this, callSiteFunctionSymbol, fun);
+ SET_PRIVATE(this, callSitePositionSymbol, TO_INT32(pos));
+ SET_PRIVATE(this, callSiteStrictSymbol, TO_BOOLEAN(strict_mode));
+}
+
+function CallSiteGetThis() {
+ return GET_PRIVATE(this, callSiteStrictSymbol)
+ ? UNDEFINED : GET_PRIVATE(this, callSiteReceiverSymbol);
+}
+
+function CallSiteGetFunction() {
+ return GET_PRIVATE(this, callSiteStrictSymbol)
+ ? UNDEFINED : GET_PRIVATE(this, callSiteFunctionSymbol);
+}
+
+function CallSiteGetPosition() {
+ return GET_PRIVATE(this, callSitePositionSymbol);
+}
+
+function CallSiteGetTypeName() {
+ return GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), false);
+}
+
+function CallSiteIsToplevel() {
+ return %CallSiteIsToplevelRT(this);
+}
+
+function CallSiteIsEval() {
+ return %CallSiteIsEvalRT(this);
+}
+
+function CallSiteGetEvalOrigin() {
+ var script = %FunctionGetScript(GET_PRIVATE(this, callSiteFunctionSymbol));
+ return FormatEvalOrigin(script);
+}
+
+function CallSiteGetScriptNameOrSourceURL() {
+ return %CallSiteGetScriptNameOrSourceUrlRT(this);
+}
+
+function CallSiteGetFunctionName() {
+ // See if the function knows its own name
+ return %CallSiteGetFunctionNameRT(this);
+}
+
+function CallSiteGetMethodName() {
+ // See if we can find a unique property on the receiver that holds
+ // this function.
+ return %CallSiteGetMethodNameRT(this);
+}
+
+function CallSiteGetFileName() {
+ return %CallSiteGetFileNameRT(this);
+}
+
+function CallSiteGetLineNumber() {
+ return %CallSiteGetLineNumberRT(this);
+}
+
+function CallSiteGetColumnNumber() {
+ return %CallSiteGetColumnNumberRT(this);
+}
+
+function CallSiteIsNative() {
+ return %CallSiteIsNativeRT(this);
+}
+
+function CallSiteIsConstructor() {
+ return %CallSiteIsConstructorRT(this);
+}
+
+function CallSiteToString() {
+ var fileName;
+ var fileLocation = "";
+ if (this.isNative()) {
+ fileLocation = "native";
+ } else {
+ fileName = this.getScriptNameOrSourceURL();
+ if (!fileName && this.isEval()) {
+ fileLocation = this.getEvalOrigin();
+ fileLocation += ", "; // Expecting source position to follow.
+ }
+
+ if (fileName) {
+ fileLocation += fileName;
+ } else {
+ // Source code does not originate from a file and is not native, but we
+ // can still get the source position inside the source string, e.g. in
+ // an eval string.
+ fileLocation += "<anonymous>";
+ }
+ var lineNumber = this.getLineNumber();
+ if (lineNumber != null) {
+ fileLocation += ":" + lineNumber;
+ var columnNumber = this.getColumnNumber();
+ if (columnNumber) {
+ fileLocation += ":" + columnNumber;
+ }
+ }
+ }
+
+ var line = "";
+ var functionName = this.getFunctionName();
+ var addSuffix = true;
+ var isConstructor = this.isConstructor();
+ var isMethodCall = !(this.isToplevel() || isConstructor);
+ if (isMethodCall) {
+ var typeName = GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), true);
+ var methodName = this.getMethodName();
+ if (functionName) {
+ if (typeName && %_Call(StringIndexOf, functionName, typeName) != 0) {
+ line += typeName + ".";
+ }
+ line += functionName;
+ if (methodName &&
+ (%_Call(StringIndexOf, functionName, "." + methodName) !=
+ functionName.length - methodName.length - 1)) {
+ line += " [as " + methodName + "]";
+ }
+ } else {
+ line += typeName + "." + (methodName || "<anonymous>");
+ }
+ } else if (isConstructor) {
+ line += "new " + (functionName || "<anonymous>");
+ } else if (functionName) {
+ line += functionName;
+ } else {
+ line += fileLocation;
+ addSuffix = false;
+ }
+ if (addSuffix) {
+ line += " (" + fileLocation + ")";
+ }
+ return line;
+}
+
+utils.SetUpLockedPrototype(CallSite, ["receiver", "fun", "pos"], [
+ "getThis", CallSiteGetThis,
+ "getTypeName", CallSiteGetTypeName,
+ "isToplevel", CallSiteIsToplevel,
+ "isEval", CallSiteIsEval,
+ "getEvalOrigin", CallSiteGetEvalOrigin,
+ "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
+ "getFunction", CallSiteGetFunction,
+ "getFunctionName", CallSiteGetFunctionName,
+ "getMethodName", CallSiteGetMethodName,
+ "getFileName", CallSiteGetFileName,
+ "getLineNumber", CallSiteGetLineNumber,
+ "getColumnNumber", CallSiteGetColumnNumber,
+ "isNative", CallSiteIsNative,
+ "getPosition", CallSiteGetPosition,
+ "isConstructor", CallSiteIsConstructor,
+ "toString", CallSiteToString
+]);
+
+
+function FormatEvalOrigin(script) {
+ var sourceURL = script.nameOrSourceURL();
+ if (sourceURL) {
+ return sourceURL;
+ }
+
+ var eval_origin = "eval at ";
+ if (script.eval_from_function_name) {
+ eval_origin += script.eval_from_function_name;
+ } else {
+ eval_origin += "<anonymous>";
+ }
+
+ var eval_from_script = script.eval_from_script;
+ if (eval_from_script) {
+ if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
+ // eval script originated from another eval.
+ eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
+ } else {
+ // eval script originated from "real" source.
+ if (eval_from_script.name) {
+ eval_origin += " (" + eval_from_script.name;
+ var location = eval_from_script.locationFromPosition(
+ script.eval_from_script_position, true);
+ if (location) {
+ eval_origin += ":" + (location.line + 1);
+ eval_origin += ":" + (location.column + 1);
+ }
+ eval_origin += ")";
+ } else {
+ eval_origin += " (unknown source)";
+ }
+ }
+ }
+
+ return eval_origin;
+}
+
+
+function FormatErrorString(error) {
+ try {
+ return %_Call(ErrorToString, error);
+ } catch (e) {
+ try {
+ return "<error: " + e + ">";
+ } catch (ee) {
+ return "<error>";
+ }
+ }
+}
+
+
+function GetStackFrames(raw_stack) {
+ var frames = new InternalArray();
+ var sloppy_frames = raw_stack[0];
+ for (var i = 1; i < raw_stack.length; i += 4) {
+ var recv = raw_stack[i];
+ var fun = raw_stack[i + 1];
+ var code = raw_stack[i + 2];
+ var pc = raw_stack[i + 3];
+ var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc);
+ sloppy_frames--;
+ frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
+ }
+ return frames;
+}
+
+
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
+function FormatStackTrace(obj, raw_stack) {
+ var frames = GetStackFrames(raw_stack);
+ if (IS_FUNCTION(GlobalError.prepareStackTrace) &&
+ !formatting_custom_stack_trace) {
+ var array = [];
+ %MoveArrayContents(frames, array);
+ formatting_custom_stack_trace = true;
+ var stack_trace = UNDEFINED;
+ try {
+ stack_trace = GlobalError.prepareStackTrace(obj, array);
+ } catch (e) {
+ throw e; // The custom formatting function threw. Rethrow.
+ } finally {
+ formatting_custom_stack_trace = false;
+ }
+ return stack_trace;
+ }
+
+ var lines = new InternalArray();
+ lines.push(FormatErrorString(obj));
+ for (var i = 0; i < frames.length; i++) {
+ var frame = frames[i];
+ var line;
+ try {
+ line = frame.toString();
+ } catch (e) {
+ try {
+ line = "<error: " + e + ">";
+ } catch (ee) {
+ // Any code that reaches this point is seriously nasty!
+ line = "<error>";
+ }
+ }
+ lines.push(" at " + line);
+ }
+ return %_Call(ArrayJoin, lines, "\n");
+}
+
+
+function GetTypeName(receiver, requireConstructor) {
+ if (IS_NULL_OR_UNDEFINED(receiver)) return null;
+ if (IS_PROXY(receiver)) return "Proxy";
+
+ var constructor = %GetDataProperty(TO_OBJECT(receiver), "constructor");
+ if (!IS_FUNCTION(constructor)) {
+ return requireConstructor ? null : %_Call(NoSideEffectsToString, receiver);
+ }
+ return %FunctionGetName(constructor);
+}
+
+
+// Format the stack trace if not yet done, and return it.
+// Cache the formatted stack trace on the holder.
+var StackTraceGetter = function() {
+ var formatted_stack_trace = UNDEFINED;
+ var holder = this;
+ while (holder) {
+ var formatted_stack_trace =
+ GET_PRIVATE(holder, formattedStackTraceSymbol);
+ if (IS_UNDEFINED(formatted_stack_trace)) {
+ // No formatted stack trace available.
+ var stack_trace = GET_PRIVATE(holder, stackTraceSymbol);
+ if (IS_UNDEFINED(stack_trace)) {
+ // Neither formatted nor structured stack trace available.
+ // Look further up the prototype chain.
+ holder = %_GetPrototype(holder);
+ continue;
+ }
+ formatted_stack_trace = FormatStackTrace(holder, stack_trace);
+ SET_PRIVATE(holder, stackTraceSymbol, UNDEFINED);
+ SET_PRIVATE(holder, formattedStackTraceSymbol, formatted_stack_trace);
+ }
+ return formatted_stack_trace;
+ }
+ return UNDEFINED;
+};
+
+
+// If the receiver equals the holder, set the formatted stack trace that the
+// getter returns.
+var StackTraceSetter = function(v) {
+ if (IsErrorObject(this)) {
+ SET_PRIVATE(this, stackTraceSymbol, UNDEFINED);
+ SET_PRIVATE(this, formattedStackTraceSymbol, v);
+ }
+};
+
+
+// Use a dummy function since we do not actually want to capture a stack trace
+// when constructing the initial Error prototytpes.
+var captureStackTrace = function() {};
+
+
+// Set up special error type constructors.
+function SetUpError(error_function) {
+ %FunctionSetInstanceClassName(error_function, 'Error');
+ var name = error_function.name;
+ var prototype = new GlobalObject();
+ if (name !== 'Error') {
+ %InternalSetPrototype(error_function, GlobalError);
+ %InternalSetPrototype(prototype, GlobalError.prototype);
+ }
+ %FunctionSetPrototype(error_function, prototype);
+
+ %AddNamedProperty(error_function.prototype, 'name', name, DONT_ENUM);
+ %AddNamedProperty(error_function.prototype, 'message', '', DONT_ENUM);
+ %AddNamedProperty(
+ error_function.prototype, 'constructor', error_function, DONT_ENUM);
+
+ %SetCode(error_function, function(m) {
+ if (IS_UNDEFINED(new.target)) return new error_function(m);
+
+ try { captureStackTrace(this, error_function); } catch (e) { }
+ // Define all the expected properties directly on the error
+ // object. This avoids going through getters and setters defined
+ // on prototype objects.
+ if (!IS_UNDEFINED(m)) {
+ %AddNamedProperty(this, 'message', TO_STRING(m), DONT_ENUM);
+ }
+ });
+
+ %SetNativeFlag(error_function);
+ return error_function;
+};
+
+GlobalError = SetUpError(global.Error);
+GlobalEvalError = SetUpError(global.EvalError);
+GlobalRangeError = SetUpError(global.RangeError);
+GlobalReferenceError = SetUpError(global.ReferenceError);
+GlobalSyntaxError = SetUpError(global.SyntaxError);
+GlobalTypeError = SetUpError(global.TypeError);
+GlobalURIError = SetUpError(global.URIError);
+
+utils.InstallFunctions(GlobalError.prototype, DONT_ENUM,
+ ['toString', ErrorToString]);
+
+function ErrorToString() {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
+ }
+
+ var name = this.name;
+ name = IS_UNDEFINED(name) ? "Error" : TO_STRING(name);
+
+ var message = this.message;
+ message = IS_UNDEFINED(message) ? "" : TO_STRING(message);
+
+ if (name == "") return message;
+ if (message == "") return name;
+ return `${name}: ${message}`
+}
+
+function MakeError(type, arg0, arg1, arg2) {
+ return MakeGenericError(GlobalError, type, arg0, arg1, arg2);
+}
+
+function MakeRangeError(type, arg0, arg1, arg2) {
+ return MakeGenericError(GlobalRangeError, type, arg0, arg1, arg2);
+}
+
+function MakeSyntaxError(type, arg0, arg1, arg2) {
+ return MakeGenericError(GlobalSyntaxError, type, arg0, arg1, arg2);
+}
+
+function MakeTypeError(type, arg0, arg1, arg2) {
+ return MakeGenericError(GlobalTypeError, type, arg0, arg1, arg2);
+}
+
+function MakeURIError() {
+ return MakeGenericError(GlobalURIError, kURIMalformed);
+}
+
+// Boilerplate for exceptions for stack overflows. Used from
+// Isolate::StackOverflow().
+var StackOverflowBoilerplate = MakeRangeError(kStackOverflow);
+utils.InstallGetterSetter(StackOverflowBoilerplate, 'stack',
+ StackTraceGetter, StackTraceSetter)
+
+// Define actual captureStackTrace function after everything has been set up.
+captureStackTrace = function captureStackTrace(obj, cons_opt) {
+ // Define accessors first, as this may fail and throw.
+ ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter,
+ set: StackTraceSetter,
+ configurable: true });
+ %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
+};
+
+GlobalError.captureStackTrace = captureStackTrace;
+
+%InstallToContext([
+ "get_stack_trace_line_fun", GetStackTraceLine,
+ "make_error_function", MakeGenericError,
+ "make_range_error", MakeRangeError,
+ "make_type_error", MakeTypeError,
+ "message_get_column_number", GetColumnNumber,
+ "message_get_line_number", GetLineNumber,
+ "message_get_source_line", GetSourceLine,
+ "no_side_effects_to_string_fun", NoSideEffectsToString,
+ "stack_overflow_boilerplate", StackOverflowBoilerplate,
+]);
+
+utils.Export(function(to) {
+ to.ErrorToString = ErrorToString;
+ to.MakeError = MakeError;
+ to.MakeRangeError = MakeRangeError;
+ to.MakeSyntaxError = MakeSyntaxError;
+ to.MakeTypeError = MakeTypeError;
+ to.MakeURIError = MakeURIError;
+});
+
+});
diff --git a/src/js/object-observe.js b/src/js/object-observe.js
new file mode 100644
index 0000000..5e256bf
--- /dev/null
+++ b/src/js/object-observe.js
@@ -0,0 +1,717 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GetHash;
+var GlobalArray = global.Array;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+
+utils.Import(function(from) {
+ GetHash = from.GetHash;
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// Overview:
+//
+// This file contains all of the routing and accounting for Object.observe.
+// User code will interact with these mechanisms via the Object.observe APIs
+// and, as a side effect of mutation objects which are observed. The V8 runtime
+// (both C++ and JS) will interact with these mechanisms primarily by enqueuing
+// proper change records for objects which were mutated. The Object.observe
+// routing and accounting consists primarily of three participants
+//
+// 1) ObjectInfo. This represents the observed state of a given object. It
+// records what callbacks are observing the object, with what options, and
+// what "change types" are in progress on the object (i.e. via
+// notifier.performChange).
+//
+// 2) CallbackInfo. This represents a callback used for observation. It holds
+// the records which must be delivered to the callback, as well as the global
+// priority of the callback (which determines delivery order between
+// callbacks).
+//
+// 3) observationState.pendingObservers. This is the set of observers which
+// have change records which must be delivered. During "normal" delivery
+// (i.e. not Object.deliverChangeRecords), this is the mechanism by which
+// callbacks are invoked in the proper order until there are no more
+// change records pending to a callback.
+//
+// Note that in order to reduce allocation and processing costs, the
+// implementation of (1) and (2) have "optimized" states which represent
+// common cases which can be handled more efficiently.
+
+var observationState;
+
+var notifierPrototype = {};
+
+// We have to wait until after bootstrapping to grab a reference to the
+// observationState object, since it's not possible to serialize that
+// reference into the snapshot.
+function GetObservationStateJS() {
+ if (IS_UNDEFINED(observationState)) {
+ observationState = %GetObservationState();
+ }
+
+ // TODO(adamk): Consider moving this code into heap.cc
+ if (IS_UNDEFINED(observationState.callbackInfoMap)) {
+ observationState.callbackInfoMap = %ObservationWeakMapCreate();
+ observationState.objectInfoMap = %ObservationWeakMapCreate();
+ observationState.notifierObjectInfoMap = %ObservationWeakMapCreate();
+ observationState.pendingObservers = null;
+ observationState.nextCallbackPriority = 0;
+ observationState.lastMicrotaskId = 0;
+ }
+
+ return observationState;
+}
+
+
+function GetPendingObservers() {
+ return GetObservationStateJS().pendingObservers;
+}
+
+
+function SetPendingObservers(pendingObservers) {
+ GetObservationStateJS().pendingObservers = pendingObservers;
+}
+
+
+function GetNextCallbackPriority() {
+ return GetObservationStateJS().nextCallbackPriority++;
+}
+
+
+function nullProtoObject() {
+ return { __proto__: null };
+}
+
+
+function TypeMapCreate() {
+ return nullProtoObject();
+}
+
+
+function TypeMapAddType(typeMap, type, ignoreDuplicate) {
+ typeMap[type] = ignoreDuplicate ? 1 : (typeMap[type] || 0) + 1;
+}
+
+
+function TypeMapRemoveType(typeMap, type) {
+ typeMap[type]--;
+}
+
+
+function TypeMapCreateFromList(typeList, length) {
+ var typeMap = TypeMapCreate();
+ for (var i = 0; i < length; i++) {
+ TypeMapAddType(typeMap, typeList[i], true);
+ }
+ return typeMap;
+}
+
+
+function TypeMapHasType(typeMap, type) {
+ return !!typeMap[type];
+}
+
+
+function TypeMapIsDisjointFrom(typeMap1, typeMap2) {
+ if (!typeMap1 || !typeMap2)
+ return true;
+
+ for (var type in typeMap1) {
+ if (TypeMapHasType(typeMap1, type) && TypeMapHasType(typeMap2, type))
+ return false;
+ }
+
+ return true;
+}
+
+
+var defaultAcceptTypes = (function() {
+ var defaultTypes = [
+ 'add',
+ 'update',
+ 'delete',
+ 'setPrototype',
+ 'reconfigure',
+ 'preventExtensions'
+ ];
+ return TypeMapCreateFromList(defaultTypes, defaultTypes.length);
+})();
+
+
+// An Observer is a registration to observe an object by a callback with
+// a given set of accept types. If the set of accept types is the default
+// set for Object.observe, the observer is represented as a direct reference
+// to the callback. An observer never changes its accept types and thus never
+// needs to "normalize".
+function ObserverCreate(callback, acceptList) {
+ if (IS_UNDEFINED(acceptList))
+ return callback;
+ var observer = nullProtoObject();
+ observer.callback = callback;
+ observer.accept = acceptList;
+ return observer;
+}
+
+
+function ObserverGetCallback(observer) {
+ return IS_CALLABLE(observer) ? observer : observer.callback;
+}
+
+
+function ObserverGetAcceptTypes(observer) {
+ return IS_CALLABLE(observer) ? defaultAcceptTypes : observer.accept;
+}
+
+
+function ObserverIsActive(observer, objectInfo) {
+ return TypeMapIsDisjointFrom(ObjectInfoGetPerformingTypes(objectInfo),
+ ObserverGetAcceptTypes(observer));
+}
+
+
+function ObjectInfoGetOrCreate(object) {
+ var objectInfo = ObjectInfoGet(object);
+ if (IS_UNDEFINED(objectInfo)) {
+ if (!IS_PROXY(object)) {
+ %SetIsObserved(object);
+ }
+ objectInfo = {
+ object: object,
+ changeObservers: null,
+ notifier: null,
+ performing: null,
+ performingCount: 0,
+ };
+ %WeakCollectionSet(GetObservationStateJS().objectInfoMap,
+ object, objectInfo, GetHash(object));
+ }
+ return objectInfo;
+}
+
+
+function ObjectInfoGet(object) {
+ return %WeakCollectionGet(GetObservationStateJS().objectInfoMap, object,
+ GetHash(object));
+}
+
+
+function ObjectInfoGetFromNotifier(notifier) {
+ return %WeakCollectionGet(GetObservationStateJS().notifierObjectInfoMap,
+ notifier, GetHash(notifier));
+}
+
+
+function ObjectInfoGetNotifier(objectInfo) {
+ if (IS_NULL(objectInfo.notifier)) {
+ var notifier = { __proto__: notifierPrototype };
+ objectInfo.notifier = notifier;
+ %WeakCollectionSet(GetObservationStateJS().notifierObjectInfoMap,
+ notifier, objectInfo, GetHash(notifier));
+ }
+
+ return objectInfo.notifier;
+}
+
+
+function ChangeObserversIsOptimized(changeObservers) {
+ return IS_CALLABLE(changeObservers) ||
+ IS_CALLABLE(changeObservers.callback);
+}
+
+
+// The set of observers on an object is called 'changeObservers'. The first
+// observer is referenced directly via objectInfo.changeObservers. When a second
+// is added, changeObservers "normalizes" to become a mapping of callback
+// priority -> observer and is then stored on objectInfo.changeObservers.
+function ObjectInfoNormalizeChangeObservers(objectInfo) {
+ if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+ var observer = objectInfo.changeObservers;
+ var callback = ObserverGetCallback(observer);
+ var callbackInfo = CallbackInfoGet(callback);
+ var priority = CallbackInfoGetPriority(callbackInfo);
+ objectInfo.changeObservers = nullProtoObject();
+ objectInfo.changeObservers[priority] = observer;
+ }
+}
+
+
+function ObjectInfoAddObserver(objectInfo, callback, acceptList) {
+ var callbackInfo = CallbackInfoGetOrCreate(callback);
+ var observer = ObserverCreate(callback, acceptList);
+
+ if (!objectInfo.changeObservers) {
+ objectInfo.changeObservers = observer;
+ return;
+ }
+
+ ObjectInfoNormalizeChangeObservers(objectInfo);
+ var priority = CallbackInfoGetPriority(callbackInfo);
+ objectInfo.changeObservers[priority] = observer;
+}
+
+function ObjectInfoRemoveObserver(objectInfo, callback) {
+ if (!objectInfo.changeObservers)
+ return;
+
+ if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+ if (callback === ObserverGetCallback(objectInfo.changeObservers))
+ objectInfo.changeObservers = null;
+ return;
+ }
+
+ var callbackInfo = CallbackInfoGet(callback);
+ var priority = CallbackInfoGetPriority(callbackInfo);
+ objectInfo.changeObservers[priority] = null;
+}
+
+function ObjectInfoHasActiveObservers(objectInfo) {
+ if (IS_UNDEFINED(objectInfo) || !objectInfo.changeObservers)
+ return false;
+
+ if (ChangeObserversIsOptimized(objectInfo.changeObservers))
+ return ObserverIsActive(objectInfo.changeObservers, objectInfo);
+
+ for (var priority in objectInfo.changeObservers) {
+ var observer = objectInfo.changeObservers[priority];
+ if (!IS_NULL(observer) && ObserverIsActive(observer, objectInfo))
+ return true;
+ }
+
+ return false;
+}
+
+
+function ObjectInfoAddPerformingType(objectInfo, type) {
+ objectInfo.performing = objectInfo.performing || TypeMapCreate();
+ TypeMapAddType(objectInfo.performing, type);
+ objectInfo.performingCount++;
+}
+
+
+function ObjectInfoRemovePerformingType(objectInfo, type) {
+ objectInfo.performingCount--;
+ TypeMapRemoveType(objectInfo.performing, type);
+}
+
+
+function ObjectInfoGetPerformingTypes(objectInfo) {
+ return objectInfo.performingCount > 0 ? objectInfo.performing : null;
+}
+
+
+function ConvertAcceptListToTypeMap(arg) {
+ // We use undefined as a sentinel for the default accept list.
+ if (IS_UNDEFINED(arg))
+ return arg;
+
+ if (!IS_RECEIVER(arg)) throw MakeTypeError(kObserveInvalidAccept);
+
+ var len = TO_INTEGER(arg.length);
+ if (len < 0) len = 0;
+
+ return TypeMapCreateFromList(arg, len);
+}
+
+
+// CallbackInfo's optimized state is just a number which represents its global
+// priority. When a change record must be enqueued for the callback, it
+// normalizes. When delivery clears any pending change records, it re-optimizes.
+function CallbackInfoGet(callback) {
+ return %WeakCollectionGet(GetObservationStateJS().callbackInfoMap, callback,
+ GetHash(callback));
+}
+
+
+function CallbackInfoSet(callback, callbackInfo) {
+ %WeakCollectionSet(GetObservationStateJS().callbackInfoMap,
+ callback, callbackInfo, GetHash(callback));
+}
+
+
+function CallbackInfoGetOrCreate(callback) {
+ var callbackInfo = CallbackInfoGet(callback);
+ if (!IS_UNDEFINED(callbackInfo))
+ return callbackInfo;
+
+ var priority = GetNextCallbackPriority();
+ CallbackInfoSet(callback, priority);
+ return priority;
+}
+
+
+function CallbackInfoGetPriority(callbackInfo) {
+ if (IS_NUMBER(callbackInfo))
+ return callbackInfo;
+ else
+ return callbackInfo.priority;
+}
+
+
+function CallbackInfoNormalize(callback) {
+ var callbackInfo = CallbackInfoGet(callback);
+ if (IS_NUMBER(callbackInfo)) {
+ var priority = callbackInfo;
+ callbackInfo = new InternalArray;
+ callbackInfo.priority = priority;
+ CallbackInfoSet(callback, callbackInfo);
+ }
+ return callbackInfo;
+}
+
+
+function ObjectObserve(object, callback, acceptList) {
+ if (!IS_RECEIVER(object))
+ throw MakeTypeError(kObserveNonObject, "observe", "observe");
+ if (%IsJSGlobalProxy(object))
+ throw MakeTypeError(kObserveGlobalProxy, "observe");
+ if (%IsAccessCheckNeeded(object))
+ throw MakeTypeError(kObserveAccessChecked, "observe");
+ if (!IS_CALLABLE(callback))
+ throw MakeTypeError(kObserveNonFunction, "observe");
+ if (%object_is_frozen(callback))
+ throw MakeTypeError(kObserveCallbackFrozen);
+
+ var objectObserveFn = %GetObjectContextObjectObserve(object);
+ return objectObserveFn(object, callback, acceptList);
+}
+
+
+function NativeObjectObserve(object, callback, acceptList) {
+ var objectInfo = ObjectInfoGetOrCreate(object);
+ var typeList = ConvertAcceptListToTypeMap(acceptList);
+ ObjectInfoAddObserver(objectInfo, callback, typeList);
+ return object;
+}
+
+
+function ObjectUnobserve(object, callback) {
+ if (!IS_RECEIVER(object))
+ throw MakeTypeError(kObserveNonObject, "unobserve", "unobserve");
+ if (%IsJSGlobalProxy(object))
+ throw MakeTypeError(kObserveGlobalProxy, "unobserve");
+ if (!IS_CALLABLE(callback))
+ throw MakeTypeError(kObserveNonFunction, "unobserve");
+
+ var objectInfo = ObjectInfoGet(object);
+ if (IS_UNDEFINED(objectInfo))
+ return object;
+
+ ObjectInfoRemoveObserver(objectInfo, callback);
+ return object;
+}
+
+
+function ArrayObserve(object, callback) {
+ return ObjectObserve(object, callback, ['add',
+ 'update',
+ 'delete',
+ 'splice']);
+}
+
+
+function ArrayUnobserve(object, callback) {
+ return ObjectUnobserve(object, callback);
+}
+
+
+function ObserverEnqueueIfActive(observer, objectInfo, changeRecord) {
+ if (!ObserverIsActive(observer, objectInfo) ||
+ !TypeMapHasType(ObserverGetAcceptTypes(observer), changeRecord.type)) {
+ return;
+ }
+
+ var callback = ObserverGetCallback(observer);
+ if (!%ObserverObjectAndRecordHaveSameOrigin(callback, changeRecord.object,
+ changeRecord)) {
+ return;
+ }
+
+ var callbackInfo = CallbackInfoNormalize(callback);
+ if (IS_NULL(GetPendingObservers())) {
+ SetPendingObservers(nullProtoObject());
+ if (DEBUG_IS_ACTIVE) {
+ var id = ++GetObservationStateJS().lastMicrotaskId;
+ var name = "Object.observe";
+ %EnqueueMicrotask(function() {
+ %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+ ObserveMicrotaskRunner();
+ %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+ });
+ %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+ } else {
+ %EnqueueMicrotask(ObserveMicrotaskRunner);
+ }
+ }
+ GetPendingObservers()[callbackInfo.priority] = callback;
+ callbackInfo.push(changeRecord);
+}
+
+
+function ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, type) {
+ if (!ObjectInfoHasActiveObservers(objectInfo))
+ return;
+
+ var hasType = !IS_UNDEFINED(type);
+ var newRecord = hasType ?
+ { object: objectInfo.object, type: type } :
+ { object: objectInfo.object };
+
+ for (var prop in changeRecord) {
+ if (prop === 'object' || (hasType && prop === 'type')) continue;
+ %DefineDataPropertyUnchecked(
+ newRecord, prop, changeRecord[prop], READ_ONLY + DONT_DELETE);
+ }
+ %object_freeze(newRecord);
+
+ ObjectInfoEnqueueInternalChangeRecord(objectInfo, newRecord);
+}
+
+
+function ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(changeRecord.name)) return;
+
+ if (ChangeObserversIsOptimized(objectInfo.changeObservers)) {
+ var observer = objectInfo.changeObservers;
+ ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
+ return;
+ }
+
+ for (var priority in objectInfo.changeObservers) {
+ var observer = objectInfo.changeObservers[priority];
+ if (IS_NULL(observer))
+ continue;
+ ObserverEnqueueIfActive(observer, objectInfo, changeRecord);
+ }
+}
+
+
+function BeginPerformSplice(array) {
+ var objectInfo = ObjectInfoGet(array);
+ if (!IS_UNDEFINED(objectInfo))
+ ObjectInfoAddPerformingType(objectInfo, 'splice');
+}
+
+
+function EndPerformSplice(array) {
+ var objectInfo = ObjectInfoGet(array);
+ if (!IS_UNDEFINED(objectInfo))
+ ObjectInfoRemovePerformingType(objectInfo, 'splice');
+}
+
+
+function EnqueueSpliceRecord(array, index, removed, addedCount) {
+ var objectInfo = ObjectInfoGet(array);
+ if (!ObjectInfoHasActiveObservers(objectInfo))
+ return;
+
+ var changeRecord = {
+ type: 'splice',
+ object: array,
+ index: index,
+ removed: removed,
+ addedCount: addedCount
+ };
+
+ %object_freeze(changeRecord);
+ %object_freeze(changeRecord.removed);
+ ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function NotifyChange(type, object, name, oldValue) {
+ var objectInfo = ObjectInfoGet(object);
+ if (!ObjectInfoHasActiveObservers(objectInfo))
+ return;
+
+ var changeRecord;
+ if (arguments.length == 2) {
+ changeRecord = { type: type, object: object };
+ } else if (arguments.length == 3) {
+ changeRecord = { type: type, object: object, name: name };
+ } else {
+ changeRecord = {
+ type: type,
+ object: object,
+ name: name,
+ oldValue: oldValue
+ };
+ }
+
+ %object_freeze(changeRecord);
+ ObjectInfoEnqueueInternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function ObjectNotifierNotify(changeRecord) {
+ if (!IS_RECEIVER(this))
+ throw MakeTypeError(kCalledOnNonObject, "notify");
+
+ var objectInfo = ObjectInfoGetFromNotifier(this);
+ if (IS_UNDEFINED(objectInfo))
+ throw MakeTypeError(kObserveNotifyNonNotifier);
+ if (!IS_STRING(changeRecord.type))
+ throw MakeTypeError(kObserveTypeNonString);
+
+ ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord);
+}
+
+
+function ObjectNotifierPerformChange(changeType, changeFn) {
+ if (!IS_RECEIVER(this))
+ throw MakeTypeError(kCalledOnNonObject, "performChange");
+
+ var objectInfo = ObjectInfoGetFromNotifier(this);
+ if (IS_UNDEFINED(objectInfo))
+ throw MakeTypeError(kObserveNotifyNonNotifier);
+ if (!IS_STRING(changeType))
+ throw MakeTypeError(kObservePerformNonString);
+ if (!IS_CALLABLE(changeFn))
+ throw MakeTypeError(kObservePerformNonFunction);
+
+ var performChangeFn = %GetObjectContextNotifierPerformChange(objectInfo);
+ performChangeFn(objectInfo, changeType, changeFn);
+}
+
+
+function NativeObjectNotifierPerformChange(objectInfo, changeType, changeFn) {
+ ObjectInfoAddPerformingType(objectInfo, changeType);
+
+ var changeRecord;
+ try {
+ changeRecord = changeFn();
+ } finally {
+ ObjectInfoRemovePerformingType(objectInfo, changeType);
+ }
+
+ if (IS_RECEIVER(changeRecord))
+ ObjectInfoEnqueueExternalChangeRecord(objectInfo, changeRecord, changeType);
+}
+
+
+function ObjectGetNotifier(object) {
+ if (!IS_RECEIVER(object))
+ throw MakeTypeError(kObserveNonObject, "getNotifier", "getNotifier");
+ if (%IsJSGlobalProxy(object))
+ throw MakeTypeError(kObserveGlobalProxy, "getNotifier");
+ if (%IsAccessCheckNeeded(object))
+ throw MakeTypeError(kObserveAccessChecked, "getNotifier");
+
+ if (%object_is_frozen(object)) return null;
+
+ if (!%ObjectWasCreatedInCurrentOrigin(object)) return null;
+
+ var getNotifierFn = %GetObjectContextObjectGetNotifier(object);
+ return getNotifierFn(object);
+}
+
+
+function NativeObjectGetNotifier(object) {
+ var objectInfo = ObjectInfoGetOrCreate(object);
+ return ObjectInfoGetNotifier(objectInfo);
+}
+
+
+function CallbackDeliverPending(callback) {
+ var callbackInfo = CallbackInfoGet(callback);
+ if (IS_UNDEFINED(callbackInfo) || IS_NUMBER(callbackInfo))
+ return false;
+
+ // Clear the pending change records from callback and return it to its
+ // "optimized" state.
+ var priority = callbackInfo.priority;
+ CallbackInfoSet(callback, priority);
+
+ var pendingObservers = GetPendingObservers();
+ if (!IS_NULL(pendingObservers))
+ delete pendingObservers[priority];
+
+ // TODO: combine the following runtime calls for perf optimization.
+ var delivered = [];
+ %MoveArrayContents(callbackInfo, delivered);
+ %DeliverObservationChangeRecords(callback, delivered);
+
+ return true;
+}
+
+
+function ObjectDeliverChangeRecords(callback) {
+ if (!IS_CALLABLE(callback))
+ throw MakeTypeError(kObserveNonFunction, "deliverChangeRecords");
+
+ while (CallbackDeliverPending(callback)) {}
+}
+
+
+function ObserveMicrotaskRunner() {
+ var pendingObservers = GetPendingObservers();
+ if (!IS_NULL(pendingObservers)) {
+ SetPendingObservers(null);
+ for (var i in pendingObservers) {
+ CallbackDeliverPending(pendingObservers[i]);
+ }
+ }
+}
+
+// -------------------------------------------------------------------
+
+utils.InstallFunctions(notifierPrototype, DONT_ENUM, [
+ "notify", ObjectNotifierNotify,
+ "performChange", ObjectNotifierPerformChange
+]);
+
+var ObserveObjectMethods = [
+ "deliverChangeRecords", ObjectDeliverChangeRecords,
+ "getNotifier", ObjectGetNotifier,
+ "observe", ObjectObserve,
+ "unobserve", ObjectUnobserve
+];
+
+var ObserveArrayMethods = [
+ "observe", ArrayObserve,
+ "unobserve", ArrayUnobserve
+];
+
+// TODO(adamk): Figure out why this prototype removal has to
+// happen as part of initial snapshotting.
+var removePrototypeFn = function(f, i) {
+ if (i % 2 === 1) %FunctionRemovePrototype(f);
+};
+ObserveObjectMethods.forEach(removePrototypeFn);
+ObserveArrayMethods.forEach(removePrototypeFn);
+
+%InstallToContext([
+ "native_object_get_notifier", NativeObjectGetNotifier,
+ "native_object_notifier_perform_change", NativeObjectNotifierPerformChange,
+ "native_object_observe", NativeObjectObserve,
+ "observers_begin_perform_splice", BeginPerformSplice,
+ "observers_end_perform_splice", EndPerformSplice,
+ "observers_enqueue_splice", EnqueueSpliceRecord,
+ "observers_notify_change", NotifyChange,
+]);
+
+utils.Export(function(to) {
+ to.ObserveArrayMethods = ObserveArrayMethods;
+ to.ObserveBeginPerformSplice = BeginPerformSplice;
+ to.ObserveEndPerformSplice = EndPerformSplice;
+ to.ObserveEnqueueSpliceRecord = EnqueueSpliceRecord;
+ to.ObserveObjectMethods = ObserveObjectMethods;
+});
+
+})
diff --git a/src/js/prologue.js b/src/js/prologue.js
new file mode 100644
index 0000000..2779393
--- /dev/null
+++ b/src/js/prologue.js
@@ -0,0 +1,342 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -----------------------------------------------------------------------
+// Utils
+
+var imports = UNDEFINED;
+var imports_from_experimental = UNDEFINED;
+var exports_container = %ExportFromRuntime({});
+var typed_array_setup = UNDEFINED;
+
+// Register context value to be initialized with a typed array in
+// Genesis::InitializeBuiltinTypedArrays.
+function SetupTypedArray(f) {
+ f.next = typed_array_setup;
+ typed_array_setup = f;
+}
+
+// Export to other scripts.
+// In normal natives, this exports functions to other normal natives.
+// In experimental natives, this exports to other experimental natives and
+// to normal natives that import using utils.ImportFromExperimental.
+function Export(f) {
+ f(exports_container);
+}
+
+
+// Import from other scripts. The actual importing happens in PostNatives and
+// PostExperimental so that we can import from scripts executed later. However,
+// that means that the import is not available until the very end. If the
+// import needs to be available immediate, use ImportNow.
+// In normal natives, this imports from other normal natives.
+// In experimental natives, this imports from other experimental natives and
+// whitelisted exports from normal natives.
+function Import(f) {
+ f.next = imports;
+ imports = f;
+}
+
+
+// Import immediately from exports of previous scripts. We need this for
+// functions called during bootstrapping. Hooking up imports in PostNatives
+// would be too late.
+function ImportNow(name) {
+ return exports_container[name];
+}
+
+
+// In normal natives, import from experimental natives.
+// Not callable from experimental natives.
+function ImportFromExperimental(f) {
+ f.next = imports_from_experimental;
+ imports_from_experimental = f;
+}
+
+
+function SetFunctionName(f, name, prefix) {
+ if (IS_SYMBOL(name)) {
+ name = "[" + %SymbolDescription(name) + "]";
+ }
+ if (IS_UNDEFINED(prefix)) {
+ %FunctionSetName(f, name);
+ } else {
+ %FunctionSetName(f, prefix + " " + name);
+ }
+}
+
+
+function InstallConstants(object, constants) {
+ %CheckIsBootstrapping();
+ %OptimizeObjectForAddingMultipleProperties(object, constants.length >> 1);
+ var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
+ for (var i = 0; i < constants.length; i += 2) {
+ var name = constants[i];
+ var k = constants[i + 1];
+ %AddNamedProperty(object, name, k, attributes);
+ }
+ %ToFastProperties(object);
+}
+
+
+function InstallFunctions(object, attributes, functions) {
+ %CheckIsBootstrapping();
+ %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
+ for (var i = 0; i < functions.length; i += 2) {
+ var key = functions[i];
+ var f = functions[i + 1];
+ SetFunctionName(f, key);
+ %FunctionRemovePrototype(f);
+ %AddNamedProperty(object, key, f, attributes);
+ %SetNativeFlag(f);
+ }
+ %ToFastProperties(object);
+}
+
+
+// Helper function to install a getter-only accessor property.
+function InstallGetter(object, name, getter, attributes, prefix) {
+ %CheckIsBootstrapping();
+ if (IS_UNDEFINED(attributes)) attributes = DONT_ENUM;
+ SetFunctionName(getter, name, IS_UNDEFINED(prefix) ? "get" : prefix);
+ %FunctionRemovePrototype(getter);
+ %DefineGetterPropertyUnchecked(object, name, getter, attributes);
+ %SetNativeFlag(getter);
+}
+
+
+// Helper function to install a getter/setter accessor property.
+function InstallGetterSetter(object, name, getter, setter, attributes) {
+ %CheckIsBootstrapping();
+ if (IS_UNDEFINED(attributes)) attributes = DONT_ENUM;
+ SetFunctionName(getter, name, "get");
+ SetFunctionName(setter, name, "set");
+ %FunctionRemovePrototype(getter);
+ %FunctionRemovePrototype(setter);
+ %DefineAccessorPropertyUnchecked(object, name, getter, setter, DONT_ENUM);
+ %SetNativeFlag(getter);
+ %SetNativeFlag(setter);
+}
+
+
+// Prevents changes to the prototype of a built-in function.
+// The "prototype" property of the function object is made non-configurable,
+// and the prototype object is made non-extensible. The latter prevents
+// changing the __proto__ property.
+function SetUpLockedPrototype(
+ constructor, fields, methods) {
+ %CheckIsBootstrapping();
+ var prototype = constructor.prototype;
+ // Install functions first, because this function is used to initialize
+ // PropertyDescriptor itself.
+ var property_count = (methods.length >> 1) + (fields ? fields.length : 0);
+ if (property_count >= 4) {
+ %OptimizeObjectForAddingMultipleProperties(prototype, property_count);
+ }
+ if (fields) {
+ for (var i = 0; i < fields.length; i++) {
+ %AddNamedProperty(prototype, fields[i],
+ UNDEFINED, DONT_ENUM | DONT_DELETE);
+ }
+ }
+ for (var i = 0; i < methods.length; i += 2) {
+ var key = methods[i];
+ var f = methods[i + 1];
+ %AddNamedProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY);
+ %SetNativeFlag(f);
+ }
+ %InternalSetPrototype(prototype, null);
+ %ToFastProperties(prototype);
+}
+
+
+// -----------------------------------------------------------------------
+// To be called by bootstrapper
+
+function PostNatives(utils) {
+ %CheckIsBootstrapping();
+
+ for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+ imports(exports_container);
+ }
+
+ // Whitelist of exports from normal natives to experimental natives and debug.
+ var expose_list = [
+ "ArrayToString",
+ "ErrorToString",
+ "GetIterator",
+ "GetMethod",
+ "IsNaN",
+ "MakeError",
+ "MakeTypeError",
+ "MapEntries",
+ "MapIterator",
+ "MapIteratorNext",
+ "MathMax",
+ "MathMin",
+ "MaxSimple",
+ "MinSimple",
+ "ObjectDefineProperty",
+ "ObserveArrayMethods",
+ "ObserveObjectMethods",
+ "PromiseChain",
+ "PromiseDeferred",
+ "PromiseResolved",
+ "SameValueZero",
+ "SetIterator",
+ "SetIteratorNext",
+ "SetValues",
+ "SymbolToString",
+ "ToPositiveInteger",
+ // From runtime:
+ "is_concat_spreadable_symbol",
+ "iterator_symbol",
+ "promise_status_symbol",
+ "promise_value_symbol",
+ "object_freeze",
+ "object_is_frozen",
+ "object_is_sealed",
+ "reflect_apply",
+ "reflect_construct",
+ "regexp_flags_symbol",
+ "to_string_tag_symbol",
+ "object_to_string",
+ "species_symbol",
+ ];
+
+ var filtered_exports = {};
+ %OptimizeObjectForAddingMultipleProperties(
+ filtered_exports, expose_list.length);
+ for (var key of expose_list) {
+ filtered_exports[key] = exports_container[key];
+ }
+ %ToFastProperties(filtered_exports);
+ exports_container = filtered_exports;
+
+ utils.PostNatives = UNDEFINED;
+ utils.ImportFromExperimental = UNDEFINED;
+}
+
+
+function PostExperimentals(utils) {
+ %CheckIsBootstrapping();
+ %ExportExperimentalFromRuntime(exports_container);
+ for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+ imports(exports_container);
+ }
+ for ( ; !IS_UNDEFINED(imports_from_experimental);
+ imports_from_experimental = imports_from_experimental.next) {
+ imports_from_experimental(exports_container);
+ }
+
+ utils.CreateDoubleResultArray();
+ utils.CreateDoubleResultArray = UNDEFINED;
+
+ utils.Export = UNDEFINED;
+ utils.PostDebug = UNDEFINED;
+ utils.PostExperimentals = UNDEFINED;
+ typed_array_setup = UNDEFINED;
+}
+
+
+function PostDebug(utils) {
+ for ( ; !IS_UNDEFINED(imports); imports = imports.next) {
+ imports(exports_container);
+ }
+
+ utils.CreateDoubleResultArray();
+ utils.CreateDoubleResultArray = UNDEFINED;
+
+ exports_container = UNDEFINED;
+
+ utils.Export = UNDEFINED;
+ utils.Import = UNDEFINED;
+ utils.ImportNow = UNDEFINED;
+ utils.PostDebug = UNDEFINED;
+ utils.PostExperimentals = UNDEFINED;
+ typed_array_setup = UNDEFINED;
+}
+
+
+function InitializeBuiltinTypedArrays(utils, rng_state, rempio2result) {
+ var setup_list = typed_array_setup;
+
+ for ( ; !IS_UNDEFINED(setup_list); setup_list = setup_list.next) {
+ setup_list(rng_state, rempio2result);
+ }
+}
+
+
+// -----------------------------------------------------------------------
+
+%OptimizeObjectForAddingMultipleProperties(utils, 14);
+
+utils.Import = Import;
+utils.ImportNow = ImportNow;
+utils.Export = Export;
+utils.ImportFromExperimental = ImportFromExperimental;
+utils.SetFunctionName = SetFunctionName;
+utils.InstallConstants = InstallConstants;
+utils.InstallFunctions = InstallFunctions;
+utils.InstallGetter = InstallGetter;
+utils.InstallGetterSetter = InstallGetterSetter;
+utils.SetUpLockedPrototype = SetUpLockedPrototype;
+utils.PostNatives = PostNatives;
+utils.PostExperimentals = PostExperimentals;
+utils.PostDebug = PostDebug;
+
+%ToFastProperties(utils);
+
+// -----------------------------------------------------------------------
+
+%OptimizeObjectForAddingMultipleProperties(extrasUtils, 5);
+
+extrasUtils.logStackTrace = function logStackTrace() {
+ %DebugTrace();
+};
+
+extrasUtils.log = function log() {
+ let message = '';
+ for (const arg of arguments) {
+ message += arg;
+ }
+
+ %GlobalPrint(message);
+};
+
+// Extras need the ability to store private state on their objects without
+// exposing it to the outside world.
+
+extrasUtils.createPrivateSymbol = function createPrivateSymbol(name) {
+ return %CreatePrivateSymbol(name);
+};
+
+// These functions are key for safe meta-programming:
+// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
+//
+// Technically they could all be derived from combinations of
+// Function.prototype.{bind,call,apply} but that introduces lots of layers of
+// indirection and slowness given how un-optimized bind is.
+
+extrasUtils.simpleBind = function simpleBind(func, thisArg) {
+ return function() {
+ return %Apply(func, thisArg, arguments, 0, arguments.length);
+ };
+};
+
+extrasUtils.uncurryThis = function uncurryThis(func) {
+ return function(thisArg) {
+ return %Apply(func, thisArg, arguments, 1, arguments.length - 1);
+ };
+};
+
+%ToFastProperties(extrasUtils);
+
+})
diff --git a/src/js/promise-extra.js b/src/js/promise-extra.js
new file mode 100644
index 0000000..f6f7959
--- /dev/null
+++ b/src/js/promise-extra.js
@@ -0,0 +1,26 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+var GlobalPromise = global.Promise;
+
+var PromiseChain = utils.ImportNow("PromiseChain");
+var PromiseDeferred = utils.ImportNow("PromiseDeferred");
+var PromiseResolved = utils.ImportNow("PromiseResolved");
+
+utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
+ "chain", PromiseChain,
+]);
+
+utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
+ "defer", PromiseDeferred,
+ "accept", PromiseResolved,
+]);
+
+})
diff --git a/src/js/promise.js b/src/js/promise.js
new file mode 100644
index 0000000..8cf6a36
--- /dev/null
+++ b/src/js/promise.js
@@ -0,0 +1,480 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils, extrasUtils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+var promiseCombinedDeferredSymbol =
+ utils.ImportNow("promise_combined_deferred_symbol");
+var promiseHasHandlerSymbol =
+ utils.ImportNow("promise_has_handler_symbol");
+var promiseOnRejectSymbol = utils.ImportNow("promise_on_reject_symbol");
+var promiseOnResolveSymbol =
+ utils.ImportNow("promise_on_resolve_symbol");
+var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
+var promiseStatusSymbol = utils.ImportNow("promise_status_symbol");
+var promiseValueSymbol = utils.ImportNow("promise_value_symbol");
+var SpeciesConstructor;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+ SpeciesConstructor = from.SpeciesConstructor;
+});
+
+// -------------------------------------------------------------------
+
+// Status values: 0 = pending, +1 = resolved, -1 = rejected
+var lastMicrotaskId = 0;
+
+function CreateResolvingFunctions(promise) {
+ var alreadyResolved = false;
+
+ var resolve = value => {
+ if (alreadyResolved === true) return;
+ alreadyResolved = true;
+ PromiseResolve(promise, value);
+ };
+
+ var reject = reason => {
+ if (alreadyResolved === true) return;
+ alreadyResolved = true;
+ PromiseReject(promise, reason);
+ };
+
+ return {
+ __proto__: null,
+ resolve: resolve,
+ reject: reject
+ };
+}
+
+
+var GlobalPromise = function Promise(resolver) {
+ if (resolver === promiseRawSymbol) {
+ return %NewObject(GlobalPromise, new.target);
+ }
+ if (IS_UNDEFINED(new.target)) throw MakeTypeError(kNotAPromise, this);
+ if (!IS_CALLABLE(resolver))
+ throw MakeTypeError(kResolverNotAFunction, resolver);
+
+ var promise = PromiseInit(%NewObject(GlobalPromise, new.target));
+ var callbacks = CreateResolvingFunctions(promise);
+
+ try {
+ %DebugPushPromise(promise, Promise);
+ resolver(callbacks.resolve, callbacks.reject);
+ } catch (e) {
+ %_Call(callbacks.reject, UNDEFINED, e);
+ } finally {
+ %DebugPopPromise();
+ }
+
+ return promise;
+}
+
+// Core functionality.
+
+function PromiseSet(promise, status, value, onResolve, onReject) {
+ SET_PRIVATE(promise, promiseStatusSymbol, status);
+ SET_PRIVATE(promise, promiseValueSymbol, value);
+ SET_PRIVATE(promise, promiseOnResolveSymbol, onResolve);
+ SET_PRIVATE(promise, promiseOnRejectSymbol, onReject);
+ if (DEBUG_IS_ACTIVE) {
+ %DebugPromiseEvent({ promise: promise, status: status, value: value });
+ }
+ return promise;
+}
+
+function PromiseCreateAndSet(status, value) {
+ var promise = new GlobalPromise(promiseRawSymbol);
+ // If debug is active, notify about the newly created promise first.
+ if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED);
+ return PromiseSet(promise, status, value);
+}
+
+function PromiseInit(promise) {
+ return PromiseSet(
+ promise, 0, UNDEFINED, new InternalArray, new InternalArray)
+}
+
+function PromiseDone(promise, status, value, promiseQueue) {
+ if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) {
+ var tasks = GET_PRIVATE(promise, promiseQueue);
+ if (tasks.length) PromiseEnqueue(value, tasks, status);
+ PromiseSet(promise, status, value);
+ }
+}
+
+function PromiseHandle(value, handler, deferred) {
+ try {
+ %DebugPushPromise(deferred.promise, PromiseHandle);
+ var result = handler(value);
+ deferred.resolve(result);
+ } catch (exception) {
+ try { deferred.reject(exception); } catch (e) { }
+ } finally {
+ %DebugPopPromise();
+ }
+}
+
+function PromiseEnqueue(value, tasks, status) {
+ var id, name, instrumenting = DEBUG_IS_ACTIVE;
+ %EnqueueMicrotask(function() {
+ if (instrumenting) {
+ %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+ }
+ for (var i = 0; i < tasks.length; i += 2) {
+ PromiseHandle(value, tasks[i], tasks[i + 1])
+ }
+ if (instrumenting) {
+ %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+ }
+ });
+ if (instrumenting) {
+ id = ++lastMicrotaskId;
+ name = status > 0 ? "Promise.resolve" : "Promise.reject";
+ %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+ }
+}
+
+function PromiseIdResolveHandler(x) { return x }
+function PromiseIdRejectHandler(r) { throw r }
+
+function PromiseNopResolver() {}
+
+// -------------------------------------------------------------------
+// Define exported functions.
+
+// For bootstrapper.
+
+function IsPromise(x) {
+ return IS_RECEIVER(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol);
+}
+
+function PromiseCreate() {
+ return new GlobalPromise(PromiseNopResolver)
+}
+
+function PromiseResolve(promise, x) {
+ if (x === promise) {
+ return PromiseReject(promise, MakeTypeError(kPromiseCyclic, x));
+ }
+ if (IS_RECEIVER(x)) {
+ // 25.4.1.3.2 steps 8-12
+ try {
+ var then = x.then;
+ } catch (e) {
+ return PromiseReject(promise, e);
+ }
+ if (IS_CALLABLE(then)) {
+ // PromiseResolveThenableJob
+ var id, name, instrumenting = DEBUG_IS_ACTIVE;
+ %EnqueueMicrotask(function() {
+ if (instrumenting) {
+ %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
+ }
+ var callbacks = CreateResolvingFunctions(promise);
+ try {
+ %_Call(then, x, callbacks.resolve, callbacks.reject);
+ } catch (e) {
+ %_Call(callbacks.reject, UNDEFINED, e);
+ }
+ if (instrumenting) {
+ %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
+ }
+ });
+ if (instrumenting) {
+ id = ++lastMicrotaskId;
+ name = "PromseResolveThenableJob";
+ %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
+ }
+ return;
+ }
+ }
+ PromiseDone(promise, +1, x, promiseOnResolveSymbol);
+}
+
+function PromiseReject(promise, r) {
+ // Check promise status to confirm that this reject has an effect.
+ // Call runtime for callbacks to the debugger or for unhandled reject.
+ if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) {
+ var debug_is_active = DEBUG_IS_ACTIVE;
+ if (debug_is_active ||
+ !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
+ %PromiseRejectEvent(promise, r, debug_is_active);
+ }
+ }
+ PromiseDone(promise, -1, r, promiseOnRejectSymbol)
+}
+
+// Convenience.
+
+function NewPromiseCapability(C) {
+ if (C === GlobalPromise) {
+ // Optimized case, avoid extra closure.
+ var promise = PromiseInit(new GlobalPromise(promiseRawSymbol));
+ var callbacks = CreateResolvingFunctions(promise);
+ return {
+ promise: promise,
+ resolve: callbacks.resolve,
+ reject: callbacks.reject
+ };
+ }
+
+ var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
+ result.promise = new C((resolve, reject) => {
+ if (!IS_UNDEFINED(result.resolve) || !IS_UNDEFINED(result.reject))
+ throw MakeTypeError(kPromiseExecutorAlreadyInvoked);
+ result.resolve = resolve;
+ result.reject = reject;
+ });
+
+ return result;
+}
+
+function PromiseDeferred() {
+ %IncrementUseCounter(kPromiseDefer);
+ return NewPromiseCapability(this);
+}
+
+function PromiseResolved(x) {
+ %IncrementUseCounter(kPromiseAccept);
+ return %_Call(PromiseCast, this, x);
+}
+
+function PromiseRejected(r) {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(kCalledOnNonObject, PromiseRejected);
+ }
+ if (this === GlobalPromise) {
+ // Optimized case, avoid extra closure.
+ var promise = PromiseCreateAndSet(-1, r);
+ // The debug event for this would always be an uncaught promise reject,
+ // which is usually simply noise. Do not trigger that debug event.
+ %PromiseRejectEvent(promise, r, false);
+ return promise;
+ } else {
+ var promiseCapability = NewPromiseCapability(this);
+ %_Call(promiseCapability.reject, UNDEFINED, r);
+ return promiseCapability.promise;
+ }
+}
+
+// Multi-unwrapped chaining with thenable coercion.
+
+function PromiseThen(onResolve, onReject) {
+ var status = GET_PRIVATE(this, promiseStatusSymbol);
+ if (IS_UNDEFINED(status)) {
+ throw MakeTypeError(kNotAPromise, this);
+ }
+
+ var constructor = SpeciesConstructor(this, GlobalPromise);
+ onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
+ onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
+ var deferred = NewPromiseCapability(constructor);
+ switch (status) {
+ case 0: // Pending
+ GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred);
+ GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred);
+ break;
+ case +1: // Resolved
+ PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol),
+ [onResolve, deferred],
+ +1);
+ break;
+ case -1: // Rejected
+ if (!HAS_DEFINED_PRIVATE(this, promiseHasHandlerSymbol)) {
+ // Promise has already been rejected, but had no handler.
+ // Revoke previously triggered reject event.
+ %PromiseRevokeReject(this);
+ }
+ PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol),
+ [onReject, deferred],
+ -1);
+ break;
+ }
+ // Mark this promise as having handler.
+ SET_PRIVATE(this, promiseHasHandlerSymbol, true);
+ if (DEBUG_IS_ACTIVE) {
+ %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
+ }
+ return deferred.promise;
+}
+
+// Chain is left around for now as an alias for then
+function PromiseChain(onResolve, onReject) {
+ %IncrementUseCounter(kPromiseChain);
+ return %_Call(PromiseThen, this, onResolve, onReject);
+}
+
+function PromiseCatch(onReject) {
+ return this.then(UNDEFINED, onReject);
+}
+
+// Combinators.
+
+function PromiseCast(x) {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(kCalledOnNonObject, PromiseCast);
+ }
+ if (IsPromise(x) && x.constructor === this) return x;
+
+ var promiseCapability = NewPromiseCapability(this);
+ var resolveResult = %_Call(promiseCapability.resolve, UNDEFINED, x);
+ return promiseCapability.promise;
+}
+
+function PromiseAll(iterable) {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(kCalledOnNonObject, "Promise.all");
+ }
+
+ var deferred = NewPromiseCapability(this);
+ var resolutions = new InternalArray();
+ var count;
+
+ function CreateResolveElementFunction(index, values, promiseCapability) {
+ var alreadyCalled = false;
+ return (x) => {
+ if (alreadyCalled === true) return;
+ alreadyCalled = true;
+ values[index] = x;
+ if (--count === 0) {
+ var valuesArray = [];
+ %MoveArrayContents(values, valuesArray);
+ %_Call(promiseCapability.resolve, UNDEFINED, valuesArray);
+ }
+ };
+ }
+
+ try {
+ var i = 0;
+ count = 1;
+ for (var value of iterable) {
+ var nextPromise = this.resolve(value);
+ ++count;
+ nextPromise.then(
+ CreateResolveElementFunction(i, resolutions, deferred),
+ deferred.reject);
+ SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
+ ++i;
+ }
+
+ // 6.d
+ if (--count === 0) {
+ var valuesArray = [];
+ %MoveArrayContents(resolutions, valuesArray);
+ %_Call(deferred.resolve, UNDEFINED, valuesArray);
+ }
+
+ } catch (e) {
+ %_Call(deferred.reject, UNDEFINED, e);
+ }
+ return deferred.promise;
+}
+
+function PromiseRace(iterable) {
+ if (!IS_RECEIVER(this)) {
+ throw MakeTypeError(kCalledOnNonObject, PromiseRace);
+ }
+
+ var deferred = NewPromiseCapability(this);
+ try {
+ for (var value of iterable) {
+ this.resolve(value).then(deferred.resolve, deferred.reject);
+ SET_PRIVATE(deferred.reject, promiseCombinedDeferredSymbol, deferred);
+ }
+ } catch (e) {
+ deferred.reject(e)
+ }
+ return deferred.promise;
+}
+
+
+// Utility for debugger
+
+function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
+ var queue = GET_PRIVATE(promise, promiseOnRejectSymbol);
+ if (IS_UNDEFINED(queue)) return false;
+ for (var i = 0; i < queue.length; i += 2) {
+ var handler = queue[i];
+ if (handler !== PromiseIdRejectHandler) {
+ var deferred = GET_PRIVATE(handler, promiseCombinedDeferredSymbol);
+ if (IS_UNDEFINED(deferred)) return true;
+ if (PromiseHasUserDefinedRejectHandlerRecursive(deferred.promise)) {
+ return true;
+ }
+ } else if (PromiseHasUserDefinedRejectHandlerRecursive(
+ queue[i + 1].promise)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return whether the promise will be handled by a user-defined reject
+// handler somewhere down the promise chain. For this, we do a depth-first
+// search for a reject handler that's not the default PromiseIdRejectHandler.
+function PromiseHasUserDefinedRejectHandler() {
+ return PromiseHasUserDefinedRejectHandlerRecursive(this);
+};
+
+// -------------------------------------------------------------------
+// Install exported functions.
+
+%AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
+%AddNamedProperty(GlobalPromise.prototype, toStringTagSymbol, "Promise",
+ DONT_ENUM | READ_ONLY);
+
+utils.InstallFunctions(GlobalPromise, DONT_ENUM, [
+ "reject", PromiseRejected,
+ "all", PromiseAll,
+ "race", PromiseRace,
+ "resolve", PromiseCast
+]);
+
+utils.InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
+ "then", PromiseThen,
+ "catch", PromiseCatch
+]);
+
+%InstallToContext([
+ "promise_catch", PromiseCatch,
+ "promise_chain", PromiseChain,
+ "promise_create", PromiseCreate,
+ "promise_has_user_defined_reject_handler", PromiseHasUserDefinedRejectHandler,
+ "promise_reject", PromiseReject,
+ "promise_resolve", PromiseResolve,
+ "promise_then", PromiseThen,
+]);
+
+// This allows extras to create promises quickly without building extra
+// resolve/reject closures, and allows them to later resolve and reject any
+// promise without having to hold on to those closures forever.
+utils.InstallFunctions(extrasUtils, 0, [
+ "createPromise", PromiseCreate,
+ "resolvePromise", PromiseResolve,
+ "rejectPromise", PromiseReject
+]);
+
+// TODO(v8:4567): Allow experimental natives to remove function prototype
+[PromiseChain, PromiseDeferred, PromiseResolved].forEach(
+ fn => %FunctionRemovePrototype(fn));
+
+utils.Export(function(to) {
+ to.PromiseChain = PromiseChain;
+ to.PromiseDeferred = PromiseDeferred;
+ to.PromiseResolved = PromiseResolved;
+});
+
+})
diff --git a/src/js/proxy.js b/src/js/proxy.js
new file mode 100644
index 0000000..842bac0
--- /dev/null
+++ b/src/js/proxy.js
@@ -0,0 +1,69 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// ----------------------------------------------------------------------------
+// Imports
+//
+var GlobalProxy = global.Proxy;
+var MakeTypeError;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+//----------------------------------------------------------------------------
+
+function ProxyCreateRevocable(target, handler) {
+ var p = new GlobalProxy(target, handler);
+ return {proxy: p, revoke: () => %JSProxyRevoke(p)};
+}
+
+// -------------------------------------------------------------------
+// Proxy Builtins
+
+// Implements part of ES6 9.5.11 Proxy.[[Enumerate]]:
+// Call the trap, which should return an iterator, exhaust the iterator,
+// and return an array containing the values.
+function ProxyEnumerate(trap, handler, target) {
+ // 7. Let trapResult be ? Call(trap, handler, «target»).
+ var trap_result = %_Call(trap, handler, target);
+ // 8. If Type(trapResult) is not Object, throw a TypeError exception.
+ if (!IS_RECEIVER(trap_result)) {
+ throw MakeTypeError(kProxyEnumerateNonObject);
+ }
+ // 9. Return trapResult.
+ var result = [];
+ for (var it = trap_result.next(); !it.done; it = trap_result.next()) {
+ var key = it.value;
+ // Not yet spec'ed as of 2015-11-25, but will be spec'ed soon:
+ // If the iterator returns a non-string value, throw a TypeError.
+ if (!IS_STRING(key)) {
+ throw MakeTypeError(kProxyEnumerateNonString);
+ }
+ result.push(key);
+ }
+ return result;
+}
+
+//-------------------------------------------------------------------
+
+//Set up non-enumerable properties of the Proxy object.
+utils.InstallFunctions(GlobalProxy, DONT_ENUM, [
+ "revocable", ProxyCreateRevocable
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+ "proxy_enumerate", ProxyEnumerate,
+]);
+
+})
diff --git a/src/js/regexp.js b/src/js/regexp.js
new file mode 100644
index 0000000..eeacd6e
--- /dev/null
+++ b/src/js/regexp.js
@@ -0,0 +1,600 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var FLAG_harmony_tolength;
+var GlobalObject = global.Object;
+var GlobalRegExp = global.RegExp;
+var GlobalRegExpPrototype;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeTypeError;
+var matchSymbol = utils.ImportNow("match_symbol");
+var searchSymbol = utils.ImportNow("search_symbol");
+var splitSymbol = utils.ImportNow("split_symbol");
+
+utils.ImportFromExperimental(function(from) {
+ FLAG_harmony_tolength = from.FLAG_harmony_tolength;
+});
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// Property of the builtins object for recording the result of the last
+// regexp match. The property RegExpLastMatchInfo includes the matchIndices
+// array of the last successful regexp match (an array of start/end index
+// pairs for the match and all the captured substrings), the invariant is
+// that there are at least two capture indeces. The array also contains
+// the subject string for the last successful match.
+var RegExpLastMatchInfo = new InternalPackedArray(
+ 2, // REGEXP_NUMBER_OF_CAPTURES
+ "", // Last subject.
+ UNDEFINED, // Last input - settable with RegExpSetInput.
+ 0, // REGEXP_FIRST_CAPTURE + 0
+ 0 // REGEXP_FIRST_CAPTURE + 1
+);
+
+// -------------------------------------------------------------------
+
+function IsRegExp(o) {
+ if (!IS_RECEIVER(o)) return false;
+ var is_regexp = o[matchSymbol];
+ if (!IS_UNDEFINED(is_regexp)) return TO_BOOLEAN(is_regexp);
+ return IS_REGEXP(o);
+}
+
+
+// ES6 section 21.2.3.2.2
+function RegExpInitialize(object, pattern, flags) {
+ pattern = IS_UNDEFINED(pattern) ? '' : TO_STRING(pattern);
+ flags = IS_UNDEFINED(flags) ? '' : TO_STRING(flags);
+ %RegExpInitializeAndCompile(object, pattern, flags);
+ return object;
+}
+
+
+function PatternFlags(pattern) {
+ return (REGEXP_GLOBAL(pattern) ? 'g' : '') +
+ (REGEXP_IGNORE_CASE(pattern) ? 'i' : '') +
+ (REGEXP_MULTILINE(pattern) ? 'm' : '') +
+ (REGEXP_UNICODE(pattern) ? 'u' : '') +
+ (REGEXP_STICKY(pattern) ? 'y' : '');
+}
+
+
+function RegExpConstructor(pattern, flags) {
+ var newtarget = new.target;
+ var pattern_is_regexp = IsRegExp(pattern);
+
+ if (IS_UNDEFINED(newtarget)) {
+ newtarget = GlobalRegExp;
+
+ // ES6 section 21.2.3.1 step 3.b
+ if (pattern_is_regexp && IS_UNDEFINED(flags) &&
+ pattern.constructor === newtarget) {
+ return pattern;
+ }
+ }
+
+ if (IS_REGEXP(pattern)) {
+ if (IS_UNDEFINED(flags)) flags = PatternFlags(pattern);
+ pattern = REGEXP_SOURCE(pattern);
+
+ } else if (pattern_is_regexp) {
+ var input_pattern = pattern;
+ pattern = pattern.source;
+ if (IS_UNDEFINED(flags)) flags = input_pattern.flags;
+ }
+
+ var object = %NewObject(GlobalRegExp, newtarget);
+ return RegExpInitialize(object, pattern, flags);
+}
+
+
+function RegExpCompileJS(pattern, flags) {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "RegExp.prototype.compile", this);
+ }
+
+ if (IS_REGEXP(pattern)) {
+ if (!IS_UNDEFINED(flags)) throw MakeTypeError(kRegExpFlags);
+
+ flags = PatternFlags(pattern);
+ pattern = REGEXP_SOURCE(pattern);
+ }
+
+ RegExpInitialize(this, pattern, flags);
+
+ // Return undefined for compatibility with JSC.
+ // See http://crbug.com/585775 for web compat details.
+}
+
+
+function DoRegExpExec(regexp, string, index) {
+ return %_RegExpExec(regexp, string, index, RegExpLastMatchInfo);
+}
+
+
+// This is kind of performance sensitive, so we want to avoid unnecessary
+// type checks on inputs. But we also don't want to inline it several times
+// manually, so we use a macro :-)
+macro RETURN_NEW_RESULT_FROM_MATCH_INFO(MATCHINFO, STRING)
+ var numResults = NUMBER_OF_CAPTURES(MATCHINFO) >> 1;
+ var start = MATCHINFO[CAPTURE0];
+ var end = MATCHINFO[CAPTURE1];
+ // Calculate the substring of the first match before creating the result array
+ // to avoid an unnecessary write barrier storing the first result.
+ var first = %_SubString(STRING, start, end);
+ var result = %_RegExpConstructResult(numResults, start, STRING);
+ result[0] = first;
+ if (numResults == 1) return result;
+ var j = REGEXP_FIRST_CAPTURE + 2;
+ for (var i = 1; i < numResults; i++) {
+ start = MATCHINFO[j++];
+ if (start != -1) {
+ end = MATCHINFO[j];
+ result[i] = %_SubString(STRING, start, end);
+ }
+ j++;
+ }
+ return result;
+endmacro
+
+
+function RegExpExecNoTests(regexp, string, start) {
+ // Must be called with RegExp, string and positive integer as arguments.
+ var matchInfo = %_RegExpExec(regexp, string, start, RegExpLastMatchInfo);
+ if (matchInfo !== null) {
+ // ES6 21.2.5.2.2 step 18.
+ if (REGEXP_STICKY(regexp)) regexp.lastIndex = matchInfo[CAPTURE1];
+ RETURN_NEW_RESULT_FROM_MATCH_INFO(matchInfo, string);
+ }
+ regexp.lastIndex = 0;
+ return null;
+}
+
+
+function RegExpExecJS(string) {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'RegExp.prototype.exec', this);
+ }
+
+ string = TO_STRING(string);
+ var lastIndex = this.lastIndex;
+
+ // Conversion is required by the ES2015 specification (RegExpBuiltinExec
+ // algorithm, step 4) even if the value is discarded for non-global RegExps.
+ var i = TO_LENGTH_OR_INTEGER(lastIndex);
+
+ var updateLastIndex = REGEXP_GLOBAL(this) || REGEXP_STICKY(this);
+ if (updateLastIndex) {
+ if (i < 0 || i > string.length) {
+ this.lastIndex = 0;
+ return null;
+ }
+ } else {
+ i = 0;
+ }
+
+ // matchIndices is either null or the RegExpLastMatchInfo array.
+ var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
+
+ if (IS_NULL(matchIndices)) {
+ this.lastIndex = 0;
+ return null;
+ }
+
+ // Successful match.
+ if (updateLastIndex) {
+ this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
+ }
+ RETURN_NEW_RESULT_FROM_MATCH_INFO(matchIndices, string);
+}
+
+
+// One-element cache for the simplified test regexp.
+var regexp_key;
+var regexp_val;
+
+// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
+// that test is defined in terms of String.prototype.exec. However, it probably
+// means the original value of String.prototype.exec, which is what everybody
+// else implements.
+function RegExpTest(string) {
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'RegExp.prototype.test', this);
+ }
+ string = TO_STRING(string);
+
+ var lastIndex = this.lastIndex;
+
+ // Conversion is required by the ES2015 specification (RegExpBuiltinExec
+ // algorithm, step 4) even if the value is discarded for non-global RegExps.
+ var i = TO_LENGTH_OR_INTEGER(lastIndex);
+
+ if (REGEXP_GLOBAL(this) || REGEXP_STICKY(this)) {
+ if (i < 0 || i > string.length) {
+ this.lastIndex = 0;
+ return false;
+ }
+ // matchIndices is either null or the RegExpLastMatchInfo array.
+ var matchIndices = %_RegExpExec(this, string, i, RegExpLastMatchInfo);
+ if (IS_NULL(matchIndices)) {
+ this.lastIndex = 0;
+ return false;
+ }
+ this.lastIndex = RegExpLastMatchInfo[CAPTURE1];
+ return true;
+ } else {
+ // Non-global, non-sticky regexp.
+ // Remove irrelevant preceeding '.*' in a test regexp. The expression
+ // checks whether this.source starts with '.*' and that the third char is
+ // not a '?'. But see https://code.google.com/p/v8/issues/detail?id=3560
+ var regexp = this;
+ var source = REGEXP_SOURCE(regexp);
+ if (regexp.length >= 3 &&
+ %_StringCharCodeAt(regexp, 0) == 46 && // '.'
+ %_StringCharCodeAt(regexp, 1) == 42 && // '*'
+ %_StringCharCodeAt(regexp, 2) != 63) { // '?'
+ regexp = TrimRegExp(regexp);
+ }
+ // matchIndices is either null or the RegExpLastMatchInfo array.
+ var matchIndices = %_RegExpExec(regexp, string, 0, RegExpLastMatchInfo);
+ if (IS_NULL(matchIndices)) {
+ this.lastIndex = 0;
+ return false;
+ }
+ return true;
+ }
+}
+
+function TrimRegExp(regexp) {
+ if (!%_ObjectEquals(regexp_key, regexp)) {
+ regexp_key = regexp;
+ regexp_val =
+ new GlobalRegExp(
+ %_SubString(REGEXP_SOURCE(regexp), 2, REGEXP_SOURCE(regexp).length),
+ (REGEXP_IGNORE_CASE(regexp) ? REGEXP_MULTILINE(regexp) ? "im" : "i"
+ : REGEXP_MULTILINE(regexp) ? "m" : ""));
+ }
+ return regexp_val;
+}
+
+
+function RegExpToString() {
+ if (!IS_REGEXP(this)) {
+ // RegExp.prototype.toString() returns '/(?:)/' as a compatibility fix;
+ // a UseCounter is incremented to track it.
+ // TODO(littledan): Remove this workaround or standardize it
+ if (this === GlobalRegExpPrototype) {
+ %IncrementUseCounter(kRegExpPrototypeToString);
+ return '/(?:)/';
+ }
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'RegExp.prototype.toString', this);
+ }
+ var result = '/' + REGEXP_SOURCE(this) + '/';
+ if (REGEXP_GLOBAL(this)) result += 'g';
+ if (REGEXP_IGNORE_CASE(this)) result += 'i';
+ if (REGEXP_MULTILINE(this)) result += 'm';
+ if (REGEXP_UNICODE(this)) result += 'u';
+ if (REGEXP_STICKY(this)) result += 'y';
+ return result;
+}
+
+
+// ES6 21.2.5.11.
+function RegExpSplit(string, limit) {
+ // TODO(yangguo): allow non-regexp receivers.
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "RegExp.prototype.@@split", this);
+ }
+ var separator = this;
+ var subject = TO_STRING(string);
+
+ limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
+ var length = subject.length;
+
+ if (limit === 0) return [];
+
+ if (length === 0) {
+ if (DoRegExpExec(separator, subject, 0, 0) !== null) return [];
+ return [subject];
+ }
+
+ var currentIndex = 0;
+ var startIndex = 0;
+ var startMatch = 0;
+ var result = new InternalArray();
+
+ outer_loop:
+ while (true) {
+ if (startIndex === length) {
+ result[result.length] = %_SubString(subject, currentIndex, length);
+ break;
+ }
+
+ var matchInfo = DoRegExpExec(separator, subject, startIndex);
+ if (matchInfo === null || length === (startMatch = matchInfo[CAPTURE0])) {
+ result[result.length] = %_SubString(subject, currentIndex, length);
+ break;
+ }
+ var endIndex = matchInfo[CAPTURE1];
+
+ // We ignore a zero-length match at the currentIndex.
+ if (startIndex === endIndex && endIndex === currentIndex) {
+ startIndex++;
+ continue;
+ }
+
+ result[result.length] = %_SubString(subject, currentIndex, startMatch);
+
+ if (result.length === limit) break;
+
+ var matchinfo_len = NUMBER_OF_CAPTURES(matchInfo) + REGEXP_FIRST_CAPTURE;
+ for (var i = REGEXP_FIRST_CAPTURE + 2; i < matchinfo_len; ) {
+ var start = matchInfo[i++];
+ var end = matchInfo[i++];
+ if (end != -1) {
+ result[result.length] = %_SubString(subject, start, end);
+ } else {
+ result[result.length] = UNDEFINED;
+ }
+ if (result.length === limit) break outer_loop;
+ }
+
+ startIndex = currentIndex = endIndex;
+ }
+
+ var array_result = [];
+ %MoveArrayContents(result, array_result);
+ return array_result;
+}
+
+
+// ES6 21.2.5.6.
+function RegExpMatch(string) {
+ // TODO(yangguo): allow non-regexp receivers.
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "RegExp.prototype.@@match", this);
+ }
+ var subject = TO_STRING(string);
+
+ if (!REGEXP_GLOBAL(this)) return RegExpExecNoTests(this, subject, 0);
+ this.lastIndex = 0;
+ var result = %StringMatch(subject, this, RegExpLastMatchInfo);
+ return result;
+}
+
+
+// ES6 21.2.5.9.
+function RegExpSearch(string) {
+ // TODO(yangguo): allow non-regexp receivers.
+ if (!IS_REGEXP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "RegExp.prototype.@@search", this);
+ }
+ var match = DoRegExpExec(this, TO_STRING(string), 0);
+ if (match) return match[CAPTURE0];
+ return -1;
+}
+
+
+// Getters for the static properties lastMatch, lastParen, leftContext, and
+// rightContext of the RegExp constructor. The properties are computed based
+// on the captures array of the last successful match and the subject string
+// of the last successful match.
+function RegExpGetLastMatch() {
+ var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
+ return %_SubString(regExpSubject,
+ RegExpLastMatchInfo[CAPTURE0],
+ RegExpLastMatchInfo[CAPTURE1]);
+}
+
+
+function RegExpGetLastParen() {
+ var length = NUMBER_OF_CAPTURES(RegExpLastMatchInfo);
+ if (length <= 2) return ''; // There were no captures.
+ // We match the SpiderMonkey behavior: return the substring defined by the
+ // last pair (after the first pair) of elements of the capture array even if
+ // it is empty.
+ var regExpSubject = LAST_SUBJECT(RegExpLastMatchInfo);
+ var start = RegExpLastMatchInfo[CAPTURE(length - 2)];
+ var end = RegExpLastMatchInfo[CAPTURE(length - 1)];
+ if (start != -1 && end != -1) {
+ return %_SubString(regExpSubject, start, end);
+ }
+ return "";
+}
+
+
+function RegExpGetLeftContext() {
+ var start_index;
+ var subject;
+ start_index = RegExpLastMatchInfo[CAPTURE0];
+ subject = LAST_SUBJECT(RegExpLastMatchInfo);
+ return %_SubString(subject, 0, start_index);
+}
+
+
+function RegExpGetRightContext() {
+ var start_index;
+ var subject;
+ start_index = RegExpLastMatchInfo[CAPTURE1];
+ subject = LAST_SUBJECT(RegExpLastMatchInfo);
+ return %_SubString(subject, start_index, subject.length);
+}
+
+
+// The properties $1..$9 are the first nine capturing substrings of the last
+// successful match, or ''. The function RegExpMakeCaptureGetter will be
+// called with indices from 1 to 9.
+function RegExpMakeCaptureGetter(n) {
+ return function foo() {
+ var index = n * 2;
+ if (index >= NUMBER_OF_CAPTURES(RegExpLastMatchInfo)) return '';
+ var matchStart = RegExpLastMatchInfo[CAPTURE(index)];
+ var matchEnd = RegExpLastMatchInfo[CAPTURE(index + 1)];
+ if (matchStart == -1 || matchEnd == -1) return '';
+ return %_SubString(LAST_SUBJECT(RegExpLastMatchInfo), matchStart, matchEnd);
+ };
+}
+
+
+// ES6 21.2.5.4.
+function RegExpGetGlobal() {
+ if (!IS_REGEXP(this)) {
+ // TODO(littledan): Remove this RegExp compat workaround
+ if (this === GlobalRegExpPrototype) {
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.global");
+ }
+ return !!REGEXP_GLOBAL(this);
+}
+%FunctionSetName(RegExpGetGlobal, "RegExp.prototype.global");
+%SetNativeFlag(RegExpGetGlobal);
+
+
+// ES6 21.2.5.5.
+function RegExpGetIgnoreCase() {
+ if (!IS_REGEXP(this)) {
+ // TODO(littledan): Remove this RegExp compat workaround
+ if (this === GlobalRegExpPrototype) {
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.ignoreCase");
+ }
+ return !!REGEXP_IGNORE_CASE(this);
+}
+%FunctionSetName(RegExpGetIgnoreCase, "RegExp.prototype.ignoreCase");
+%SetNativeFlag(RegExpGetIgnoreCase);
+
+
+// ES6 21.2.5.7.
+function RegExpGetMultiline() {
+ if (!IS_REGEXP(this)) {
+ // TODO(littledan): Remove this RegExp compat workaround
+ if (this === GlobalRegExpPrototype) {
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.multiline");
+ }
+ return !!REGEXP_MULTILINE(this);
+}
+%FunctionSetName(RegExpGetMultiline, "RegExp.prototype.multiline");
+%SetNativeFlag(RegExpGetMultiline);
+
+
+// ES6 21.2.5.10.
+function RegExpGetSource() {
+ if (!IS_REGEXP(this)) {
+ // TODO(littledan): Remove this RegExp compat workaround
+ if (this === GlobalRegExpPrototype) {
+ return UNDEFINED;
+ }
+ throw MakeTypeError(kRegExpNonRegExp, "RegExp.prototype.source");
+ }
+ return REGEXP_SOURCE(this);
+}
+%FunctionSetName(RegExpGetSource, "RegExp.prototype.source");
+%SetNativeFlag(RegExpGetSource);
+
+// -------------------------------------------------------------------
+
+%FunctionSetInstanceClassName(GlobalRegExp, 'RegExp');
+GlobalRegExpPrototype = new GlobalObject();
+%FunctionSetPrototype(GlobalRegExp, GlobalRegExpPrototype);
+%AddNamedProperty(
+ GlobalRegExp.prototype, 'constructor', GlobalRegExp, DONT_ENUM);
+%SetCode(GlobalRegExp, RegExpConstructor);
+
+utils.InstallFunctions(GlobalRegExp.prototype, DONT_ENUM, [
+ "exec", RegExpExecJS,
+ "test", RegExpTest,
+ "toString", RegExpToString,
+ "compile", RegExpCompileJS,
+ matchSymbol, RegExpMatch,
+ searchSymbol, RegExpSearch,
+ splitSymbol, RegExpSplit,
+]);
+
+utils.InstallGetter(GlobalRegExp.prototype, 'global', RegExpGetGlobal);
+utils.InstallGetter(GlobalRegExp.prototype, 'ignoreCase', RegExpGetIgnoreCase);
+utils.InstallGetter(GlobalRegExp.prototype, 'multiline', RegExpGetMultiline);
+utils.InstallGetter(GlobalRegExp.prototype, 'source', RegExpGetSource);
+
+// The length of compile is 1 in SpiderMonkey.
+%FunctionSetLength(GlobalRegExp.prototype.compile, 1);
+
+// The properties `input` and `$_` are aliases for each other. When this
+// value is set the value it is set to is coerced to a string.
+// Getter and setter for the input.
+var RegExpGetInput = function() {
+ var regExpInput = LAST_INPUT(RegExpLastMatchInfo);
+ return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
+};
+var RegExpSetInput = function(string) {
+ LAST_INPUT(RegExpLastMatchInfo) = TO_STRING(string);
+};
+
+%OptimizeObjectForAddingMultipleProperties(GlobalRegExp, 22);
+utils.InstallGetterSetter(GlobalRegExp, 'input', RegExpGetInput, RegExpSetInput,
+ DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$_', RegExpGetInput, RegExpSetInput,
+ DONT_ENUM | DONT_DELETE);
+
+
+var NoOpSetter = function(ignored) {};
+
+
+// Static properties set by a successful match.
+utils.InstallGetterSetter(GlobalRegExp, 'lastMatch', RegExpGetLastMatch,
+ NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$&', RegExpGetLastMatch, NoOpSetter,
+ DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'lastParen', RegExpGetLastParen,
+ NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$+', RegExpGetLastParen, NoOpSetter,
+ DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'leftContext', RegExpGetLeftContext,
+ NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, '$`', RegExpGetLeftContext, NoOpSetter,
+ DONT_ENUM | DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, 'rightContext', RegExpGetRightContext,
+ NoOpSetter, DONT_DELETE);
+utils.InstallGetterSetter(GlobalRegExp, "$'", RegExpGetRightContext, NoOpSetter,
+ DONT_ENUM | DONT_DELETE);
+
+for (var i = 1; i < 10; ++i) {
+ utils.InstallGetterSetter(GlobalRegExp, '$' + i, RegExpMakeCaptureGetter(i),
+ NoOpSetter, DONT_DELETE);
+}
+%ToFastProperties(GlobalRegExp);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.RegExpExec = DoRegExpExec;
+ to.RegExpExecNoTests = RegExpExecNoTests;
+ to.RegExpLastMatchInfo = RegExpLastMatchInfo;
+ to.RegExpTest = RegExpTest;
+});
+
+})
diff --git a/src/js/runtime.js b/src/js/runtime.js
new file mode 100644
index 0000000..301d75a
--- /dev/null
+++ b/src/js/runtime.js
@@ -0,0 +1,191 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This files contains runtime support implemented in JavaScript.
+
+// CAUTION: Some of the functions specified in this file are called
+// directly from compiled code. These are the functions with names in
+// ALL CAPS. The compiled code passes the first argument in 'this'.
+
+
+// The following declarations are shared with other native JS files.
+// They are all declared at this one spot to avoid redeclaration errors.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+var FLAG_harmony_species;
+var GlobalArray = global.Array;
+var GlobalBoolean = global.Boolean;
+var GlobalString = global.String;
+var MakeRangeError;
+var MakeTypeError;
+var speciesSymbol;
+
+utils.Import(function(from) {
+ MakeRangeError = from.MakeRangeError;
+ MakeTypeError = from.MakeTypeError;
+ speciesSymbol = from.species_symbol;
+});
+
+utils.ImportFromExperimental(function(from) {
+ FLAG_harmony_species = from.FLAG_harmony_species;
+});
+
+// ----------------------------------------------------------------------------
+
+/* -----------------------------
+ - - - H e l p e r s - - -
+ -----------------------------
+*/
+
+function CONCAT_ITERABLE_TO_ARRAY(iterable) {
+ return %concat_iterable_to_array(this, iterable);
+};
+
+
+/* -------------------------------------
+ - - - C o n v e r s i o n s - - -
+ -------------------------------------
+*/
+
+// ES5, section 9.12
+function SameValue(x, y) {
+ if (typeof x != typeof y) return false;
+ if (IS_NUMBER(x)) {
+ if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
+ // x is +0 and y is -0 or vice versa.
+ if (x === 0 && y === 0 && %_IsMinusZero(x) != %_IsMinusZero(y)) {
+ return false;
+ }
+ }
+ if (IS_SIMD_VALUE(x)) return %SimdSameValue(x, y);
+ return x === y;
+}
+
+
+// ES6, section 7.2.4
+function SameValueZero(x, y) {
+ if (typeof x != typeof y) return false;
+ if (IS_NUMBER(x)) {
+ if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) return true;
+ }
+ if (IS_SIMD_VALUE(x)) return %SimdSameValueZero(x, y);
+ return x === y;
+}
+
+
+function ConcatIterableToArray(target, iterable) {
+ var index = target.length;
+ for (var element of iterable) {
+ AddIndexedProperty(target, index++, element);
+ }
+ return target;
+}
+
+
+/* ---------------------------------
+ - - - U t i l i t i e s - - -
+ ---------------------------------
+*/
+
+
+// This function should be called rather than %AddElement in contexts where the
+// argument might not be less than 2**32-1. ES2015 ToLength semantics mean that
+// this is a concern at basically all callsites.
+function AddIndexedProperty(obj, index, value) {
+ if (index === TO_UINT32(index) && index !== kMaxUint32) {
+ %AddElement(obj, index, value);
+ } else {
+ %AddNamedProperty(obj, TO_STRING(index), value, NONE);
+ }
+}
+%SetForceInlineFlag(AddIndexedProperty);
+
+
+function ToPositiveInteger(x, rangeErrorIndex) {
+ var i = TO_INTEGER_MAP_MINUS_ZERO(x);
+ if (i < 0) throw MakeRangeError(rangeErrorIndex);
+ return i;
+}
+
+
+function MaxSimple(a, b) {
+ return a > b ? a : b;
+}
+
+
+function MinSimple(a, b) {
+ return a > b ? b : a;
+}
+
+
+%SetForceInlineFlag(MaxSimple);
+%SetForceInlineFlag(MinSimple);
+
+
+// ES2015 7.3.20
+// For the fallback with --harmony-species off, there are two possible choices:
+// - "conservative": return defaultConstructor
+// - "not conservative": return object.constructor
+// This fallback path is only needed in the transition to ES2015, and the
+// choice is made simply to preserve the previous behavior so that we don't
+// have a three-step upgrade: old behavior, unspecified intermediate behavior,
+// and ES2015.
+// In some cases, we were "conservative" (e.g., ArrayBuffer, RegExp), and in
+// other cases we were "not conservative (e.g., TypedArray, Promise).
+function SpeciesConstructor(object, defaultConstructor, conservative) {
+ if (FLAG_harmony_species) {
+ var constructor = object.constructor;
+ if (IS_UNDEFINED(constructor)) {
+ return defaultConstructor;
+ }
+ if (!IS_RECEIVER(constructor)) {
+ throw MakeTypeError(kConstructorNotReceiver);
+ }
+ var species = constructor[speciesSymbol];
+ if (IS_NULL_OR_UNDEFINED(species)) {
+ return defaultConstructor;
+ }
+ if (%IsConstructor(species)) {
+ return species;
+ }
+ throw MakeTypeError(kSpeciesNotConstructor);
+ } else {
+ return conservative ? defaultConstructor : object.constructor;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// NOTE: Setting the prototype for Array must take place as early as
+// possible due to code generation for array literals. When
+// generating code for a array literal a boilerplate array is created
+// that is cloned when running the code. It is essential that the
+// boilerplate gets the right prototype.
+%FunctionSetPrototype(GlobalArray, new GlobalArray(0));
+
+// ----------------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.AddIndexedProperty = AddIndexedProperty;
+ to.MaxSimple = MaxSimple;
+ to.MinSimple = MinSimple;
+ to.SameValue = SameValue;
+ to.SameValueZero = SameValueZero;
+ to.ToPositiveInteger = ToPositiveInteger;
+ to.SpeciesConstructor = SpeciesConstructor;
+});
+
+%InstallToContext([
+ "concat_iterable_to_array_builtin", CONCAT_ITERABLE_TO_ARRAY,
+]);
+
+%InstallToContext([
+ "concat_iterable_to_array", ConcatIterableToArray,
+]);
+
+})
diff --git a/src/js/spread.js b/src/js/spread.js
new file mode 100644
index 0000000..235c91a
--- /dev/null
+++ b/src/js/spread.js
@@ -0,0 +1,56 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+'use strict';
+
+// -------------------------------------------------------------------
+// Imports
+var InternalArray = utils.InternalArray;
+var MakeTypeError;
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function SpreadArguments() {
+ var count = %_ArgumentsLength();
+ var args = new InternalArray();
+
+ for (var i = 0; i < count; ++i) {
+ var array = %_Arguments(i);
+ var length = array.length;
+ for (var j = 0; j < length; ++j) {
+ args.push(array[j]);
+ }
+ }
+
+ return args;
+}
+
+
+function SpreadIterable(collection) {
+ if (IS_NULL_OR_UNDEFINED(collection)) {
+ throw MakeTypeError(kNotIterable, collection);
+ }
+
+ var args = new InternalArray();
+ for (var value of collection) {
+ args.push(value);
+ }
+ return args;
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+%InstallToContext([
+ "spread_arguments", SpreadArguments,
+ "spread_iterable", SpreadIterable,
+]);
+
+})
diff --git a/src/js/string-iterator.js b/src/js/string-iterator.js
new file mode 100644
index 0000000..3c331dd
--- /dev/null
+++ b/src/js/string-iterator.js
@@ -0,0 +1,102 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalString = global.String;
+var IteratorPrototype = utils.ImportNow("IteratorPrototype");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var stringIteratorIteratedStringSymbol =
+ utils.ImportNow("string_iterator_iterated_string_symbol");
+var stringIteratorNextIndexSymbol =
+ utils.ImportNow("string_iterator_next_index_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+function StringIterator() {}
+
+
+// 21.1.5.1 CreateStringIterator Abstract Operation
+function CreateStringIterator(string) {
+ var s = TO_STRING(string);
+ var iterator = new StringIterator;
+ SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, s);
+ SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, 0);
+ return iterator;
+}
+
+
+// ES6 section 21.1.5.2.1 %StringIteratorPrototype%.next ( )
+function StringIteratorNext() {
+ var iterator = this;
+ var value = UNDEFINED;
+ var done = true;
+
+ if (!IS_RECEIVER(iterator) ||
+ !HAS_DEFINED_PRIVATE(iterator, stringIteratorNextIndexSymbol)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'String Iterator.prototype.next');
+ }
+
+ var s = GET_PRIVATE(iterator, stringIteratorIteratedStringSymbol);
+ if (!IS_UNDEFINED(s)) {
+ var position = GET_PRIVATE(iterator, stringIteratorNextIndexSymbol);
+ var length = TO_UINT32(s.length);
+ if (position >= length) {
+ SET_PRIVATE(iterator, stringIteratorIteratedStringSymbol, UNDEFINED);
+ } else {
+ var first = %_StringCharCodeAt(s, position);
+ value = %_StringCharFromCode(first);
+ done = false;
+ position++;
+
+ if (first >= 0xD800 && first <= 0xDBFF && position < length) {
+ var second = %_StringCharCodeAt(s, position);
+ if (second >= 0xDC00 && second <= 0xDFFF) {
+ value += %_StringCharFromCode(second);
+ position++;
+ }
+ }
+
+ SET_PRIVATE(iterator, stringIteratorNextIndexSymbol, position);
+ }
+ }
+ return %_CreateIterResultObject(value, done);
+}
+
+
+// 21.1.3.27 String.prototype [ @@iterator ]( )
+function StringPrototypeIterator() {
+ return CreateStringIterator(this);
+}
+
+//-------------------------------------------------------------------
+
+%FunctionSetPrototype(StringIterator, {__proto__: IteratorPrototype});
+%FunctionSetInstanceClassName(StringIterator, 'String Iterator');
+
+utils.InstallFunctions(StringIterator.prototype, DONT_ENUM, [
+ 'next', StringIteratorNext
+]);
+%AddNamedProperty(StringIterator.prototype, toStringTagSymbol,
+ "String Iterator", READ_ONLY | DONT_ENUM);
+
+utils.SetFunctionName(StringPrototypeIterator, iteratorSymbol);
+%AddNamedProperty(GlobalString.prototype, iteratorSymbol,
+ StringPrototypeIterator, DONT_ENUM);
+
+})
diff --git a/src/js/string.js b/src/js/string.js
new file mode 100644
index 0000000..b220038
--- /dev/null
+++ b/src/js/string.js
@@ -0,0 +1,1112 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayIndexOf;
+var ArrayJoin;
+var GlobalRegExp = global.RegExp;
+var GlobalString = global.String;
+var InternalArray = utils.InternalArray;
+var InternalPackedArray = utils.InternalPackedArray;
+var MakeRangeError;
+var MakeTypeError;
+var MathMax;
+var MathMin;
+var matchSymbol = utils.ImportNow("match_symbol");
+var RegExpExec;
+var RegExpExecNoTests;
+var RegExpLastMatchInfo;
+var searchSymbol = utils.ImportNow("search_symbol");
+var splitSymbol = utils.ImportNow("split_symbol");
+
+utils.Import(function(from) {
+ ArrayIndexOf = from.ArrayIndexOf;
+ ArrayJoin = from.ArrayJoin;
+ MakeRangeError = from.MakeRangeError;
+ MakeTypeError = from.MakeTypeError;
+ MathMax = from.MathMax;
+ MathMin = from.MathMin;
+ RegExpExec = from.RegExpExec;
+ RegExpExecNoTests = from.RegExpExecNoTests;
+ RegExpLastMatchInfo = from.RegExpLastMatchInfo;
+});
+
+//-------------------------------------------------------------------
+
+// ECMA-262 section 15.5.4.2
+function StringToString() {
+ if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
+ throw MakeTypeError(kNotGeneric, 'String.prototype.toString');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ECMA-262 section 15.5.4.3
+function StringValueOf() {
+ if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) {
+ throw MakeTypeError(kNotGeneric, 'String.prototype.valueOf');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ECMA-262, section 15.5.4.4
+function StringCharAtJS(pos) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
+
+ var result = %_StringCharAt(this, pos);
+ if (%_IsSmi(result)) {
+ result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
+ }
+ return result;
+}
+
+
+// ECMA-262 section 15.5.4.5
+function StringCharCodeAtJS(pos) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
+
+ var result = %_StringCharCodeAt(this, pos);
+ if (!%_IsSmi(result)) {
+ result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
+ }
+ return result;
+}
+
+
+// ECMA-262, section 15.5.4.6
+function StringConcat(other /* and more */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.concat");
+ var len = %_ArgumentsLength();
+ var this_as_string = TO_STRING(this);
+ if (len === 1) {
+ return this_as_string + TO_STRING(other);
+ }
+ var parts = new InternalArray(len + 1);
+ parts[0] = this_as_string;
+ for (var i = 0; i < len; i++) {
+ var part = %_Arguments(i);
+ parts[i + 1] = TO_STRING(part);
+ }
+ return %StringBuilderConcat(parts, len + 1, "");
+}
+
+
+// ECMA-262 section 15.5.4.7
+function StringIndexOfJS(pattern /* position */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.indexOf");
+
+ var subject = TO_STRING(this);
+ pattern = TO_STRING(pattern);
+ var index = 0;
+ if (%_ArgumentsLength() > 1) {
+ index = %_Arguments(1); // position
+ index = TO_INTEGER(index);
+ if (index < 0) index = 0;
+ if (index > subject.length) index = subject.length;
+ }
+ return %StringIndexOf(subject, pattern, index);
+}
+
+
+// ECMA-262 section 15.5.4.8
+function StringLastIndexOfJS(pat /* position */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.lastIndexOf");
+
+ var sub = TO_STRING(this);
+ var subLength = sub.length;
+ var pat = TO_STRING(pat);
+ var patLength = pat.length;
+ var index = subLength - patLength;
+ if (%_ArgumentsLength() > 1) {
+ var position = TO_NUMBER(%_Arguments(1));
+ if (!NUMBER_IS_NAN(position)) {
+ position = TO_INTEGER(position);
+ if (position < 0) {
+ position = 0;
+ }
+ if (position + patLength < subLength) {
+ index = position;
+ }
+ }
+ }
+ if (index < 0) {
+ return -1;
+ }
+ return %StringLastIndexOf(sub, pat, index);
+}
+
+
+// ECMA-262 section 15.5.4.9
+//
+// This function is implementation specific. For now, we do not
+// do anything locale specific.
+function StringLocaleCompareJS(other) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.localeCompare");
+
+ return %StringLocaleCompare(TO_STRING(this), TO_STRING(other));
+}
+
+
+// ES6 21.1.3.11.
+function StringMatchJS(pattern) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.match");
+
+ if (!IS_NULL_OR_UNDEFINED(pattern)) {
+ var matcher = pattern[matchSymbol];
+ if (!IS_UNDEFINED(matcher)) {
+ return %_Call(matcher, pattern, this);
+ }
+ }
+
+ var subject = TO_STRING(this);
+
+ // Non-regexp argument.
+ var regexp = new GlobalRegExp(pattern);
+ return RegExpExecNoTests(regexp, subject, 0);
+}
+
+
+// ECMA-262 v6, section 21.1.3.12
+//
+// For now we do nothing, as proper normalization requires big tables.
+// If Intl is enabled, then i18n.js will override it and provide the the
+// proper functionality.
+function StringNormalizeJS() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.normalize");
+ var s = TO_STRING(this);
+
+ var formArg = %_Arguments(0);
+ var form = IS_UNDEFINED(formArg) ? 'NFC' : TO_STRING(formArg);
+
+ var NORMALIZATION_FORMS = ['NFC', 'NFD', 'NFKC', 'NFKD'];
+ var normalizationForm = %_Call(ArrayIndexOf, NORMALIZATION_FORMS, form);
+ if (normalizationForm === -1) {
+ throw MakeRangeError(kNormalizationForm,
+ %_Call(ArrayJoin, NORMALIZATION_FORMS, ', '));
+ }
+
+ return s;
+}
+
+
+// This has the same size as the RegExpLastMatchInfo array, and can be used
+// for functions that expect that structure to be returned. It is used when
+// the needle is a string rather than a regexp. In this case we can't update
+// lastMatchArray without erroneously affecting the properties on the global
+// RegExp object.
+var reusableMatchInfo = [2, "", "", -1, -1];
+
+
+// ECMA-262, section 15.5.4.11
+function StringReplace(search, replace) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.replace");
+
+ var subject = TO_STRING(this);
+
+ // Decision tree for dispatch
+ // .. regexp search
+ // .... string replace
+ // ...... non-global search
+ // ........ empty string replace
+ // ........ non-empty string replace (with $-expansion)
+ // ...... global search
+ // ........ no need to circumvent last match info override
+ // ........ need to circument last match info override
+ // .... function replace
+ // ...... global search
+ // ...... non-global search
+ // .. string search
+ // .... special case that replaces with one single character
+ // ...... function replace
+ // ...... string replace (with $-expansion)
+
+ if (IS_REGEXP(search)) {
+ if (!IS_CALLABLE(replace)) {
+ replace = TO_STRING(replace);
+
+ if (!REGEXP_GLOBAL(search)) {
+ // Non-global regexp search, string replace.
+ var match = RegExpExec(search, subject, 0);
+ if (match == null) {
+ search.lastIndex = 0
+ return subject;
+ }
+ if (replace.length == 0) {
+ return %_SubString(subject, 0, match[CAPTURE0]) +
+ %_SubString(subject, match[CAPTURE1], subject.length)
+ }
+ return ExpandReplacement(replace, subject, RegExpLastMatchInfo,
+ %_SubString(subject, 0, match[CAPTURE0])) +
+ %_SubString(subject, match[CAPTURE1], subject.length);
+ }
+
+ // Global regexp search, string replace.
+ search.lastIndex = 0;
+ return %StringReplaceGlobalRegExpWithString(
+ subject, search, replace, RegExpLastMatchInfo);
+ }
+
+ if (REGEXP_GLOBAL(search)) {
+ // Global regexp search, function replace.
+ return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
+ }
+ // Non-global regexp search, function replace.
+ return StringReplaceNonGlobalRegExpWithFunction(subject, search, replace);
+ }
+
+ search = TO_STRING(search);
+
+ if (search.length == 1 &&
+ subject.length > 0xFF &&
+ IS_STRING(replace) &&
+ %StringIndexOf(replace, '$', 0) < 0) {
+ // Searching by traversing a cons string tree and replace with cons of
+ // slices works only when the replaced string is a single character, being
+ // replaced by a simple string and only pays off for long strings.
+ return %StringReplaceOneCharWithString(subject, search, replace);
+ }
+ var start = %StringIndexOf(subject, search, 0);
+ if (start < 0) return subject;
+ var end = start + search.length;
+
+ var result = %_SubString(subject, 0, start);
+
+ // Compute the string to replace with.
+ if (IS_CALLABLE(replace)) {
+ result += replace(search, start, subject);
+ } else {
+ reusableMatchInfo[CAPTURE0] = start;
+ reusableMatchInfo[CAPTURE1] = end;
+ result = ExpandReplacement(TO_STRING(replace),
+ subject,
+ reusableMatchInfo,
+ result);
+ }
+
+ return result + %_SubString(subject, end, subject.length);
+}
+
+
+// Expand the $-expressions in the string and return a new string with
+// the result.
+function ExpandReplacement(string, subject, matchInfo, result) {
+ var length = string.length;
+ var next = %StringIndexOf(string, '$', 0);
+ if (next < 0) {
+ if (length > 0) result += string;
+ return result;
+ }
+
+ if (next > 0) result += %_SubString(string, 0, next);
+
+ while (true) {
+ var expansion = '$';
+ var position = next + 1;
+ if (position < length) {
+ var peek = %_StringCharCodeAt(string, position);
+ if (peek == 36) { // $$
+ ++position;
+ result += '$';
+ } else if (peek == 38) { // $& - match
+ ++position;
+ result +=
+ %_SubString(subject, matchInfo[CAPTURE0], matchInfo[CAPTURE1]);
+ } else if (peek == 96) { // $` - prefix
+ ++position;
+ result += %_SubString(subject, 0, matchInfo[CAPTURE0]);
+ } else if (peek == 39) { // $' - suffix
+ ++position;
+ result += %_SubString(subject, matchInfo[CAPTURE1], subject.length);
+ } else if (peek >= 48 && peek <= 57) {
+ // Valid indices are $1 .. $9, $01 .. $09 and $10 .. $99
+ var scaled_index = (peek - 48) << 1;
+ var advance = 1;
+ var number_of_captures = NUMBER_OF_CAPTURES(matchInfo);
+ if (position + 1 < string.length) {
+ var next = %_StringCharCodeAt(string, position + 1);
+ if (next >= 48 && next <= 57) {
+ var new_scaled_index = scaled_index * 10 + ((next - 48) << 1);
+ if (new_scaled_index < number_of_captures) {
+ scaled_index = new_scaled_index;
+ advance = 2;
+ }
+ }
+ }
+ if (scaled_index != 0 && scaled_index < number_of_captures) {
+ var start = matchInfo[CAPTURE(scaled_index)];
+ if (start >= 0) {
+ result +=
+ %_SubString(subject, start, matchInfo[CAPTURE(scaled_index + 1)]);
+ }
+ position += advance;
+ } else {
+ result += '$';
+ }
+ } else {
+ result += '$';
+ }
+ } else {
+ result += '$';
+ }
+
+ // Go the the next $ in the string.
+ next = %StringIndexOf(string, '$', position);
+
+ // Return if there are no more $ characters in the string. If we
+ // haven't reached the end, we need to append the suffix.
+ if (next < 0) {
+ if (position < length) {
+ result += %_SubString(string, position, length);
+ }
+ return result;
+ }
+
+ // Append substring between the previous and the next $ character.
+ if (next > position) {
+ result += %_SubString(string, position, next);
+ }
+ }
+ return result;
+}
+
+
+// Compute the string of a given regular expression capture.
+function CaptureString(string, lastCaptureInfo, index) {
+ // Scale the index.
+ var scaled = index << 1;
+ // Compute start and end.
+ var start = lastCaptureInfo[CAPTURE(scaled)];
+ // If start isn't valid, return undefined.
+ if (start < 0) return;
+ var end = lastCaptureInfo[CAPTURE(scaled + 1)];
+ return %_SubString(string, start, end);
+}
+
+
+// TODO(lrn): This array will survive indefinitely if replace is never
+// called again. However, it will be empty, since the contents are cleared
+// in the finally block.
+var reusableReplaceArray = new InternalArray(4);
+
+// Helper function for replacing regular expressions with the result of a
+// function application in String.prototype.replace.
+function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) {
+ var resultArray = reusableReplaceArray;
+ if (resultArray) {
+ reusableReplaceArray = null;
+ } else {
+ // Inside a nested replace (replace called from the replacement function
+ // of another replace) or we have failed to set the reusable array
+ // back due to an exception in a replacement function. Create a new
+ // array to use in the future, or until the original is written back.
+ resultArray = new InternalArray(16);
+ }
+ var res = %RegExpExecMultiple(regexp,
+ subject,
+ RegExpLastMatchInfo,
+ resultArray);
+ regexp.lastIndex = 0;
+ if (IS_NULL(res)) {
+ // No matches at all.
+ reusableReplaceArray = resultArray;
+ return subject;
+ }
+ var len = res.length;
+ if (NUMBER_OF_CAPTURES(RegExpLastMatchInfo) == 2) {
+ // If the number of captures is two then there are no explicit captures in
+ // the regexp, just the implicit capture that captures the whole match. In
+ // this case we can simplify quite a bit and end up with something faster.
+ // The builder will consist of some integers that indicate slices of the
+ // input string and some replacements that were returned from the replace
+ // function.
+ var match_start = 0;
+ for (var i = 0; i < len; i++) {
+ var elem = res[i];
+ if (%_IsSmi(elem)) {
+ // Integers represent slices of the original string.
+ if (elem > 0) {
+ match_start = (elem >> 11) + (elem & 0x7ff);
+ } else {
+ match_start = res[++i] - elem;
+ }
+ } else {
+ var func_result = replace(elem, match_start, subject);
+ // Overwrite the i'th element in the results with the string we got
+ // back from the callback function.
+ res[i] = TO_STRING(func_result);
+ match_start += elem.length;
+ }
+ }
+ } else {
+ for (var i = 0; i < len; i++) {
+ var elem = res[i];
+ if (!%_IsSmi(elem)) {
+ // elem must be an Array.
+ // Use the apply argument as backing for global RegExp properties.
+ var func_result = %Apply(replace, UNDEFINED, elem, 0, elem.length);
+ // Overwrite the i'th element in the results with the string we got
+ // back from the callback function.
+ res[i] = TO_STRING(func_result);
+ }
+ }
+ }
+ var result = %StringBuilderConcat(res, len, subject);
+ resultArray.length = 0;
+ reusableReplaceArray = resultArray;
+ return result;
+}
+
+
+function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) {
+ var matchInfo = RegExpExec(regexp, subject, 0);
+ if (IS_NULL(matchInfo)) {
+ regexp.lastIndex = 0;
+ return subject;
+ }
+ var index = matchInfo[CAPTURE0];
+ var result = %_SubString(subject, 0, index);
+ var endOfMatch = matchInfo[CAPTURE1];
+ // Compute the parameter list consisting of the match, captures, index,
+ // and subject for the replace function invocation.
+ // The number of captures plus one for the match.
+ var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
+ var replacement;
+ if (m == 1) {
+ // No captures, only the match, which is always valid.
+ var s = %_SubString(subject, index, endOfMatch);
+ // Don't call directly to avoid exposing the built-in global object.
+ replacement = replace(s, index, subject);
+ } else {
+ var parameters = new InternalArray(m + 2);
+ for (var j = 0; j < m; j++) {
+ parameters[j] = CaptureString(subject, matchInfo, j);
+ }
+ parameters[j] = index;
+ parameters[j + 1] = subject;
+
+ replacement = %Apply(replace, UNDEFINED, parameters, 0, j + 2);
+ }
+
+ result += replacement; // The add method converts to string if necessary.
+ // Can't use matchInfo any more from here, since the function could
+ // overwrite it.
+ return result + %_SubString(subject, endOfMatch, subject.length);
+}
+
+
+// ES6 21.1.3.15.
+function StringSearch(pattern) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.search");
+
+ if (!IS_NULL_OR_UNDEFINED(pattern)) {
+ var searcher = pattern[searchSymbol];
+ if (!IS_UNDEFINED(searcher)) {
+ return %_Call(searcher, pattern, this);
+ }
+ }
+
+ var subject = TO_STRING(this);
+ var regexp = new GlobalRegExp(pattern);
+ return %_Call(regexp[searchSymbol], regexp, subject);
+}
+
+
+// ECMA-262 section 15.5.4.13
+function StringSlice(start, end) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.slice");
+
+ var s = TO_STRING(this);
+ var s_len = s.length;
+ var start_i = TO_INTEGER(start);
+ var end_i = s_len;
+ if (!IS_UNDEFINED(end)) {
+ end_i = TO_INTEGER(end);
+ }
+
+ if (start_i < 0) {
+ start_i += s_len;
+ if (start_i < 0) {
+ start_i = 0;
+ }
+ } else {
+ if (start_i > s_len) {
+ return '';
+ }
+ }
+
+ if (end_i < 0) {
+ end_i += s_len;
+ if (end_i < 0) {
+ return '';
+ }
+ } else {
+ if (end_i > s_len) {
+ end_i = s_len;
+ }
+ }
+
+ if (end_i <= start_i) {
+ return '';
+ }
+
+ return %_SubString(s, start_i, end_i);
+}
+
+
+// ES6 21.1.3.17.
+function StringSplitJS(separator, limit) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.split");
+
+ if (!IS_NULL_OR_UNDEFINED(separator)) {
+ var splitter = separator[splitSymbol];
+ if (!IS_UNDEFINED(splitter)) {
+ return %_Call(splitter, separator, this, limit);
+ }
+ }
+
+ var subject = TO_STRING(this);
+ limit = (IS_UNDEFINED(limit)) ? kMaxUint32 : TO_UINT32(limit);
+
+ var length = subject.length;
+ var separator_string = TO_STRING(separator);
+
+ if (limit === 0) return [];
+
+ // ECMA-262 says that if separator is undefined, the result should
+ // be an array of size 1 containing the entire string.
+ if (IS_UNDEFINED(separator)) return [subject];
+
+ var separator_length = separator_string.length;
+
+ // If the separator string is empty then return the elements in the subject.
+ if (separator_length === 0) return %StringToArray(subject, limit);
+
+ return %StringSplit(subject, separator_string, limit);
+}
+
+
+// ECMA-262 section 15.5.4.15
+function StringSubstring(start, end) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.subString");
+
+ var s = TO_STRING(this);
+ var s_len = s.length;
+
+ var start_i = TO_INTEGER(start);
+ if (start_i < 0) {
+ start_i = 0;
+ } else if (start_i > s_len) {
+ start_i = s_len;
+ }
+
+ var end_i = s_len;
+ if (!IS_UNDEFINED(end)) {
+ end_i = TO_INTEGER(end);
+ if (end_i > s_len) {
+ end_i = s_len;
+ } else {
+ if (end_i < 0) end_i = 0;
+ if (start_i > end_i) {
+ var tmp = end_i;
+ end_i = start_i;
+ start_i = tmp;
+ }
+ }
+ }
+
+ return %_SubString(s, start_i, end_i);
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.1
+function StringSubstr(start, n) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.substr");
+
+ var s = TO_STRING(this);
+ var len;
+
+ // Correct n: If not given, set to string length; if explicitly
+ // set to undefined, zero, or negative, returns empty string.
+ if (IS_UNDEFINED(n)) {
+ len = s.length;
+ } else {
+ len = TO_INTEGER(n);
+ if (len <= 0) return '';
+ }
+
+ // Correct start: If not given (or undefined), set to zero; otherwise
+ // convert to integer and handle negative case.
+ if (IS_UNDEFINED(start)) {
+ start = 0;
+ } else {
+ start = TO_INTEGER(start);
+ // If positive, and greater than or equal to the string length,
+ // return empty string.
+ if (start >= s.length) return '';
+ // If negative and absolute value is larger than the string length,
+ // use zero.
+ if (start < 0) {
+ start += s.length;
+ if (start < 0) start = 0;
+ }
+ }
+
+ var end = start + len;
+ if (end > s.length) end = s.length;
+
+ return %_SubString(s, start, end);
+}
+
+
+// ECMA-262, 15.5.4.16
+function StringToLowerCaseJS() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLowerCase");
+
+ return %StringToLowerCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.17
+function StringToLocaleLowerCase() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleLowerCase");
+
+ return %StringToLowerCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.18
+function StringToUpperCaseJS() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.toUpperCase");
+
+ return %StringToUpperCase(TO_STRING(this));
+}
+
+
+// ECMA-262, 15.5.4.19
+function StringToLocaleUpperCase() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.toLocaleUpperCase");
+
+ return %StringToUpperCase(TO_STRING(this));
+}
+
+// ES5, 15.5.4.20
+function StringTrimJS() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.trim");
+
+ return %StringTrim(TO_STRING(this), true, true);
+}
+
+function StringTrimLeft() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimLeft");
+
+ return %StringTrim(TO_STRING(this), true, false);
+}
+
+function StringTrimRight() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.trimRight");
+
+ return %StringTrim(TO_STRING(this), false, true);
+}
+
+
+// ECMA-262, section 15.5.3.2
+function StringFromCharCode(code) {
+ var n = %_ArgumentsLength();
+ if (n == 1) return %_StringCharFromCode(code & 0xffff);
+
+ var one_byte = %NewString(n, NEW_ONE_BYTE_STRING);
+ var i;
+ for (i = 0; i < n; i++) {
+ code = %_Arguments(i) & 0xffff;
+ if (code > 0xff) break;
+ %_OneByteSeqStringSetChar(i, code, one_byte);
+ }
+ if (i == n) return one_byte;
+ one_byte = %TruncateString(one_byte, i);
+
+ var two_byte = %NewString(n - i, NEW_TWO_BYTE_STRING);
+ %_TwoByteSeqStringSetChar(0, code, two_byte);
+ i++;
+ for (var j = 1; i < n; i++, j++) {
+ code = %_Arguments(i) & 0xffff;
+ %_TwoByteSeqStringSetChar(j, code, two_byte);
+ }
+ return one_byte + two_byte;
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.2.1
+function HtmlEscape(str) {
+ return %_Call(StringReplace, TO_STRING(str), /"/g, """);
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.2
+function StringAnchor(name) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.anchor");
+ return "<a name=\"" + HtmlEscape(name) + "\">" + TO_STRING(this) +
+ "</a>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.3
+function StringBig() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.big");
+ return "<big>" + TO_STRING(this) + "</big>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.4
+function StringBlink() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.blink");
+ return "<blink>" + TO_STRING(this) + "</blink>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.5
+function StringBold() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.bold");
+ return "<b>" + TO_STRING(this) + "</b>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.6
+function StringFixed() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.fixed");
+ return "<tt>" + TO_STRING(this) + "</tt>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.7
+function StringFontcolor(color) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontcolor");
+ return "<font color=\"" + HtmlEscape(color) + "\">" + TO_STRING(this) +
+ "</font>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.8
+function StringFontsize(size) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.fontsize");
+ return "<font size=\"" + HtmlEscape(size) + "\">" + TO_STRING(this) +
+ "</font>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.9
+function StringItalics() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.italics");
+ return "<i>" + TO_STRING(this) + "</i>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.10
+function StringLink(s) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.link");
+ return "<a href=\"" + HtmlEscape(s) + "\">" + TO_STRING(this) + "</a>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.11
+function StringSmall() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.small");
+ return "<small>" + TO_STRING(this) + "</small>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.12
+function StringStrike() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.strike");
+ return "<strike>" + TO_STRING(this) + "</strike>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.13
+function StringSub() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.sub");
+ return "<sub>" + TO_STRING(this) + "</sub>";
+}
+
+
+// ES6 draft, revision 26 (2014-07-18), section B.2.3.14
+function StringSup() {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.sup");
+ return "<sup>" + TO_STRING(this) + "</sup>";
+}
+
+// ES6, section 21.1.3.13
+function StringRepeat(count) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.repeat");
+
+ var s = TO_STRING(this);
+ var n = TO_INTEGER(count);
+
+ if (n < 0 || n === INFINITY) throw MakeRangeError(kInvalidCountValue);
+
+ // Early return to allow an arbitrarily-large repeat of the empty string.
+ if (s.length === 0) return "";
+
+ // The maximum string length is stored in a smi, so a longer repeat
+ // must result in a range error.
+ if (n > %_MaxSmi()) throw MakeRangeError(kInvalidCountValue);
+
+ var r = "";
+ while (true) {
+ if (n & 1) r += s;
+ n >>= 1;
+ if (n === 0) return r;
+ s += s;
+ }
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.18
+function StringStartsWith(searchString /* position */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.startsWith");
+
+ var s = TO_STRING(this);
+
+ if (IS_REGEXP(searchString)) {
+ throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.startsWith");
+ }
+
+ var ss = TO_STRING(searchString);
+ var pos = 0;
+ if (%_ArgumentsLength() > 1) {
+ var arg = %_Arguments(1); // position
+ if (!IS_UNDEFINED(arg)) {
+ pos = TO_INTEGER(arg);
+ }
+ }
+
+ var s_len = s.length;
+ var start = MathMin(MathMax(pos, 0), s_len);
+ var ss_len = ss.length;
+ if (ss_len + start > s_len) {
+ return false;
+ }
+
+ return %_SubString(s, start, start + ss_len) === ss;
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.7
+function StringEndsWith(searchString /* position */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.endsWith");
+
+ var s = TO_STRING(this);
+
+ if (IS_REGEXP(searchString)) {
+ throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.endsWith");
+ }
+
+ var ss = TO_STRING(searchString);
+ var s_len = s.length;
+ var pos = s_len;
+ if (%_ArgumentsLength() > 1) {
+ var arg = %_Arguments(1); // position
+ if (!IS_UNDEFINED(arg)) {
+ pos = TO_INTEGER(arg);
+ }
+ }
+
+ var end = MathMin(MathMax(pos, 0), s_len);
+ var ss_len = ss.length;
+ var start = end - ss_len;
+ if (start < 0) {
+ return false;
+ }
+
+ return %_SubString(s, start, start + ss_len) === ss;
+}
+
+
+// ES6 draft 04-05-14, section 21.1.3.6
+function StringIncludes(searchString /* position */) { // length == 1
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.includes");
+
+ var string = TO_STRING(this);
+
+ if (IS_REGEXP(searchString)) {
+ throw MakeTypeError(kFirstArgumentNotRegExp, "String.prototype.includes");
+ }
+
+ searchString = TO_STRING(searchString);
+ var pos = 0;
+ if (%_ArgumentsLength() > 1) {
+ pos = %_Arguments(1); // position
+ pos = TO_INTEGER(pos);
+ }
+
+ var stringLength = string.length;
+ if (pos < 0) pos = 0;
+ if (pos > stringLength) pos = stringLength;
+ var searchStringLength = searchString.length;
+
+ if (searchStringLength + pos > stringLength) {
+ return false;
+ }
+
+ return %StringIndexOf(string, searchString, pos) !== -1;
+}
+
+
+// ES6 Draft 05-22-2014, section 21.1.3.3
+function StringCodePointAt(pos) {
+ CHECK_OBJECT_COERCIBLE(this, "String.prototype.codePointAt");
+
+ var string = TO_STRING(this);
+ var size = string.length;
+ pos = TO_INTEGER(pos);
+ if (pos < 0 || pos >= size) {
+ return UNDEFINED;
+ }
+ var first = %_StringCharCodeAt(string, pos);
+ if (first < 0xD800 || first > 0xDBFF || pos + 1 == size) {
+ return first;
+ }
+ var second = %_StringCharCodeAt(string, pos + 1);
+ if (second < 0xDC00 || second > 0xDFFF) {
+ return first;
+ }
+ return (first - 0xD800) * 0x400 + second + 0x2400;
+}
+
+
+// ES6 Draft 05-22-2014, section 21.1.2.2
+function StringFromCodePoint(_) { // length = 1
+ var code;
+ var length = %_ArgumentsLength();
+ var index;
+ var result = "";
+ for (index = 0; index < length; index++) {
+ code = %_Arguments(index);
+ if (!%_IsSmi(code)) {
+ code = TO_NUMBER(code);
+ }
+ if (code < 0 || code > 0x10FFFF || code !== TO_INTEGER(code)) {
+ throw MakeRangeError(kInvalidCodePoint, code);
+ }
+ if (code <= 0xFFFF) {
+ result += %_StringCharFromCode(code);
+ } else {
+ code -= 0x10000;
+ result += %_StringCharFromCode((code >>> 10) & 0x3FF | 0xD800);
+ result += %_StringCharFromCode(code & 0x3FF | 0xDC00);
+ }
+ }
+ return result;
+}
+
+
+// -------------------------------------------------------------------
+// String methods related to templates
+
+// ES6 Draft 03-17-2015, section 21.1.2.4
+function StringRaw(callSite) {
+ // TODO(caitp): Use rest parameters when implemented
+ var numberOfSubstitutions = %_ArgumentsLength();
+ var cooked = TO_OBJECT(callSite);
+ var raw = TO_OBJECT(cooked.raw);
+ var literalSegments = TO_LENGTH(raw.length);
+ if (literalSegments <= 0) return "";
+
+ var result = TO_STRING(raw[0]);
+
+ for (var i = 1; i < literalSegments; ++i) {
+ if (i < numberOfSubstitutions) {
+ result += TO_STRING(%_Arguments(i));
+ }
+ result += TO_STRING(raw[i]);
+ }
+
+ return result;
+}
+
+// -------------------------------------------------------------------
+
+// Set the String function and constructor.
+%FunctionSetPrototype(GlobalString, new GlobalString());
+
+// Set up the constructor property on the String prototype object.
+%AddNamedProperty(
+ GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
+
+// Set up the non-enumerable functions on the String object.
+utils.InstallFunctions(GlobalString, DONT_ENUM, [
+ "fromCharCode", StringFromCharCode,
+ "fromCodePoint", StringFromCodePoint,
+ "raw", StringRaw
+]);
+
+// Set up the non-enumerable functions on the String prototype object.
+utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
+ "valueOf", StringValueOf,
+ "toString", StringToString,
+ "charAt", StringCharAtJS,
+ "charCodeAt", StringCharCodeAtJS,
+ "codePointAt", StringCodePointAt,
+ "concat", StringConcat,
+ "endsWith", StringEndsWith,
+ "includes", StringIncludes,
+ "indexOf", StringIndexOfJS,
+ "lastIndexOf", StringLastIndexOfJS,
+ "localeCompare", StringLocaleCompareJS,
+ "match", StringMatchJS,
+ "normalize", StringNormalizeJS,
+ "repeat", StringRepeat,
+ "replace", StringReplace,
+ "search", StringSearch,
+ "slice", StringSlice,
+ "split", StringSplitJS,
+ "substring", StringSubstring,
+ "substr", StringSubstr,
+ "startsWith", StringStartsWith,
+ "toLowerCase", StringToLowerCaseJS,
+ "toLocaleLowerCase", StringToLocaleLowerCase,
+ "toUpperCase", StringToUpperCaseJS,
+ "toLocaleUpperCase", StringToLocaleUpperCase,
+ "trim", StringTrimJS,
+ "trimLeft", StringTrimLeft,
+ "trimRight", StringTrimRight,
+
+ "link", StringLink,
+ "anchor", StringAnchor,
+ "fontcolor", StringFontcolor,
+ "fontsize", StringFontsize,
+ "big", StringBig,
+ "blink", StringBlink,
+ "bold", StringBold,
+ "fixed", StringFixed,
+ "italics", StringItalics,
+ "small", StringSmall,
+ "strike", StringStrike,
+ "sub", StringSub,
+ "sup", StringSup
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.StringCharAt = StringCharAtJS;
+ to.StringIndexOf = StringIndexOfJS;
+ to.StringLastIndexOf = StringLastIndexOfJS;
+ to.StringMatch = StringMatchJS;
+ to.StringReplace = StringReplace;
+ to.StringSlice = StringSlice;
+ to.StringSplit = StringSplitJS;
+ to.StringSubstr = StringSubstr;
+ to.StringSubstring = StringSubstring;
+});
+
+})
diff --git a/src/js/symbol.js b/src/js/symbol.js
new file mode 100644
index 0000000..5be6e01
--- /dev/null
+++ b/src/js/symbol.js
@@ -0,0 +1,135 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var GlobalSymbol = global.Symbol;
+var hasInstanceSymbol = utils.ImportNow("has_instance_symbol");
+var isConcatSpreadableSymbol =
+ utils.ImportNow("is_concat_spreadable_symbol");
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeTypeError;
+var toPrimitiveSymbol = utils.ImportNow("to_primitive_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+var unscopablesSymbol = utils.ImportNow("unscopables_symbol");
+
+utils.Import(function(from) {
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+
+// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint )
+function SymbolToPrimitive(hint) {
+ if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Symbol.prototype [ @@toPrimitive ]", this);
+ }
+ return %_ValueOf(this);
+}
+
+
+function SymbolToString() {
+ if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Symbol.prototype.toString", this);
+ }
+ return %SymbolDescriptiveString(%_ValueOf(this));
+}
+
+
+function SymbolValueOf() {
+ if (!(IS_SYMBOL(this) || IS_SYMBOL_WRAPPER(this))) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Symbol.prototype.valueOf", this);
+ }
+ return %_ValueOf(this);
+}
+
+
+function SymbolFor(key) {
+ key = TO_STRING(key);
+ var registry = %SymbolRegistry();
+ if (IS_UNDEFINED(registry.for[key])) {
+ var symbol = %CreateSymbol(key);
+ registry.for[key] = symbol;
+ registry.keyFor[symbol] = key;
+ }
+ return registry.for[key];
+}
+
+
+function SymbolKeyFor(symbol) {
+ if (!IS_SYMBOL(symbol)) throw MakeTypeError(kSymbolKeyFor, symbol);
+ return %SymbolRegistry().keyFor[symbol];
+}
+
+
+// ES6 19.1.2.8
+function ObjectGetOwnPropertySymbols(obj) {
+ obj = TO_OBJECT(obj);
+
+ return %GetOwnPropertyKeys(obj, PROPERTY_FILTER_SKIP_STRINGS);
+}
+
+// -------------------------------------------------------------------
+
+%FunctionSetPrototype(GlobalSymbol, new GlobalObject());
+
+utils.InstallConstants(GlobalSymbol, [
+ // TODO(rossberg): expose when implemented.
+ // "hasInstance", hasInstanceSymbol,
+ // "isConcatSpreadable", isConcatSpreadableSymbol,
+ "iterator", iteratorSymbol,
+ // TODO(yangguo): expose when implemented.
+ // "match", matchSymbol,
+ // "replace", replaceSymbol,
+ // "search", searchSymbol,
+ // "split, splitSymbol,
+ "toPrimitive", toPrimitiveSymbol,
+ // TODO(dslomov, caitp): Currently defined in harmony-tostring.js ---
+ // Move here when shipping
+ // "toStringTag", toStringTagSymbol,
+ "unscopables", unscopablesSymbol,
+]);
+
+utils.InstallFunctions(GlobalSymbol, DONT_ENUM, [
+ "for", SymbolFor,
+ "keyFor", SymbolKeyFor
+]);
+
+%AddNamedProperty(
+ GlobalSymbol.prototype, "constructor", GlobalSymbol, DONT_ENUM);
+%AddNamedProperty(
+ GlobalSymbol.prototype, toStringTagSymbol, "Symbol", DONT_ENUM | READ_ONLY);
+
+utils.InstallFunctions(GlobalSymbol.prototype, DONT_ENUM | READ_ONLY, [
+ toPrimitiveSymbol, SymbolToPrimitive
+]);
+
+utils.InstallFunctions(GlobalSymbol.prototype, DONT_ENUM, [
+ "toString", SymbolToString,
+ "valueOf", SymbolValueOf
+]);
+
+utils.InstallFunctions(GlobalObject, DONT_ENUM, [
+ "getOwnPropertySymbols", ObjectGetOwnPropertySymbols
+]);
+
+// -------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.SymbolToString = SymbolToString;
+})
+
+})
diff --git a/src/js/templates.js b/src/js/templates.js
new file mode 100644
index 0000000..7236d5c
--- /dev/null
+++ b/src/js/templates.js
@@ -0,0 +1,84 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Called from a desugaring in the parser.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GlobalMap = global.Map;
+var InternalArray = utils.InternalArray;
+
+// -------------------------------------------------------------------
+
+var callSiteCache = new GlobalMap;
+var mapGetFn = GlobalMap.prototype.get;
+var mapSetFn = GlobalMap.prototype.set;
+
+
+function SameCallSiteElements(rawStrings, other) {
+ var length = rawStrings.length;
+ var other = other.raw;
+
+ if (length !== other.length) return false;
+
+ for (var i = 0; i < length; ++i) {
+ if (rawStrings[i] !== other[i]) return false;
+ }
+
+ return true;
+}
+
+
+function GetCachedCallSite(siteObj, hash) {
+ var obj = %_Call(mapGetFn, callSiteCache, hash);
+
+ if (IS_UNDEFINED(obj)) return;
+
+ var length = obj.length;
+ for (var i = 0; i < length; ++i) {
+ if (SameCallSiteElements(siteObj, obj[i])) return obj[i];
+ }
+}
+
+
+function SetCachedCallSite(siteObj, hash) {
+ var obj = %_Call(mapGetFn, callSiteCache, hash);
+ var array;
+
+ if (IS_UNDEFINED(obj)) {
+ array = new InternalArray(1);
+ array[0] = siteObj;
+ %_Call(mapSetFn, callSiteCache, hash, array);
+ } else {
+ obj.push(siteObj);
+ }
+
+ return siteObj;
+}
+
+
+function GetTemplateCallSite(siteObj, rawStrings, hash) {
+ var cached = GetCachedCallSite(rawStrings, hash);
+
+ if (!IS_UNDEFINED(cached)) return cached;
+
+ %AddNamedProperty(siteObj, "raw", %object_freeze(rawStrings),
+ READ_ONLY | DONT_ENUM | DONT_DELETE);
+
+ return SetCachedCallSite(%object_freeze(siteObj), hash);
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+%InstallToContext(["get_template_call_site", GetTemplateCallSite]);
+
+})
diff --git a/src/js/typedarray.js b/src/js/typedarray.js
new file mode 100644
index 0000000..fd668a5
--- /dev/null
+++ b/src/js/typedarray.js
@@ -0,0 +1,980 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var ArrayFrom;
+var ArrayToString;
+var ArrayValues;
+var GlobalArray = global.Array;
+var GlobalArrayBuffer = global.ArrayBuffer;
+var GlobalDataView = global.DataView;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var InnerArrayCopyWithin;
+var InnerArrayEvery;
+var InnerArrayFill;
+var InnerArrayFilter;
+var InnerArrayFind;
+var InnerArrayFindIndex;
+var InnerArrayForEach;
+var InnerArrayIncludes;
+var InnerArrayIndexOf;
+var InnerArrayJoin;
+var InnerArrayLastIndexOf;
+var InnerArrayReduce;
+var InnerArrayReduceRight;
+var InnerArraySome;
+var InnerArraySort;
+var InnerArrayToLocaleString;
+var InternalArray = utils.InternalArray;
+var IsNaN;
+var MakeRangeError;
+var MakeTypeError;
+var MaxSimple;
+var MinSimple;
+var PackedArrayReverse;
+var SpeciesConstructor;
+var ToPositiveInteger;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+macro TYPED_ARRAYS(FUNCTION)
+// arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
+FUNCTION(1, Uint8Array, 1)
+FUNCTION(2, Int8Array, 1)
+FUNCTION(3, Uint16Array, 2)
+FUNCTION(4, Int16Array, 2)
+FUNCTION(5, Uint32Array, 4)
+FUNCTION(6, Int32Array, 4)
+FUNCTION(7, Float32Array, 4)
+FUNCTION(8, Float64Array, 8)
+FUNCTION(9, Uint8ClampedArray, 1)
+endmacro
+
+macro DECLARE_GLOBALS(INDEX, NAME, SIZE)
+var GlobalNAME = global.NAME;
+endmacro
+
+TYPED_ARRAYS(DECLARE_GLOBALS)
+
+utils.Import(function(from) {
+ ArrayFrom = from.ArrayFrom;
+ ArrayToString = from.ArrayToString;
+ ArrayValues = from.ArrayValues;
+ InnerArrayCopyWithin = from.InnerArrayCopyWithin;
+ InnerArrayEvery = from.InnerArrayEvery;
+ InnerArrayFill = from.InnerArrayFill;
+ InnerArrayFilter = from.InnerArrayFilter;
+ InnerArrayFind = from.InnerArrayFind;
+ InnerArrayFindIndex = from.InnerArrayFindIndex;
+ InnerArrayForEach = from.InnerArrayForEach;
+ InnerArrayIncludes = from.InnerArrayIncludes;
+ InnerArrayIndexOf = from.InnerArrayIndexOf;
+ InnerArrayJoin = from.InnerArrayJoin;
+ InnerArrayLastIndexOf = from.InnerArrayLastIndexOf;
+ InnerArrayReduce = from.InnerArrayReduce;
+ InnerArrayReduceRight = from.InnerArrayReduceRight;
+ InnerArraySome = from.InnerArraySome;
+ InnerArraySort = from.InnerArraySort;
+ InnerArrayToLocaleString = from.InnerArrayToLocaleString;
+ IsNaN = from.IsNaN;
+ MakeRangeError = from.MakeRangeError;
+ MakeTypeError = from.MakeTypeError;
+ MaxSimple = from.MaxSimple;
+ MinSimple = from.MinSimple;
+ PackedArrayReverse = from.PackedArrayReverse;
+ SpeciesConstructor = from.SpeciesConstructor;
+ ToPositiveInteger = from.ToPositiveInteger;
+});
+
+// --------------- Typed Arrays ---------------------
+
+function TypedArrayDefaultConstructor(typedArray) {
+ switch (%_ClassOf(typedArray)) {
+macro TYPED_ARRAY_CONSTRUCTOR_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
+ case "NAME":
+ return GlobalNAME;
+endmacro
+TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR_CASE)
+ }
+ // The TypeError should not be generated since all callers should
+ // have already called ValidateTypedArray.
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "TypedArrayDefaultConstructor", this);
+}
+
+function TypedArrayCreate(constructor, arg0, arg1, arg2) {
+ if (IS_UNDEFINED(arg1)) {
+ var newTypedArray = new constructor(arg0);
+ } else {
+ var newTypedArray = new constructor(arg0, arg1, arg2);
+ }
+ if (!%_IsTypedArray(newTypedArray)) throw MakeTypeError(kNotTypedArray);
+ // TODO(littledan): Check for being detached, here and elsewhere
+ // All callers where the first argument is a Number have no additional
+ // arguments.
+ if (IS_NUMBER(arg0) && %_TypedArrayGetLength(newTypedArray) < arg0) {
+ throw MakeTypeError(kTypedArrayTooShort);
+ }
+ return newTypedArray;
+}
+
+function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2, conservative) {
+ var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
+ var constructor = SpeciesConstructor(exemplar, defaultConstructor,
+ conservative);
+ return TypedArrayCreate(constructor, arg0, arg1, arg2);
+}
+
+macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
+function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
+ if (!IS_UNDEFINED(byteOffset)) {
+ byteOffset = ToPositiveInteger(byteOffset, kInvalidTypedArrayLength);
+ }
+ if (!IS_UNDEFINED(length)) {
+ length = ToPositiveInteger(length, kInvalidTypedArrayLength);
+ }
+
+ var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
+ var offset;
+ if (IS_UNDEFINED(byteOffset)) {
+ offset = 0;
+ } else {
+ offset = byteOffset;
+
+ if (offset % ELEMENT_SIZE !== 0) {
+ throw MakeRangeError(kInvalidTypedArrayAlignment,
+ "start offset", "NAME", ELEMENT_SIZE);
+ }
+ if (offset > bufferByteLength) {
+ throw MakeRangeError(kInvalidTypedArrayOffset);
+ }
+ }
+
+ var newByteLength;
+ var newLength;
+ if (IS_UNDEFINED(length)) {
+ if (bufferByteLength % ELEMENT_SIZE !== 0) {
+ throw MakeRangeError(kInvalidTypedArrayAlignment,
+ "byte length", "NAME", ELEMENT_SIZE);
+ }
+ newByteLength = bufferByteLength - offset;
+ newLength = newByteLength / ELEMENT_SIZE;
+ } else {
+ var newLength = length;
+ newByteLength = newLength * ELEMENT_SIZE;
+ }
+ if ((offset + newByteLength > bufferByteLength)
+ || (newLength > %_MaxSmi())) {
+ throw MakeRangeError(kInvalidTypedArrayLength);
+ }
+ %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true);
+}
+
+function NAMEConstructByLength(obj, length) {
+ var l = IS_UNDEFINED(length) ?
+ 0 : ToPositiveInteger(length, kInvalidTypedArrayLength);
+ if (l > %_MaxSmi()) {
+ throw MakeRangeError(kInvalidTypedArrayLength);
+ }
+ var byteLength = l * ELEMENT_SIZE;
+ if (byteLength > %_TypedArrayMaxSizeInHeap()) {
+ var buffer = new GlobalArrayBuffer(byteLength);
+ %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true);
+ } else {
+ %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true);
+ }
+}
+
+function NAMEConstructByArrayLike(obj, arrayLike) {
+ var length = arrayLike.length;
+ var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
+
+ if (l > %_MaxSmi()) {
+ throw MakeRangeError(kInvalidTypedArrayLength);
+ }
+ var initialized = false;
+ var byteLength = l * ELEMENT_SIZE;
+ if (byteLength <= %_TypedArrayMaxSizeInHeap()) {
+ %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false);
+ } else {
+ initialized =
+ %TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l);
+ }
+ if (!initialized) {
+ for (var i = 0; i < l; i++) {
+ // It is crucial that we let any execptions from arrayLike[i]
+ // propagate outside the function.
+ obj[i] = arrayLike[i];
+ }
+ }
+}
+
+function NAMEConstructByIterable(obj, iterable, iteratorFn) {
+ var list = new InternalArray();
+ // Reading the Symbol.iterator property of iterable twice would be
+ // observable with getters, so instead, we call the function which
+ // was already looked up, and wrap it in another iterable. The
+ // __proto__ of the new iterable is set to null to avoid any chance
+ // of modifications to Object.prototype being observable here.
+ var iterator = %_Call(iteratorFn, iterable);
+ var newIterable = {
+ __proto__: null
+ };
+ // TODO(littledan): Computed properties don't work yet in nosnap.
+ // Rephrase when they do.
+ newIterable[iteratorSymbol] = function() { return iterator; }
+ for (var value of newIterable) {
+ list.push(value);
+ }
+ NAMEConstructByArrayLike(obj, list);
+}
+
+function NAMEConstructor(arg1, arg2, arg3) {
+ if (!IS_UNDEFINED(new.target)) {
+ if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
+ NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
+ } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
+ IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
+ NAMEConstructByLength(this, arg1);
+ } else {
+ // TODO(littledan): If arg1 is a TypedArray, follow the constructor
+ // path in ES2015 22.2.4.3, and call SpeciesConstructor, in a
+ // path that seems to be an optimized version of what's below, but
+ // in an observably different way.
+ var iteratorFn = arg1[iteratorSymbol];
+ if (IS_UNDEFINED(iteratorFn) || iteratorFn === ArrayValues) {
+ NAMEConstructByArrayLike(this, arg1);
+ } else {
+ NAMEConstructByIterable(this, arg1, iteratorFn);
+ }
+ }
+ } else {
+ throw MakeTypeError(kConstructorNotFunction, "NAME")
+ }
+}
+
+// TODO(littledan): Remove this performance workaround BUG(chromium:579905)
+function NAME_GetLength() {
+ if (!(%_ClassOf(this) === 'NAME')) {
+ throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.length", this);
+ }
+ return %_TypedArrayGetLength(this);
+}
+
+function NAMESubArray(begin, end) {
+ var beginInt = TO_INTEGER(begin);
+ if (!IS_UNDEFINED(end)) {
+ var endInt = TO_INTEGER(end);
+ var srcLength = %_TypedArrayGetLength(this);
+ } else {
+ var srcLength = %_TypedArrayGetLength(this);
+ var endInt = srcLength;
+ }
+
+ if (beginInt < 0) {
+ beginInt = MaxSimple(0, srcLength + beginInt);
+ } else {
+ beginInt = MinSimple(beginInt, srcLength);
+ }
+
+ if (endInt < 0) {
+ endInt = MaxSimple(0, srcLength + endInt);
+ } else {
+ endInt = MinSimple(endInt, srcLength);
+ }
+
+ if (endInt < beginInt) {
+ endInt = beginInt;
+ }
+
+ var newLength = endInt - beginInt;
+ var beginByteOffset =
+ %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
+ return TypedArraySpeciesCreate(this, %TypedArrayGetBuffer(this),
+ beginByteOffset, newLength, true);
+}
+endmacro
+
+TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
+
+function TypedArraySubArray(begin, end) {
+ switch (%_ClassOf(this)) {
+macro TYPED_ARRAY_SUBARRAY_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
+ case "NAME":
+ return %_Call(NAMESubArray, this, begin, end);
+endmacro
+TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
+ }
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "get TypedArray.prototype.subarray", this);
+}
+%SetForceInlineFlag(TypedArraySubArray);
+
+function TypedArrayGetBuffer() {
+ if (!%_IsTypedArray(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "get TypedArray.prototype.buffer", this);
+ }
+ return %TypedArrayGetBuffer(this);
+}
+%SetForceInlineFlag(TypedArrayGetBuffer);
+
+function TypedArrayGetByteLength() {
+ if (!%_IsTypedArray(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "get TypedArray.prototype.byteLength", this);
+ }
+ return %_ArrayBufferViewGetByteLength(this);
+}
+%SetForceInlineFlag(TypedArrayGetByteLength);
+
+function TypedArrayGetByteOffset() {
+ if (!%_IsTypedArray(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "get TypedArray.prototype.byteOffset", this);
+ }
+ return %_ArrayBufferViewGetByteOffset(this);
+}
+%SetForceInlineFlag(TypedArrayGetByteOffset);
+
+function TypedArrayGetLength() {
+ if (!%_IsTypedArray(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "get TypedArray.prototype.length", this);
+ }
+ return %_TypedArrayGetLength(this);
+}
+%SetForceInlineFlag(TypedArrayGetLength);
+
+
+
+function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
+ if (offset > 0) {
+ for (var i = 0; i < sourceLength; i++) {
+ target[offset + i] = source[i];
+ }
+ }
+ else {
+ for (var i = 0; i < sourceLength; i++) {
+ target[i] = source[i];
+ }
+ }
+}
+
+function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
+ var sourceElementSize = source.BYTES_PER_ELEMENT;
+ var targetElementSize = target.BYTES_PER_ELEMENT;
+ var sourceLength = source.length;
+
+ // Copy left part.
+ function CopyLeftPart() {
+ // First un-mutated byte after the next write
+ var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
+ // Next read at sourcePtr. We do not care for memory changing before
+ // sourcePtr - we have already copied it.
+ var sourcePtr = source.byteOffset;
+ for (var leftIndex = 0;
+ leftIndex < sourceLength && targetPtr <= sourcePtr;
+ leftIndex++) {
+ target[offset + leftIndex] = source[leftIndex];
+ targetPtr += targetElementSize;
+ sourcePtr += sourceElementSize;
+ }
+ return leftIndex;
+ }
+ var leftIndex = CopyLeftPart();
+
+ // Copy rigth part;
+ function CopyRightPart() {
+ // First unmutated byte before the next write
+ var targetPtr =
+ target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
+ // Next read before sourcePtr. We do not care for memory changing after
+ // sourcePtr - we have already copied it.
+ var sourcePtr =
+ source.byteOffset + sourceLength * sourceElementSize;
+ for(var rightIndex = sourceLength - 1;
+ rightIndex >= leftIndex && targetPtr >= sourcePtr;
+ rightIndex--) {
+ target[offset + rightIndex] = source[rightIndex];
+ targetPtr -= targetElementSize;
+ sourcePtr -= sourceElementSize;
+ }
+ return rightIndex;
+ }
+ var rightIndex = CopyRightPart();
+
+ var temp = new GlobalArray(rightIndex + 1 - leftIndex);
+ for (var i = leftIndex; i <= rightIndex; i++) {
+ temp[i - leftIndex] = source[i];
+ }
+ for (i = leftIndex; i <= rightIndex; i++) {
+ target[offset + i] = temp[i - leftIndex];
+ }
+}
+
+function TypedArraySet(obj, offset) {
+ var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
+ if (intOffset < 0) throw MakeTypeError(kTypedArraySetNegativeOffset);
+
+ if (intOffset > %_MaxSmi()) {
+ throw MakeRangeError(kTypedArraySetSourceTooLarge);
+ }
+ switch (%TypedArraySetFastCases(this, obj, intOffset)) {
+ // These numbers should be synchronized with runtime.cc.
+ case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
+ return;
+ case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
+ TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
+ return;
+ case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
+ TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
+ return;
+ case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
+ var l = obj.length;
+ if (IS_UNDEFINED(l)) {
+ if (IS_NUMBER(obj)) {
+ // For number as a first argument, throw TypeError
+ // instead of silently ignoring the call, so that
+ // the user knows (s)he did something wrong.
+ // (Consistent with Firefox and Blink/WebKit)
+ throw MakeTypeError(kInvalidArgument);
+ }
+ return;
+ }
+ l = TO_LENGTH(l);
+ if (intOffset + l > this.length) {
+ throw MakeRangeError(kTypedArraySetSourceTooLarge);
+ }
+ TypedArraySetFromArrayLike(this, obj, l, intOffset);
+ return;
+ }
+}
+
+function TypedArrayGetToStringTag() {
+ if (!%_IsTypedArray(this)) return;
+ var name = %_ClassOf(this);
+ if (IS_UNDEFINED(name)) return;
+ return name;
+}
+
+
+function TypedArrayCopyWithin(target, start, end) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ // TODO(littledan): Replace with a memcpy for better performance
+ return InnerArrayCopyWithin(target, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayCopyWithin, 2);
+
+
+// ES6 draft 05-05-15, section 22.2.3.7
+function TypedArrayEvery(f, receiver) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayEvery(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArrayEvery, 1);
+
+
+// ES6 draft 08-24-14, section 22.2.3.12
+function TypedArrayForEach(f, receiver) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ InnerArrayForEach(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArrayForEach, 1);
+
+
+// ES6 draft 04-05-14 section 22.2.3.8
+function TypedArrayFill(value, start, end) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayFill(value, start, end, this, length);
+}
+%FunctionSetLength(TypedArrayFill, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.9
+function TypedArrayFilter(f, thisArg) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+ var result = new InternalArray();
+ InnerArrayFilter(f, thisArg, this, length, result);
+ var captured = result.length;
+ var output = TypedArraySpeciesCreate(this, captured);
+ for (var i = 0; i < captured; i++) {
+ output[i] = result[i];
+ }
+ return output;
+}
+%FunctionSetLength(TypedArrayFilter, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.10
+function TypedArrayFind(predicate, thisArg) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayFind(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFind, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.11
+function TypedArrayFindIndex(predicate, thisArg) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayFindIndex(predicate, thisArg, this, length);
+}
+%FunctionSetLength(TypedArrayFindIndex, 1);
+
+
+// ES6 draft 05-18-15, section 22.2.3.21
+function TypedArrayReverse() {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return PackedArrayReverse(this, length);
+}
+
+
+function TypedArrayComparefn(x, y) {
+ if (IsNaN(x) && IsNaN(y)) {
+ return IsNaN(y) ? 0 : 1;
+ }
+ if (IsNaN(x)) {
+ return 1;
+ }
+ if (x === 0 && x === y) {
+ if (%_IsMinusZero(x)) {
+ if (!%_IsMinusZero(y)) {
+ return -1;
+ }
+ } else if (%_IsMinusZero(y)) {
+ return 1;
+ }
+ }
+ return x - y;
+}
+
+
+// ES6 draft 05-18-15, section 22.2.3.25
+function TypedArraySort(comparefn) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ if (IS_UNDEFINED(comparefn)) {
+ comparefn = TypedArrayComparefn;
+ }
+
+ return InnerArraySort(this, length, comparefn);
+}
+
+
+// ES6 section 22.2.3.13
+function TypedArrayIndexOf(element, index) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+ return InnerArrayIndexOf(this, element, index, length);
+}
+%FunctionSetLength(TypedArrayIndexOf, 1);
+
+
+// ES6 section 22.2.3.16
+function TypedArrayLastIndexOf(element, index) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayLastIndexOf(this, element, index, length,
+ %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayLastIndexOf, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.18
+function TypedArrayMap(f, thisArg) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+ var result = TypedArraySpeciesCreate(this, length);
+ if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
+ for (var i = 0; i < length; i++) {
+ var element = this[i];
+ result[i] = %_Call(f, thisArg, element, i, this);
+ }
+ return result;
+}
+%FunctionSetLength(TypedArrayMap, 1);
+
+
+// ES6 draft 05-05-15, section 22.2.3.24
+function TypedArraySome(f, receiver) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArraySome(f, receiver, this, length);
+}
+%FunctionSetLength(TypedArraySome, 1);
+
+
+// ES6 section 22.2.3.27
+function TypedArrayToLocaleString() {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayToLocaleString(this, length);
+}
+
+
+// ES6 section 22.2.3.28
+function TypedArrayToString() {
+ return %_Call(ArrayToString, this);
+}
+
+
+// ES6 section 22.2.3.14
+function TypedArrayJoin(separator) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayJoin(separator, this, length);
+}
+
+
+// ES6 draft 07-15-13, section 22.2.3.19
+function TypedArrayReduce(callback, current) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+ return InnerArrayReduce(callback, current, this, length,
+ %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayReduce, 1);
+
+
+// ES6 draft 07-15-13, section 22.2.3.19
+function TypedArrayReduceRight(callback, current) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+ return InnerArrayReduceRight(callback, current, this, length,
+ %_ArgumentsLength());
+}
+%FunctionSetLength(TypedArrayReduceRight, 1);
+
+
+function TypedArraySlice(start, end) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+ var len = %_TypedArrayGetLength(this);
+
+ var relativeStart = TO_INTEGER(start);
+
+ var k;
+ if (relativeStart < 0) {
+ k = MaxSimple(len + relativeStart, 0);
+ } else {
+ k = MinSimple(relativeStart, len);
+ }
+
+ var relativeEnd;
+ if (IS_UNDEFINED(end)) {
+ relativeEnd = len;
+ } else {
+ relativeEnd = TO_INTEGER(end);
+ }
+
+ var final;
+ if (relativeEnd < 0) {
+ final = MaxSimple(len + relativeEnd, 0);
+ } else {
+ final = MinSimple(relativeEnd, len);
+ }
+
+ var count = MaxSimple(final - k, 0);
+ var array = TypedArraySpeciesCreate(this, count);
+ // The code below is the 'then' branch; the 'else' branch species
+ // a memcpy. Because V8 doesn't canonicalize NaN, the difference is
+ // unobservable.
+ var n = 0;
+ while (k < final) {
+ var kValue = this[k];
+ array[n] = kValue;
+ k++;
+ n++;
+ }
+ return array;
+}
+
+
+// ES2016 draft, section 22.2.3.14
+function TypedArrayIncludes(searchElement, fromIndex) {
+ if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
+
+ var length = %_TypedArrayGetLength(this);
+
+ return InnerArrayIncludes(searchElement, fromIndex, this, length);
+}
+%FunctionSetLength(TypedArrayIncludes, 1);
+
+
+// ES6 draft 08-24-14, section 22.2.2.2
+function TypedArrayOf() {
+ var length = %_ArgumentsLength();
+ var array = TypedArrayCreate(this, length);
+ for (var i = 0; i < length; i++) {
+ array[i] = %_Arguments(i);
+ }
+ return array;
+}
+
+
+function TypedArrayFrom(source, mapfn, thisArg) {
+ // TODO(littledan): Investigate if there is a receiver which could be
+ // faster to accumulate on than Array, e.g., a TypedVector.
+ // TODO(littledan): Rewrite this code to ensure that things happen
+ // in the right order, e.g., the constructor needs to be called before
+ // the mapping function on array-likes.
+ var array = %_Call(ArrayFrom, GlobalArray, source, mapfn, thisArg);
+ return TypedArrayCreate(this, array);
+}
+%FunctionSetLength(TypedArrayFrom, 1);
+
+function TypedArray() {
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNonCallable, "TypedArray");
+ }
+ if (new.target === TypedArray) {
+ throw MakeTypeError(kConstructAbstractClass, "TypedArray");
+ }
+}
+
+// -------------------------------------------------------------------
+
+%FunctionSetPrototype(TypedArray, new GlobalObject());
+%AddNamedProperty(TypedArray.prototype,
+ "constructor", TypedArray, DONT_ENUM);
+utils.InstallFunctions(TypedArray, DONT_ENUM | DONT_DELETE | READ_ONLY, [
+ "from", TypedArrayFrom,
+ "of", TypedArrayOf
+]);
+utils.InstallGetter(TypedArray.prototype, "buffer", TypedArrayGetBuffer);
+utils.InstallGetter(TypedArray.prototype, "byteOffset", TypedArrayGetByteOffset,
+ DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, "byteLength",
+ TypedArrayGetByteLength, DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, "length", TypedArrayGetLength,
+ DONT_ENUM | DONT_DELETE);
+utils.InstallGetter(TypedArray.prototype, toStringTagSymbol,
+ TypedArrayGetToStringTag);
+utils.InstallFunctions(TypedArray.prototype, DONT_ENUM, [
+ "subarray", TypedArraySubArray,
+ "set", TypedArraySet,
+ "copyWithin", TypedArrayCopyWithin,
+ "every", TypedArrayEvery,
+ "fill", TypedArrayFill,
+ "filter", TypedArrayFilter,
+ "find", TypedArrayFind,
+ "findIndex", TypedArrayFindIndex,
+ "includes", TypedArrayIncludes,
+ "indexOf", TypedArrayIndexOf,
+ "join", TypedArrayJoin,
+ "lastIndexOf", TypedArrayLastIndexOf,
+ "forEach", TypedArrayForEach,
+ "map", TypedArrayMap,
+ "reduce", TypedArrayReduce,
+ "reduceRight", TypedArrayReduceRight,
+ "reverse", TypedArrayReverse,
+ "slice", TypedArraySlice,
+ "some", TypedArraySome,
+ "sort", TypedArraySort,
+ "toString", TypedArrayToString,
+ "toLocaleString", TypedArrayToLocaleString
+]);
+
+
+macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
+ %SetCode(GlobalNAME, NAMEConstructor);
+ %FunctionSetPrototype(GlobalNAME, new GlobalObject());
+ %InternalSetPrototype(GlobalNAME, TypedArray);
+ %InternalSetPrototype(GlobalNAME.prototype, TypedArray.prototype);
+
+ %AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
+ READ_ONLY | DONT_ENUM | DONT_DELETE);
+
+ %AddNamedProperty(GlobalNAME.prototype,
+ "constructor", global.NAME, DONT_ENUM);
+ %AddNamedProperty(GlobalNAME.prototype,
+ "BYTES_PER_ELEMENT", ELEMENT_SIZE,
+ READ_ONLY | DONT_ENUM | DONT_DELETE);
+ // TODO(littledan): Remove this performance workaround BUG(chromium:579905)
+ utils.InstallGetter(GlobalNAME.prototype, "length", NAME_GetLength,
+ DONT_ENUM | DONT_DELETE);
+endmacro
+
+TYPED_ARRAYS(SETUP_TYPED_ARRAY)
+
+// --------------------------- DataView -----------------------------
+
+function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNotFunction, "DataView");
+ }
+
+ // TODO(binji): support SharedArrayBuffers?
+ if (!IS_ARRAYBUFFER(buffer)) throw MakeTypeError(kDataViewNotArrayBuffer);
+ if (!IS_UNDEFINED(byteOffset)) {
+ byteOffset = ToPositiveInteger(byteOffset, kInvalidDataViewOffset);
+ }
+ if (!IS_UNDEFINED(byteLength)) {
+ byteLength = TO_INTEGER(byteLength);
+ }
+
+ var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
+
+ var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset;
+ if (offset > bufferByteLength) throw MakeRangeError(kInvalidDataViewOffset);
+
+ var length = IS_UNDEFINED(byteLength)
+ ? bufferByteLength - offset
+ : byteLength;
+ if (length < 0 || offset + length > bufferByteLength) {
+ throw new MakeRangeError(kInvalidDataViewLength);
+ }
+ var result = %NewObject(GlobalDataView, new.target);
+ %_DataViewInitialize(result, buffer, offset, length);
+ return result;
+}
+
+function DataViewGetBufferJS() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver, 'DataView.buffer', this);
+ }
+ return %DataViewGetBuffer(this);
+}
+
+function DataViewGetByteOffset() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'DataView.byteOffset', this);
+ }
+ return %_ArrayBufferViewGetByteOffset(this);
+}
+
+function DataViewGetByteLength() {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'DataView.byteLength', this);
+ }
+ return %_ArrayBufferViewGetByteLength(this);
+}
+
+macro DATA_VIEW_TYPES(FUNCTION)
+ FUNCTION(Int8)
+ FUNCTION(Uint8)
+ FUNCTION(Int16)
+ FUNCTION(Uint16)
+ FUNCTION(Int32)
+ FUNCTION(Uint32)
+ FUNCTION(Float32)
+ FUNCTION(Float64)
+endmacro
+
+
+macro DATA_VIEW_GETTER_SETTER(TYPENAME)
+function DataViewGetTYPENAMEJS(offset, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'DataView.getTYPENAME', this);
+ }
+ if (%_ArgumentsLength() < 1) throw MakeTypeError(kInvalidArgument);
+ offset = ToPositiveInteger(offset, kInvalidDataViewAccessorOffset);
+ return %DataViewGetTYPENAME(this, offset, !!little_endian);
+}
+
+function DataViewSetTYPENAMEJS(offset, value, little_endian) {
+ if (!IS_DATAVIEW(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'DataView.setTYPENAME', this);
+ }
+ if (%_ArgumentsLength() < 2) throw MakeTypeError(kInvalidArgument);
+ offset = ToPositiveInteger(offset, kInvalidDataViewAccessorOffset);
+ %DataViewSetTYPENAME(this, offset, TO_NUMBER(value), !!little_endian);
+}
+endmacro
+
+DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
+
+// Setup the DataView constructor.
+%SetCode(GlobalDataView, DataViewConstructor);
+%FunctionSetPrototype(GlobalDataView, new GlobalObject);
+
+// Set up constructor property on the DataView prototype.
+%AddNamedProperty(GlobalDataView.prototype, "constructor", GlobalDataView,
+ DONT_ENUM);
+%AddNamedProperty(GlobalDataView.prototype, toStringTagSymbol, "DataView",
+ READ_ONLY|DONT_ENUM);
+
+utils.InstallGetter(GlobalDataView.prototype, "buffer", DataViewGetBufferJS);
+utils.InstallGetter(GlobalDataView.prototype, "byteOffset",
+ DataViewGetByteOffset);
+utils.InstallGetter(GlobalDataView.prototype, "byteLength",
+ DataViewGetByteLength);
+
+utils.InstallFunctions(GlobalDataView.prototype, DONT_ENUM, [
+ "getInt8", DataViewGetInt8JS,
+ "setInt8", DataViewSetInt8JS,
+
+ "getUint8", DataViewGetUint8JS,
+ "setUint8", DataViewSetUint8JS,
+
+ "getInt16", DataViewGetInt16JS,
+ "setInt16", DataViewSetInt16JS,
+
+ "getUint16", DataViewGetUint16JS,
+ "setUint16", DataViewSetUint16JS,
+
+ "getInt32", DataViewGetInt32JS,
+ "setInt32", DataViewSetInt32JS,
+
+ "getUint32", DataViewGetUint32JS,
+ "setUint32", DataViewSetUint32JS,
+
+ "getFloat32", DataViewGetFloat32JS,
+ "setFloat32", DataViewSetFloat32JS,
+
+ "getFloat64", DataViewGetFloat64JS,
+ "setFloat64", DataViewSetFloat64JS
+]);
+
+})
diff --git a/src/js/uri.js b/src/js/uri.js
new file mode 100644
index 0000000..712d7e6
--- /dev/null
+++ b/src/js/uri.js
@@ -0,0 +1,380 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains support for URI manipulations written in
+// JavaScript.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+//- ------------------------------------------------------------------
+// Imports
+
+var GlobalObject = global.Object;
+var GlobalArray = global.Array;
+var InternalArray = utils.InternalArray;
+var MakeURIError;
+
+utils.Import(function(from) {
+ MakeURIError = from.MakeURIError;
+});
+
+
+// -------------------------------------------------------------------
+// Define internal helper functions.
+
+function HexValueOf(code) {
+ // 0-9
+ if (code >= 48 && code <= 57) return code - 48;
+ // A-F
+ if (code >= 65 && code <= 70) return code - 55;
+ // a-f
+ if (code >= 97 && code <= 102) return code - 87;
+
+ return -1;
+}
+
+// Does the char code correspond to an alpha-numeric char.
+function isAlphaNumeric(cc) {
+ // a - z
+ if (97 <= cc && cc <= 122) return true;
+ // A - Z
+ if (65 <= cc && cc <= 90) return true;
+ // 0 - 9
+ if (48 <= cc && cc <= 57) return true;
+
+ return false;
+}
+
+// Lazily initialized.
+var hexCharCodeArray = 0;
+
+function URIAddEncodedOctetToBuffer(octet, result, index) {
+ result[index++] = 37; // Char code of '%'.
+ result[index++] = hexCharCodeArray[octet >> 4];
+ result[index++] = hexCharCodeArray[octet & 0x0F];
+ return index;
+}
+
+function URIEncodeOctets(octets, result, index) {
+ if (hexCharCodeArray === 0) {
+ hexCharCodeArray = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 65, 66, 67, 68, 69, 70];
+ }
+ index = URIAddEncodedOctetToBuffer(octets[0], result, index);
+ if (octets[1]) index = URIAddEncodedOctetToBuffer(octets[1], result, index);
+ if (octets[2]) index = URIAddEncodedOctetToBuffer(octets[2], result, index);
+ if (octets[3]) index = URIAddEncodedOctetToBuffer(octets[3], result, index);
+ return index;
+}
+
+function URIEncodeSingle(cc, result, index) {
+ var x = (cc >> 12) & 0xF;
+ var y = (cc >> 6) & 63;
+ var z = cc & 63;
+ var octets = new GlobalArray(3);
+ if (cc <= 0x007F) {
+ octets[0] = cc;
+ } else if (cc <= 0x07FF) {
+ octets[0] = y + 192;
+ octets[1] = z + 128;
+ } else {
+ octets[0] = x + 224;
+ octets[1] = y + 128;
+ octets[2] = z + 128;
+ }
+ return URIEncodeOctets(octets, result, index);
+}
+
+function URIEncodePair(cc1 , cc2, result, index) {
+ var u = ((cc1 >> 6) & 0xF) + 1;
+ var w = (cc1 >> 2) & 0xF;
+ var x = cc1 & 3;
+ var y = (cc2 >> 6) & 0xF;
+ var z = cc2 & 63;
+ var octets = new GlobalArray(4);
+ octets[0] = (u >> 2) + 240;
+ octets[1] = (((u & 3) << 4) | w) + 128;
+ octets[2] = ((x << 4) | y) + 128;
+ octets[3] = z + 128;
+ return URIEncodeOctets(octets, result, index);
+}
+
+function URIHexCharsToCharCode(highChar, lowChar) {
+ var highCode = HexValueOf(highChar);
+ var lowCode = HexValueOf(lowChar);
+ if (highCode == -1 || lowCode == -1) throw MakeURIError();
+ return (highCode << 4) | lowCode;
+}
+
+// Callers must ensure that |result| is a sufficiently long sequential
+// two-byte string!
+function URIDecodeOctets(octets, result, index) {
+ var value;
+ var o0 = octets[0];
+ if (o0 < 0x80) {
+ value = o0;
+ } else if (o0 < 0xc2) {
+ throw MakeURIError();
+ } else {
+ var o1 = octets[1];
+ if (o0 < 0xe0) {
+ var a = o0 & 0x1f;
+ if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+ var b = o1 & 0x3f;
+ value = (a << 6) + b;
+ if (value < 0x80 || value > 0x7ff) throw MakeURIError();
+ } else {
+ var o2 = octets[2];
+ if (o0 < 0xf0) {
+ var a = o0 & 0x0f;
+ if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+ var b = o1 & 0x3f;
+ if ((o2 < 0x80) || (o2 > 0xbf)) throw MakeURIError();
+ var c = o2 & 0x3f;
+ value = (a << 12) + (b << 6) + c;
+ if ((value < 0x800) || (value > 0xffff)) throw MakeURIError();
+ } else {
+ var o3 = octets[3];
+ if (o0 < 0xf8) {
+ var a = (o0 & 0x07);
+ if ((o1 < 0x80) || (o1 > 0xbf)) throw MakeURIError();
+ var b = (o1 & 0x3f);
+ if ((o2 < 0x80) || (o2 > 0xbf)) {
+ throw MakeURIError();
+ }
+ var c = (o2 & 0x3f);
+ if ((o3 < 0x80) || (o3 > 0xbf)) throw MakeURIError();
+ var d = (o3 & 0x3f);
+ value = (a << 18) + (b << 12) + (c << 6) + d;
+ if ((value < 0x10000) || (value > 0x10ffff)) throw MakeURIError();
+ } else {
+ throw MakeURIError();
+ }
+ }
+ }
+ }
+ if (0xD800 <= value && value <= 0xDFFF) throw MakeURIError();
+ if (value < 0x10000) {
+ %_TwoByteSeqStringSetChar(index++, value, result);
+ } else {
+ %_TwoByteSeqStringSetChar(index++, (value >> 10) + 0xd7c0, result);
+ %_TwoByteSeqStringSetChar(index++, (value & 0x3ff) + 0xdc00, result);
+ }
+ return index;
+}
+
+// ECMA-262, section 15.1.3
+function Encode(uri, unescape) {
+ uri = TO_STRING(uri);
+ var uriLength = uri.length;
+ var array = new InternalArray(uriLength);
+ var index = 0;
+ for (var k = 0; k < uriLength; k++) {
+ var cc1 = %_StringCharCodeAt(uri, k);
+ if (unescape(cc1)) {
+ array[index++] = cc1;
+ } else {
+ if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) throw MakeURIError();
+ if (cc1 < 0xD800 || cc1 > 0xDBFF) {
+ index = URIEncodeSingle(cc1, array, index);
+ } else {
+ k++;
+ if (k == uriLength) throw MakeURIError();
+ var cc2 = %_StringCharCodeAt(uri, k);
+ if (cc2 < 0xDC00 || cc2 > 0xDFFF) throw MakeURIError();
+ index = URIEncodePair(cc1, cc2, array, index);
+ }
+ }
+ }
+
+ var result = %NewString(array.length, NEW_ONE_BYTE_STRING);
+ for (var i = 0; i < array.length; i++) {
+ %_OneByteSeqStringSetChar(i, array[i], result);
+ }
+ return result;
+}
+
+// ECMA-262, section 15.1.3
+function Decode(uri, reserved) {
+ uri = TO_STRING(uri);
+ var uriLength = uri.length;
+ var one_byte = %NewString(uriLength, NEW_ONE_BYTE_STRING);
+ var index = 0;
+ var k = 0;
+
+ // Optimistically assume one-byte string.
+ for ( ; k < uriLength; k++) {
+ var code = %_StringCharCodeAt(uri, k);
+ if (code == 37) { // '%'
+ if (k + 2 >= uriLength) throw MakeURIError();
+ var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, k+1),
+ %_StringCharCodeAt(uri, k+2));
+ if (cc >> 7) break; // Assumption wrong, two-byte string.
+ if (reserved(cc)) {
+ %_OneByteSeqStringSetChar(index++, 37, one_byte); // '%'.
+ %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+1),
+ one_byte);
+ %_OneByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k+2),
+ one_byte);
+ } else {
+ %_OneByteSeqStringSetChar(index++, cc, one_byte);
+ }
+ k += 2;
+ } else {
+ if (code > 0x7f) break; // Assumption wrong, two-byte string.
+ %_OneByteSeqStringSetChar(index++, code, one_byte);
+ }
+ }
+
+ one_byte = %TruncateString(one_byte, index);
+ if (k == uriLength) return one_byte;
+
+ // Write into two byte string.
+ var two_byte = %NewString(uriLength - k, NEW_TWO_BYTE_STRING);
+ index = 0;
+
+ for ( ; k < uriLength; k++) {
+ var code = %_StringCharCodeAt(uri, k);
+ if (code == 37) { // '%'
+ if (k + 2 >= uriLength) throw MakeURIError();
+ var cc = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
+ %_StringCharCodeAt(uri, ++k));
+ if (cc >> 7) {
+ var n = 0;
+ while (((cc << ++n) & 0x80) != 0) { }
+ if (n == 1 || n > 4) throw MakeURIError();
+ var octets = new GlobalArray(n);
+ octets[0] = cc;
+ if (k + 3 * (n - 1) >= uriLength) throw MakeURIError();
+ for (var i = 1; i < n; i++) {
+ if (uri[++k] != '%') throw MakeURIError();
+ octets[i] = URIHexCharsToCharCode(%_StringCharCodeAt(uri, ++k),
+ %_StringCharCodeAt(uri, ++k));
+ }
+ index = URIDecodeOctets(octets, two_byte, index);
+ } else if (reserved(cc)) {
+ %_TwoByteSeqStringSetChar(index++, 37, two_byte); // '%'.
+ %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k - 1),
+ two_byte);
+ %_TwoByteSeqStringSetChar(index++, %_StringCharCodeAt(uri, k),
+ two_byte);
+ } else {
+ %_TwoByteSeqStringSetChar(index++, cc, two_byte);
+ }
+ } else {
+ %_TwoByteSeqStringSetChar(index++, code, two_byte);
+ }
+ }
+
+ two_byte = %TruncateString(two_byte, index);
+ return one_byte + two_byte;
+}
+
+// -------------------------------------------------------------------
+// Define exported functions.
+
+// ECMA-262 - B.2.1.
+function URIEscapeJS(s) {
+ return %URIEscape(s);
+}
+
+// ECMA-262 - B.2.2.
+function URIUnescapeJS(s) {
+ return %URIUnescape(s);
+}
+
+// ECMA-262 - 15.1.3.1.
+function URIDecode(uri) {
+ var reservedPredicate = function(cc) {
+ // #$
+ if (35 <= cc && cc <= 36) return true;
+ // &
+ if (cc == 38) return true;
+ // +,
+ if (43 <= cc && cc <= 44) return true;
+ // /
+ if (cc == 47) return true;
+ // :;
+ if (58 <= cc && cc <= 59) return true;
+ // =
+ if (cc == 61) return true;
+ // ?@
+ if (63 <= cc && cc <= 64) return true;
+
+ return false;
+ };
+ return Decode(uri, reservedPredicate);
+}
+
+// ECMA-262 - 15.1.3.2.
+function URIDecodeComponent(component) {
+ var reservedPredicate = function(cc) { return false; };
+ return Decode(component, reservedPredicate);
+}
+
+// ECMA-262 - 15.1.3.3.
+function URIEncode(uri) {
+ var unescapePredicate = function(cc) {
+ if (isAlphaNumeric(cc)) return true;
+ // !
+ if (cc == 33) return true;
+ // #$
+ if (35 <= cc && cc <= 36) return true;
+ // &'()*+,-./
+ if (38 <= cc && cc <= 47) return true;
+ // :;
+ if (58 <= cc && cc <= 59) return true;
+ // =
+ if (cc == 61) return true;
+ // ?@
+ if (63 <= cc && cc <= 64) return true;
+ // _
+ if (cc == 95) return true;
+ // ~
+ if (cc == 126) return true;
+
+ return false;
+ };
+ return Encode(uri, unescapePredicate);
+}
+
+// ECMA-262 - 15.1.3.4
+function URIEncodeComponent(component) {
+ var unescapePredicate = function(cc) {
+ if (isAlphaNumeric(cc)) return true;
+ // !
+ if (cc == 33) return true;
+ // '()*
+ if (39 <= cc && cc <= 42) return true;
+ // -.
+ if (45 <= cc && cc <= 46) return true;
+ // _
+ if (cc == 95) return true;
+ // ~
+ if (cc == 126) return true;
+
+ return false;
+ };
+ return Encode(component, unescapePredicate);
+}
+
+// -------------------------------------------------------------------
+// Install exported functions.
+
+// Set up non-enumerable URI functions on the global object and set
+// their names.
+utils.InstallFunctions(global, DONT_ENUM, [
+ "escape", URIEscapeJS,
+ "unescape", URIUnescapeJS,
+ "decodeURI", URIDecode,
+ "decodeURIComponent", URIDecodeComponent,
+ "encodeURI", URIEncode,
+ "encodeURIComponent", URIEncodeComponent
+]);
+
+})
diff --git a/src/js/v8natives.js b/src/js/v8natives.js
new file mode 100644
index 0000000..26447da
--- /dev/null
+++ b/src/js/v8natives.js
@@ -0,0 +1,1190 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+%CheckIsBootstrapping();
+
+// ----------------------------------------------------------------------------
+// Imports
+
+var GlobalArray = global.Array;
+var GlobalBoolean = global.Boolean;
+var GlobalNumber = global.Number;
+var GlobalObject = global.Object;
+var InternalArray = utils.InternalArray;
+var iteratorSymbol = utils.ImportNow("iterator_symbol");
+var MakeRangeError;
+var MakeSyntaxError;
+var MakeTypeError;
+var MathAbs;
+var NaN = %GetRootNaN();
+var ObjectToString = utils.ImportNow("object_to_string");
+var ObserveBeginPerformSplice;
+var ObserveEndPerformSplice;
+var ObserveEnqueueSpliceRecord;
+var SameValue = utils.ImportNow("SameValue");
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ MakeRangeError = from.MakeRangeError;
+ MakeSyntaxError = from.MakeSyntaxError;
+ MakeTypeError = from.MakeTypeError;
+ MathAbs = from.MathAbs;
+ ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
+ ObserveEndPerformSplice = from.ObserveEndPerformSplice;
+ ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
+});
+
+// ----------------------------------------------------------------------------
+
+
+// ES6 18.2.3 isNaN(number)
+function GlobalIsNaN(number) {
+ number = TO_NUMBER(number);
+ return NUMBER_IS_NAN(number);
+}
+
+
+// ES6 18.2.2 isFinite(number)
+function GlobalIsFinite(number) {
+ number = TO_NUMBER(number);
+ return NUMBER_IS_FINITE(number);
+}
+
+
+// ES6 18.2.5 parseInt(string, radix)
+function GlobalParseInt(string, radix) {
+ if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
+ // Some people use parseInt instead of Math.floor. This
+ // optimization makes parseInt on a Smi 12 times faster (60ns
+ // vs 800ns). The following optimization makes parseInt on a
+ // non-Smi number 9 times faster (230ns vs 2070ns). Together
+ // they make parseInt on a string 1.4% slower (274ns vs 270ns).
+ if (%_IsSmi(string)) return string;
+ if (IS_NUMBER(string) &&
+ ((0.01 < string && string < 1e9) ||
+ (-1e9 < string && string < -0.01))) {
+ // Truncate number.
+ return string | 0;
+ }
+ string = TO_STRING(string);
+ radix = radix | 0;
+ } else {
+ // The spec says ToString should be evaluated before ToInt32.
+ string = TO_STRING(string);
+ radix = TO_INT32(radix);
+ if (!(radix == 0 || (2 <= radix && radix <= 36))) {
+ return NaN;
+ }
+ }
+
+ if (%_HasCachedArrayIndex(string) &&
+ (radix == 0 || radix == 10)) {
+ return %_GetCachedArrayIndex(string);
+ }
+ return %StringParseInt(string, radix);
+}
+
+
+// ES6 18.2.4 parseFloat(string)
+function GlobalParseFloat(string) {
+ // 1. Let inputString be ? ToString(string).
+ string = TO_STRING(string);
+ if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
+ return %StringParseFloat(string);
+}
+
+
+// ----------------------------------------------------------------------------
+
+// Set up global object.
+var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
+
+utils.InstallConstants(global, [
+ // ES6 18.1.1
+ "Infinity", INFINITY,
+ // ES6 18.1.2
+ "NaN", NaN,
+ // ES6 18.1.3
+ "undefined", UNDEFINED,
+]);
+
+// Set up non-enumerable function on the global object.
+utils.InstallFunctions(global, DONT_ENUM, [
+ "isNaN", GlobalIsNaN,
+ "isFinite", GlobalIsFinite,
+ "parseInt", GlobalParseInt,
+ "parseFloat", GlobalParseFloat,
+]);
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+// ES6 19.1.3.5 Object.prototype.toLocaleString([reserved1 [,reserved2]])
+function ObjectToLocaleString() {
+ CHECK_OBJECT_COERCIBLE(this, "Object.prototype.toLocaleString");
+ return this.toString();
+}
+
+
+// ES6 19.1.3.7 Object.prototype.valueOf()
+function ObjectValueOf() {
+ return TO_OBJECT(this);
+}
+
+
+// ES6 7.3.11
+function ObjectHasOwnProperty(value) {
+ var name = TO_NAME(value);
+ var object = TO_OBJECT(this);
+ return %HasOwnProperty(object, name);
+}
+
+
+// ES6 19.1.3.3 Object.prototype.isPrototypeOf(V)
+function ObjectIsPrototypeOf(V) {
+ if (!IS_RECEIVER(V)) return false;
+ var O = TO_OBJECT(this);
+ return %HasInPrototypeChain(V, O);
+}
+
+
+// ES6 19.1.3.4
+function ObjectPropertyIsEnumerable(V) {
+ var P = TO_NAME(V);
+ return %PropertyIsEnumerable(TO_OBJECT(this), P);
+}
+
+
+// Extensions for providing property getters and setters.
+function ObjectDefineGetter(name, fun) {
+ var receiver = this;
+ if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+ receiver = %GlobalProxy(ObjectDefineGetter);
+ }
+ if (!IS_CALLABLE(fun)) {
+ throw MakeTypeError(kObjectGetterExpectingFunction);
+ }
+ var desc = new PropertyDescriptor();
+ desc.setGet(fun);
+ desc.setEnumerable(true);
+ desc.setConfigurable(true);
+ DefineOwnProperty(TO_OBJECT(receiver), TO_NAME(name), desc, false);
+}
+
+
+function ObjectLookupGetter(name) {
+ var receiver = this;
+ if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+ receiver = %GlobalProxy(ObjectLookupGetter);
+ }
+ return %LookupAccessor(TO_OBJECT(receiver), TO_NAME(name), GETTER);
+}
+
+
+function ObjectDefineSetter(name, fun) {
+ var receiver = this;
+ if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+ receiver = %GlobalProxy(ObjectDefineSetter);
+ }
+ if (!IS_CALLABLE(fun)) {
+ throw MakeTypeError(kObjectSetterExpectingFunction);
+ }
+ var desc = new PropertyDescriptor();
+ desc.setSet(fun);
+ desc.setEnumerable(true);
+ desc.setConfigurable(true);
+ DefineOwnProperty(TO_OBJECT(receiver), TO_NAME(name), desc, false);
+}
+
+
+function ObjectLookupSetter(name) {
+ var receiver = this;
+ if (IS_NULL(receiver) || IS_UNDEFINED(receiver)) {
+ receiver = %GlobalProxy(ObjectLookupSetter);
+ }
+ return %LookupAccessor(TO_OBJECT(receiver), TO_NAME(name), SETTER);
+}
+
+
+// ES6 6.2.4.1
+function IsAccessorDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return desc.hasGetter() || desc.hasSetter();
+}
+
+
+// ES6 6.2.4.2
+function IsDataDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return desc.hasValue() || desc.hasWritable();
+}
+
+
+// ES6 6.2.4.3
+function IsGenericDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return false;
+ return !(IsAccessorDescriptor(desc) || IsDataDescriptor(desc));
+}
+
+
+function IsInconsistentDescriptor(desc) {
+ return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
+}
+
+
+// Harmony Proxies
+function FromGenericPropertyDescriptor(desc) {
+ if (IS_UNDEFINED(desc)) return desc;
+ var obj = new GlobalObject();
+
+ if (desc.hasValue()) {
+ %AddNamedProperty(obj, "value", desc.getValue(), NONE);
+ }
+ if (desc.hasWritable()) {
+ %AddNamedProperty(obj, "writable", desc.isWritable(), NONE);
+ }
+ if (desc.hasGetter()) {
+ %AddNamedProperty(obj, "get", desc.getGet(), NONE);
+ }
+ if (desc.hasSetter()) {
+ %AddNamedProperty(obj, "set", desc.getSet(), NONE);
+ }
+ if (desc.hasEnumerable()) {
+ %AddNamedProperty(obj, "enumerable", desc.isEnumerable(), NONE);
+ }
+ if (desc.hasConfigurable()) {
+ %AddNamedProperty(obj, "configurable", desc.isConfigurable(), NONE);
+ }
+ return obj;
+}
+
+
+// ES6 6.2.4.5
+function ToPropertyDescriptor(obj) {
+ if (!IS_RECEIVER(obj)) throw MakeTypeError(kPropertyDescObject, obj);
+
+ var desc = new PropertyDescriptor();
+
+ if ("enumerable" in obj) {
+ desc.setEnumerable(TO_BOOLEAN(obj.enumerable));
+ }
+
+ if ("configurable" in obj) {
+ desc.setConfigurable(TO_BOOLEAN(obj.configurable));
+ }
+
+ if ("value" in obj) {
+ desc.setValue(obj.value);
+ }
+
+ if ("writable" in obj) {
+ desc.setWritable(TO_BOOLEAN(obj.writable));
+ }
+
+ if ("get" in obj) {
+ var get = obj.get;
+ if (!IS_UNDEFINED(get) && !IS_CALLABLE(get)) {
+ throw MakeTypeError(kObjectGetterCallable, get);
+ }
+ desc.setGet(get);
+ }
+
+ if ("set" in obj) {
+ var set = obj.set;
+ if (!IS_UNDEFINED(set) && !IS_CALLABLE(set)) {
+ throw MakeTypeError(kObjectSetterCallable, set);
+ }
+ desc.setSet(set);
+ }
+
+ if (IsInconsistentDescriptor(desc)) {
+ throw MakeTypeError(kValueAndAccessor, obj);
+ }
+ return desc;
+}
+
+// TODO(cbruni): remove once callers have been removed
+function ToCompletePropertyDescriptor(obj) {
+ var desc = ToPropertyDescriptor(obj);
+ if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
+ if (!desc.hasValue()) desc.setValue(UNDEFINED);
+ if (!desc.hasWritable()) desc.setWritable(false);
+ } else {
+ // Is accessor descriptor.
+ if (!desc.hasGetter()) desc.setGet(UNDEFINED);
+ if (!desc.hasSetter()) desc.setSet(UNDEFINED);
+ }
+ if (!desc.hasEnumerable()) desc.setEnumerable(false);
+ if (!desc.hasConfigurable()) desc.setConfigurable(false);
+ return desc;
+}
+
+
+function PropertyDescriptor() {
+ // Initialize here so they are all in-object and have the same map.
+ // Default values from ES5 8.6.1.
+ this.value_ = UNDEFINED;
+ this.hasValue_ = false;
+ this.writable_ = false;
+ this.hasWritable_ = false;
+ this.enumerable_ = false;
+ this.hasEnumerable_ = false;
+ this.configurable_ = false;
+ this.hasConfigurable_ = false;
+ this.get_ = UNDEFINED;
+ this.hasGetter_ = false;
+ this.set_ = UNDEFINED;
+ this.hasSetter_ = false;
+}
+
+utils.SetUpLockedPrototype(PropertyDescriptor, [
+ "value_",
+ "hasValue_",
+ "writable_",
+ "hasWritable_",
+ "enumerable_",
+ "hasEnumerable_",
+ "configurable_",
+ "hasConfigurable_",
+ "get_",
+ "hasGetter_",
+ "set_",
+ "hasSetter_"
+], [
+ "toString", function PropertyDescriptor_ToString() {
+ return "[object PropertyDescriptor]";
+ },
+ "setValue", function PropertyDescriptor_SetValue(value) {
+ this.value_ = value;
+ this.hasValue_ = true;
+ },
+ "getValue", function PropertyDescriptor_GetValue() {
+ return this.value_;
+ },
+ "hasValue", function PropertyDescriptor_HasValue() {
+ return this.hasValue_;
+ },
+ "setEnumerable", function PropertyDescriptor_SetEnumerable(enumerable) {
+ this.enumerable_ = enumerable;
+ this.hasEnumerable_ = true;
+ },
+ "isEnumerable", function PropertyDescriptor_IsEnumerable() {
+ return this.enumerable_;
+ },
+ "hasEnumerable", function PropertyDescriptor_HasEnumerable() {
+ return this.hasEnumerable_;
+ },
+ "setWritable", function PropertyDescriptor_SetWritable(writable) {
+ this.writable_ = writable;
+ this.hasWritable_ = true;
+ },
+ "isWritable", function PropertyDescriptor_IsWritable() {
+ return this.writable_;
+ },
+ "hasWritable", function PropertyDescriptor_HasWritable() {
+ return this.hasWritable_;
+ },
+ "setConfigurable",
+ function PropertyDescriptor_SetConfigurable(configurable) {
+ this.configurable_ = configurable;
+ this.hasConfigurable_ = true;
+ },
+ "hasConfigurable", function PropertyDescriptor_HasConfigurable() {
+ return this.hasConfigurable_;
+ },
+ "isConfigurable", function PropertyDescriptor_IsConfigurable() {
+ return this.configurable_;
+ },
+ "setGet", function PropertyDescriptor_SetGetter(get) {
+ this.get_ = get;
+ this.hasGetter_ = true;
+ },
+ "getGet", function PropertyDescriptor_GetGetter() {
+ return this.get_;
+ },
+ "hasGetter", function PropertyDescriptor_HasGetter() {
+ return this.hasGetter_;
+ },
+ "setSet", function PropertyDescriptor_SetSetter(set) {
+ this.set_ = set;
+ this.hasSetter_ = true;
+ },
+ "getSet", function PropertyDescriptor_GetSetter() {
+ return this.set_;
+ },
+ "hasSetter", function PropertyDescriptor_HasSetter() {
+ return this.hasSetter_;
+ }
+]);
+
+
+// Converts an array returned from Runtime_GetOwnProperty to an actual
+// property descriptor. For a description of the array layout please
+// see the runtime.cc file.
+function ConvertDescriptorArrayToDescriptor(desc_array) {
+ if (IS_UNDEFINED(desc_array)) {
+ return UNDEFINED;
+ }
+
+ var desc = new PropertyDescriptor();
+ // This is an accessor.
+ if (desc_array[IS_ACCESSOR_INDEX]) {
+ desc.setGet(desc_array[GETTER_INDEX]);
+ desc.setSet(desc_array[SETTER_INDEX]);
+ } else {
+ desc.setValue(desc_array[VALUE_INDEX]);
+ desc.setWritable(desc_array[WRITABLE_INDEX]);
+ }
+ desc.setEnumerable(desc_array[ENUMERABLE_INDEX]);
+ desc.setConfigurable(desc_array[CONFIGURABLE_INDEX]);
+
+ return desc;
+}
+
+
+// For Harmony proxies.
+function GetTrap(handler, name, defaultTrap) {
+ var trap = handler[name];
+ if (IS_UNDEFINED(trap)) {
+ if (IS_UNDEFINED(defaultTrap)) {
+ throw MakeTypeError(kIllegalInvocation);
+ }
+ trap = defaultTrap;
+ } else if (!IS_CALLABLE(trap)) {
+ throw MakeTypeError(kIllegalInvocation);
+ }
+ return trap;
+}
+
+
+function CallTrap1(handler, name, defaultTrap, x) {
+ return %_Call(GetTrap(handler, name, defaultTrap), handler, x);
+}
+
+
+function CallTrap2(handler, name, defaultTrap, x, y) {
+ return %_Call(GetTrap(handler, name, defaultTrap), handler, x, y);
+}
+
+
+// ES5 section 8.12.1.
+// TODO(jkummerow): Deprecated. Migrate all callers to
+// ObjectGetOwnPropertyDescriptor and delete this.
+function GetOwnPropertyJS(obj, v) {
+ var p = TO_NAME(v);
+ if (IS_PROXY(obj)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(v)) return UNDEFINED;
+
+ var handler = %JSProxyGetHandler(obj);
+ var descriptor = CallTrap1(
+ handler, "getOwnPropertyDescriptor", UNDEFINED, p);
+ if (IS_UNDEFINED(descriptor)) return descriptor;
+ var desc = ToCompletePropertyDescriptor(descriptor);
+ if (!desc.isConfigurable()) {
+ throw MakeTypeError(kIllegalInvocation);
+ }
+ return desc;
+ }
+
+ // GetOwnProperty returns an array indexed by the constants
+ // defined in macros.py.
+ // If p is not a property on obj undefined is returned.
+ var props = %GetOwnProperty_Legacy(TO_OBJECT(obj), p);
+
+ return ConvertDescriptorArrayToDescriptor(props);
+}
+
+
+// ES6 7.3.9
+function GetMethod(obj, p) {
+ var func = obj[p];
+ if (IS_NULL_OR_UNDEFINED(func)) return UNDEFINED;
+ if (IS_CALLABLE(func)) return func;
+ throw MakeTypeError(kCalledNonCallable, typeof func);
+}
+
+
+// Harmony proxies.
+function DefineProxyProperty(obj, p, attributes, should_throw) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(p)) return false;
+
+ var handler = %JSProxyGetHandler(obj);
+ var result = CallTrap2(handler, "defineProperty", UNDEFINED, p, attributes);
+ if (!result) {
+ if (should_throw) {
+ throw MakeTypeError(kIllegalInvocation);
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+// ES6 9.1.6 [[DefineOwnProperty]](P, Desc)
+function DefineObjectProperty(obj, p, desc, should_throw) {
+ var current_array = %GetOwnProperty_Legacy(obj, TO_NAME(p));
+ var current = ConvertDescriptorArrayToDescriptor(current_array);
+ var extensible = %object_is_extensible(obj);
+
+ if (IS_UNDEFINED(current) && !extensible) {
+ if (should_throw) {
+ throw MakeTypeError(kDefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+
+ if (!IS_UNDEFINED(current)) {
+ if ((IsGenericDescriptor(desc) ||
+ IsDataDescriptor(desc) == IsDataDescriptor(current)) &&
+ (!desc.hasEnumerable() ||
+ SameValue(desc.isEnumerable(), current.isEnumerable())) &&
+ (!desc.hasConfigurable() ||
+ SameValue(desc.isConfigurable(), current.isConfigurable())) &&
+ (!desc.hasWritable() ||
+ SameValue(desc.isWritable(), current.isWritable())) &&
+ (!desc.hasValue() ||
+ SameValue(desc.getValue(), current.getValue())) &&
+ (!desc.hasGetter() ||
+ SameValue(desc.getGet(), current.getGet())) &&
+ (!desc.hasSetter() ||
+ SameValue(desc.getSet(), current.getSet()))) {
+ return true;
+ }
+ if (!current.isConfigurable()) {
+ // Step 7
+ if (desc.isConfigurable() ||
+ (desc.hasEnumerable() &&
+ desc.isEnumerable() != current.isEnumerable())) {
+ if (should_throw) {
+ throw MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ // Step 8
+ if (!IsGenericDescriptor(desc)) {
+ // Step 9a
+ if (IsDataDescriptor(current) != IsDataDescriptor(desc)) {
+ if (should_throw) {
+ throw MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ // Step 10a
+ if (IsDataDescriptor(current) && IsDataDescriptor(desc)) {
+ var currentIsWritable = current.isWritable();
+ if (currentIsWritable != desc.isWritable()) {
+ if (!currentIsWritable || IS_STRONG(obj)) {
+ if (should_throw) {
+ throw currentIsWritable
+ ? MakeTypeError(kStrongRedefineDisallowed, obj, p)
+ : MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ }
+ if (!currentIsWritable && desc.hasValue() &&
+ !SameValue(desc.getValue(), current.getValue())) {
+ if (should_throw) {
+ throw MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ }
+ // Step 11
+ if (IsAccessorDescriptor(desc) && IsAccessorDescriptor(current)) {
+ if (desc.hasSetter() &&
+ !SameValue(desc.getSet(), current.getSet())) {
+ if (should_throw) {
+ throw MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ if (desc.hasGetter() && !SameValue(desc.getGet(),current.getGet())) {
+ if (should_throw) {
+ throw MakeTypeError(kRedefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Send flags - enumerable and configurable are common - writable is
+ // only send to the data descriptor.
+ // Take special care if enumerable and configurable is not defined on
+ // desc (we need to preserve the existing values from current).
+ var flag = NONE;
+ if (desc.hasEnumerable()) {
+ flag |= desc.isEnumerable() ? 0 : DONT_ENUM;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isEnumerable() ? 0 : DONT_ENUM;
+ } else {
+ flag |= DONT_ENUM;
+ }
+
+ if (desc.hasConfigurable()) {
+ flag |= desc.isConfigurable() ? 0 : DONT_DELETE;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isConfigurable() ? 0 : DONT_DELETE;
+ } else
+ flag |= DONT_DELETE;
+
+ if (IsDataDescriptor(desc) ||
+ (IsGenericDescriptor(desc) &&
+ (IS_UNDEFINED(current) || IsDataDescriptor(current)))) {
+ // There are 3 cases that lead here:
+ // Step 4a - defining a new data property.
+ // Steps 9b & 12 - replacing an existing accessor property with a data
+ // property.
+ // Step 12 - updating an existing data property with a data or generic
+ // descriptor.
+
+ if (desc.hasWritable()) {
+ flag |= desc.isWritable() ? 0 : READ_ONLY;
+ } else if (!IS_UNDEFINED(current)) {
+ flag |= current.isWritable() ? 0 : READ_ONLY;
+ } else {
+ flag |= READ_ONLY;
+ }
+
+ var value = UNDEFINED; // Default value is undefined.
+ if (desc.hasValue()) {
+ value = desc.getValue();
+ } else if (!IS_UNDEFINED(current) && IsDataDescriptor(current)) {
+ value = current.getValue();
+ }
+
+ %DefineDataPropertyUnchecked(obj, p, value, flag);
+ } else {
+ // There are 3 cases that lead here:
+ // Step 4b - defining a new accessor property.
+ // Steps 9c & 12 - replacing an existing data property with an accessor
+ // property.
+ // Step 12 - updating an existing accessor property with an accessor
+ // descriptor.
+ var getter = null;
+ if (desc.hasGetter()) {
+ getter = desc.getGet();
+ } else if (IsAccessorDescriptor(current) && current.hasGetter()) {
+ getter = current.getGet();
+ }
+ var setter = null;
+ if (desc.hasSetter()) {
+ setter = desc.getSet();
+ } else if (IsAccessorDescriptor(current) && current.hasSetter()) {
+ setter = current.getSet();
+ }
+ %DefineAccessorPropertyUnchecked(obj, p, getter, setter, flag);
+ }
+ return true;
+}
+
+
+// ES5 section 15.4.5.1.
+function DefineArrayProperty(obj, p, desc, should_throw) {
+ // Step 3 - Special handling for array index.
+ if (!IS_SYMBOL(p)) {
+ var index = TO_UINT32(p);
+ var emit_splice = false;
+ if (TO_STRING(index) == p && index != 4294967295) {
+ var length = obj.length;
+ if (index >= length && %IsObserved(obj)) {
+ emit_splice = true;
+ ObserveBeginPerformSplice(obj);
+ }
+
+ var length_desc = GetOwnPropertyJS(obj, "length");
+ if ((index >= length && !length_desc.isWritable()) ||
+ !DefineObjectProperty(obj, p, desc, true)) {
+ if (emit_splice)
+ ObserveEndPerformSplice(obj);
+ if (should_throw) {
+ throw MakeTypeError(kDefineDisallowed, p);
+ } else {
+ return false;
+ }
+ }
+ if (index >= length) {
+ obj.length = index + 1;
+ }
+ if (emit_splice) {
+ ObserveEndPerformSplice(obj);
+ ObserveEnqueueSpliceRecord(obj, length, [], index + 1 - length);
+ }
+ return true;
+ }
+ }
+
+ // Step 5 - Fallback to default implementation.
+ return DefineObjectProperty(obj, p, desc, should_throw);
+}
+
+
+// ES5 section 8.12.9, ES5 section 15.4.5.1 and Harmony proxies.
+function DefineOwnProperty(obj, p, desc, should_throw) {
+ if (IS_PROXY(obj)) {
+ // TODO(rossberg): adjust once there is a story for symbols vs proxies.
+ if (IS_SYMBOL(p)) return false;
+
+ var attributes = FromGenericPropertyDescriptor(desc);
+ return DefineProxyProperty(obj, p, attributes, should_throw);
+ } else if (IS_ARRAY(obj)) {
+ return DefineArrayProperty(obj, p, desc, should_throw);
+ } else {
+ return DefineObjectProperty(obj, p, desc, should_throw);
+ }
+}
+
+
+// ES6 section 19.1.2.9
+function ObjectGetPrototypeOf(obj) {
+ return %_GetPrototype(TO_OBJECT(obj));
+}
+
+// ES6 section 19.1.2.18.
+function ObjectSetPrototypeOf(obj, proto) {
+ CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
+
+ if (proto !== null && !IS_RECEIVER(proto)) {
+ throw MakeTypeError(kProtoObjectOrNull, proto);
+ }
+
+ if (IS_RECEIVER(obj)) {
+ %SetPrototype(obj, proto);
+ }
+
+ return obj;
+}
+
+
+// ES6 section 19.1.2.6
+function ObjectGetOwnPropertyDescriptor(obj, p) {
+ return %GetOwnProperty(obj, p);
+}
+
+
+// ES5 section 15.2.3.4.
+function ObjectGetOwnPropertyNames(obj) {
+ obj = TO_OBJECT(obj);
+ return %GetOwnPropertyKeys(obj, PROPERTY_FILTER_SKIP_SYMBOLS);
+}
+
+
+// ES5 section 15.2.3.6.
+function ObjectDefineProperty(obj, p, attributes) {
+ // The new pure-C++ implementation doesn't support O.o.
+ // TODO(jkummerow): Implement missing features and remove fallback path.
+ if (%IsObserved(obj)) {
+ if (!IS_RECEIVER(obj)) {
+ throw MakeTypeError(kCalledOnNonObject, "Object.defineProperty");
+ }
+ var name = TO_NAME(p);
+ var desc = ToPropertyDescriptor(attributes);
+ DefineOwnProperty(obj, name, desc, true);
+ return obj;
+ }
+ return %ObjectDefineProperty(obj, p, attributes);
+}
+
+
+function GetOwnEnumerablePropertyNames(object) {
+ return %GetOwnPropertyKeys(object, PROPERTY_FILTER_ONLY_ENUMERABLE);
+}
+
+
+// ES5 section 15.2.3.7.
+function ObjectDefineProperties(obj, properties) {
+ // The new pure-C++ implementation doesn't support O.o.
+ // TODO(jkummerow): Implement missing features and remove fallback path.
+ if (%IsObserved(obj)) {
+ if (!IS_RECEIVER(obj)) {
+ throw MakeTypeError(kCalledOnNonObject, "Object.defineProperties");
+ }
+ var props = TO_OBJECT(properties);
+ var names = GetOwnEnumerablePropertyNames(props);
+ var descriptors = new InternalArray();
+ for (var i = 0; i < names.length; i++) {
+ descriptors.push(ToPropertyDescriptor(props[names[i]]));
+ }
+ for (var i = 0; i < names.length; i++) {
+ DefineOwnProperty(obj, names[i], descriptors[i], true);
+ }
+ return obj;
+ }
+ return %ObjectDefineProperties(obj, properties);
+}
+
+
+// ES6 B.2.2.1.1
+function ObjectGetProto() {
+ return %_GetPrototype(TO_OBJECT(this));
+}
+
+
+// ES6 B.2.2.1.2
+function ObjectSetProto(proto) {
+ CHECK_OBJECT_COERCIBLE(this, "Object.prototype.__proto__");
+
+ if ((IS_RECEIVER(proto) || IS_NULL(proto)) && IS_RECEIVER(this)) {
+ %SetPrototype(this, proto);
+ }
+}
+
+
+// ES6 19.1.1.1
+function ObjectConstructor(x) {
+ if (GlobalObject != new.target && !IS_UNDEFINED(new.target)) {
+ return this;
+ }
+ if (IS_NULL(x) || IS_UNDEFINED(x)) return {};
+ return TO_OBJECT(x);
+}
+
+
+// ----------------------------------------------------------------------------
+// Object
+
+%SetNativeFlag(GlobalObject);
+%SetCode(GlobalObject, ObjectConstructor);
+
+%AddNamedProperty(GlobalObject.prototype, "constructor", GlobalObject,
+ DONT_ENUM);
+
+// Set up non-enumerable functions on the Object.prototype object.
+utils.InstallFunctions(GlobalObject.prototype, DONT_ENUM, [
+ "toString", ObjectToString,
+ "toLocaleString", ObjectToLocaleString,
+ "valueOf", ObjectValueOf,
+ "hasOwnProperty", ObjectHasOwnProperty,
+ "isPrototypeOf", ObjectIsPrototypeOf,
+ "propertyIsEnumerable", ObjectPropertyIsEnumerable,
+ "__defineGetter__", ObjectDefineGetter,
+ "__lookupGetter__", ObjectLookupGetter,
+ "__defineSetter__", ObjectDefineSetter,
+ "__lookupSetter__", ObjectLookupSetter
+]);
+utils.InstallGetterSetter(GlobalObject.prototype, "__proto__", ObjectGetProto,
+ ObjectSetProto);
+
+// Set up non-enumerable functions in the Object object.
+utils.InstallFunctions(GlobalObject, DONT_ENUM, [
+ // assign is added in bootstrapper.cc.
+ // keys is added in bootstrapper.cc.
+ "defineProperty", ObjectDefineProperty,
+ "defineProperties", ObjectDefineProperties,
+ "getPrototypeOf", ObjectGetPrototypeOf,
+ "setPrototypeOf", ObjectSetPrototypeOf,
+ "getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
+ "getOwnPropertyNames", ObjectGetOwnPropertyNames,
+ // getOwnPropertySymbols is added in symbol.js.
+ "is", SameValue, // ECMA-262, Edition 6, section 19.1.2.10
+ // deliverChangeRecords, getNotifier, observe and unobserve are added
+ // in object-observe.js.
+]);
+
+
+// ----------------------------------------------------------------------------
+// Boolean
+
+function BooleanConstructor(x) {
+ // TODO(bmeurer): Move this to toplevel.
+ "use strict";
+ if (!IS_UNDEFINED(new.target)) {
+ %_SetValueOf(this, TO_BOOLEAN(x));
+ } else {
+ return TO_BOOLEAN(x);
+ }
+}
+
+
+function BooleanToString() {
+ // NOTE: Both Boolean objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ var b = this;
+ if (!IS_BOOLEAN(b)) {
+ if (!IS_BOOLEAN_WRAPPER(b)) {
+ throw MakeTypeError(kNotGeneric, 'Boolean.prototype.toString');
+ }
+ b = %_ValueOf(b);
+ }
+ return b ? 'true' : 'false';
+}
+
+
+function BooleanValueOf() {
+ // NOTE: Both Boolean objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) {
+ throw MakeTypeError(kNotGeneric, 'Boolean.prototype.valueOf');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ----------------------------------------------------------------------------
+
+%SetCode(GlobalBoolean, BooleanConstructor);
+%FunctionSetPrototype(GlobalBoolean, new GlobalBoolean(false));
+%AddNamedProperty(GlobalBoolean.prototype, "constructor", GlobalBoolean,
+ DONT_ENUM);
+
+utils.InstallFunctions(GlobalBoolean.prototype, DONT_ENUM, [
+ "toString", BooleanToString,
+ "valueOf", BooleanValueOf
+]);
+
+
+// ----------------------------------------------------------------------------
+// Number
+
+// ES6 Number.prototype.toString([ radix ])
+function NumberToStringJS(radix) {
+ // NOTE: Both Number objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ var number = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError(kNotGeneric, 'Number.prototype.toString');
+ }
+ // Get the value of this number in case it's an object.
+ number = %_ValueOf(this);
+ }
+ // Fast case: Convert number in radix 10.
+ if (IS_UNDEFINED(radix) || radix === 10) {
+ return %_NumberToString(number);
+ }
+
+ // Convert the radix to an integer and check the range.
+ radix = TO_INTEGER(radix);
+ if (radix < 2 || radix > 36) throw MakeRangeError(kToRadixFormatRange);
+ // Convert the number to a string in the given radix.
+ return %NumberToRadixString(number, radix);
+}
+
+
+// ES6 20.1.3.4 Number.prototype.toLocaleString([reserved1 [, reserved2]])
+function NumberToLocaleString() {
+ return %_Call(NumberToStringJS, this);
+}
+
+
+// ES6 20.1.3.7 Number.prototype.valueOf()
+function NumberValueOf() {
+ // NOTE: Both Number objects and values can enter here as
+ // 'this'. This is not as dictated by ECMA-262.
+ if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError(kNotGeneric, 'Number.prototype.valueOf');
+ }
+ return %_ValueOf(this);
+}
+
+
+// ES6 20.1.3.3 Number.prototype.toFixed(fractionDigits)
+function NumberToFixedJS(fractionDigits) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Number.prototype.toFixed", this);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ var f = TO_INTEGER(fractionDigits);
+
+ if (f < 0 || f > 20) {
+ throw MakeRangeError(kNumberFormatRange, "toFixed() digits");
+ }
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == INFINITY) return "Infinity";
+ if (x == -INFINITY) return "-Infinity";
+
+ return %NumberToFixed(x, f);
+}
+
+
+// ES6 20.1.3.2 Number.prototype.toExponential(fractionDigits)
+function NumberToExponentialJS(fractionDigits) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Number.prototype.toExponential", this);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ var f = IS_UNDEFINED(fractionDigits) ? UNDEFINED : TO_INTEGER(fractionDigits);
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == INFINITY) return "Infinity";
+ if (x == -INFINITY) return "-Infinity";
+
+ if (IS_UNDEFINED(f)) {
+ f = -1; // Signal for runtime function that f is not defined.
+ } else if (f < 0 || f > 20) {
+ throw MakeRangeError(kNumberFormatRange, "toExponential()");
+ }
+ return %NumberToExponential(x, f);
+}
+
+
+// ES6 20.1.3.5 Number.prototype.toPrecision(precision)
+function NumberToPrecisionJS(precision) {
+ var x = this;
+ if (!IS_NUMBER(this)) {
+ if (!IS_NUMBER_WRAPPER(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ "Number.prototype.toPrecision", this);
+ }
+ // Get the value of this number in case it's an object.
+ x = %_ValueOf(this);
+ }
+ if (IS_UNDEFINED(precision)) return TO_STRING(x);
+ var p = TO_INTEGER(precision);
+
+ if (NUMBER_IS_NAN(x)) return "NaN";
+ if (x == INFINITY) return "Infinity";
+ if (x == -INFINITY) return "-Infinity";
+
+ if (p < 1 || p > 21) {
+ throw MakeRangeError(kToPrecisionFormatRange);
+ }
+ return %NumberToPrecision(x, p);
+}
+
+
+// Harmony isFinite.
+function NumberIsFinite(number) {
+ return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
+}
+
+
+// Harmony isInteger
+function NumberIsInteger(number) {
+ return NumberIsFinite(number) && TO_INTEGER(number) == number;
+}
+
+
+// Harmony isNaN.
+function NumberIsNaN(number) {
+ return IS_NUMBER(number) && NUMBER_IS_NAN(number);
+}
+
+
+// Harmony isSafeInteger
+function NumberIsSafeInteger(number) {
+ if (NumberIsFinite(number)) {
+ var integral = TO_INTEGER(number);
+ if (integral == number) {
+ return MathAbs(integral) <= kMaxSafeInteger;
+ }
+ }
+ return false;
+}
+
+
+// ----------------------------------------------------------------------------
+
+%FunctionSetPrototype(GlobalNumber, new GlobalNumber(0));
+
+%OptimizeObjectForAddingMultipleProperties(GlobalNumber.prototype, 8);
+// Set up the constructor property on the Number prototype object.
+%AddNamedProperty(GlobalNumber.prototype, "constructor", GlobalNumber,
+ DONT_ENUM);
+
+utils.InstallConstants(GlobalNumber, [
+ // ECMA-262 section 15.7.3.1.
+ "MAX_VALUE", 1.7976931348623157e+308,
+ // ECMA-262 section 15.7.3.2.
+ "MIN_VALUE", 5e-324,
+ // ECMA-262 section 15.7.3.3.
+ "NaN", NaN,
+ // ECMA-262 section 15.7.3.4.
+ "NEGATIVE_INFINITY", -INFINITY,
+ // ECMA-262 section 15.7.3.5.
+ "POSITIVE_INFINITY", INFINITY,
+
+ // --- Harmony constants (no spec refs until settled.)
+
+ "MAX_SAFE_INTEGER", %_MathPow(2, 53) - 1,
+ "MIN_SAFE_INTEGER", -%_MathPow(2, 53) + 1,
+ "EPSILON", %_MathPow(2, -52)
+]);
+
+// Set up non-enumerable functions on the Number prototype object.
+utils.InstallFunctions(GlobalNumber.prototype, DONT_ENUM, [
+ "toString", NumberToStringJS,
+ "toLocaleString", NumberToLocaleString,
+ "valueOf", NumberValueOf,
+ "toFixed", NumberToFixedJS,
+ "toExponential", NumberToExponentialJS,
+ "toPrecision", NumberToPrecisionJS
+]);
+
+// Harmony Number constructor additions
+utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
+ "isFinite", NumberIsFinite,
+ "isInteger", NumberIsInteger,
+ "isNaN", NumberIsNaN,
+ "isSafeInteger", NumberIsSafeInteger,
+ "parseInt", GlobalParseInt,
+ "parseFloat", GlobalParseFloat
+]);
+
+%SetForceInlineFlag(NumberIsNaN);
+
+
+// ----------------------------------------------------------------------------
+// Iterator related spec functions.
+
+// ES6 7.4.1 GetIterator(obj, method)
+function GetIterator(obj, method) {
+ if (IS_UNDEFINED(method)) {
+ method = obj[iteratorSymbol];
+ }
+ if (!IS_CALLABLE(method)) {
+ throw MakeTypeError(kNotIterable, obj);
+ }
+ var iterator = %_Call(method, obj);
+ if (!IS_RECEIVER(iterator)) {
+ throw MakeTypeError(kNotAnIterator, iterator);
+ }
+ return iterator;
+}
+
+// ----------------------------------------------------------------------------
+// Exports
+
+utils.Export(function(to) {
+ to.GetIterator = GetIterator;
+ to.GetMethod = GetMethod;
+ to.IsFinite = GlobalIsFinite;
+ to.IsNaN = GlobalIsNaN;
+ to.NumberIsNaN = NumberIsNaN;
+ to.ObjectDefineProperties = ObjectDefineProperties;
+ to.ObjectDefineProperty = ObjectDefineProperty;
+ to.ObjectHasOwnProperty = ObjectHasOwnProperty;
+});
+
+%InstallToContext([
+ "object_value_of", ObjectValueOf,
+]);
+
+})
diff --git a/src/js/weak-collection.js b/src/js/weak-collection.js
new file mode 100644
index 0000000..308b9ed
--- /dev/null
+++ b/src/js/weak-collection.js
@@ -0,0 +1,190 @@
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(function(global, utils) {
+
+"use strict";
+
+%CheckIsBootstrapping();
+
+// -------------------------------------------------------------------
+// Imports
+
+var GetExistingHash;
+var GetHash;
+var GlobalObject = global.Object;
+var GlobalWeakMap = global.WeakMap;
+var GlobalWeakSet = global.WeakSet;
+var MakeTypeError;
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
+
+utils.Import(function(from) {
+ GetExistingHash = from.GetExistingHash;
+ GetHash = from.GetHash;
+ MakeTypeError = from.MakeTypeError;
+});
+
+// -------------------------------------------------------------------
+// Harmony WeakMap
+
+function WeakMapConstructor(iterable) {
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNotFunction, "WeakMap");
+ }
+
+ %WeakCollectionInitialize(this);
+
+ if (!IS_NULL_OR_UNDEFINED(iterable)) {
+ var adder = this.set;
+ if (!IS_CALLABLE(adder)) {
+ throw MakeTypeError(kPropertyNotFunction, adder, 'set', this);
+ }
+ for (var nextItem of iterable) {
+ if (!IS_RECEIVER(nextItem)) {
+ throw MakeTypeError(kIteratorValueNotAnObject, nextItem);
+ }
+ %_Call(adder, this, nextItem[0], nextItem[1]);
+ }
+ }
+}
+
+
+function WeakMapGet(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakMap.prototype.get', this);
+ }
+ if (!IS_RECEIVER(key)) return UNDEFINED;
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return UNDEFINED;
+ return %WeakCollectionGet(this, key, hash);
+}
+
+
+function WeakMapSet(key, value) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakMap.prototype.set', this);
+ }
+ if (!IS_RECEIVER(key)) throw MakeTypeError(kInvalidWeakMapKey);
+ return %WeakCollectionSet(this, key, value, GetHash(key));
+}
+
+
+function WeakMapHas(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakMap.prototype.has', this);
+ }
+ if (!IS_RECEIVER(key)) return false;
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return false;
+ return %WeakCollectionHas(this, key, hash);
+}
+
+
+function WeakMapDelete(key) {
+ if (!IS_WEAKMAP(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakMap.prototype.delete', this);
+ }
+ if (!IS_RECEIVER(key)) return false;
+ var hash = GetExistingHash(key);
+ if (IS_UNDEFINED(hash)) return false;
+ return %WeakCollectionDelete(this, key, hash);
+}
+
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalWeakMap, WeakMapConstructor);
+%FunctionSetLength(GlobalWeakMap, 0);
+%FunctionSetPrototype(GlobalWeakMap, new GlobalObject());
+%AddNamedProperty(GlobalWeakMap.prototype, "constructor", GlobalWeakMap,
+ DONT_ENUM);
+%AddNamedProperty(GlobalWeakMap.prototype, toStringTagSymbol, "WeakMap",
+ DONT_ENUM | READ_ONLY);
+
+// Set up the non-enumerable functions on the WeakMap prototype object.
+utils.InstallFunctions(GlobalWeakMap.prototype, DONT_ENUM, [
+ "get", WeakMapGet,
+ "set", WeakMapSet,
+ "has", WeakMapHas,
+ "delete", WeakMapDelete
+]);
+
+// -------------------------------------------------------------------
+// Harmony WeakSet
+
+function WeakSetConstructor(iterable) {
+ if (IS_UNDEFINED(new.target)) {
+ throw MakeTypeError(kConstructorNotFunction, "WeakSet");
+ }
+
+ %WeakCollectionInitialize(this);
+
+ if (!IS_NULL_OR_UNDEFINED(iterable)) {
+ var adder = this.add;
+ if (!IS_CALLABLE(adder)) {
+ throw MakeTypeError(kPropertyNotFunction, adder, 'add', this);
+ }
+ for (var value of iterable) {
+ %_Call(adder, this, value);
+ }
+ }
+}
+
+
+function WeakSetAdd(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakSet.prototype.add', this);
+ }
+ if (!IS_RECEIVER(value)) throw MakeTypeError(kInvalidWeakSetValue);
+ return %WeakCollectionSet(this, value, true, GetHash(value));
+}
+
+
+function WeakSetHas(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakSet.prototype.has', this);
+ }
+ if (!IS_RECEIVER(value)) return false;
+ var hash = GetExistingHash(value);
+ if (IS_UNDEFINED(hash)) return false;
+ return %WeakCollectionHas(this, value, hash);
+}
+
+
+function WeakSetDelete(value) {
+ if (!IS_WEAKSET(this)) {
+ throw MakeTypeError(kIncompatibleMethodReceiver,
+ 'WeakSet.prototype.delete', this);
+ }
+ if (!IS_RECEIVER(value)) return false;
+ var hash = GetExistingHash(value);
+ if (IS_UNDEFINED(hash)) return false;
+ return %WeakCollectionDelete(this, value, hash);
+}
+
+
+// -------------------------------------------------------------------
+
+%SetCode(GlobalWeakSet, WeakSetConstructor);
+%FunctionSetLength(GlobalWeakSet, 0);
+%FunctionSetPrototype(GlobalWeakSet, new GlobalObject());
+%AddNamedProperty(GlobalWeakSet.prototype, "constructor", GlobalWeakSet,
+ DONT_ENUM);
+%AddNamedProperty(GlobalWeakSet.prototype, toStringTagSymbol, "WeakSet",
+ DONT_ENUM | READ_ONLY);
+
+// Set up the non-enumerable functions on the WeakSet prototype object.
+utils.InstallFunctions(GlobalWeakSet.prototype, DONT_ENUM, [
+ "add", WeakSetAdd,
+ "has", WeakSetHas,
+ "delete", WeakSetDelete
+]);
+
+})