diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/builtins | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/builtins')
35 files changed, 5114 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/builtins/ArrayConstructor.js b/Source/JavaScriptCore/builtins/ArrayConstructor.js new file mode 100644 index 000000000..73add1a70 --- /dev/null +++ b/Source/JavaScriptCore/builtins/ArrayConstructor.js @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function of(/* items... */) +{ + "use strict"; + + var length = arguments.length; + var array = @isConstructor(this) ? new this(length) : @newArrayWithSize(length); + for (var k = 0; k < length; ++k) + @putByValDirect(array, k, arguments[k]); + array.length = length; + return array; +} + +function from(items /*, mapFn, thisArg */) +{ + "use strict"; + + var thisObj = this; + + var mapFn = @argument(1); + + var thisArg; + + if (mapFn !== @undefined) { + if (typeof mapFn !== "function") + @throwTypeError("Array.from requires that the second argument, when provided, be a function"); + + thisArg = @argument(2); + } + + if (items == null) + @throwTypeError("Array.from requires an array-like object - not null or undefined"); + + var iteratorMethod = items.@iteratorSymbol; + if (iteratorMethod != null) { + if (typeof iteratorMethod !== "function") + @throwTypeError("Array.from requires that the property of the first argument, items[Symbol.iterator], when exists, be a function"); + + var result = @isConstructor(thisObj) ? new thisObj() : []; + + var k = 0; + var iterator = iteratorMethod.@call(items); + + // Since for-of loop once more looks up the @@iterator property of a given iterable, + // it could be observable if the user defines a getter for @@iterator. + // To avoid this situation, we define a wrapper object that @@iterator just returns a given iterator. + var wrapper = {} + wrapper.@iteratorSymbol = function() { return iterator; }; + + for (var value of wrapper) { + if (mapFn) + @putByValDirect(result, k, thisArg === @undefined ? mapFn(value, k) : mapFn.@call(thisArg, value, k)); + else + @putByValDirect(result, k, value); + k += 1; + } + + result.length = k; + return result; + } + + var arrayLike = @Object(items); + var arrayLikeLength = @toLength(arrayLike.length); + + var result = @isConstructor(thisObj) ? new thisObj(arrayLikeLength) : @newArrayWithSize(arrayLikeLength); + + var k = 0; + while (k < arrayLikeLength) { + var value = arrayLike[k]; + if (mapFn) + @putByValDirect(result, k, thisArg === @undefined ? mapFn(value, k) : mapFn.@call(thisArg, value, k)); + else + @putByValDirect(result, k, value); + k += 1; + } + + result.length = arrayLikeLength; + return result; +} + +function isArray(array) +{ + "use strict"; + + if (@isJSArray(array) || @isDerivedArray(array)) + return true; + if (!@isProxyObject(array)) + return false; + return @isArraySlow(array); +} diff --git a/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js b/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js new file mode 100644 index 000000000..92640f7c8 --- /dev/null +++ b/Source/JavaScriptCore/builtins/ArrayIteratorPrototype.js @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function next() +{ + "use strict"; + + if (this == null) + @throwTypeError("%ArrayIteratorPrototype%.next requires that |this| not be null or undefined"); + + let next = this.@arrayIteratorNext; + if (next === @undefined) + @throwTypeError("%ArrayIteratorPrototype%.next requires that |this| be an Array Iterator instance"); + + return next.@call(this); +} + +@globalPrivate +function arrayIteratorValueNext() +{ + "use strict"; + var done = true; + var value; + + var array = this.@iteratedObject; + if (!this.@arrayIteratorIsDone) { + var index = this.@arrayIteratorNextIndex; + var length = array.length >>> 0; + if (index >= length) { + this.@arrayIteratorIsDone = true; + } else { + this.@arrayIteratorNextIndex = index + 1; + done = false; + value = array[index]; + } + } + + return { done, value }; +} + +@globalPrivate +function arrayIteratorKeyNext() +{ + "use strict"; + var done = true; + var value; + + var array = this.@iteratedObject; + if (!this.@arrayIteratorIsDone) { + var index = this.@arrayIteratorNextIndex; + var length = array.length >>> 0; + if (index >= length) { + this.@arrayIteratorIsDone = true; + } else { + this.@arrayIteratorNextIndex = index + 1; + done = false; + value = index; + } + } + + return { done, value }; +} + +@globalPrivate +function arrayIteratorKeyValueNext() +{ + "use strict"; + var done = true; + var value; + + var array = this.@iteratedObject; + if (!this.@arrayIteratorIsDone) { + var index = this.@arrayIteratorNextIndex; + var length = array.length >>> 0; + if (index >= length) { + this.@arrayIteratorIsDone = true; + } else { + this.@arrayIteratorNextIndex = index + 1; + done = false; + value = [ index, array[index] ]; + } + } + + return { done, value }; +} diff --git a/Source/JavaScriptCore/builtins/ArrayPrototype.js b/Source/JavaScriptCore/builtins/ArrayPrototype.js new file mode 100644 index 000000000..b9f835562 --- /dev/null +++ b/Source/JavaScriptCore/builtins/ArrayPrototype.js @@ -0,0 +1,782 @@ +/* + * Copyright (C) 2014-2017 Apple Inc. All rights reserved. + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +@constructor +@globalPrivate +function createArrayIterator(iteratedObject, kind, iterationFunction) +{ + this.@iteratedObject = iteratedObject; + this.@arrayIteratorKind = kind; + this.@arrayIteratorNextIndex = 0; + this.@arrayIteratorNext = iterationFunction; + this.@arrayIteratorIsDone = false; +} + +function values() +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.values requires that |this| not be null or undefined"); + + return new @createArrayIterator(@Object(this), "value", @arrayIteratorValueNext); +} + +function keys() +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.keys requires that |this| not be null or undefined"); + + return new @createArrayIterator(@Object(this), "key", @arrayIteratorKeyNext); +} + +function entries() +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.entries requires that |this| not be null or undefined"); + + return new @createArrayIterator(@Object(this), "key+value", @arrayIteratorKeyValueNext); +} + +function reduce(callback /*, initialValue */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.reduce requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.reduce callback must be a function"); + + var argumentCount = @argumentCount(); + if (length === 0 && argumentCount < 2) + @throwTypeError("reduce of empty array with no initial value"); + + var accumulator, k = 0; + if (argumentCount > 1) + accumulator = @argument(1); + else { + while (k < length && !(k in array)) + k += 1; + if (k >= length) + @throwTypeError("reduce of empty array with no initial value"); + accumulator = array[k++]; + } + + while (k < length) { + if (k in array) + accumulator = callback.@call(@undefined, accumulator, array[k], k, array); + k += 1; + } + return accumulator; +} + +function reduceRight(callback /*, initialValue */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.reduceRight requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.reduceRight callback must be a function"); + + var argumentCount = @argumentCount(); + if (length === 0 && argumentCount < 2) + @throwTypeError("reduceRight of empty array with no initial value"); + + var accumulator, k = length - 1; + if (argumentCount > 1) + accumulator = @argument(1); + else { + while (k >= 0 && !(k in array)) + k -= 1; + if (k < 0) + @throwTypeError("reduceRight of empty array with no initial value"); + accumulator = array[k--]; + } + + while (k >= 0) { + if (k in array) + accumulator = callback.@call(@undefined, accumulator, array[k], k, array); + k -= 1; + } + return accumulator; +} + +function every(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.every requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.every callback must be a function"); + + var thisArg = @argument(1); + + for (var i = 0; i < length; i++) { + if (!(i in array)) + continue; + if (!callback.@call(thisArg, array[i], i, array)) + return false; + } + + return true; +} + +function forEach(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.forEach requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.forEach callback must be a function"); + + var thisArg = @argument(1); + + for (var i = 0; i < length; i++) { + if (i in array) + callback.@call(thisArg, array[i], i, array); + } +} + +function filter(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.filter requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.filter callback must be a function"); + + var thisArg = @argument(1); + + // Do 9.4.2.3 ArraySpeciesCreate + var result; + var constructor; + if (@isArray(array)) { + constructor = array.constructor; + // We have this check so that if some array from a different global object + // calls this map they don't get an array with the Array.prototype of the + // other global object. + if (@isArrayConstructor(constructor) && @Array !== constructor) + constructor = @undefined; + if (@isObject(constructor)) { + constructor = constructor.@speciesSymbol; + if (constructor === null) + constructor = @undefined; + } + } + if (constructor === @Array || constructor === @undefined) + result = @newArrayWithSize(0); + else + result = new constructor(0); + + var nextIndex = 0; + for (var i = 0; i < length; i++) { + if (!(i in array)) + continue; + var current = array[i] + if (callback.@call(thisArg, current, i, array)) { + @putByValDirect(result, nextIndex, current); + ++nextIndex; + } + } + return result; +} + +function map(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.map requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.map callback must be a function"); + + var thisArg = @argument(1); + + // Do 9.4.2.3 ArraySpeciesCreate + var result; + var constructor; + if (@isArray(array)) { + constructor = array.constructor; + // We have this check so that if some array from a different global object + // calls this map they don't get an array with the Array.prototype of the + // other global object. + if (@isArrayConstructor(constructor) && @Array !== constructor) + constructor = @undefined; + if (@isObject(constructor)) { + constructor = constructor.@speciesSymbol; + if (constructor === null) + constructor = @undefined; + } + } + if (constructor === @Array || constructor === @undefined) + result = @newArrayWithSize(length); + else + result = new constructor(length); + + var nextIndex = 0; + for (var i = 0; i < length; i++) { + if (!(i in array)) + continue; + var mappedValue = callback.@call(thisArg, array[i], i, array); + @putByValDirect(result, i, mappedValue); + } + return result; +} + +function some(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.some requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.some callback must be a function"); + + var thisArg = @argument(1); + for (var i = 0; i < length; i++) { + if (!(i in array)) + continue; + if (callback.@call(thisArg, array[i], i, array)) + return true; + } + return false; +} + +function fill(value /* [, start [, end]] */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.fill requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + var relativeStart = @toInteger(@argument(1)); + var k = 0; + if (relativeStart < 0) { + k = length + relativeStart; + if (k < 0) + k = 0; + } else { + k = relativeStart; + if (k > length) + k = length; + } + var relativeEnd = length; + var end = @argument(2); + if (end !== @undefined) + relativeEnd = @toInteger(end); + var final = 0; + if (relativeEnd < 0) { + final = length + relativeEnd; + if (final < 0) + final = 0; + } else { + final = relativeEnd; + if (final > length) + final = length; + } + for (; k < final; k++) + array[k] = value; + return array; +} + +function find(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.find requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.find callback must be a function"); + + var thisArg = @argument(1); + for (var i = 0; i < length; i++) { + var kValue = array[i]; + if (callback.@call(thisArg, kValue, i, array)) + return kValue; + } + return @undefined; +} + +function findIndex(callback /*, thisArg */) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.findIndex requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (typeof callback !== "function") + @throwTypeError("Array.prototype.findIndex callback must be a function"); + + var thisArg = @argument(1); + for (var i = 0; i < length; i++) { + if (callback.@call(thisArg, array[i], i, array)) + return i; + } + return -1; +} + +function includes(searchElement /*, fromIndex*/) +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.includes requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + if (length === 0) + return false; + + var fromIndex = 0; + var from = @argument(1); + if (from !== @undefined) + fromIndex = @toInteger(from); + + var index; + if (fromIndex >= 0) + index = fromIndex; + else + index = length + fromIndex; + + if (index < 0) + index = 0; + + var currentElement; + for (; index < length; ++index) { + currentElement = array[index]; + // Use SameValueZero comparison, rather than just StrictEquals. + if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) + return true; + } + return false; +} + +function sort(comparator) +{ + "use strict"; + + function min(a, b) + { + return a < b ? a : b; + } + + function stringComparator(a, b) + { + var aString = a.string; + var bString = b.string; + + var aLength = aString.length; + var bLength = bString.length; + var length = min(aLength, bLength); + + for (var i = 0; i < length; ++i) { + var aCharCode = aString.@charCodeAt(i); + var bCharCode = bString.@charCodeAt(i); + + if (aCharCode == bCharCode) + continue; + + return aCharCode - bCharCode; + } + + return aLength - bLength; + } + + // Move undefineds and holes to the end of a sparse array. Result is [values..., undefineds..., holes...]. + function compactSparse(array, dst, src, length) + { + var values = [ ]; + var seen = { }; + var valueCount = 0; + var undefinedCount = 0; + + // Clean up after the in-progress non-sparse compaction that failed. + for (var i = dst; i < src; ++i) + delete array[i]; + + for (var object = array; object; object = @Object.@getPrototypeOf(object)) { + var propertyNames = @Object.@getOwnPropertyNames(object); + for (var i = 0; i < propertyNames.length; ++i) { + var index = propertyNames[i]; + if (index < length) { // Exclude non-numeric properties and properties past length. + if (seen[index]) // Exclude duplicates. + continue; + seen[index] = 1; + + var value = array[index]; + delete array[index]; + + if (value === @undefined) { + ++undefinedCount; + continue; + } + + array[valueCount++] = value; + } + } + } + + for (var i = valueCount; i < valueCount + undefinedCount; ++i) + array[i] = @undefined; + + return valueCount; + } + + function compactSlow(array, length) + { + var holeCount = 0; + + for (var dst = 0, src = 0; src < length; ++src) { + if (!(src in array)) { + ++holeCount; + if (holeCount < 256) + continue; + return compactSparse(array, dst, src, length); + } + + var value = array[src]; + if (value === @undefined) + continue; + + array[dst++] = value; + } + + var valueCount = dst; + var undefinedCount = length - valueCount - holeCount; + + for (var i = valueCount; i < valueCount + undefinedCount; ++i) + array[i] = @undefined; + + for (var i = valueCount + undefinedCount; i < length; ++i) + delete array[i]; + + return valueCount; + } + + // Move undefineds and holes to the end of an array. Result is [values..., undefineds..., holes...]. + function compact(array, length) + { + for (var i = 0; i < array.length; ++i) { + if (array[i] === @undefined) + return compactSlow(array, length); + } + + return length; + } + + function merge(dst, src, srcIndex, srcEnd, width, comparator) + { + var left = srcIndex; + var leftEnd = min(left + width, srcEnd); + var right = leftEnd; + var rightEnd = min(right + width, srcEnd); + + for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) { + if (right < rightEnd) { + if (left >= leftEnd || comparator(src[right], src[left]) < 0) { + dst[dstIndex] = src[right++]; + continue; + } + } + + dst[dstIndex] = src[left++]; + } + } + + function mergeSort(array, valueCount, comparator) + { + var buffer = [ ]; + buffer.length = valueCount; + + var dst = buffer; + var src = array; + for (var width = 1; width < valueCount; width *= 2) { + for (var srcIndex = 0; srcIndex < valueCount; srcIndex += 2 * width) + merge(dst, src, srcIndex, valueCount, width, comparator); + + var tmp = src; + src = dst; + dst = tmp; + } + + if (src != array) { + for(var i = 0; i < valueCount; i++) + array[i] = src[i]; + } + } + + function bucketSort(array, dst, bucket, depth) + { + if (bucket.length < 32 || depth > 32) { + mergeSort(bucket, bucket.length, stringComparator); + for (var i = 0; i < bucket.length; ++i) + array[dst++] = bucket[i].value; + return dst; + } + + var buckets = [ ]; + for (var i = 0; i < bucket.length; ++i) { + var entry = bucket[i]; + var string = entry.string; + if (string.length == depth) { + array[dst++] = entry.value; + continue; + } + + var c = string.@charCodeAt(depth); + if (!buckets[c]) + buckets[c] = [ ]; + buckets[c][buckets[c].length] = entry; + } + + for (var i = 0; i < buckets.length; ++i) { + if (!buckets[i]) + continue; + dst = bucketSort(array, dst, buckets[i], depth + 1); + } + + return dst; + } + + function comparatorSort(array, length, comparator) + { + var valueCount = compact(array, length); + mergeSort(array, valueCount, comparator); + } + + function stringSort(array, length) + { + var valueCount = compact(array, length); + + var strings = @newArrayWithSize(valueCount); + for (var i = 0; i < valueCount; ++i) + strings[i] = { string: @toString(array[i]), value: array[i] }; + + bucketSort(array, 0, strings, 0); + } + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.sort requires that |this| not be null or undefined"); + + var array = @Object(this); + + var length = array.length >>> 0; + + // For compatibility with Firefox and Chrome, do nothing observable + // to the target array if it has 0 or 1 sortable properties. + if (length < 2) + return array; + + if (typeof comparator == "function") + comparatorSort(array, length, comparator); + else if (comparator === @undefined) + stringSort(array, length); + else + @throwTypeError("Array.prototype.sort requires the comparsion function be a function or undefined"); + + return array; +} + +function concatSlowPath() +{ + "use strict"; + + if (this === null || this === @undefined) + @throwTypeError("Array.prototype.concat requires that |this| not be null or undefined"); + + var currentElement = @Object(this); + + var constructor; + if (@isArray(currentElement)) { + constructor = currentElement.constructor; + // We have this check so that if some array from a different global object + // calls this map they don't get an array with the Array.prototype of the + // other global object. + if (@isArrayConstructor(constructor) && @Array !== constructor) + constructor = @undefined; + else if (@isObject(constructor)) { + constructor = constructor.@speciesSymbol; + if (constructor === null) + constructor = @Array; + } + } + + var argCount = arguments.length; + var result; + if (constructor === @Array || constructor === @undefined) + result = @newArrayWithSize(0); + else + result = new constructor(0); + var resultIsArray = @isJSArray(result); + + var resultIndex = 0; + var argIndex = 0; + + do { + let spreadable = @isObject(currentElement) && currentElement.@isConcatSpreadableSymbol; + if ((spreadable === @undefined && @isArray(currentElement)) || spreadable) { + let length = @toLength(currentElement.length); + if (length + resultIndex > @MAX_ARRAY_INDEX) + @throwRangeError("Length exceeded the maximum array length"); + if (resultIsArray && @isJSArray(currentElement)) { + @appendMemcpy(result, currentElement, resultIndex); + resultIndex += length; + } else { + for (var i = 0; i < length; i++) { + if (i in currentElement) + @putByValDirect(result, resultIndex, currentElement[i]); + resultIndex++; + } + } + } else { + if (resultIndex >= @MAX_ARRAY_INDEX) + @throwRangeError("Length exceeded the maximum array length"); + @putByValDirect(result, resultIndex++, currentElement); + } + currentElement = arguments[argIndex]; + } while (argIndex++ < argCount); + + result.length = resultIndex; + return result; +} + +function concat(first) +{ + "use strict"; + + if (@argumentCount() === 1 + && @isJSArray(this) + && this.@isConcatSpreadableSymbol === @undefined + && (!@isObject(first) || first.@isConcatSpreadableSymbol === @undefined)) { + + let result = @concatMemcpy(this, first); + if (result !== null) + return result; + } + + return @tailCallForwardArguments(@concatSlowPath, this); +} + +function copyWithin(target, start /*, end */) +{ + "use strict"; + + function maxWithPositives(a, b) + { + return (a < b) ? b : a; + } + + function minWithMaybeNegativeZeroAndPositive(maybeNegativeZero, positive) + { + return (maybeNegativeZero < positive) ? maybeNegativeZero : positive; + } + + if (this === null || this === @undefined) + @throwTypeError("Array.copyWithin requires that |this| not be null or undefined"); + + var array = @Object(this); + var length = @toLength(array.length); + + var relativeTarget = @toInteger(target); + var to = (relativeTarget < 0) ? maxWithPositives(length + relativeTarget, 0) : minWithMaybeNegativeZeroAndPositive(relativeTarget, length); + + var relativeStart = @toInteger(start); + var from = (relativeStart < 0) ? maxWithPositives(length + relativeStart, 0) : minWithMaybeNegativeZeroAndPositive(relativeStart, length); + + var relativeEnd; + var end = @argument(2); + if (end === @undefined) + relativeEnd = length; + else + relativeEnd = @toInteger(end); + + var finalValue = (relativeEnd < 0) ? maxWithPositives(length + relativeEnd, 0) : minWithMaybeNegativeZeroAndPositive(relativeEnd, length); + + var count = minWithMaybeNegativeZeroAndPositive(finalValue - from, length - to); + + var direction = 1; + if (from < to && to < from + count) { + direction = -1; + from = from + count - 1; + to = to + count - 1; + } + + for (var i = 0; i < count; ++i, from += direction, to += direction) { + if (from in array) + array[to] = array[from]; + else + delete array[to]; + } + + return array; +} diff --git a/Source/JavaScriptCore/builtins/AsyncFunctionPrototype.js b/Source/JavaScriptCore/builtins/AsyncFunctionPrototype.js new file mode 100644 index 000000000..88cfb01d1 --- /dev/null +++ b/Source/JavaScriptCore/builtins/AsyncFunctionPrototype.js @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Caitlin Potter <caitp@igalia.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +@globalPrivate +function asyncFunctionResume(generator, promiseCapability, sentValue, resumeMode) +{ + "use strict"; + let state = generator.@generatorState; + let value = @undefined; + + if (state === @GeneratorStateCompleted || (resumeMode !== @GeneratorResumeModeNormal && resumeMode !== @GeneratorResumeModeThrow)) + @throwTypeError("Async function illegally resumed"); + + try { + generator.@generatorState = @GeneratorStateExecuting; + value = generator.@generatorNext.@call(generator.@generatorThis, generator, state, sentValue, resumeMode, generator.@generatorFrame); + if (generator.@generatorState === @GeneratorStateExecuting) { + generator.@generatorState = @GeneratorStateCompleted; + promiseCapability.@resolve(value); + return promiseCapability.@promise; + } + } catch (error) { + generator.@generatorState = @GeneratorStateCompleted; + promiseCapability.@reject(error); + return promiseCapability.@promise; + } + + let wrappedValue = @newPromiseCapability(@Promise); + wrappedValue.@resolve.@call(@undefined, value); + + wrappedValue.@promise.@then( + function(value) { @asyncFunctionResume(generator, promiseCapability, value, @GeneratorResumeModeNormal); }, + function(error) { @asyncFunctionResume(generator, promiseCapability, error, @GeneratorResumeModeThrow); }); + + return promiseCapability.@promise; +} diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp new file mode 100644 index 000000000..2b79e7e6e --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#include "config.h" +#include "BuiltinExecutableCreator.h" + +#include "BuiltinExecutables.h" + +namespace JSC { + +UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& source, const Identifier& ident, ConstructorKind kind, ConstructAbility ability) +{ + return BuiltinExecutables::createExecutable(vm, source, ident, kind, ability); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h new file mode 100644 index 000000000..19c0884b7 --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinExecutableCreator.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#include "ConstructAbility.h" +#include "ParserModes.h" +#include "SourceCode.h" + +namespace JSC { + +JS_EXPORT_PRIVATE UnlinkedFunctionExecutable* createBuiltinExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility); + +} // namespace JSC diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp b/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp new file mode 100644 index 000000000..a5be5c995 --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinExecutables.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + + +#include "config.h" +#include "BuiltinExecutables.h" + +#include "BuiltinNames.h" +#include "JSCInlines.h" +#include "Parser.h" +#include <wtf/NeverDestroyed.h> + +namespace JSC { + +BuiltinExecutables::BuiltinExecutables(VM& vm) + : m_vm(vm) +#define INITIALIZE_BUILTIN_SOURCE_MEMBERS(name, functionName, length) , m_##name##Source(makeSource(StringImpl::createFromLiteral(s_##name, length), { })) + JSC_FOREACH_BUILTIN_CODE(INITIALIZE_BUILTIN_SOURCE_MEMBERS) +#undef EXPOSE_BUILTIN_STRINGS +{ +} + +UnlinkedFunctionExecutable* BuiltinExecutables::createDefaultConstructor(ConstructorKind constructorKind, const Identifier& name) +{ + static NeverDestroyed<const String> baseConstructorCode(ASCIILiteral("(function () { })")); + static NeverDestroyed<const String> derivedConstructorCode(ASCIILiteral("(function (...args) { super(...args); })")); + + switch (constructorKind) { + case ConstructorKind::None: + break; + case ConstructorKind::Base: + return createExecutable(m_vm, makeSource(baseConstructorCode, { }), name, constructorKind, ConstructAbility::CanConstruct); + case ConstructorKind::Extends: + return createExecutable(m_vm, makeSource(derivedConstructorCode, { }), name, constructorKind, ConstructAbility::CanConstruct); + } + ASSERT_NOT_REACHED(); + return nullptr; +} + +UnlinkedFunctionExecutable* BuiltinExecutables::createBuiltinExecutable(const SourceCode& code, const Identifier& name, ConstructAbility constructAbility) +{ + return createExecutable(m_vm, code, name, ConstructorKind::None, constructAbility); +} + +UnlinkedFunctionExecutable* createBuiltinExecutable(VM& vm, const SourceCode& code, const Identifier& name, ConstructAbility constructAbility) +{ + return BuiltinExecutables::createExecutable(vm, code, name, ConstructorKind::None, constructAbility); +} + +UnlinkedFunctionExecutable* BuiltinExecutables::createExecutable(VM& vm, const SourceCode& source, const Identifier& name, ConstructorKind constructorKind, ConstructAbility constructAbility) +{ + JSTextPosition positionBeforeLastNewline; + ParserError error; + bool isParsingDefaultConstructor = constructorKind != ConstructorKind::None; + JSParserBuiltinMode builtinMode = isParsingDefaultConstructor ? JSParserBuiltinMode::NotBuiltin : JSParserBuiltinMode::Builtin; + UnlinkedFunctionKind kind = isParsingDefaultConstructor ? UnlinkedNormalFunction : UnlinkedBuiltinFunction; + SourceCode parentSourceOverride = isParsingDefaultConstructor ? source : SourceCode(); + std::unique_ptr<ProgramNode> program = parse<ProgramNode>( + &vm, source, Identifier(), builtinMode, + JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded, error, + &positionBeforeLastNewline, constructorKind); + + if (!program) { + dataLog("Fatal error compiling builtin function '", name.string(), "': ", error.message()); + CRASH(); + } + + StatementNode* exprStatement = program->singleStatement(); + RELEASE_ASSERT(exprStatement); + RELEASE_ASSERT(exprStatement->isExprStatement()); + ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); + RELEASE_ASSERT(funcExpr); + RELEASE_ASSERT(funcExpr->isFuncExprNode()); + FunctionMetadataNode* metadata = static_cast<FuncExprNode*>(funcExpr)->metadata(); + RELEASE_ASSERT(!program->hasCapturedVariables()); + + metadata->setEndPosition(positionBeforeLastNewline); + RELEASE_ASSERT(metadata); + RELEASE_ASSERT(metadata->ident().isNull()); + + // This function assumes an input string that would result in a single anonymous function expression. + metadata->setEndPosition(positionBeforeLastNewline); + RELEASE_ASSERT(metadata); + metadata->overrideName(name); + VariableEnvironment dummyTDZVariables; + UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, kind, constructAbility, JSParserScriptMode::Classic, dummyTDZVariables, DerivedContextType::None, WTFMove(parentSourceOverride)); + return functionExecutable; +} + +void BuiltinExecutables::finalize(Handle<Unknown>, void* context) +{ + static_cast<Weak<UnlinkedFunctionExecutable>*>(context)->clear(); +} + +#define DEFINE_BUILTIN_EXECUTABLES(name, functionName, length) \ +UnlinkedFunctionExecutable* BuiltinExecutables::name##Executable() \ +{\ + if (!m_##name##Executable)\ + m_##name##Executable = Weak<UnlinkedFunctionExecutable>(createBuiltinExecutable(m_##name##Source, m_vm.propertyNames->builtinNames().functionName##PublicName(), s_##name##ConstructAbility), this, &m_##name##Executable);\ + return m_##name##Executable.get();\ +} +JSC_FOREACH_BUILTIN_CODE(DEFINE_BUILTIN_EXECUTABLES) +#undef EXPOSE_BUILTIN_SOURCES + +} diff --git a/Source/JavaScriptCore/builtins/BuiltinExecutables.h b/Source/JavaScriptCore/builtins/BuiltinExecutables.h new file mode 100644 index 000000000..ee0eaad02 --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinExecutables.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#include "JSCBuiltins.h" +#include "ParserModes.h" +#include "SourceCode.h" +#include "Weak.h" +#include "WeakHandleOwner.h" + +namespace JSC { + +class UnlinkedFunctionExecutable; +class Identifier; +class VM; + +class BuiltinExecutables final: private WeakHandleOwner { + WTF_MAKE_FAST_ALLOCATED; +public: + explicit BuiltinExecutables(VM&); + +#define EXPOSE_BUILTIN_EXECUTABLES(name, functionName, length) \ +UnlinkedFunctionExecutable* name##Executable(); \ +const SourceCode& name##Source() { return m_##name##Source; } + + JSC_FOREACH_BUILTIN_CODE(EXPOSE_BUILTIN_EXECUTABLES) +#undef EXPOSE_BUILTIN_SOURCES + + UnlinkedFunctionExecutable* createDefaultConstructor(ConstructorKind, const Identifier& name); + + static UnlinkedFunctionExecutable* createExecutable(VM&, const SourceCode&, const Identifier&, ConstructorKind, ConstructAbility); +private: + void finalize(Handle<Unknown>, void* context) override; + + VM& m_vm; + + UnlinkedFunctionExecutable* createBuiltinExecutable(const SourceCode&, const Identifier&, ConstructAbility); + +#define DECLARE_BUILTIN_SOURCE_MEMBERS(name, functionName, length)\ + SourceCode m_##name##Source; \ + Weak<UnlinkedFunctionExecutable> m_##name##Executable; + JSC_FOREACH_BUILTIN_CODE(DECLARE_BUILTIN_SOURCE_MEMBERS) +#undef DECLARE_BUILTIN_SOURCE_MEMBERS +}; + +} diff --git a/Source/JavaScriptCore/builtins/BuiltinNames.h b/Source/JavaScriptCore/builtins/BuiltinNames.h new file mode 100644 index 000000000..03aa44c68 --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinNames.h @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#include "BuiltinUtils.h" +#include "BytecodeIntrinsicRegistry.h" +#include "CommonIdentifiers.h" +#include "JSCBuiltins.h" + +namespace JSC { + +#define JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \ + JSC_COMMON_BYTECODE_INTRINSIC_FUNCTIONS_EACH_NAME(macro) \ + JSC_COMMON_BYTECODE_INTRINSIC_CONSTANTS_EACH_NAME(macro) \ + macro(arrayIteratorNextIndex) \ + macro(arrayIterationKind) \ + macro(arrayIteratorNext) \ + macro(arrayIteratorIsDone) \ + macro(arrayIteratorKind) \ + macro(charCodeAt) \ + macro(isView) \ + macro(iteratedObject) \ + macro(iteratedString) \ + macro(stringIteratorNextIndex) \ + macro(promise) \ + macro(fulfillmentHandler) \ + macro(rejectionHandler) \ + macro(index) \ + macro(deferred) \ + macro(countdownHolder) \ + macro(Object) \ + macro(ownEnumerablePropertyKeys) \ + macro(Number) \ + macro(Array) \ + macro(ArrayBuffer) \ + macro(String) \ + macro(RegExp) \ + macro(Map) \ + macro(Promise) \ + macro(Reflect) \ + macro(InternalPromise) \ + macro(abs) \ + macro(floor) \ + macro(trunc) \ + macro(create) \ + macro(defineProperty) \ + macro(getPrototypeOf) \ + macro(getOwnPropertyDescriptor) \ + macro(getOwnPropertyNames) \ + macro(ownKeys) \ + macro(Error) \ + macro(RangeError) \ + macro(TypeError) \ + macro(typedArrayLength) \ + macro(typedArraySort) \ + macro(typedArrayGetOriginalConstructor) \ + macro(typedArraySubarrayCreate) \ + macro(BuiltinLog) \ + macro(homeObject) \ + macro(getTemplateObject) \ + macro(templateRegistryKey) \ + macro(enqueueJob) \ + macro(promiseState) \ + macro(promiseReactions) \ + macro(promiseResult) \ + macro(onFulfilled) \ + macro(onRejected) \ + macro(push) \ + macro(repeatCharacter) \ + macro(capabilities) \ + macro(starDefault) \ + macro(InspectorInstrumentation) \ + macro(get) \ + macro(set) \ + macro(shift) \ + macro(allocateTypedArray) \ + macro(Int8Array) \ + macro(Int16Array) \ + macro(Int32Array) \ + macro(Uint8Array) \ + macro(Uint8ClampedArray) \ + macro(Uint16Array) \ + macro(Uint32Array) \ + macro(Float32Array) \ + macro(Float64Array) \ + macro(exec) \ + macro(generator) \ + macro(generatorNext) \ + macro(generatorState) \ + macro(generatorFrame) \ + macro(generatorValue) \ + macro(generatorThis) \ + macro(generatorResumeMode) \ + macro(Collator) \ + macro(DateTimeFormat) \ + macro(NumberFormat) \ + macro(intlSubstituteValue) \ + macro(thisTimeValue) \ + macro(thisNumberValue) \ + macro(newTargetLocal) \ + macro(derivedConstructor) \ + macro(isTypedArrayView) \ + macro(isBoundFunction) \ + macro(hasInstanceBoundFunction) \ + macro(instanceOf) \ + macro(isArraySlow) \ + macro(isArrayConstructor) \ + macro(isConstructor) \ + macro(isDerivedConstructor) \ + macro(concatMemcpy) \ + macro(appendMemcpy) \ + macro(predictFinalLengthFromArgumunts) \ + macro(print) \ + macro(regExpCreate) \ + macro(SetIterator) \ + macro(setIteratorNext) \ + macro(replaceUsingRegExp) \ + macro(replaceUsingStringSearch) \ + macro(MapIterator) \ + macro(mapIteratorNext) \ + macro(regExpBuiltinExec) \ + macro(regExpMatchFast) \ + macro(regExpProtoFlagsGetter) \ + macro(regExpProtoGlobalGetter) \ + macro(regExpProtoIgnoreCaseGetter) \ + macro(regExpProtoMultilineGetter) \ + macro(regExpProtoSourceGetter) \ + macro(regExpProtoStickyGetter) \ + macro(regExpProtoUnicodeGetter) \ + macro(regExpPrototypeSymbolReplace) \ + macro(regExpReplaceFast) \ + macro(regExpSearchFast) \ + macro(regExpSplitFast) \ + macro(regExpTestFast) \ + macro(stringIncludesInternal) \ + macro(stringSplitFast) \ + macro(stringSubstrInternal) \ + macro(makeBoundFunction) \ + macro(hasOwnLengthProperty) \ + macro(importModule) \ + macro(WebAssembly) \ + macro(Module) \ + macro(Instance) \ + macro(Memory) \ + macro(Table) \ + macro(CompileError) \ + macro(LinkError) \ + macro(RuntimeError) \ + + +#define INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY(name) m_privateToPublicMap.add(m_##name##PrivateName.impl(), &m_##name); +#define INITIALIZE_PUBLIC_TO_PRIVATE_ENTRY(name) m_publicToPrivateMap.add(m_##name.impl(), &m_##name##PrivateName); + +// We commandeer the publicToPrivateMap to allow us to convert private symbol names into the appropriate symbol. +// e.g. @iteratorSymbol points to Symbol.iterator in this map rather than to a an actual private name. +// FIXME: This is a weird hack and we shouldn't need to do this. +#define INITIALIZE_SYMBOL_PUBLIC_TO_PRIVATE_ENTRY(name) m_publicToPrivateMap.add(m_##name##SymbolPrivateIdentifier.impl(), &m_##name##Symbol); + +class BuiltinNames { + WTF_MAKE_NONCOPYABLE(BuiltinNames); WTF_MAKE_FAST_ALLOCATED; + +public: + // We treat the dollarVM name as a special case below for $vm (because CommonIdentifiers does not + // yet support the $ character). + + BuiltinNames(VM* vm, CommonIdentifiers* commonIdentifiers) + : m_emptyIdentifier(commonIdentifiers->emptyIdentifier) + JSC_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_BUILTIN_NAMES) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_BUILTIN_NAMES) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_BUILTIN_SYMBOLS) + , m_dollarVMName(Identifier::fromString(vm, "$vm")) + , m_dollarVMPrivateName(Identifier::fromUid(PrivateName(PrivateName::Description, ASCIILiteral("PrivateSymbol.$vm")))) + { + JSC_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PRIVATE_TO_PUBLIC_ENTRY) + JSC_FOREACH_BUILTIN_FUNCTION_NAME(INITIALIZE_PUBLIC_TO_PRIVATE_ENTRY) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(INITIALIZE_PUBLIC_TO_PRIVATE_ENTRY) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(INITIALIZE_SYMBOL_PUBLIC_TO_PRIVATE_ENTRY) + m_privateToPublicMap.add(m_dollarVMPrivateName.impl(), &m_dollarVMName); + m_publicToPrivateMap.add(m_dollarVMName.impl(), &m_dollarVMPrivateName); + } + + bool isPrivateName(SymbolImpl& uid) const; + bool isPrivateName(UniquedStringImpl& uid) const; + bool isPrivateName(const Identifier&) const; + const Identifier* lookUpPrivateName(const Identifier&) const; + const Identifier& lookUpPublicName(const Identifier&) const; + + void appendExternalName(const Identifier& publicName, const Identifier& privateName); + + JSC_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_IDENTIFIER_ACCESSOR) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_BUILTIN_SYMBOL_ACCESSOR) + const JSC::Identifier& dollarVMPublicName() const { return m_dollarVMName; } + const JSC::Identifier& dollarVMPrivateName() const { return m_dollarVMPrivateName; } + +private: + Identifier m_emptyIdentifier; + JSC_FOREACH_BUILTIN_FUNCTION_NAME(DECLARE_BUILTIN_NAMES) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_PROPERTY_NAME(DECLARE_BUILTIN_NAMES) + JSC_COMMON_PRIVATE_IDENTIFIERS_EACH_WELL_KNOWN_SYMBOL(DECLARE_BUILTIN_SYMBOLS) + const JSC::Identifier m_dollarVMName; + const JSC::Identifier m_dollarVMPrivateName; + typedef HashMap<RefPtr<UniquedStringImpl>, const Identifier*, IdentifierRepHash> BuiltinNamesMap; + BuiltinNamesMap m_publicToPrivateMap; + BuiltinNamesMap m_privateToPublicMap; +}; + +inline bool BuiltinNames::isPrivateName(SymbolImpl& uid) const +{ + return m_privateToPublicMap.contains(&uid); +} + +inline bool BuiltinNames::isPrivateName(UniquedStringImpl& uid) const +{ + if (!uid.isSymbol()) + return false; + return m_privateToPublicMap.contains(&uid); +} + +inline bool BuiltinNames::isPrivateName(const Identifier& ident) const +{ + if (ident.isNull()) + return false; + return isPrivateName(*ident.impl()); +} + +inline const Identifier* BuiltinNames::lookUpPrivateName(const Identifier& ident) const +{ + auto iter = m_publicToPrivateMap.find(ident.impl()); + if (iter != m_publicToPrivateMap.end()) + return iter->value; + return 0; +} + +inline const Identifier& BuiltinNames::lookUpPublicName(const Identifier& ident) const +{ + auto iter = m_privateToPublicMap.find(ident.impl()); + if (iter != m_privateToPublicMap.end()) + return *iter->value; + return m_emptyIdentifier; +} + +inline void BuiltinNames::appendExternalName(const Identifier& publicName, const Identifier& privateName) +{ +#ifndef NDEBUG + for (const auto& key : m_publicToPrivateMap.keys()) + ASSERT(publicName.string() != *key); +#endif + + m_privateToPublicMap.add(privateName.impl(), &publicName); + m_publicToPrivateMap.add(publicName.impl(), &privateName); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/builtins/BuiltinUtils.h b/Source/JavaScriptCore/builtins/BuiltinUtils.h new file mode 100644 index 000000000..26da2919c --- /dev/null +++ b/Source/JavaScriptCore/builtins/BuiltinUtils.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * Copyright (C) 2015 Canon Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +#pragma once + +#include "ConstructAbility.h" + +namespace JSC { + +#define INITIALIZE_BUILTIN_NAMES(name) , m_##name(JSC::Identifier::fromString(vm, #name)), m_##name##PrivateName(JSC::Identifier::fromUid(JSC::PrivateName(JSC::PrivateName::Description, ASCIILiteral("PrivateSymbol." #name)))) +#define DECLARE_BUILTIN_NAMES(name) const JSC::Identifier m_##name; const JSC::Identifier m_##name##PrivateName; +#define DECLARE_BUILTIN_IDENTIFIER_ACCESSOR(name) \ + const JSC::Identifier& name##PublicName() const { return m_##name; } \ + const JSC::Identifier& name##PrivateName() const { return m_##name##PrivateName; } + +#define INITIALIZE_BUILTIN_SYMBOLS(name) , m_##name##Symbol(JSC::Identifier::fromUid(JSC::PrivateName(JSC::PrivateName::Description, ASCIILiteral("Symbol." #name)))), m_##name##SymbolPrivateIdentifier(JSC::Identifier::fromString(vm, #name "Symbol")) +#define DECLARE_BUILTIN_SYMBOLS(name) const JSC::Identifier m_##name##Symbol; const JSC::Identifier m_##name##SymbolPrivateIdentifier; +#define DECLARE_BUILTIN_SYMBOL_ACCESSOR(name) \ + const JSC::Identifier& name##Symbol() const { return m_##name##Symbol; } + +class Identifier; +class SourceCode; +class UnlinkedFunctionExecutable; +class VM; + +JS_EXPORT_PRIVATE UnlinkedFunctionExecutable* createBuiltinExecutable(VM&, const SourceCode&, const Identifier&, ConstructAbility); + +} // namespace JSC diff --git a/Source/JavaScriptCore/builtins/DatePrototype.js b/Source/JavaScriptCore/builtins/DatePrototype.js new file mode 100644 index 000000000..234f18522 --- /dev/null +++ b/Source/JavaScriptCore/builtins/DatePrototype.js @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2015 Andy VanWagoner <thetalecrafter@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// @conditional=ENABLE(INTL) + +function toLocaleString(/* locales, options */) +{ + "use strict"; + + function toDateTimeOptionsAnyAll(opts) + { + // ToDateTimeOptions(options, "any", "all") + // http://www.ecma-international.org/ecma-402/2.0/#sec-InitializeDateTimeFormat + + var options; + if (opts === @undefined) + options = null; + else if (opts === null) + @throwTypeError("null is not an object"); + else + options = @Object(opts); + + // Check original instead of descendant to reduce lookups up the prototype chain. + var needsDefaults = !options || ( + options.weekday === @undefined && + options.year === @undefined && + options.month === @undefined && + options.day === @undefined && + options.hour === @undefined && + options.minute === @undefined && + options.second === @undefined + ); + + // Only create descendant if it will have own properties. + if (needsDefaults) { + options = @Object.@create(options); + options.year = "numeric"; + options.month = "numeric"; + options.day = "numeric"; + options.hour = "numeric"; + options.minute = "numeric"; + options.second = "numeric"; + } + + // 9. Return options. + return options; + } + + // 13.3.1 Date.prototype.toLocaleString ([locales [, options ]]) (ECMA-402 2.0) + // http://www.ecma-international.org/ecma-402/2.0/#sec-Date.prototype.toLocaleString + + var value = @thisTimeValue.@call(this); + if (@isNaN(value)) + return "Invalid Date"; + + var options = toDateTimeOptionsAnyAll(@argument(1)); + var locales = @argument(0); + + var dateFormat = new @DateTimeFormat(locales, options); + return dateFormat.format(value); +} + +function toLocaleDateString(/* locales, options */) +{ + "use strict"; + + function toDateTimeOptionsDateDate(opts) + { + // ToDateTimeOptions(options, "date", "date") + // http://www.ecma-international.org/ecma-402/2.0/#sec-InitializeDateTimeFormat + + var options; + if (opts === @undefined) + options = null; + else if (opts === null) + @throwTypeError("null is not an object"); + else + options = @Object(opts); + + // Check original instead of descendant to reduce lookups up the prototype chain. + var needsDefaults = !options || ( + options.weekday === @undefined && + options.year === @undefined && + options.month === @undefined && + options.day === @undefined + ); + + // Only create descendant if it will have own properties. + if (needsDefaults) { + options = @Object.@create(options); + options.year = "numeric"; + options.month = "numeric"; + options.day = "numeric"; + } + + return options; + } + + // 13.3.2 Date.prototype.toLocaleDateString ([locales [, options ]]) (ECMA-402 2.0) + // http://www.ecma-international.org/ecma-402/2.0/#sec-Date.prototype.toLocaleDateString + + var value = @thisTimeValue.@call(this); + if (@isNaN(value)) + return "Invalid Date"; + + var options = toDateTimeOptionsDateDate(@argument(1)); + var locales = @argument(0); + + var dateFormat = new @DateTimeFormat(locales, options); + return dateFormat.format(value); +} + +function toLocaleTimeString(/* locales, options */) +{ + "use strict"; + + function toDateTimeOptionsTimeTime(opts) + { + // ToDateTimeOptions(options, "time", "time") + // http://www.ecma-international.org/ecma-402/2.0/#sec-InitializeDateTimeFormat + + var options; + if (opts === @undefined) + options = null; + else if (opts === null) + @throwTypeError("null is not an object"); + else + options = @Object(opts); + + // Check original instead of descendant to reduce lookups up the prototype chain. + var needsDefaults = !options || ( + options.hour === @undefined && + options.minute === @undefined && + options.second === @undefined + ); + + // Only create descendant if it will have own properties. + if (needsDefaults) { + options = @Object.@create(options); + options.hour = "numeric"; + options.minute = "numeric"; + options.second = "numeric"; + } + + return options; + } + + // 13.3.3 Date.prototype.toLocaleTimeString ([locales [, options ]]) (ECMA-402 2.0) + // http://www.ecma-international.org/ecma-402/2.0/#sec-Date.prototype.toLocaleTimeString + + var value = @thisTimeValue.@call(this); + if (@isNaN(value)) + return "Invalid Date"; + + var options = toDateTimeOptionsTimeTime(@argument(1)); + var locales = @argument(0); + + var dateFormat = new @DateTimeFormat(locales, options); + return dateFormat.format(value); +} diff --git a/Source/JavaScriptCore/builtins/FunctionPrototype.js b/Source/JavaScriptCore/builtins/FunctionPrototype.js new file mode 100644 index 000000000..f1ee867ef --- /dev/null +++ b/Source/JavaScriptCore/builtins/FunctionPrototype.js @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function call(thisArgument) +{ + "use strict"; + + let argumentValues = []; + // Start from 1 to ignore thisArgument + for (let i = 1; i < arguments.length; i++) + @putByValDirect(argumentValues, i-1, arguments[i]); + + return this.@apply(thisArgument, argumentValues); +} + +function apply(thisValue, argumentValues) +{ + "use strict"; + + return this.@apply(thisValue, argumentValues); +} + +// FIXME: this should have a different name: https://bugs.webkit.org/show_bug.cgi?id=151363 +function symbolHasInstance(value) +{ + "use strict"; + + if (typeof this !== "function") + return false; + + if (@isBoundFunction(this)) + return @hasInstanceBoundFunction(this, value); + + let target = this.prototype; + return @instanceOf(value, target); +} + +function bind(thisValue) +{ + "use strict"; + + let target = this; + if (typeof target !== "function") + @throwTypeError("|this| is not a function inside Function.prototype.bind"); + + let argumentCount = arguments.length; + let boundArgs = null; + let numBoundArgs = 0; + if (argumentCount > 1) { + numBoundArgs = argumentCount - 1; + boundArgs = @newArrayWithSize(numBoundArgs); + for (let i = 0; i < numBoundArgs; i++) + @putByValDirect(boundArgs, i, arguments[i + 1]); + } + + let length = 0; + if (@hasOwnLengthProperty(target)) { + let lengthValue = target.length; + if (typeof lengthValue === "number") { + lengthValue = lengthValue | 0; + // Note that we only care about positive lengthValues, however, this comparision + // against numBoundArgs suffices to prove we're not a negative number. + if (lengthValue > numBoundArgs) + length = lengthValue - numBoundArgs; + } + } + + let name = target.name; + if (typeof name !== "string") + name = ""; + + return @makeBoundFunction(target, arguments[0], boundArgs, length, name); +} diff --git a/Source/JavaScriptCore/builtins/GeneratorPrototype.js b/Source/JavaScriptCore/builtins/GeneratorPrototype.js new file mode 100644 index 000000000..4128a3532 --- /dev/null +++ b/Source/JavaScriptCore/builtins/GeneratorPrototype.js @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015-2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// 25.3.3.3 GeneratorResume ( generator, value ) +// 25.3.3.4 GeneratorResumeAbrupt(generator, abruptCompletion) +@globalPrivate +function generatorResume(generator, sentValue, resumeMode) +{ + "use strict"; + + let state = generator.@generatorState; + let done = false; + let value = @undefined; + + if (typeof state !== 'number') + @throwTypeError("|this| should be a generator"); + + if (state === @GeneratorStateExecuting) + @throwTypeError("Generator is executing"); + + if (state === @GeneratorStateCompleted) { + if (resumeMode === @GeneratorResumeModeThrow) + throw sentValue; + + done = true; + if (resumeMode === @GeneratorResumeModeReturn) + value = sentValue; + } else { + try { + generator.@generatorState = @GeneratorStateExecuting; + value = generator.@generatorNext.@call(generator.@generatorThis, generator, state, sentValue, resumeMode, generator.@generatorFrame); + if (generator.@generatorState === @GeneratorStateExecuting) { + generator.@generatorState = @GeneratorStateCompleted; + done = true; + } + } catch (error) { + generator.@generatorState = @GeneratorStateCompleted; + throw error; + } + } + return { done, value }; +} + +function next(value) +{ + "use strict"; + + return @generatorResume(this, value, @GeneratorResumeModeNormal); +} + +function return(value) +{ + "use strict"; + + return @generatorResume(this, value, @GeneratorResumeModeReturn); +} + +function throw(exception) +{ + "use strict"; + + return @generatorResume(this, exception, @GeneratorResumeModeThrow); +} diff --git a/Source/JavaScriptCore/builtins/GlobalObject.js b/Source/JavaScriptCore/builtins/GlobalObject.js new file mode 100644 index 000000000..804930c84 --- /dev/null +++ b/Source/JavaScriptCore/builtins/GlobalObject.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015-2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +@globalPrivate +function isFinite(value) +{ + "use strict"; + + var numberValue = @toNumber(value); + // Return false if numberValue is |NaN|. + if (numberValue !== numberValue) + return false; + return numberValue !== @Infinity && numberValue !== -@Infinity; +} + +@globalPrivate +function isNaN(value) +{ + "use strict"; + + var numberValue = @toNumber(value); + return numberValue !== numberValue; +} diff --git a/Source/JavaScriptCore/builtins/GlobalOperations.js b/Source/JavaScriptCore/builtins/GlobalOperations.js new file mode 100644 index 000000000..22220cf2e --- /dev/null +++ b/Source/JavaScriptCore/builtins/GlobalOperations.js @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// @internal + +@globalPrivate +function toInteger(target) +{ + "use strict"; + + var numberValue = @Number(target); + + // isNaN(numberValue) + if (numberValue !== numberValue) + return 0; + return @trunc(numberValue); +} + +@globalPrivate +function toLength(target) +{ + "use strict"; + + var length = @toInteger(target); + // originally Math.min(Math.max(length, 0), maxSafeInteger)); + return length > 0 ? (length < @MAX_SAFE_INTEGER ? length : @MAX_SAFE_INTEGER) : 0; +} + +@globalPrivate +function isDictionary(object) +{ + "use strict"; + + return object == null || typeof object === "object"; +} + +// FIXME: this needs to have it's name changed to "get [Symbol.species]". +// see: https://bugs.webkit.org/show_bug.cgi?id=151363 +@globalPrivate +function speciesGetter() +{ + return this; +} + +@globalPrivate +function speciesConstructor(obj, defaultConstructor) +{ + var constructor = obj.constructor; + if (constructor === @undefined) + return defaultConstructor; + if (!@isObject(constructor)) + @throwTypeError("|this|.constructor is not an Object or undefined"); + constructor = constructor.@speciesSymbol; + if (constructor == null) + return defaultConstructor; + if (@isConstructor(constructor)) + return constructor; + @throwTypeError("|this|.constructor[Symbol.species] is not a constructor"); +} diff --git a/Source/JavaScriptCore/builtins/InspectorInstrumentationObject.js b/Source/JavaScriptCore/builtins/InspectorInstrumentationObject.js new file mode 100644 index 000000000..fb7d9eae6 --- /dev/null +++ b/Source/JavaScriptCore/builtins/InspectorInstrumentationObject.js @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function promiseFulfilled(promise, value, reactions) +{ + "use strict"; + + if (!this.isEnabled) + return; +} + +function promiseRejected(promise, reason, reactions) +{ + "use strict"; + + if (!this.isEnabled) + return; +} diff --git a/Source/JavaScriptCore/builtins/InternalPromiseConstructor.js b/Source/JavaScriptCore/builtins/InternalPromiseConstructor.js new file mode 100644 index 000000000..d01f5f7e5 --- /dev/null +++ b/Source/JavaScriptCore/builtins/InternalPromiseConstructor.js @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function internalAll(array) +{ + // This function is intended to be used in the JSC internals. + // The implementation should take care not to perform the user + // observable / trappable operations. + // + // 1. Don't use for-of and iterables. This function only accepts + // the dense array of the promises. + // 2. Don't look up this.constructor / @@species. Always construct + // the plain Promise object. + + "use strict"; + + var promiseCapability = @newPromiseCapability(@InternalPromise); + + var values = []; + var index = 0; + var remainingElementsCount = 0; + + function newResolveElement(index) + { + var alreadyCalled = false; + return function (argument) + { + if (alreadyCalled) + return @undefined; + alreadyCalled = true; + + @putByValDirect(values, index, argument); + + --remainingElementsCount; + if (remainingElementsCount === 0) + return promiseCapability.@resolve.@call(@undefined, values); + + return @undefined; + } + } + + try { + if (array.length === 0) + promiseCapability.@resolve.@call(@undefined, values); + else { + for (var index = 0, length = array.length; index < length; ++index) { + var value = array[index]; + @putByValDirect(values, index, @undefined); + + var nextPromiseCapability = @newPromiseCapability(@InternalPromise); + nextPromiseCapability.@resolve.@call(@undefined, value); + var nextPromise = nextPromiseCapability.@promise; + + var resolveElement = newResolveElement(index); + ++remainingElementsCount; + nextPromise.then(resolveElement, promiseCapability.@reject); + } + } + } catch (error) { + promiseCapability.@reject.@call(@undefined, error); + } + + return promiseCapability.@promise; +} diff --git a/Source/JavaScriptCore/builtins/IteratorHelpers.js b/Source/JavaScriptCore/builtins/IteratorHelpers.js new file mode 100644 index 000000000..f565d4464 --- /dev/null +++ b/Source/JavaScriptCore/builtins/IteratorHelpers.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function performIteration(iterable) +{ + "use strict"; + // This is performing a spread operation on the iterable passed in, + // and returning the result in an array. + // https://tc39.github.io/ecma262/#sec-runtime-semantics-arrayaccumulation + + let result = []; + + let iterator = iterable.@iteratorSymbol(); + let item; + let index = 0; + while (true) { + item = iterator.next(); + if (!@isObject(item)) + @throwTypeError("Iterator result interface is not an object"); + if (item.done) + return result; + @putByValDirect(result, index++, item.value); + } +} diff --git a/Source/JavaScriptCore/builtins/IteratorPrototype.js b/Source/JavaScriptCore/builtins/IteratorPrototype.js new file mode 100644 index 000000000..5c1691a3d --- /dev/null +++ b/Source/JavaScriptCore/builtins/IteratorPrototype.js @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function symbolIteratorGetter() +{ + "use strict"; + + return this; +} diff --git a/Source/JavaScriptCore/builtins/MapPrototype.js b/Source/JavaScriptCore/builtins/MapPrototype.js new file mode 100644 index 000000000..830260269 --- /dev/null +++ b/Source/JavaScriptCore/builtins/MapPrototype.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function forEach(callback /*, thisArg */) +{ + "use strict"; + + if (!@isMap(this)) + @throwTypeError("Map operation called on non-Map object"); + + if (typeof callback !== 'function') + @throwTypeError("Map.prototype.forEach callback must be a function"); + + var thisArg = @argument(1); + var iterator = @MapIterator(this); + + // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results. + var value = [ @undefined, @undefined ]; + for (;;) { + if (@mapIteratorNext.@call(iterator, value)) + break; + callback.@call(thisArg, value[1], value[0], this); + } +} diff --git a/Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js b/Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js new file mode 100644 index 000000000..29556ea3d --- /dev/null +++ b/Source/JavaScriptCore/builtins/ModuleLoaderPrototype.js @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// https://whatwg.github.io/loader/#loader-object +// Module Loader has several hooks that can be customized by the platform. +// For example, the [[Fetch]] hook can be provided by the JavaScriptCore shell +// as fetching the payload from the local file system. +// Currently, there are 4 hooks. +// 1. Loader.resolve +// 2. Loader.fetch +// 3. Loader.instantiate + +@globalPrivate +function setStateToMax(entry, newState) +{ + // https://whatwg.github.io/loader/#set-state-to-max + + "use strict"; + + if (entry.state < newState) + entry.state = newState; +} + +@globalPrivate +function newRegistryEntry(key) +{ + // https://whatwg.github.io/loader/#registry + // + // Each registry entry becomes one of the 5 states. + // 1. Fetch + // Ready to fetch (or now fetching) the resource of this module. + // Typically, we fetch the source code over the network or from the file system. + // a. If the status is Fetch and there is no entry.fetch promise, the entry is ready to fetch. + // b. If the status is Fetch and there is the entry.fetch promise, the entry is just fetching the resource. + // + // 2. Instantiate (AnalyzeModule) + // Ready to instantiate (or now instantiating) the module record from the fetched + // source code. + // Typically, we parse the module code, extract the dependencies and binding information. + // a. If the status is Instantiate and there is no entry.instantiate promise, the entry is ready to instantiate. + // b. If the status is Instantiate and there is the entry.fetch promise, the entry is just instantiating + // the module record. + // + // 3. Satisfy + // Ready to request the dependent modules (or now requesting & resolving). + // Without this state, the current draft causes infinite recursion when there is circular dependency. + // a. If the status is Satisfy and there is no entry.satisfy promise, the entry is ready to resolve the dependencies. + // b. If the status is Satisfy and there is the entry.satisfy promise, the entry is just resolving + // the dependencies. + // + // 4. Link + // Ready to link the module with the other modules. + // Linking means that the module imports and exports the bindings from/to the other modules. + // + // 5. Ready + // The module is linked, so the module is ready to be executed. + // + // Each registry entry has the 4 promises; "fetch", "instantiate" and "satisfy". + // They are assigned when starting the each phase. And they are fulfilled when the each phase is completed. + // + // In the current module draft, linking will be performed after the whole modules are instantiated and the dependencies are resolved. + // And execution is also done after the all modules are linked. + // + // TODO: We need to exploit the way to execute the module while fetching non-related modules. + // One solution; introducing the ready promise chain to execute the modules concurrently while keeping + // the execution order. + + "use strict"; + + return { + key: key, + state: @ModuleFetch, + fetch: @undefined, + instantiate: @undefined, + satisfy: @undefined, + dependencies: [], // To keep the module order, we store the module keys in the array. + dependenciesMap: @undefined, + module: @undefined, // JSModuleRecord + linkError: @undefined, + linkSucceeded: true, + }; +} + +function ensureRegistered(key) +{ + // https://whatwg.github.io/loader/#ensure-registered + + "use strict"; + + var entry = this.registry.@get(key); + if (entry) + return entry; + + entry = @newRegistryEntry(key); + this.registry.@set(key, entry); + + return entry; +} + +function forceFulfillPromise(promise, value) +{ + "use strict"; + + if (promise.@promiseState === @promiseStatePending) + @fulfillPromise(promise, value); +} + +function fulfillFetch(entry, source) +{ + // https://whatwg.github.io/loader/#fulfill-fetch + + "use strict"; + + if (!entry.fetch) + entry.fetch = @newPromiseCapability(@InternalPromise).@promise; + this.forceFulfillPromise(entry.fetch, source); + @setStateToMax(entry, @ModuleInstantiate); +} + +function fulfillInstantiate(entry, optionalInstance, source) +{ + // https://whatwg.github.io/loader/#fulfill-instantiate + + "use strict"; + + if (!entry.instantiate) + entry.instantiate = @newPromiseCapability(@InternalPromise).@promise; + this.commitInstantiated(entry, optionalInstance, source); + + // FIXME: The draft fulfills the promise in the CommitInstantiated operation. + // But it CommitInstantiated is also used in the requestInstantiate and + // we should not "force fulfill" there. + // So we separate "force fulfill" operation from the CommitInstantiated operation. + // https://github.com/whatwg/loader/pull/67 + this.forceFulfillPromise(entry.instantiate, entry); +} + +function commitInstantiated(entry, optionalInstance, source) +{ + // https://whatwg.github.io/loader/#commit-instantiated + + "use strict"; + + var moduleRecord = this.instantiation(optionalInstance, source, entry); + + // FIXME: Described in the draft, + // 4. Fulfill entry.[[Instantiate]] with instance. + // But, instantiate promise should be fulfilled with the entry. + // We remove this statement because instantiate promise will be + // fulfilled without this "force fulfill" operation. + // https://github.com/whatwg/loader/pull/67 + + var dependencies = []; + var dependenciesMap = moduleRecord.dependenciesMap; + moduleRecord.registryEntry = entry; + var requestedModules = this.requestedModules(moduleRecord); + for (var i = 0, length = requestedModules.length; i < length; ++i) { + var depKey = requestedModules[i]; + var pair = { + key: depKey, + value: @undefined + }; + @putByValDirect(dependencies, dependencies.length, pair); + dependenciesMap.@set(depKey, pair); + } + entry.dependencies = dependencies; + entry.dependenciesMap = dependenciesMap; + entry.module = moduleRecord; + @setStateToMax(entry, @ModuleSatisfy); +} + +function instantiation(result, source, entry) +{ + // https://whatwg.github.io/loader/#instantiation + // FIXME: Current implementation does not support optionalInstance. + // https://bugs.webkit.org/show_bug.cgi?id=148171 + + "use strict"; + + return this.parseModule(entry.key, source); +} + +// Loader. + +function requestFetch(key, fetcher) +{ + // https://whatwg.github.io/loader/#request-fetch + + "use strict"; + + var entry = this.ensureRegistered(key); + if (entry.fetch) + return entry.fetch; + + // Hook point. + // 2. Loader.fetch + // https://whatwg.github.io/loader/#browser-fetch + // Take the key and fetch the resource actually. + // For example, JavaScriptCore shell can provide the hook fetching the resource + // from the local file system. + var fetchPromise = this.fetch(key, fetcher).then((source) => { + @setStateToMax(entry, @ModuleInstantiate); + return source; + }); + entry.fetch = fetchPromise; + return fetchPromise; +} + +function requestInstantiate(key, fetcher) +{ + // https://whatwg.github.io/loader/#request-instantiate + + "use strict"; + + var entry = this.ensureRegistered(key); + if (entry.instantiate) + return entry.instantiate; + + var instantiatePromise = this.requestFetch(key, fetcher).then((source) => { + // Hook point. + // 3. Loader.instantiate + // https://whatwg.github.io/loader/#browser-instantiate + // Take the key and the fetched source code, and instantiate the module record + // by parsing the module source code. + // It has the chance to provide the optional module instance that is different from + // the ordinary one. + return this.instantiate(key, source, fetcher).then((optionalInstance) => { + this.commitInstantiated(entry, optionalInstance, source); + return entry; + }); + }); + entry.instantiate = instantiatePromise; + return instantiatePromise; +} + +function requestSatisfy(key, fetcher) +{ + // https://whatwg.github.io/loader/#satisfy-instance + + "use strict"; + + var entry = this.ensureRegistered(key); + if (entry.satisfy) + return entry.satisfy; + + var satisfyPromise = this.requestInstantiate(key, fetcher).then((entry) => { + var depLoads = []; + for (var i = 0, length = entry.dependencies.length; i < length; ++i) { + let pair = entry.dependencies[i]; + + // Hook point. + // 1. Loader.resolve. + // https://whatwg.github.io/loader/#browser-resolve + // Take the name and resolve it to the unique identifier for the resource location. + // For example, take the "jquery" and return the URL for the resource. + var promise = this.resolve(pair.key, key, fetcher).then((depKey) => { + var depEntry = this.ensureRegistered(depKey); + + // Recursive resolving. The dependencies of this entry is being resolved or already resolved. + // Stop tracing the circular dependencies. + // But to retrieve the instantiated module record correctly, + // we need to wait for the instantiation for the dependent module. + // For example, reaching here, the module is starting resolving the dependencies. + // But the module may or may not reach the instantiation phase in the loader's pipeline. + // If we wait for the Satisfy for this module, it construct the circular promise chain and + // rejected by the Promises runtime. Since only we need is the instantiated module, instead of waiting + // the Satisfy for this module, we just wait Instantiate for this. + if (depEntry.satisfy) { + return depEntry.instantiate.then((entry) => { + pair.value = entry.module; + return entry; + }); + } + + return this.requestSatisfy(depKey, fetcher).then((entry) => { + pair.value = entry.module; + return entry; + }); + }); + @putByValDirect(depLoads, depLoads.length, promise); + } + + return @InternalPromise.internalAll(depLoads).then((modules) => { + @setStateToMax(entry, @ModuleLink); + return entry; + }); + }); + + entry.satisfy = satisfyPromise; + return satisfyPromise; +} + +function requestLink(key, fetcher) +{ + // https://whatwg.github.io/loader/#request-link + + "use strict"; + + var entry = this.ensureRegistered(key); + if (entry.state > @ModuleLink) { + var deferred = @newPromiseCapability(@InternalPromise); + deferred.@resolve.@call(@undefined, entry); + return deferred.@promise; + } + + return this.requestSatisfy(key, fetcher).then((entry) => { + this.link(entry, fetcher); + return entry; + }); +} + +function requestReady(key, fetcher) +{ + // https://whatwg.github.io/loader/#request-ready + + "use strict"; + + return this.requestLink(key, fetcher).then((entry) => { + this.moduleEvaluation(entry.module, fetcher); + }); +} + +// Linking semantics. + +function link(entry, fetcher) +{ + // https://whatwg.github.io/loader/#link + + "use strict"; + + if (!entry.linkSucceeded) + throw entry.linkError; + if (entry.state === @ModuleReady) + return; + @setStateToMax(entry, @ModuleReady); + + try { + // Since we already have the "dependencies" field, + // we can call moduleDeclarationInstantiation with the correct order + // without constructing the dependency graph by calling dependencyGraph. + var dependencies = entry.dependencies; + for (var i = 0, length = dependencies.length; i < length; ++i) { + var pair = dependencies[i]; + this.link(pair.value.registryEntry, fetcher); + } + + this.moduleDeclarationInstantiation(entry.module, fetcher); + } catch (error) { + entry.linkSucceeded = false; + entry.linkError = error; + throw error; + } +} + +// Module semantics. + +function moduleEvaluation(moduleRecord, fetcher) +{ + // http://www.ecma-international.org/ecma-262/6.0/#sec-moduleevaluation + + "use strict"; + + if (moduleRecord.evaluated) + return; + moduleRecord.evaluated = true; + + var entry = moduleRecord.registryEntry; + + // The contents of the [[RequestedModules]] is cloned into entry.dependencies. + var dependencies = entry.dependencies; + for (var i = 0, length = dependencies.length; i < length; ++i) { + var pair = dependencies[i]; + var requiredModuleRecord = pair.value; + this.moduleEvaluation(requiredModuleRecord, fetcher); + } + this.evaluate(entry.key, moduleRecord, fetcher); +} + +// APIs to control the module loader. + +function provide(key, stage, value) +{ + "use strict"; + + var entry = this.ensureRegistered(key); + + if (stage === @ModuleFetch) { + if (entry.state > @ModuleFetch) + @throwTypeError("Requested module is already fetched."); + this.fulfillFetch(entry, value); + return; + } + + if (stage === @ModuleInstantiate) { + if (entry.state > @ModuleInstantiate) + @throwTypeError("Requested module is already instantiated."); + this.fulfillFetch(entry, @undefined); + entry.fetch.then((source) => { + this.fulfillInstantiate(entry, value, source); + }); + return; + } + + @throwTypeError("Requested module is already ready to be executed."); +} + +function loadAndEvaluateModule(moduleName, referrer, fetcher) +{ + "use strict"; + + // Loader.resolve hook point. + // resolve: moduleName => Promise(moduleKey) + // Take the name and resolve it to the unique identifier for the resource location. + // For example, take the "jquery" and return the URL for the resource. + return this.resolve(moduleName, referrer, fetcher).then((key) => { + return this.requestReady(key, fetcher); + }); +} + +function loadModule(moduleName, referrer, fetcher) +{ + "use strict"; + + // Loader.resolve hook point. + // resolve: moduleName => Promise(moduleKey) + // Take the name and resolve it to the unique identifier for the resource location. + // For example, take the "jquery" and return the URL for the resource. + return this.resolve(moduleName, referrer, fetcher).then((key) => { + return this.requestSatisfy(key, fetcher); + }).then((entry) => { + return entry.key; + }); +} + +function linkAndEvaluateModule(key, fetcher) +{ + "use strict"; + + var entry = this.ensureRegistered(key); + if (entry.state < @ModuleLink) + @throwTypeError("Requested module is not instantiated yet."); + + this.link(entry, fetcher); + return this.moduleEvaluation(entry.module, fetcher); +} + +function requestImportModule(key, fetcher) +{ + "use strict"; + + return this.requestSatisfy(key, fetcher).then((entry) => { + this.linkAndEvaluateModule(entry.key, fetcher); + return this.getModuleNamespaceObject(entry.module); + }); +} diff --git a/Source/JavaScriptCore/builtins/NumberConstructor.js b/Source/JavaScriptCore/builtins/NumberConstructor.js new file mode 100644 index 000000000..2c0e4c868 --- /dev/null +++ b/Source/JavaScriptCore/builtins/NumberConstructor.js @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function isFinite(value) +{ + "use strict"; + + if (typeof value !== "number") + return false; + + // Return false if value is |NaN|. + if (value !== value) + return false; + + return value !== @Infinity && value !== -@Infinity; +} + +function isNaN(value) +{ + "use strict"; + + return value !== value; +} diff --git a/Source/JavaScriptCore/builtins/NumberPrototype.js b/Source/JavaScriptCore/builtins/NumberPrototype.js new file mode 100644 index 000000000..435ea789b --- /dev/null +++ b/Source/JavaScriptCore/builtins/NumberPrototype.js @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 Andy VanWagoner <thetalecrafter@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// @conditional=ENABLE(INTL) + +function toLocaleString(/* locales, options */) +{ + "use strict"; + + // 13.2.1 Number.prototype.toLocaleString ([locales [, options ]]) (ECMA-402 2.0) + // http://ecma-international.org/publications/standards/Ecma-402.htm + + // 1. Let x be thisNumberValue(this value). + // 2. ReturnIfAbrupt(x). + var number = @thisNumberValue.@call(this); + + // 3. Let numberFormat be Construct(%NumberFormat%, «locales, options»). + // 4. ReturnIfAbrupt(numberFormat). + var numberFormat = new @NumberFormat(@argument(0), @argument(1)); + + // 5. Return FormatNumber(numberFormat, x). + return numberFormat.format(number); +} diff --git a/Source/JavaScriptCore/builtins/ObjectConstructor.js b/Source/JavaScriptCore/builtins/ObjectConstructor.js new file mode 100644 index 000000000..d855beb2a --- /dev/null +++ b/Source/JavaScriptCore/builtins/ObjectConstructor.js @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 Oleksandr Skachkov <gskachkov@gmail.com>. + * Copyright (C) 2015 Jordan Harband. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +@globalPrivate +function enumerableOwnProperties(object, kind) +{ + "use strict"; + + const obj = @Object(object); + const ownKeys = @Reflect.@ownKeys(obj); + const properties = []; + for (let i = 0, keysLength = ownKeys.length; i < keysLength; ++i) { + let nextKey = ownKeys[i]; + if (typeof nextKey === 'string') { + let descriptor = @Reflect.@getOwnPropertyDescriptor(obj, nextKey); + if (descriptor !== @undefined && descriptor.enumerable) { + if (kind === @iterationKindValue) + properties.@push(obj[nextKey]); + else if (kind === @iterationKindKeyValue) + properties.@push([nextKey, obj[nextKey]]); + } + } + } + + return properties; +} + +function values(object) +{ + "use strict"; + + if (object == null) + @throwTypeError("Object.values requires that input parameter not be null or undefined"); + + return @enumerableOwnProperties(object, @iterationKindValue); +} + +function entries(object) +{ + "use strict"; + + if (object == null) + @throwTypeError("Object.entries requires that input parameter not be null or undefined"); + + return @enumerableOwnProperties(object, @iterationKindKeyValue); +} + +function assign(target/*[*/, /*...*/sources/*] */) +{ + "use strict"; + + if (target == null) + @throwTypeError("Object.assign requires that input parameter not be null or undefined"); + + let objTarget = @Object(target); + for (let s = 1, argumentsLength = arguments.length; s < argumentsLength; ++s) { + let nextSource = arguments[s]; + if (nextSource != null) { + let from = @Object(nextSource); + let keys = @Reflect.@ownKeys(from); + for (let i = 0, keysLength = keys.length; i < keysLength; ++i) { + let nextKey = keys[i]; + let descriptor = @Reflect.@getOwnPropertyDescriptor(from, nextKey); + if (descriptor !== @undefined && descriptor.enumerable) + objTarget[nextKey] = from[nextKey]; + } + } + } + return objTarget; +} diff --git a/Source/JavaScriptCore/builtins/PromiseConstructor.js b/Source/JavaScriptCore/builtins/PromiseConstructor.js new file mode 100644 index 000000000..3f0848dfa --- /dev/null +++ b/Source/JavaScriptCore/builtins/PromiseConstructor.js @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function all(iterable) +{ + "use strict"; + + if (!@isObject(this)) + @throwTypeError("|this| is not a object"); + + var promiseCapability = @newPromiseCapability(this); + + var values = []; + var index = 0; + var remainingElementsCount = 1; + + function newResolveElement(index) + { + var alreadyCalled = false; + return function (argument) + { + if (alreadyCalled) + return @undefined; + alreadyCalled = true; + + @putByValDirect(values, index, argument); + + --remainingElementsCount; + if (remainingElementsCount === 0) + return promiseCapability.@resolve.@call(@undefined, values); + + return @undefined; + } + } + + try { + for (var value of iterable) { + @putByValDirect(values, index, @undefined); + var nextPromise = this.resolve(value); + var resolveElement = newResolveElement(index); + ++remainingElementsCount; + nextPromise.then(resolveElement, promiseCapability.@reject); + ++index; + } + + --remainingElementsCount; + if (remainingElementsCount === 0) + promiseCapability.@resolve.@call(@undefined, values); + } catch (error) { + promiseCapability.@reject.@call(@undefined, error); + } + + return promiseCapability.@promise; +} + +function race(iterable) +{ + "use strict"; + + if (!@isObject(this)) + @throwTypeError("|this| is not a object"); + + var promiseCapability = @newPromiseCapability(this); + + try { + for (var value of iterable) { + var nextPromise = this.resolve(value); + nextPromise.then(promiseCapability.@resolve, promiseCapability.@reject); + } + } catch (error) { + promiseCapability.@reject.@call(@undefined, error); + } + + return promiseCapability.@promise; +} + +function reject(reason) +{ + "use strict"; + + if (!@isObject(this)) + @throwTypeError("|this| is not a object"); + + var promiseCapability = @newPromiseCapability(this); + + promiseCapability.@reject.@call(@undefined, reason); + + return promiseCapability.@promise; +} + +function resolve(value) +{ + "use strict"; + + if (!@isObject(this)) + @throwTypeError("|this| is not a object"); + + if (@isPromise(value)) { + var valueConstructor = value.constructor; + if (valueConstructor === this) + return value; + } + + var promiseCapability = @newPromiseCapability(this); + + promiseCapability.@resolve.@call(@undefined, value); + + return promiseCapability.@promise; +} diff --git a/Source/JavaScriptCore/builtins/PromiseOperations.js b/Source/JavaScriptCore/builtins/PromiseOperations.js new file mode 100644 index 000000000..61564e7cd --- /dev/null +++ b/Source/JavaScriptCore/builtins/PromiseOperations.js @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// @internal + +@globalPrivate +function isPromise(promise) +{ + "use strict"; + + return @isObject(promise) && !!promise.@promiseState; +} + +@globalPrivate +function newPromiseReaction(capability, onFulfilled, onRejected) +{ + "use strict"; + + return { + @capabilities: capability, + @onFulfilled: onFulfilled, + @onRejected: onRejected, + }; +} + +@globalPrivate +function newPromiseCapability(constructor) +{ + "use strict"; + + if (!@isConstructor(constructor)) + @throwTypeError("promise capability requires a constructor function"); + + var promiseCapability = { + @promise: @undefined, + @resolve: @undefined, + @reject: @undefined + }; + + function executor(resolve, reject) + { + if (promiseCapability.@resolve !== @undefined) + @throwTypeError("resolve function is already set"); + if (promiseCapability.@reject !== @undefined) + @throwTypeError("reject function is already set"); + + promiseCapability.@resolve = resolve; + promiseCapability.@reject = reject; + } + + var promise = new constructor(executor); + + if (typeof promiseCapability.@resolve !== "function") + @throwTypeError("executor did not take a resolve function"); + + if (typeof promiseCapability.@reject !== "function") + @throwTypeError("executor did not take a reject function"); + + promiseCapability.@promise = promise; + + return promiseCapability; +} + +@globalPrivate +function triggerPromiseReactions(state, reactions, argument) +{ + "use strict"; + + for (var index = 0, length = reactions.length; index < length; ++index) + @enqueueJob(@promiseReactionJob, [state, reactions[index], argument]); +} + +@globalPrivate +function rejectPromise(promise, reason) +{ + "use strict"; + + var reactions = promise.@promiseReactions; + promise.@promiseResult = reason; + promise.@promiseReactions = @undefined; + promise.@promiseState = @promiseStateRejected; + + @InspectorInstrumentation.promiseRejected(promise, reason, reactions); + + @triggerPromiseReactions(@promiseStateRejected, reactions, reason); +} + +@globalPrivate +function fulfillPromise(promise, value) +{ + "use strict"; + + var reactions = promise.@promiseReactions; + promise.@promiseResult = value; + promise.@promiseReactions = @undefined; + promise.@promiseState = @promiseStateFulfilled; + + @InspectorInstrumentation.promiseFulfilled(promise, value, reactions); + + @triggerPromiseReactions(@promiseStateFulfilled, reactions, value); +} + +@globalPrivate +function createResolvingFunctions(promise) +{ + "use strict"; + + var alreadyResolved = false; + + var resolve = function (resolution) { + if (alreadyResolved) + return @undefined; + alreadyResolved = true; + + if (resolution === promise) + return @rejectPromise(promise, new @TypeError("Resolve a promise with itself")); + + if (!@isObject(resolution)) + return @fulfillPromise(promise, resolution); + + var then; + try { + then = resolution.then; + } catch (error) { + return @rejectPromise(promise, error); + } + + if (typeof then !== 'function') + return @fulfillPromise(promise, resolution); + + @enqueueJob(@promiseResolveThenableJob, [promise, resolution, then]); + + return @undefined; + }; + + var reject = function (reason) { + if (alreadyResolved) + return @undefined; + alreadyResolved = true; + + return @rejectPromise(promise, reason); + }; + + return { + @resolve: resolve, + @reject: reject + }; +} + +@globalPrivate +function promiseReactionJob(state, reaction, argument) +{ + "use strict"; + + var promiseCapability = reaction.@capabilities; + + var result; + var handler = (state === @promiseStateFulfilled) ? reaction.@onFulfilled: reaction.@onRejected; + try { + result = handler(argument); + } catch (error) { + return promiseCapability.@reject.@call(@undefined, error); + } + + return promiseCapability.@resolve.@call(@undefined, result); +} + +@globalPrivate +function promiseResolveThenableJob(promiseToResolve, thenable, then) +{ + "use strict"; + + var resolvingFunctions = @createResolvingFunctions(promiseToResolve); + + try { + return then.@call(thenable, resolvingFunctions.@resolve, resolvingFunctions.@reject); + } catch (error) { + return resolvingFunctions.@reject.@call(@undefined, error); + } +} + +@globalPrivate +function initializePromise(executor) +{ + "use strict"; + + if (typeof executor !== 'function') + @throwTypeError("Promise constructor takes a function argument"); + + this.@promiseState = @promiseStatePending; + this.@promiseReactions = []; + + var resolvingFunctions = @createResolvingFunctions(this); + try { + executor(resolvingFunctions.@resolve, resolvingFunctions.@reject); + } catch (error) { + return resolvingFunctions.@reject.@call(@undefined, error); + } + + return this; +} diff --git a/Source/JavaScriptCore/builtins/PromisePrototype.js b/Source/JavaScriptCore/builtins/PromisePrototype.js new file mode 100644 index 000000000..6065ad837 --- /dev/null +++ b/Source/JavaScriptCore/builtins/PromisePrototype.js @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. + */ + +function catch(onRejected) +{ + "use strict"; + + return this.then(@undefined, onRejected); +} + +function then(onFulfilled, onRejected) +{ + "use strict"; + + if (!@isPromise(this)) + @throwTypeError("|this| is not a object"); + + var constructor = @speciesConstructor(this, @Promise); + + var resultCapability = @newPromiseCapability(constructor); + + if (typeof onFulfilled !== "function") + onFulfilled = function (argument) { return argument; }; + + if (typeof onRejected !== "function") + onRejected = function (argument) { throw argument; }; + + var reaction = @newPromiseReaction(resultCapability, onFulfilled, onRejected); + + var state = this.@promiseState; + if (state === @promiseStatePending) + @putByValDirect(this.@promiseReactions, this.@promiseReactions.length, reaction); + else + @enqueueJob(@promiseReactionJob, [state, reaction, this.@promiseResult]); + + return resultCapability.@promise; +} diff --git a/Source/JavaScriptCore/builtins/ReflectObject.js b/Source/JavaScriptCore/builtins/ReflectObject.js new file mode 100644 index 000000000..1aaa1f407 --- /dev/null +++ b/Source/JavaScriptCore/builtins/ReflectObject.js @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// https://tc39.github.io/ecma262/#sec-reflect.apply +function apply(target, thisArgument, argumentsList) +{ + "use strict"; + + if (typeof target !== "function") + @throwTypeError("Reflect.apply requires the first argument be a function"); + + if (!@isObject(argumentsList)) + @throwTypeError("Reflect.apply requires the third argument be an object"); + + return target.@apply(thisArgument, argumentsList); +} + +// https://tc39.github.io/ecma262/#sec-reflect.deleteproperty +function deleteProperty(target, propertyKey) +{ + // Intentionally keep the code the sloppy mode to suppress the TypeError + // raised by the delete operator under the strict mode. + + if (!@isObject(target)) + @throwTypeError("Reflect.deleteProperty requires the first argument be an object"); + + return delete target[propertyKey]; +} + +// https://tc39.github.io/ecma262/#sec-reflect.has +function has(target, propertyKey) +{ + "use strict"; + + if (!@isObject(target)) + @throwTypeError("Reflect.has requires the first argument be an object"); + + return propertyKey in target; +} diff --git a/Source/JavaScriptCore/builtins/RegExpPrototype.js b/Source/JavaScriptCore/builtins/RegExpPrototype.js new file mode 100644 index 000000000..017a81cd1 --- /dev/null +++ b/Source/JavaScriptCore/builtins/RegExpPrototype.js @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +@globalPrivate +function advanceStringIndex(string, index, unicode) +{ + // This function implements AdvanceStringIndex described in ES6 21.2.5.2.3. + "use strict"; + + if (!unicode) + return index + 1; + + if (index + 1 >= string.length) + return index + 1; + + let first = string.@charCodeAt(index); + if (first < 0xD800 || first > 0xDBFF) + return index + 1; + + let second = string.@charCodeAt(index + 1); + if (second < 0xDC00 || second > 0xDFFF) + return index + 1; + + return index + 2; +} + +@globalPrivate +function regExpExec(regexp, str) +{ + "use strict"; + + let exec = regexp.exec; + let builtinExec = @regExpBuiltinExec; + if (exec !== builtinExec && typeof exec === "function") { + let result = exec.@call(regexp, str); + if (result !== null && !@isObject(result)) + @throwTypeError("The result of a RegExp exec must be null or an object"); + return result; + } + return builtinExec.@call(regexp, str); +} + +@globalPrivate +function hasObservableSideEffectsForRegExpMatch(regexp) { + // This is accessed by the RegExpExec internal function. + let regexpExec = @tryGetById(regexp, "exec"); + if (regexpExec !== @regExpBuiltinExec) + return true; + + let regexpGlobal = @tryGetById(regexp, "global"); + if (regexpGlobal !== @regExpProtoGlobalGetter) + return true; + let regexpUnicode = @tryGetById(regexp, "unicode"); + if (regexpUnicode !== @regExpProtoUnicodeGetter) + return true; + + return !@isRegExpObject(regexp); +} + +function match(strArg) +{ + "use strict"; + + if (!@isObject(this)) + @throwTypeError("RegExp.prototype.@@match requires that |this| be an Object"); + + let regexp = this; + + // Check for observable side effects and call the fast path if there aren't any. + if (!@hasObservableSideEffectsForRegExpMatch(regexp)) + return @regExpMatchFast.@call(regexp, strArg); + + let str = @toString(strArg); + + if (!regexp.global) + return @regExpExec(regexp, str); + + let unicode = regexp.unicode; + regexp.lastIndex = 0; + let resultList = []; + + // FIXME: It would be great to implement a solution similar to what we do in + // RegExpObject::matchGlobal(). It's not clear if this is possible, since this loop has + // effects. https://bugs.webkit.org/show_bug.cgi?id=158145 + const maximumReasonableMatchSize = 100000000; + + while (true) { + let result = @regExpExec(regexp, str); + + if (result === null) { + if (resultList.length === 0) + return null; + return resultList; + } + + if (resultList.length > maximumReasonableMatchSize) + @throwOutOfMemoryError(); + + if (!@isObject(result)) + @throwTypeError("RegExp.prototype.@@match call to RegExp.exec didn't return null or an object"); + + let resultString = @toString(result[0]); + + if (!resultString.length) + regexp.lastIndex = @advanceStringIndex(str, regexp.lastIndex, unicode); + + resultList.@push(resultString); + } +} + +function replace(strArg, replace) +{ + "use strict"; + + function getSubstitution(matched, str, position, captures, replacement) + { + "use strict"; + + let matchLength = matched.length; + let stringLength = str.length; + let tailPos = position + matchLength; + let m = captures.length; + let replacementLength = replacement.length; + let result = ""; + let lastStart = 0; + + for (let start = 0; start = replacement.indexOf("$", lastStart), start !== -1; lastStart = start) { + if (start - lastStart > 0) + result = result + replacement.substring(lastStart, start); + start++; + let ch = replacement.charAt(start); + if (ch === "") + result = result + "$"; + else { + switch (ch) + { + case "$": + result = result + "$"; + start++; + break; + case "&": + result = result + matched; + start++; + break; + case "`": + if (position > 0) + result = result + str.substring(0, position); + start++; + break; + case "'": + if (tailPos < stringLength) + result = result + str.substring(tailPos); + start++; + break; + default: + let chCode = ch.charCodeAt(0); + if (chCode >= 0x30 && chCode <= 0x39) { + start++; + let n = chCode - 0x30; + if (n > m) + break; + if (start < replacementLength) { + let nextChCode = replacement.charCodeAt(start); + if (nextChCode >= 0x30 && nextChCode <= 0x39) { + let nn = 10 * n + nextChCode - 0x30; + if (nn <= m) { + n = nn; + start++; + } + } + } + + if (n == 0) + break; + + if (captures[n] != @undefined) + result = result + captures[n]; + } else + result = result + "$"; + break; + } + } + } + + return result + replacement.substring(lastStart); + } + + if (!@isObject(this)) + @throwTypeError("RegExp.prototype.@@replace requires that |this| be an Object"); + + let regexp = this; + + let str = @toString(strArg); + let stringLength = str.length; + let functionalReplace = typeof replace === 'function'; + + if (!functionalReplace) + replace = @toString(replace); + + let global = regexp.global; + let unicode = false; + + if (global) { + unicode = regexp.unicode; + regexp.lastIndex = 0; + } + + let resultList = []; + let result; + let done = false; + while (!done) { + result = @regExpExec(regexp, str); + + if (result === null) + done = true; + else { + resultList.@push(result); + if (!global) + done = true; + else { + let matchStr = @toString(result[0]); + + if (!matchStr.length) + regexp.lastIndex = @advanceStringIndex(str, regexp.lastIndex, unicode); + } + } + } + + let accumulatedResult = ""; + let nextSourcePosition = 0; + let lastPosition = 0; + + for (result of resultList) { + let nCaptures = result.length - 1; + if (nCaptures < 0) + nCaptures = 0; + let matched = @toString(result[0]); + let matchLength = matched.length; + let position = result.index; + position = (position > stringLength) ? stringLength : position; + position = (position < 0) ? 0 : position; + + let captures = []; + for (let n = 1; n <= nCaptures; n++) { + let capN = result[n]; + if (capN !== @undefined) + capN = @toString(capN); + captures[n] = capN; + } + + let replacement; + + if (functionalReplace) { + let replacerArgs = [ matched ].concat(captures.slice(1)); + replacerArgs.@push(position); + replacerArgs.@push(str); + + let replValue = replace.@apply(@undefined, replacerArgs); + replacement = @toString(replValue); + } else + replacement = getSubstitution(matched, str, position, captures, replace); + + if (position >= nextSourcePosition && position >= lastPosition) { + accumulatedResult = accumulatedResult + str.substring(nextSourcePosition, position) + replacement; + nextSourcePosition = position + matchLength; + lastPosition = position; + } + } + + if (nextSourcePosition >= stringLength) + return accumulatedResult; + + return accumulatedResult + str.substring(nextSourcePosition); +} + +// 21.2.5.9 RegExp.prototype[@@search] (string) +function search(strArg) +{ + "use strict"; + + let regexp = this; + + // Check for observable side effects and call the fast path if there aren't any. + if (@isRegExpObject(regexp) && @tryGetById(regexp, "exec") === @regExpBuiltinExec) + return @regExpSearchFast.@call(regexp, strArg); + + // 1. Let rx be the this value. + // 2. If Type(rx) is not Object, throw a TypeError exception. + if (!@isObject(this)) + @throwTypeError("RegExp.prototype.@@search requires that |this| be an Object"); + + // 3. Let S be ? ToString(string). + let str = @toString(strArg) + + // 4. Let previousLastIndex be ? Get(rx, "lastIndex"). + let previousLastIndex = regexp.lastIndex; + // 5. Perform ? Set(rx, "lastIndex", 0, true). + regexp.lastIndex = 0; + // 6. Let result be ? RegExpExec(rx, S). + let result = @regExpExec(regexp, str); + // 7. Perform ? Set(rx, "lastIndex", previousLastIndex, true). + regexp.lastIndex = previousLastIndex; + // 8. If result is null, return -1. + if (result === null) + return -1; + // 9. Return ? Get(result, "index"). + return result.index; +} + +@globalPrivate +function hasObservableSideEffectsForRegExpSplit(regexp) { + // This is accessed by the RegExpExec internal function. + let regexpExec = @tryGetById(regexp, "exec"); + if (regexpExec !== @regExpBuiltinExec) + return true; + + // This is accessed by step 5 below. + let regexpFlags = @tryGetById(regexp, "flags"); + if (regexpFlags !== @regExpProtoFlagsGetter) + return true; + + // These are accessed by the builtin flags getter. + let regexpGlobal = @tryGetById(regexp, "global"); + if (regexpGlobal !== @regExpProtoGlobalGetter) + return true; + let regexpIgnoreCase = @tryGetById(regexp, "ignoreCase"); + if (regexpIgnoreCase !== @regExpProtoIgnoreCaseGetter) + return true; + let regexpMultiline = @tryGetById(regexp, "multiline"); + if (regexpMultiline !== @regExpProtoMultilineGetter) + return true; + let regexpSticky = @tryGetById(regexp, "sticky"); + if (regexpSticky !== @regExpProtoStickyGetter) + return true; + let regexpUnicode = @tryGetById(regexp, "unicode"); + if (regexpUnicode !== @regExpProtoUnicodeGetter) + return true; + + // This is accessed by the RegExp species constructor. + let regexpSource = @tryGetById(regexp, "source"); + if (regexpSource !== @regExpProtoSourceGetter) + return true; + + return !@isRegExpObject(regexp); +} + +// ES 21.2.5.11 RegExp.prototype[@@split](string, limit) +function split(string, limit) +{ + "use strict"; + + // 1. Let rx be the this value. + // 2. If Type(rx) is not Object, throw a TypeError exception. + if (!@isObject(this)) + @throwTypeError("RegExp.prototype.@@split requires that |this| be an Object"); + let regexp = this; + + // 3. Let S be ? ToString(string). + let str = @toString(string); + + // 4. Let C be ? SpeciesConstructor(rx, %RegExp%). + let speciesConstructor = @speciesConstructor(regexp, @RegExp); + + if (speciesConstructor === @RegExp && !@hasObservableSideEffectsForRegExpSplit(regexp)) + return @regExpSplitFast.@call(regexp, str, limit); + + // 5. Let flags be ? ToString(? Get(rx, "flags")). + let flags = @toString(regexp.flags); + + // 6. If flags contains "u", let unicodeMatching be true. + // 7. Else, let unicodeMatching be false. + let unicodeMatching = @stringIncludesInternal.@call(flags, "u"); + // 8. If flags contains "y", let newFlags be flags. + // 9. Else, let newFlags be the string that is the concatenation of flags and "y". + let newFlags = @stringIncludesInternal.@call(flags, "y") ? flags : flags + "y"; + + // 10. Let splitter be ? Construct(C, « rx, newFlags »). + let splitter = new speciesConstructor(regexp, newFlags); + + // We need to check again for RegExp subclasses that will fail the speciesConstructor test + // but can still use the fast path after we invoke the constructor above. + if (!@hasObservableSideEffectsForRegExpSplit(splitter)) + return @regExpSplitFast.@call(splitter, str, limit); + + // 11. Let A be ArrayCreate(0). + // 12. Let lengthA be 0. + let result = []; + + // 13. If limit is undefined, let lim be 2^32-1; else let lim be ? ToUint32(limit). + limit = (limit === @undefined) ? 0xffffffff : limit >>> 0; + + // 16. If lim = 0, return A. + if (!limit) + return result; + + // 14. [Defered from above] Let size be the number of elements in S. + let size = str.length; + + // 17. If size = 0, then + if (!size) { + // a. Let z be ? RegExpExec(splitter, S). + let z = @regExpExec(splitter, str); + // b. If z is not null, return A. + if (z != null) + return result; + // c. Perform ! CreateDataProperty(A, "0", S). + @putByValDirect(result, 0, str); + // d. Return A. + return result; + } + + // 15. [Defered from above] Let p be 0. + let position = 0; + // 18. Let q be p. + let matchPosition = 0; + + // 19. Repeat, while q < size + while (matchPosition < size) { + // a. Perform ? Set(splitter, "lastIndex", q, true). + splitter.lastIndex = matchPosition; + // b. Let z be ? RegExpExec(splitter, S). + let matches = @regExpExec(splitter, str); + // c. If z is null, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (matches === null) + matchPosition = @advanceStringIndex(str, matchPosition, unicodeMatching); + // d. Else z is not null, + else { + // i. Let e be ? ToLength(? Get(splitter, "lastIndex")). + let endPosition = @toLength(splitter.lastIndex); + // ii. Let e be min(e, size). + endPosition = (endPosition <= size) ? endPosition : size; + // iii. If e = p, let q be AdvanceStringIndex(S, q, unicodeMatching). + if (endPosition === position) + matchPosition = @advanceStringIndex(str, matchPosition, unicodeMatching); + // iv. Else e != p, + else { + // 1. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through q (exclusive). + let subStr = @stringSubstrInternal.@call(str, position, matchPosition - position); + // 2. Perform ! CreateDataProperty(A, ! ToString(lengthA), T). + // 3. Let lengthA be lengthA + 1. + @putByValDirect(result, result.length, subStr); + // 4. If lengthA = lim, return A. + if (result.length == limit) + return result; + + // 5. Let p be e. + position = endPosition; + // 6. Let numberOfCaptures be ? ToLength(? Get(z, "length")). + // 7. Let numberOfCaptures be max(numberOfCaptures-1, 0). + let numberOfCaptures = matches.length > 1 ? matches.length - 1 : 0; + + // 8. Let i be 1. + let i = 1; + // 9. Repeat, while i <= numberOfCaptures, + while (i <= numberOfCaptures) { + // a. Let nextCapture be ? Get(z, ! ToString(i)). + let nextCapture = matches[i]; + // b. Perform ! CreateDataProperty(A, ! ToString(lengthA), nextCapture). + // d. Let lengthA be lengthA + 1. + @putByValDirect(result, result.length, nextCapture); + // e. If lengthA = lim, return A. + if (result.length == limit) + return result; + // c. Let i be i + 1. + i++; + } + // 10. Let q be p. + matchPosition = position; + } + } + } + // 20. Let T be a String value equal to the substring of S consisting of the elements at indices p (inclusive) through size (exclusive). + let remainingStr = @stringSubstrInternal.@call(str, position, size); + // 21. Perform ! CreateDataProperty(A, ! ToString(lengthA), T). + @putByValDirect(result, result.length, remainingStr); + // 22. Return A. + return result; +} + +// ES 21.2.5.13 RegExp.prototype.test(string) +@intrinsic=RegExpTestIntrinsic +function test(strArg) +{ + "use strict"; + + let regexp = this; + + // Check for observable side effects and call the fast path if there aren't any. + if (@isRegExpObject(regexp) && @tryGetById(regexp, "exec") === @regExpBuiltinExec) + return @regExpTestFast.@call(regexp, strArg); + + // 1. Let R be the this value. + // 2. If Type(R) is not Object, throw a TypeError exception. + if (!@isObject(regexp)) + @throwTypeError("RegExp.prototype.test requires that |this| be an Object"); + + // 3. Let string be ? ToString(S). + let str = @toString(strArg); + + // 4. Let match be ? RegExpExec(R, string). + let match = @regExpExec(regexp, str); + + // 5. If match is not null, return true; else return false. + if (match !== null) + return true; + return false; +} diff --git a/Source/JavaScriptCore/builtins/SetPrototype.js b/Source/JavaScriptCore/builtins/SetPrototype.js new file mode 100644 index 000000000..e9b6626e9 --- /dev/null +++ b/Source/JavaScriptCore/builtins/SetPrototype.js @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function forEach(callback /*, thisArg */) +{ + "use strict"; + + if (!@isSet(this)) + @throwTypeError("Set operation called on non-Set object"); + + if (typeof callback !== 'function') + @throwTypeError("Set.prototype.forEach callback must be a function"); + + var thisArg = @argument(1); + var iterator = @SetIterator(this); + + // To avoid object allocations for iterator result objects, we pass the placeholder to the special "next" function in order to fill the results. + var value = [ @undefined ]; + for (;;) { + if (@setIteratorNext.@call(iterator, value)) + break; + callback.@call(thisArg, value[0], value[0], this); + } +} diff --git a/Source/JavaScriptCore/builtins/StringConstructor.js b/Source/JavaScriptCore/builtins/StringConstructor.js new file mode 100644 index 000000000..a3293328b --- /dev/null +++ b/Source/JavaScriptCore/builtins/StringConstructor.js @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function raw(template) +{ + "use strict"; + + if (template === null || template === @undefined) + @throwTypeError("String.raw requires template not be null or undefined"); + var cookedSegments = @Object(template); + + var rawValue = cookedSegments.raw; + if (rawValue === null || rawValue === @undefined) + @throwTypeError("String.raw requires template.raw not be null or undefined"); + var rawSegments = @Object(rawValue); + + var numberOfSubstitutions = arguments.length - 1; + + var segmentCount = @toLength(rawSegments.length); + + if (segmentCount <= 0) + return ''; + + var stringElements = ''; + for (var i = 0; ; ++i) { + var segment = @toString(rawSegments[i]); + stringElements += segment; + + if ((i + 1) === segmentCount) + return stringElements; + + if (i < numberOfSubstitutions) { + var substitutionIndexInArguments = i + 1; + var next = @toString(arguments[substitutionIndexInArguments]); + stringElements += next; + } + } +} diff --git a/Source/JavaScriptCore/builtins/StringIteratorPrototype.js b/Source/JavaScriptCore/builtins/StringIteratorPrototype.js new file mode 100644 index 000000000..52762dbf3 --- /dev/null +++ b/Source/JavaScriptCore/builtins/StringIteratorPrototype.js @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function next() +{ + "use strict"; + + if (this == null) + @throwTypeError("%StringIteratorPrototype%.next requires that |this| not be null or undefined"); + + var position = this.@stringIteratorNextIndex; + if (position === @undefined) + @throwTypeError("%StringIteratorPrototype%.next requires that |this| be a String Iterator instance"); + + var done = true; + var value = @undefined; + + var string = this.@iteratedString; + if (string !== @undefined) { + var length = string.length >>> 0; + if (position >= length) { + this.@iteratedString = @undefined; + } else { + done = false; + + var first = string.@charCodeAt(position); + if (first < 0xD800 || first > 0xDBFF || position + 1 === length) + value = string[position]; + else { + var second = string.@charCodeAt(position + 1); + if (second < 0xDC00 || second > 0xDFFF) + value = string[position]; + else + value = string[position] + string[position + 1]; + } + + this.@stringIteratorNextIndex = position + value.length; + } + } + + return {done, value}; +} diff --git a/Source/JavaScriptCore/builtins/StringPrototype.js b/Source/JavaScriptCore/builtins/StringPrototype.js new file mode 100644 index 000000000..c9cdfcf02 --- /dev/null +++ b/Source/JavaScriptCore/builtins/StringPrototype.js @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2015 Andy VanWagoner <thetalecrafter@gmail.com>. + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com> + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +function match(regexp) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.match requires that |this| not be null or undefined"); + + if (regexp != null) { + var matcher = regexp.@matchSymbol; + if (matcher != @undefined) + return matcher.@call(regexp, this); + } + + let thisString = @toString(this); + let createdRegExp = @regExpCreate(regexp, @undefined); + return createdRegExp.@matchSymbol(thisString); +} + +@globalPrivate +function repeatSlowPath(string, count) +{ + "use strict"; + + // Return an empty string. + if (count === 0 || string.length === 0) + return ""; + + // Return the original string. + if (count === 1) + return string; + + if (string.length * count > @MAX_STRING_LENGTH) + @throwOutOfMemoryError(); + + // Bit operation onto |count| is safe because |count| should be within Int32 range, + // Repeat log N times to generate the repeated string rope. + var result = ""; + var operand = string; + while (true) { + if (count & 1) + result += operand; + count >>= 1; + if (!count) + return result; + operand += operand; + } +} + +@globalPrivate +function repeatCharactersSlowPath(string, count) +{ + "use strict"; + var repeatCount = (count / string.length) | 0; + var remainingCharacters = count - repeatCount * string.length; + var result = ""; + var operand = string; + // Bit operation onto |repeatCount| is safe because |repeatCount| should be within Int32 range, + // Repeat log N times to generate the repeated string rope. + while (true) { + if (repeatCount & 1) + result += operand; + repeatCount >>= 1; + if (!repeatCount) + break; + operand += operand; + } + if (remainingCharacters) + result += @stringSubstrInternal.@call(string, 0, remainingCharacters); + return result; +} + + +function repeat(count) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.repeat requires that |this| not be null or undefined"); + + var string = @toString(this); + count = @toInteger(count); + + if (count < 0 || count === @Infinity) + @throwRangeError("String.prototype.repeat argument must be greater than or equal to 0 and not be Infinity"); + + if (string.length === 1) + return @repeatCharacter(string, count); + + return @repeatSlowPath(string, count); +} + +function padStart(maxLength/*, fillString*/) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.padStart requires that |this| not be null or undefined"); + + var string = @toString(this); + maxLength = @toLength(maxLength); + + var stringLength = string.length; + if (maxLength <= stringLength) + return string; + + var filler; + var fillString = @argument(1); + if (fillString === @undefined) + filler = " "; + else { + filler = @toString(fillString); + if (filler === "") + return string; + } + + if (maxLength > @MAX_STRING_LENGTH) + @throwOutOfMemoryError(); + + var fillLength = maxLength - stringLength; + var truncatedStringFiller; + + if (filler.length === 1) + truncatedStringFiller = @repeatCharacter(filler, fillLength); + else + truncatedStringFiller = @repeatCharactersSlowPath(filler, fillLength); + return truncatedStringFiller + string; +} + +function padEnd(maxLength/*, fillString*/) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.padEnd requires that |this| not be null or undefined"); + + var string = @toString(this); + maxLength = @toLength(maxLength); + + var stringLength = string.length; + if (maxLength <= stringLength) + return string; + + var filler; + var fillString = @argument(1); + if (fillString === @undefined) + filler = " "; + else { + filler = @toString(fillString); + if (filler === "") + return string; + } + + if (maxLength > @MAX_STRING_LENGTH) + @throwOutOfMemoryError(); + + var fillLength = maxLength - stringLength; + var truncatedStringFiller; + + if (filler.length === 1) + truncatedStringFiller = @repeatCharacter(filler, fillLength); + else + truncatedStringFiller = @repeatCharactersSlowPath(filler, fillLength); + return string + truncatedStringFiller; +} + +@globalPrivate +function hasObservableSideEffectsForStringReplace(regexp, replacer) { + if (replacer !== @regExpPrototypeSymbolReplace) + return true; + + let regexpExec = @tryGetById(regexp, "exec"); + if (regexpExec !== @regExpBuiltinExec) + return true; + + let regexpGlobal = @tryGetById(regexp, "global"); + if (regexpGlobal !== @regExpProtoGlobalGetter) + return true; + + let regexpUnicode = @tryGetById(regexp, "unicode"); + if (regexpUnicode !== @regExpProtoUnicodeGetter) + return true; + + return !@isRegExpObject(regexp); +} + +@intrinsic=StringPrototypeReplaceIntrinsic +function replace(search, replace) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.replace requires that |this| not be null or undefined"); + + if (search != null) { + let replacer = search.@replaceSymbol; + if (replacer !== @undefined) { + if (!@hasObservableSideEffectsForStringReplace(search, replacer)) + return @toString(this).@replaceUsingRegExp(search, replace); + return replacer.@call(search, this, replace); + } + } + + let thisString = @toString(this); + let searchString = @toString(search); + return thisString.@replaceUsingStringSearch(searchString, replace); +} + +function localeCompare(that/*, locales, options */) +{ + "use strict"; + + // 13.1.1 String.prototype.localeCompare (that [, locales [, options ]]) (ECMA-402 2.0) + // http://ecma-international.org/publications/standards/Ecma-402.htm + + // 1. Let O be RequireObjectCoercible(this value). + if (this == null) + @throwTypeError("String.prototype.localeCompare requires that |this| not be null or undefined"); + + // 2. Let S be ToString(O). + // 3. ReturnIfAbrupt(S). + var thisString = @toString(this); + + // 4. Let That be ToString(that). + // 5. ReturnIfAbrupt(That). + var thatString = @toString(that); + + // Avoid creating a collator for defaults. + var locales = @argument(1); + var options = @argument(2); + if (locales === @undefined && options === @undefined) + return @Collator.prototype.compare(thisString, thatString); + + // 6. Let collator be Construct(%Collator%, «locales, options»). + // 7. ReturnIfAbrupt(collator). + var collator = new @Collator(locales, options); + + // 8. Return CompareStrings(collator, S, That). + return collator.compare(thisString, thatString); +} + +function search(regexp) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.search requires that |this| not be null or undefined"); + + if (regexp != null) { + var searcher = regexp.@searchSymbol; + if (searcher != @undefined) + return searcher.@call(regexp, this); + } + + var thisString = @toString(this); + var createdRegExp = @regExpCreate(regexp, @undefined); + return createdRegExp.@searchSymbol(thisString); +} + +function split(separator, limit) +{ + "use strict"; + + if (this == null) + @throwTypeError("String.prototype.split requires that |this| not be null or undefined"); + + if (separator != null) { + var splitter = separator.@splitSymbol; + if (splitter != @undefined) + return splitter.@call(separator, this, limit); + } + + return @stringSplitFast.@call(this, separator, limit); +} diff --git a/Source/JavaScriptCore/builtins/TypedArrayConstructor.js b/Source/JavaScriptCore/builtins/TypedArrayConstructor.js new file mode 100644 index 000000000..54a957b23 --- /dev/null +++ b/Source/JavaScriptCore/builtins/TypedArrayConstructor.js @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// According to the spec we are supposed to crawl the prototype chain looking +// for the a TypedArray constructor. The way we implement this is with a +// private function, @alloctateTypedArray, on each of the prototypes. +// This enables us to optimize this lookup in the inline cache. + +function of(/* items... */) +{ + "use strict"; + let len = arguments.length; + let constructFunction = this.@allocateTypedArray; + if (constructFunction === @undefined) + @throwTypeError("TypedArray.of requires its this argument to subclass a TypedArray constructor"); + + let result = constructFunction(len); + + for (let i = 0; i < len; i++) + result[i] = arguments[i]; + + return result; +} + +function from(items /* [ , mapfn [ , thisArg ] ] */) +{ + "use strict"; + + let mapFn = @argument(1); + + let thisArg; + + if (mapFn !== @undefined) { + if (typeof mapFn !== "function") + @throwTypeError("TypedArray.from requires that the second argument, when provided, be a function"); + + thisArg = @argument(2); + } + + if (items == null) + @throwTypeError("TypedArray.from requires an array-like object - not null or undefined"); + + let iteratorMethod = items.@iteratorSymbol; + if (iteratorMethod != null) { + if (typeof iteratorMethod !== "function") + @throwTypeError("TypedArray.from requires that the property of the first argument, items[Symbol.iterator], when exists, be a function"); + + let accumulator = []; + + let k = 0; + let iterator = iteratorMethod.@call(items); + + // Since for-of loop once more looks up the @@iterator property of a given iterable, + // it could be observable if the user defines a getter for @@iterator. + // To avoid this situation, we define a wrapper object that @@iterator just returns a given iterator. + let wrapper = {}; + wrapper.@iteratorSymbol = function() { return iterator; } + + for (let value of wrapper) { + if (mapFn) + @putByValDirect(accumulator, k, thisArg === @undefined ? mapFn(value, k) : mapFn.@call(thisArg, value, k)); + else + @putByValDirect(accumulator, k, value); + k++; + } + + let constructFunction = this.@allocateTypedArray; + if (constructFunction === @undefined) + @throwTypeError("TypedArray.from requires its this argument subclass a TypedArray constructor"); + + let result = constructFunction(k); + + for (let i = 0; i < k; i++) + result[i] = accumulator[i]; + + + return result; + } + + let arrayLike = @Object(items); + let arrayLikeLength = @toLength(arrayLike.length); + + let constructFunction = this.@allocateTypedArray; + if (constructFunction === @undefined) + @throwTypeError("this does not subclass a TypedArray constructor"); + + let result = constructFunction(arrayLikeLength); + + let k = 0; + while (k < arrayLikeLength) { + let value = arrayLike[k]; + if (mapFn) + result[k] = thisArg === @undefined ? mapFn(value, k) : mapFn.@call(thisArg, value, k); + else + result[k] = value; + k++; + } + + return result; +} + +function allocateInt8Array(length) +{ + return new @Int8Array(length); +} + +function allocateInt16Array(length) +{ + return new @Int16Array(length); +} + +function allocateInt32Array(length) +{ + return new @Int32Array(length); +} + +function allocateUint32Array(length) +{ + return new @Uint32Array(length); +} + +function allocateUint16Array(length) +{ + return new @Uint16Array(length); +} + +function allocateUint8Array(length) +{ + return new @Uint8Array(length); +} + +function allocateUint8ClampedArray(length) +{ + return new @Uint8ClampedArray(length); +} + +function allocateFloat32Array(length) +{ + return new @Float32Array(length); +} + +function allocateFloat64Array(length) +{ + return new @Float64Array(length); +} diff --git a/Source/JavaScriptCore/builtins/TypedArrayPrototype.js b/Source/JavaScriptCore/builtins/TypedArrayPrototype.js new file mode 100644 index 000000000..53674bf7b --- /dev/null +++ b/Source/JavaScriptCore/builtins/TypedArrayPrototype.js @@ -0,0 +1,413 @@ +/* + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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. + */ + +// Note that the intrisic @typedArrayLength checks that the argument passed is a typed array +// and throws if it is not. + + +// Typed Arrays have their own species constructor function since they need +// to look up their default constructor, which is expensive. If we used the +// normal speciesConstructor helper we would need to look up the default +// constructor every time. +@globalPrivate +function typedArraySpeciesConstructor(value) +{ + "use strict"; + let constructor = value.constructor; + if (constructor === @undefined) + return @typedArrayGetOriginalConstructor(value); + + if (!@isObject(constructor)) + @throwTypeError("|this|.constructor is not an Object or undefined"); + + constructor = constructor.@speciesSymbol; + if (constructor == null) + return @typedArrayGetOriginalConstructor(value); + // The lack of an @isConstructor(constructor) check here is not observable because + // the first thing we will do with the value is attempt to construct the result with it. + // If any user of this function does not immediately construct the result they need to + // verify that the result is a constructor. + return constructor; +} + +@globalPrivate +function typedArrayClampArgumentToStartOrEnd(value, length, undefinedValue) +{ + "use strict"; + + if (value === @undefined) + return undefinedValue; + + let int = @toInteger(value); + if (int < 0) { + int += length; + return int < 0 ? 0 : int; + } + return int > length ? length : int; +} + +function values() +{ + "use strict"; + @typedArrayLength(this); + return new @createArrayIterator(this, "value", @arrayIteratorValueNext); +} + +function keys() +{ + "use strict"; + @typedArrayLength(this); + return new @createArrayIterator(this, "key", @arrayIteratorKeyNext); +} + +function entries() +{ + "use strict"; + @typedArrayLength(this); + return new @createArrayIterator(this, "key+value", @arrayIteratorKeyValueNext); +} + +function every(callback /*, thisArg */) +{ + "use strict"; + var length = @typedArrayLength(this); + var thisArg = @argument(1); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.every callback must be a function"); + + for (var i = 0; i < length; i++) { + if (!callback.@call(thisArg, this[i], i, this)) + return false; + } + + return true; +} + +function fill(value /* [, start [, end]] */) +{ + "use strict"; + + let length = @typedArrayLength(this); + + let start = @argument(1); + let end = @argument(2); + + start = @typedArrayClampArgumentToStartOrEnd(start, length, 0); + end = @typedArrayClampArgumentToStartOrEnd(end, length, length); + + for (let i = start; i < end; i++) + this[i] = value; + return this; +} + +function find(callback /* [, thisArg] */) +{ + "use strict"; + var length = @typedArrayLength(this); + var thisArg = @argument(1); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.find callback must be a function"); + + for (var i = 0; i < length; i++) { + let elem = this[i]; + if (callback.@call(thisArg, elem, i, this)) + return elem; + } + return @undefined; +} + +function findIndex(callback /* [, thisArg] */) +{ + "use strict"; + var length = @typedArrayLength(this); + var thisArg = @argument(1); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.findIndex callback must be a function"); + + for (var i = 0; i < length; i++) { + if (callback.@call(thisArg, this[i], i, this)) + return i; + } + return -1; +} + +function forEach(callback /* [, thisArg] */) +{ + "use strict"; + var length = @typedArrayLength(this); + var thisArg = @argument(1); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.forEach callback must be a function"); + + for (var i = 0; i < length; i++) + callback.@call(thisArg, this[i], i, this); +} + +function some(callback /* [, thisArg] */) +{ + // 22.2.3.24 + "use strict"; + var length = @typedArrayLength(this); + var thisArg = @argument(1); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.some callback must be a function"); + + for (var i = 0; i < length; i++) { + if (callback.@call(thisArg, this[i], i, this)) + return true; + } + + return false; +} + +function sort(comparator) +{ + // 22.2.3.25 + "use strict"; + + function min(a, b) + { + return a < b ? a : b; + } + + function merge(dst, src, srcIndex, srcEnd, width, comparator) + { + var left = srcIndex; + var leftEnd = min(left + width, srcEnd); + var right = leftEnd; + var rightEnd = min(right + width, srcEnd); + + for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) { + if (right < rightEnd) { + if (left >= leftEnd || comparator(src[right], src[left]) < 0) { + dst[dstIndex] = src[right++]; + continue; + } + } + + dst[dstIndex] = src[left++]; + } + } + + function mergeSort(array, valueCount, comparator) + { + var buffer = [ ]; + buffer.length = valueCount; + + var dst = buffer; + var src = array; + + for (var width = 1; width < valueCount; width *= 2) { + for (var srcIndex = 0; srcIndex < valueCount; srcIndex += 2 * width) + merge(dst, src, srcIndex, valueCount, width, comparator); + + var tmp = src; + src = dst; + dst = tmp; + } + + if (src != array) { + for(var i = 0; i < valueCount; i++) + array[i] = src[i]; + } + } + + var length = @typedArrayLength(this); + + if (length < 2) + return; + + if (typeof comparator == "function") + mergeSort(this, length, comparator); + else + @typedArraySort(this); + + return this; +} + +function subarray(begin, end) +{ + "use strict"; + + if (!@isTypedArrayView(this)) + @throwTypeError("|this| should be a typed array view"); + + let start = @toInteger(begin); + let finish; + if (end !== @undefined) + finish = @toInteger(end); + + let constructor = @typedArraySpeciesConstructor(this); + + return @typedArraySubarrayCreate.@call(this, start, finish, constructor); +} + +function reduce(callback /* [, initialValue] */) +{ + // 22.2.3.19 + "use strict"; + + var length = @typedArrayLength(this); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.reduce callback must be a function"); + + var argumentCount = @argumentCount(); + if (length === 0 && argumentCount < 2) + @throwTypeError("TypedArray.prototype.reduce of empty array with no initial value"); + + var accumulator, k = 0; + if (argumentCount > 1) + accumulator = @argument(1); + else + accumulator = this[k++]; + + for (; k < length; k++) + accumulator = callback.@call(@undefined, accumulator, this[k], k, this); + + return accumulator; +} + +function reduceRight(callback /* [, initialValue] */) +{ + // 22.2.3.20 + "use strict"; + + var length = @typedArrayLength(this); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.reduceRight callback must be a function"); + + var argumentCount = @argumentCount(); + if (length === 0 && argumentCount < 2) + @throwTypeError("TypedArray.prototype.reduceRight of empty array with no initial value"); + + var accumulator, k = length - 1; + if (argumentCount > 1) + accumulator = @argument(1); + else + accumulator = this[k--]; + + for (; k >= 0; k--) + accumulator = callback.@call(@undefined, accumulator, this[k], k, this); + + return accumulator; +} + +function map(callback /*, thisArg */) +{ + // 22.2.3.18 + "use strict"; + + var length = @typedArrayLength(this); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.map callback must be a function"); + + var thisArg = @argument(1); + + // Do species construction + var constructor = this.constructor; + var result; + if (constructor === @undefined) + result = new (@typedArrayGetOriginalConstructor(this))(length); + else { + var speciesConstructor = @Object(constructor).@speciesSymbol; + if (speciesConstructor === null || speciesConstructor === @undefined) + result = new (@typedArrayGetOriginalConstructor(this))(length); + else { + result = new speciesConstructor(length); + // typedArrayLength throws if it doesn't get a view. + @typedArrayLength(result); + } + } + + for (var i = 0; i < length; i++) { + var mappedValue = callback.@call(thisArg, this[i], i, this); + result[i] = mappedValue; + } + return result; +} + +function filter(callback /*, thisArg */) +{ + "use strict"; + + var length = @typedArrayLength(this); + + if (typeof callback !== "function") + @throwTypeError("TypedArray.prototype.filter callback must be a function"); + + var thisArg = @argument(1); + var kept = []; + + for (var i = 0; i < length; i++) { + var value = this[i]; + if (callback.@call(thisArg, value, i, this)) + kept.@push(value); + } + + var constructor = this.constructor; + var result; + var resultLength = kept.length; + if (constructor === @undefined) + result = new (@typedArrayGetOriginalConstructor(this))(resultLength); + else { + var speciesConstructor = @Object(constructor).@speciesSymbol; + if (speciesConstructor === null || speciesConstructor === @undefined) + result = new (@typedArrayGetOriginalConstructor(this))(resultLength); + else { + result = new speciesConstructor(resultLength); + // typedArrayLength throws if it doesn't get a view. + @typedArrayLength(result); + } + } + + for (var i = 0; i < kept.length; i++) + result[i] = kept[i]; + + return result; +} + +function toLocaleString() +{ + "use strict"; + + var length = @typedArrayLength(this); + + if (length == 0) + return ""; + + var string = this[0].toLocaleString(); + for (var i = 1; i < length; i++) + string += "," + this[i].toLocaleString(); + + return string; +} |