diff --git a/js/js.translator/src/org/jetbrains/k2js/config/Config.java b/js/js.translator/src/org/jetbrains/k2js/config/Config.java index c0a12bcd31b..641799d113b 100644 --- a/js/js.translator/src/org/jetbrains/k2js/config/Config.java +++ b/js/js.translator/src/org/jetbrains/k2js/config/Config.java @@ -36,8 +36,6 @@ import java.util.List; * Base class representing a configuration of translator. */ public abstract class Config { - - //NOTE: hard-coded in kotlin-lib files //NOTE: a hacky solution to be able to rerun code samples with lib loaded only once: used by tests and web demo @NotNull public static final String REWRITABLE_MODULE_NAME = "JS_TESTS"; diff --git a/js/js.translator/testFiles/kotlin_lib.js b/js/js.translator/testFiles/kotlin_lib.js index ee941e792f3..f45cc244185 100644 --- a/js/js.translator/testFiles/kotlin_lib.js +++ b/js/js.translator/testFiles/kotlin_lib.js @@ -14,19 +14,36 @@ * limitations under the License. */ +"use strict"; + +// todo inlined +String.prototype.startsWith = function (s) { + return this.indexOf(s) === 0; +}; + +String.prototype.endsWith = function (s) { + return this.indexOf(s, this.length - s.length) !== -1; +}; + +String.prototype.contains = function (s) { + return this.indexOf(s) !== -1; +}; + // todo org.jetbrains.k2js.test.semantics.WebDemoExamples2Test#testBuilder var kotlin = {set:function (receiver, key, value) { return receiver.put(key, value); }}; (function () { - "use strict"; - Kotlin.equals = function (obj1, obj2) { - if ((obj1 === null) || (obj1 === undefined)) return obj2 === null; + if (obj1 === null || obj1 === undefined) { + return obj2 === null; + } + if (obj1 instanceof Array) { - if (!(obj2 instanceof Array)) return false; - if (obj1.length != obj2.length) return false; + if (!(obj2 instanceof Array) || obj1.length != obj2.length) { + return false; + } for (var i = 0; i < obj1.length; i++) { if (!Kotlin.equals(obj1[i], obj2[i])) { return false; @@ -34,22 +51,16 @@ var kotlin = {set:function (receiver, key, value) { } return true; } - if (typeof obj1 == "object") { - if (obj1.equals !== undefined) { - return obj1.equals(obj2); - } + + if (typeof obj1 == "object" && obj1.equals !== undefined) { + return obj1.equals(obj2); } + return obj1 === obj2; }; Kotlin.array = function (args) { - var answer = []; - if (args !== null && args !== undefined) { - for (var i = 0, n = args.length; i < n; ++i) { - answer[i] = args[i] - } - } - return answer; + return args === null || args === undefined ? [] : args.slice(); }; Kotlin.intUpto = function (from, limit) { @@ -61,20 +72,20 @@ var kotlin = {set:function (receiver, key, value) { }; Kotlin.modules = {}; - Kotlin.Exceptions = {}; + Kotlin.Exception = Kotlin.$createClass(); Kotlin.RuntimeException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.IndexOutOfBounds = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.NullPointerException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.NoSuchElementException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.IllegalArgumentException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.IllegalStateException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.IndexOutOfBoundsException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.UnsupportedOperationException = Kotlin.$createClass(Kotlin.Exception); - Kotlin.Exceptions.IOException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.IndexOutOfBounds = Kotlin.$createClass(Kotlin.Exception); + Kotlin.NullPointerException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.NoSuchElementException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.IllegalArgumentException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.IllegalStateException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.IndexOutOfBoundsException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.UnsupportedOperationException = Kotlin.$createClass(Kotlin.Exception); + Kotlin.IOException = Kotlin.$createClass(Kotlin.Exception); Kotlin.throwNPE = function () { - throw Kotlin.$new(Kotlin.Exceptions.NullPointerException)(); + throw Kotlin.$new(Kotlin.NullPointerException)(); }; function throwAbstractFunctionInvocationError(funName) { @@ -93,7 +104,8 @@ var kotlin = {set:function (receiver, key, value) { initialize: function () { }, next: throwAbstractFunctionInvocationError("Iterator#next"), - get_hasNext: throwAbstractFunctionInvocationError("Iterator#get_hasNext") + get_hasNext: throwAbstractFunctionInvocationError("Iterator#get_hasNext"), + hasNext: throwAbstractFunctionInvocationError("Iterator#hasNext") }); var ArrayIterator = Kotlin.$createClass(Kotlin.Iterator, { @@ -107,6 +119,9 @@ var kotlin = {set:function (receiver, key, value) { }, get_hasNext: function () { return this.index < this.size; + }, + hasNext: function () { + return this.index < this.size; } }); @@ -124,52 +139,6 @@ var kotlin = {set:function (receiver, key, value) { } }); - Kotlin.AbstractCollection = Kotlin.$createClass({ - isEmpty: function () { - return this.size() == 0; - }, - addAll: function (collection) { - var it = collection.iterator(); - while (it.get_hasNext()) { - this.add(it.next()); - } - }, - equals: function (o) { - if (o === null || o === undefined) return false; - if (this.size() === o.size()) { - var iter1 = this.iterator(); - var iter2 = o.iterator(); - while (true) { - var hn1 = iter1.get_hasNext(); - var hn2 = iter2.get_hasNext(); - if (hn1 != hn2) return false; - if (!hn2) - return true; - else { - var o1 = iter1.next(); - var o2 = iter2.next(); - if (!Kotlin.equals(o1, o2)) return false; - } - } - } - return false; - }, - toString: function() { - var builder = "["; - var iter = this.iterator(); - var first = true; - while (iter.get_hasNext()) { - if (first) - first = false; - else - builder += ", "; - builder += iter.next(); - } - builder += "]"; - return builder; - } - }); - Kotlin.Collection = Kotlin.$createClass(); Kotlin.AbstractList = Kotlin.$createClass(Kotlin.Collection, { @@ -229,6 +198,7 @@ var kotlin = {set:function (receiver, key, value) { return this.toArray(); } }); + Kotlin.ArrayList = Kotlin.$createClass(Kotlin.AbstractList, { initialize: function () { this.array = []; @@ -328,7 +298,7 @@ var kotlin = {set:function (receiver, key, value) { }; Kotlin.safeParseDouble = function(str) { - var r = parseFloat(str, 10); + var r = parseFloat(str); return isNaN(r) ? null : r; }; @@ -351,16 +321,16 @@ var kotlin = {set:function (receiver, key, value) { }; return { - out:function () { + out: function () { return { - print:print, - println:println + print: print, + println: println }; }, - output:function () { + output: function () { return output; }, - flush:function () { + flush: function () { output = ""; } }; @@ -506,12 +476,11 @@ var kotlin = {set:function (receiver, key, value) { }; Kotlin.arrayFromFun = function (size, initFun) { - var res = []; - var i = size; - while (i > 0) { - res[--i] = initFun(i); + var result = new Array(size); + for (var i = 0; i < size; i++) { + result[i] = initFun(i); } - return res; + return result; }; Kotlin.arrayIndices = function (arr) { @@ -560,502 +529,51 @@ var kotlin = {set:function (receiver, key, value) { return obj; }; - (function () { - var FUNCTION = "function"; - - var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ? - function (arr, idx) { - arr.splice(idx, 1); - } : - - function (arr, idx) { - var itemsAfterDeleted, i, len; - if (idx === arr.length - 1) { - arr.length = idx; - } - else { - itemsAfterDeleted = arr.slice(idx + 1); - arr.length = idx; - for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) { - arr[idx + i] = itemsAfterDeleted[i]; - } - } - }; - - function hashObject(obj) { - var hashCode; - if (typeof obj == "string") { - return obj; - } else if (typeof obj.hashCode == FUNCTION) { - // Check the hashCode method really has returned a string - hashCode = obj.hashCode(); - return (typeof hashCode == "string") ? hashCode : hashObject(hashCode); - } else if (typeof obj.toString == FUNCTION) { - return obj.toString(); - } - else { - try { - return String(obj); - } catch (ex) { - // For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when - // passed to String() - return Object.prototype.toString.call(obj); - } - } + // native concat doesn't work for arguments + Kotlin.concat = function (a, b) { + var r = new Array(a.length + b.length); + var i = 0; + var n = a.length; + for (; i < n; i++) { + r[i] = a[i]; } - - function equals_fixedValueHasEquals(fixedValue, variableValue) { - return fixedValue.equals(variableValue); + n = b.length; + for (var j = 0; j < n;) { + r[i++] = b[j++]; } - - function equals_fixedValueNoEquals(fixedValue, variableValue) { - return (typeof variableValue.equals == FUNCTION) ? - variableValue.equals(fixedValue) : (fixedValue === variableValue); - } - - function createKeyValCheck(kvStr) { - return function (kv) { - if (kv === null) { - throw new Error("null is not a valid " + kvStr); - } else if (typeof kv == "undefined") { - throw new Error(kvStr + " must not be undefined"); - } - }; - } - - var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); - - /*----------------------------------------------------------------------------------------------------------------*/ - - function Bucket(hash, firstKey, firstValue, equalityFunction) { - this[0] = hash; - this.entries = []; - this.addEntry(firstKey, firstValue); - - if (equalityFunction !== null) { - this.getEqualityFunction = function () { - return equalityFunction; - }; - } - } - - var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; - - function createBucketSearcher(mode) { - return function (key) { - var i = this.entries.length, entry, equals = this.getEqualityFunction(key); - while (i--) { - entry = this.entries[i]; - if (equals(key, entry[0])) { - switch (mode) { - case EXISTENCE: - return true; - case ENTRY: - return entry; - case ENTRY_INDEX_AND_VALUE: - return [ i, entry[1] ]; - } - } - } - return false; - }; - } - - function createBucketLister(entryProperty) { - return function (aggregatedArr) { - var startIndex = aggregatedArr.length; - for (var i = 0, len = this.entries.length; i < len; ++i) { - aggregatedArr[startIndex + i] = this.entries[i][entryProperty]; - } - }; - } - - Bucket.prototype = { - getEqualityFunction:function (searchValue) { - return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; - }, - - getEntryForKey:createBucketSearcher(ENTRY), - - getEntryAndIndexForKey:createBucketSearcher(ENTRY_INDEX_AND_VALUE), - - removeEntryForKey:function (key) { - var result = this.getEntryAndIndexForKey(key); - if (result) { - arrayRemoveAt(this.entries, result[0]); - return result[1]; - } - return null; - }, - - addEntry:function (key, value) { - this.entries[this.entries.length] = [key, value]; - }, - - keys:createBucketLister(0), - - values:createBucketLister(1), - - getEntries:function (entries) { - var startIndex = entries.length; - for (var i = 0, len = this.entries.length; i < len; ++i) { - // Clone the entry stored in the bucket before adding to array - entries[startIndex + i] = this.entries[i].slice(0); - } - }, - - containsKey:createBucketSearcher(EXISTENCE), - - containsValue:function (value) { - var i = this.entries.length; - while (i--) { - if (value === this.entries[i][1]) { - return true; - } - } - return false; - } - }; - - /*----------------------------------------------------------------------------------------------------------------*/ - - // Supporting functions for searching hashtable buckets - - function searchBuckets(buckets, hash) { - var i = buckets.length, bucket; - while (i--) { - bucket = buckets[i]; - if (hash === bucket[0]) { - return i; - } - } - return null; - } - - function getBucketForHash(bucketsByHash, hash) { - var bucket = bucketsByHash[hash]; - - // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype - return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; - } - - /*----------------------------------------------------------------------------------------------------------------*/ - - var Hashtable = function (hashingFunctionParam, equalityFunctionParam) { - var that = this; - var buckets = []; - var bucketsByHash = {}; - - var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject; - var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null; - - this.put = function (key, value) { - checkKey(key); - checkValue(value); - var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null; - - // Check if a bucket exists for the bucket key - bucket = getBucketForHash(bucketsByHash, hash); - if (bucket) { - // Check this bucket to see if it already contains this key - bucketEntry = bucket.getEntryForKey(key); - if (bucketEntry) { - // This bucket entry is the current mapping of key to value, so replace old value and we're done. - oldValue = bucketEntry[1]; - bucketEntry[1] = value; - } - else { - // The bucket does not contain an entry for this key, so add one - bucket.addEntry(key, value); - } - } - else { - // No bucket exists for the key, so create one and put our key/value mapping in - bucket = new Bucket(hash, key, value, equalityFunction); - buckets[buckets.length] = bucket; - bucketsByHash[hash] = bucket; - } - return oldValue; - }; - - this.get = function (key) { - checkKey(key); - - var hash = hashingFunction(key); - - // Check if a bucket exists for the bucket key - var bucket = getBucketForHash(bucketsByHash, hash); - if (bucket) { - // Check this bucket to see if it contains this key - var bucketEntry = bucket.getEntryForKey(key); - if (bucketEntry) { - // This bucket entry is the current mapping of key to value, so return the value. - return bucketEntry[1]; - } - } - return null; - }; - - this.containsKey = function (key) { - checkKey(key); - var bucketKey = hashingFunction(key); - - // Check if a bucket exists for the bucket key - var bucket = getBucketForHash(bucketsByHash, bucketKey); - - return bucket ? bucket.containsKey(key) : false; - }; - - this.containsValue = function (value) { - checkValue(value); - var i = buckets.length; - while (i--) { - if (buckets[i].containsValue(value)) { - return true; - } - } - return false; - }; - - this.clear = function () { - buckets.length = 0; - bucketsByHash = {}; - }; - - this.isEmpty = function () { - return !buckets.length; - }; - - var createBucketAggregator = function (bucketFuncName) { - return function () { - var aggregated = [], i = buckets.length; - while (i--) { - buckets[i][bucketFuncName](aggregated); - } - return aggregated; - }; - }; - - this._keys = createBucketAggregator("keys"); - this._values = createBucketAggregator("values"); - this._entries = createBucketAggregator("getEntries"); - - this.values = function() { - var values = this._values(); - var i = values.length - var result = Kotlin.$new(Kotlin.ArrayList)(); - while (--i) { - result.add(values[i]); - } - return result; - }; - - this.remove = function (key) { - checkKey(key); - - var hash = hashingFunction(key), bucketIndex, oldValue = null; - - // Check if a bucket exists for the bucket key - var bucket = getBucketForHash(bucketsByHash, hash); - - if (bucket) { - // Remove entry from this bucket for this key - oldValue = bucket.removeEntryForKey(key); - if (oldValue !== null) { - // Entry was removed, so check if bucket is empty - if (!bucket.entries.length) { - // Bucket is empty, so remove it from the bucket collections - bucketIndex = searchBuckets(buckets, hash); - arrayRemoveAt(buckets, bucketIndex); - delete bucketsByHash[hash]; - } - } - } - return oldValue; - }; - - this.size = function () { - var total = 0, i = buckets.length; - while (i--) { - total += buckets[i].entries.length; - } - return total; - }; - - this.each = function (callback) { - var entries = that.entries(), i = entries.length, entry; - while (i--) { - entry = entries[i]; - callback(entry[0], entry[1]); - } - }; - - - this.putAll = function (hashtable, conflictCallback) { - var entries = hashtable.entries(); - var entry, key, value, thisValue, i = entries.length; - var hasConflictCallback = (typeof conflictCallback == FUNCTION); - while (i--) { - entry = entries[i]; - key = entry[0]; - value = entry[1]; - - // Check for a conflict. The default behaviour is to overwrite the value for an existing key - if (hasConflictCallback && (thisValue = that.get(key))) { - value = conflictCallback(key, thisValue, value); - } - that.put(key, value); - } - }; - - this.clone = function () { - var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam); - clone.putAll(that); - return clone; - }; - - this.keySet = function () { - var res = Kotlin.$new(Kotlin.HashSet)(); - var keys = this._keys(); - var i = keys.length; - while (i--) { - res.add(keys[i]); - } - return res; - }; - }; - - Kotlin.HashTable = Hashtable; - })(); - - Kotlin.HashMap = Kotlin.$createClass({ - initialize: function () { - Kotlin.HashTable.call(this); - } - }); - - (function () { - function HashSet(hashingFunction, equalityFunction) { - var hashTable = new Kotlin.HashTable(hashingFunction, equalityFunction); - - this.add = function (o) { - hashTable.put(o, true); - }; - - this.addAll = function (arr) { - var i = arr.length; - while (i--) { - hashTable.put(arr[i], true); - } - }; - - this.values = function () { - return hashTable._keys(); - }; - - this.iterator = function () { - return Kotlin.arrayIterator(this.values()); - }; - - this.remove = function (o) { - return hashTable.remove(o) ? o : null; - }; - - this.contains = function (o) { - return hashTable.containsKey(o); - }; - - this.clear = function () { - hashTable.clear(); - }; - - this.size = function () { - return hashTable.size(); - }; - - this.isEmpty = function () { - return hashTable.isEmpty(); - }; - - this.clone = function () { - var h = new HashSet(hashingFunction, equalityFunction); - h.addAll(hashTable.keys()); - return h; - }; - - this.equals = function (o) { - if (o === null || o === undefined) return false; - if (this.size() === o.size()) { - var iter1 = this.iterator(); - var iter2 = o.iterator(); - while (true) { - var hn1 = iter1.get_hasNext(); - var hn2 = iter2.get_hasNext(); - if (hn1 != hn2) return false; - if (!hn2) - return true; - else { - var o1 = iter1.next(); - var o2 = iter2.next(); - if (!Kotlin.equals(o1, o2)) return false; - } - } - } - return false; - }; - - this.toString = function() { - var builder = "["; - var iter = this.iterator(); - var first = true; - while (iter.get_hasNext()) { - if (first) - first = false; - else - builder += ", "; - builder += iter.next(); - } - builder += "]"; - return builder; - }; - - this.intersection = function (hashSet) { - var intersection = new HashSet(hashingFunction, equalityFunction); - var values = hashSet.values(), i = values.length, val; - while (i--) { - val = values[i]; - if (hashTable.containsKey(val)) { - intersection.add(val); - } - } - return intersection; - }; - - this.union = function (hashSet) { - var union = this.clone(); - var values = hashSet.values(), i = values.length, val; - while (i--) { - val = values[i]; - if (!hashTable.containsKey(val)) { - union.add(val); - } - } - return union; - }; - - this.isSubsetOf = function (hashSet) { - var values = hashTable.keys(), i = values.length; - while (i--) { - if (!hashSet.contains(values[i])) { - return false; - } - } - return true; - }; - } - - Kotlin.HashSet = Kotlin.$createClass({initialize: function () { - HashSet.call(this); - }}); - }()); + return r; + } })(); + +Kotlin.assignOwner = function(f, o) { + f.o = o; + return f; +}; + +// we cannot use Function.bind, because if we bind with null self, but call with not null — fun must receive passed not null self +// test case: WebDemoExamples2Test.testBuilder +Kotlin.b0 = function (f, self, value) { + return function () { + return f.call(self !== null ? self : this, value); + } +}; +Kotlin.b1 = function (f, self, values) { + return function () { + return f.apply(self !== null ? self : this, values); + } +}; +Kotlin.b2 = function (f, self, values) { + return function () { + return f.apply(self !== null ? self : this, Kotlin.concat(values, arguments)); + } +}; +Kotlin.b3 = function (f, self) { + return function () { + return f.call(self) + } +}; +Kotlin.b4 = function (f, self) { + return function () { + return f.apply(self, Kotlin.argumentsToArrayLike(arguments)); + } +}; diff --git a/js/js.translator/testFiles/kotlin_lib_ecma3.js b/js/js.translator/testFiles/kotlin_lib_ecma3.js index 2ec6e085be0..ded8f10e831 100644 --- a/js/js.translator/testFiles/kotlin_lib_ecma3.js +++ b/js/js.translator/testFiles/kotlin_lib_ecma3.js @@ -1,10 +1,10 @@ -/* Prototype JavaScript framework, version 1.6.1 -* (c) 2005-2009 Sam Stephenson -* -* Prototype is freely distributable under the terms of an MIT-style license. -* For details, see the Prototype web site: http://www.prototypejs.org/ -* -*--------------------------------------------------------------------------*/ +/* Prototype JavaScript framework, version 1.6.1 + * (c) 2005-2009 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ var Kotlin = {}; (function () { @@ -12,6 +12,17 @@ var Kotlin = {}; var emptyFunction = function () { }; + Kotlin.keys = Object.keys || function (o) { + var result = []; + var i = 0; + for (var p in o) { + if (o.hasOwnProperty(p)) { + result[i++] = p; + } + } + return result; + }; + Kotlin.argumentsToArrayLike = function (args) { var n = args.length; var result = new Array(n); @@ -21,80 +32,16 @@ var Kotlin = {}; return result; }; - (function () { - function extend(destination, source) { - for (var property in source) { - destination[property] = source[property]; + function copyProperties(to, from) { + for (var p in from) { + if (from.hasOwnProperty(p)) { + to[p] = from[p]; } - return destination; } - - function keys(object) { - var results = []; - for (var property in object) { - if (object.hasOwnProperty(property)) { - results.push(property); - } - } - return results; - } - - function values(object) { - var results = []; - for (var property in object) { - results.push(object[property]); - } - return results; - } - - extend(Object, { - extend: extend, - keys: Object.keys || keys, - values: values - }); - })(); - - Object.extend(Function.prototype, (function () { - var slice = Array.prototype.slice; - - function update(array, args) { - var arrayLength = array.length, length = args.length; - while (length--) array[arrayLength + length] = args[length]; - return array; - } - - function argumentNames() { - var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] - .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') - .replace(/\s+/g, '').split(','); - return names.length == 1 && !names[0] ? [] : names; - } - - function bindAsEventListener(context) { - var __method = this, args = slice.call(arguments, 1); - return function (event) { - var a = update([event || window.event], args); - return __method.apply(context, a); - }; - } - - function wrap(wrapper) { - var __method = this; - return function () { - var a = update([__method.bind(this)], arguments); - return wrapper.apply(this, a); - }; - } - - return { - argumentNames: argumentNames, - bindAsEventListener: bindAsEventListener, - wrap: wrap - }; - })()); + } Kotlin.isType = function (object, klass) { - if (object === null) { + if (object === null || object === undefined) { return false; } @@ -109,28 +56,20 @@ var Kotlin = {}; }; Kotlin.createTrait = (function () { - function add(object, source) { - var properties = Object.keys(source); - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i]; - object[property] = source[property]; - } - } - return function () { var result = arguments[0]; for (var i = 1, n = arguments.length; i < n; i++) { - add(result, arguments[i]); + copyProperties(result, arguments[i]); } return result; } })(); - Kotlin.definePackage = Kotlin.createTrait; + Kotlin.definePackage = function (members) { + return members === null ? {} : members; + }; Kotlin.createClass = (function () { - var METHODS = {addMethods: addMethods}; - function subclass() { } @@ -147,7 +86,7 @@ var Kotlin = {}; } } - Object.extend(klass, METHODS); + klass.addMethods = addMethods; klass.superclass = parent; klass.subclasses = []; @@ -157,21 +96,15 @@ var Kotlin = {}; parent.subclasses.push(klass); } - klass.addMethods( - { - get_class: function () { - return klass; - } - }); + klass.addMethods({get_class: function () { + return klass; + }}); if (parent !== null) { - klass.addMethods( - { - super_init: function () { - this.initializing = this.initializing.superclass; - this.initializing.prototype.initialize.apply(this, arguments); - } - }); + klass.addMethods({super_init: function () { + this.initializing = this.initializing.superclass; + this.initializing.prototype.initialize.apply(this, arguments); + }}); } for (var i = 0, length = properties.length; i < length; i++) { @@ -187,25 +120,7 @@ var Kotlin = {}; } function addMethods(source) { - var ancestor = this.superclass && this.superclass.prototype, - properties = Object.keys(source); - - - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && (typeof (value) == "function") && - value.argumentNames()[0] == "$super") { - var method = value; - value = (function (m) { - return function () { - return ancestor[m].apply(this, arguments); - }; - })(property).wrap(method); - - } - this.prototype[property] = value; - } - + copyProperties(this.prototype, source); return this; } @@ -228,10 +143,10 @@ var Kotlin = {}; }; Kotlin.defineModule = function (id, module) { - if ((id in Kotlin.modules) && (id !== "JS_TESTS")) { - throw Kotlin.$new(Kotlin.Exceptions.IllegalArgumentException)(); + if (id in Kotlin.modules) { + throw Kotlin.$new(Kotlin.IllegalArgumentException)(); } Kotlin.modules[id] = module; }; -})(); \ No newline at end of file +})(); diff --git a/js/js.translator/testFiles/kotlin_lib_ecma5.js b/js/js.translator/testFiles/kotlin_lib_ecma5.js index aee4fbe3c72..bee9e704943 100644 --- a/js/js.translator/testFiles/kotlin_lib_ecma5.js +++ b/js/js.translator/testFiles/kotlin_lib_ecma5.js @@ -1,19 +1,26 @@ // Be aware — Google Chrome has serious issue — you can rewrite READ-ONLY property (if it is defined in prototype). Firefox and Safari work correct. // Always test property access issues in Firefox, but not in Chrome. -var Kotlin = {}; +var Kotlin = Object.create(null); (function () { "use strict"; - Kotlin.isType = function (object, klass) { - if (object === null) { + // ecma5 is still sucks — concat doesn't accept arguments, but apply does, so, we just return arguments + Kotlin.argumentsToArrayLike = function (args) { + return args; + }; + + Kotlin.keys = Object.keys; + + Kotlin.isType = function (object, type) { + if (object === null || object === undefined) { return false; } var proto = Object.getPrototypeOf(object); // todo test nested class //noinspection RedundantIfStatementJS - if (proto == klass.proto) { + if (proto == type.proto) { return true; } @@ -21,11 +28,17 @@ var Kotlin = {}; }; // as separated function to reduce scope - function createConstructor(proto, initializer) { - return function () { - var o = Object.create(proto); + function createConstructor() { + return function $fun() { + var o = Object.create($fun.proto); + var initializer = $fun.initializer; if (initializer != null) { - initializer.apply(o, arguments); + if (initializer.length == 0) { + initializer.call(o); + } + else { + initializer.apply(o, arguments); + } } Object.seal(o); @@ -38,11 +51,11 @@ var Kotlin = {}; for (var i = 0, n = bases.length; i < n; i++) { var base = bases[i]; var baseProto = base.proto; - if (baseProto == null || base.properties == null) { + if (baseProto === null || base.properties === null) { continue; } - if (!proto) { + if (proto === null) { proto = Object.create(baseProto, properties || undefined); continue; } @@ -53,33 +66,59 @@ var Kotlin = {}; return proto; } - // proto must be created for class even if it is not needed (requires for is operator) + Kotlin.createTrait = function (bases, properties) { + return createClass(bases, null, properties, false); + }; + Kotlin.createClass = function (bases, initializer, properties) { + // proto must be created for class even if it is not needed (requires for is operator) + return createClass(bases, initializer === null ? function () {} : initializer, properties, true); + }; + + function computeProto2(bases, properties) { + if (bases === null) { + return null; + } + return Array.isArray(bases) ? computeProto(bases, properties) : bases.proto; + } + + Kotlin.createObject = function (bases, initializer, properties) { + var o = Object.create(computeProto2(bases, properties), properties || undefined); + if (initializer !== null) { + if (bases !== null) { + Object.defineProperty(initializer, "baseInitializer", {value: Array.isArray(bases) ? bases[0].initializer : bases.initializer}); + } + initializer.call(o); + } + Object.seal(o); + return o; + }; + + function createClass(bases, initializer, properties, isClass) { var proto; - var baseInitializer = null; - var isTrait = initializer == null; - if (!bases) { - proto = !properties && isTrait ? null : Object.create(null, properties || undefined); + var baseInitializer; + if (bases === null) { + baseInitializer = null; + proto = !isClass && properties === null ? null : Object.create(null, properties || undefined); } else if (!Array.isArray(bases)) { baseInitializer = bases.initializer; - proto = !properties && isTrait ? bases.proto : Object.create(bases.proto, properties || undefined); + proto = !isClass && properties === null ? bases.proto : Object.create(bases.proto, properties || undefined); } else { - proto = computeProto(bases, properties); // first is superclass, other are traits baseInitializer = bases[0].initializer; + proto = computeProto(bases, properties); // all bases are traits without properties - if (proto == null && !isTrait) { + if (proto === null && isClass) { proto = Object.create(null, properties || undefined); } } - var constructor = createConstructor(proto, initializer); + var constructor = createConstructor(); Object.defineProperty(constructor, "proto", {value: proto}); Object.defineProperty(constructor, "properties", {value: properties || null}); - // null for trait - if (!isTrait) { + if (isClass) { Object.defineProperty(constructor, "initializer", {value: initializer}); Object.defineProperty(initializer, "baseInitializer", {value: baseInitializer}); @@ -88,28 +127,33 @@ var Kotlin = {}; Object.freeze(constructor); return constructor; - }; + } - Kotlin.createObject = function (initializer, properties) { - var o = Object.create(null, properties || undefined); - initializer.call(o); - return o; - }; - - - Kotlin.definePackage = function (functionsAndClasses, nestedNamespaces) { - var p = Object.create(null, functionsAndClasses || undefined); - if (nestedNamespaces) { - var keys = Object.keys(nestedNamespaces); - for (var i = 0, n = keys.length; i < n; i++) { - var name = keys[i]; - Object.defineProperty(p, name, {value:nestedNamespaces[name]}); - } + Kotlin.definePackage = function (initializer, members) { + var definition = Object.create(null, members === null ? undefined : members); + if (initializer === null) { + return {value: definition}; + } + else { + var getter = createPackageGetter(definition, initializer); + Object.freeze(getter); + return {get: getter}; } - - return p; }; + function createPackageGetter(instance, initializer) { + return function () { + if (initializer !== null) { + var tmp = initializer; + initializer = null; + tmp.call(instance); + Object.seal(instance); + } + + return instance; + }; + } + Kotlin.$new = function (f) { return f; }; @@ -154,12 +198,11 @@ var Kotlin = {}; }; Kotlin.defineModule = function (id, module) { - var isTestMode = id === "JS_TESTS"; - if ((id in Kotlin.modules) && (!isTestMode)) { - throw Kotlin.$new(Kotlin.Exceptions.IllegalArgumentException)(); + if (id in Kotlin.modules) { + throw Kotlin.$new(Kotlin.IllegalArgumentException)(); } Object.freeze(module); - Object.defineProperty(Kotlin.modules, id, {value: module, writable: isTestMode}); + Object.defineProperty(Kotlin.modules, id, {value: module}); }; })(); diff --git a/js/js.translator/testFiles/maps.js b/js/js.translator/testFiles/maps.js new file mode 100644 index 00000000000..0e59969b9b6 --- /dev/null +++ b/js/js.translator/testFiles/maps.js @@ -0,0 +1,620 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +"use strict"; +(function () { + var FUNCTION = "function"; + var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ? + function (arr, idx) { + arr.splice(idx, 1); + } : + + function (arr, idx) { + var itemsAfterDeleted, i, len; + if (idx === arr.length - 1) { + arr.length = idx; + } + else { + itemsAfterDeleted = arr.slice(idx + 1); + arr.length = idx; + for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) { + arr[idx + i] = itemsAfterDeleted[i]; + } + } + }; + + function hashObject(obj) { + var hashCode; + if (typeof obj == "string") { + return obj; + } + else if (typeof obj.hashCode == FUNCTION) { + // Check the hashCode method really has returned a string + hashCode = obj.hashCode(); + return (typeof hashCode == "string") ? hashCode : hashObject(hashCode); + } + else if (typeof obj.toString == FUNCTION) { + return obj.toString(); + } + else { + try { + return String(obj); + } + catch (ex) { + // For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when + // passed to String() + return Object.prototype.toString.call(obj); + } + } + } + + function equals_fixedValueHasEquals(fixedValue, variableValue) { + return fixedValue.equals(variableValue); + } + + function equals_fixedValueNoEquals(fixedValue, variableValue) { + return (typeof variableValue.equals == FUNCTION) ? + variableValue.equals(fixedValue) : (fixedValue === variableValue); + } + + function createKeyValCheck(kvStr) { + return function (kv) { + if (kv === null) { + throw new Error("null is not a valid " + kvStr); + } + else if (typeof kv == "undefined") { + throw new Error(kvStr + " must not be undefined"); + } + }; + } + + var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); + + function Bucket(hash, firstKey, firstValue, equalityFunction) { + this[0] = hash; + this.entries = []; + this.addEntry(firstKey, firstValue); + + if (equalityFunction !== null) { + this.getEqualityFunction = function () { + return equalityFunction; + }; + } + } + + var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; + + function createBucketSearcher(mode) { + return function (key) { + var i = this.entries.length, entry, equals = this.getEqualityFunction(key); + while (i--) { + entry = this.entries[i]; + if (equals(key, entry[0])) { + switch (mode) { + case EXISTENCE: + return true; + case ENTRY: + return entry; + case ENTRY_INDEX_AND_VALUE: + return [ i, entry[1] ]; + } + } + } + return false; + }; + } + + function createBucketLister(entryProperty) { + return function (aggregatedArr) { + var startIndex = aggregatedArr.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + aggregatedArr[startIndex + i] = this.entries[i][entryProperty]; + } + }; + } + + Bucket.prototype = { + getEqualityFunction: function (searchValue) { + return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; + }, + + getEntryForKey: createBucketSearcher(ENTRY), + + getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE), + + removeEntryForKey: function (key) { + var result = this.getEntryAndIndexForKey(key); + if (result) { + arrayRemoveAt(this.entries, result[0]); + return result[1]; + } + return null; + }, + + addEntry: function (key, value) { + this.entries[this.entries.length] = [key, value]; + }, + + keys: createBucketLister(0), + + values: createBucketLister(1), + + getEntries: function (entries) { + var startIndex = entries.length; + for (var i = 0, len = this.entries.length; i < len; ++i) { + // Clone the entry stored in the bucket before adding to array + entries[startIndex + i] = this.entries[i].slice(0); + } + }, + + containsKey: createBucketSearcher(EXISTENCE), + + containsValue: function (value) { + var i = this.entries.length; + while (i--) { + if (value === this.entries[i][1]) { + return true; + } + } + return false; + } + }; + + /*----------------------------------------------------------------------------------------------------------------*/ + + // Supporting functions for searching hashtable buckets + + function searchBuckets(buckets, hash) { + var i = buckets.length, bucket; + while (i--) { + bucket = buckets[i]; + if (hash === bucket[0]) { + return i; + } + } + return null; + } + + function getBucketForHash(bucketsByHash, hash) { + var bucket = bucketsByHash[hash]; + + // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype + return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; + } + + /*----------------------------------------------------------------------------------------------------------------*/ + + var Hashtable = function (hashingFunctionParam, equalityFunctionParam) { + var that = this; + var buckets = []; + var bucketsByHash = {}; + + var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject; + var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null; + + this.put = function (key, value) { + checkKey(key); + checkValue(value); + var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null; + + // Check if a bucket exists for the bucket key + bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it already contains this key + bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so replace old value and we're done. + oldValue = bucketEntry[1]; + bucketEntry[1] = value; + } + else { + // The bucket does not contain an entry for this key, so add one + bucket.addEntry(key, value); + } + } + else { + // No bucket exists for the key, so create one and put our key/value mapping in + bucket = new Bucket(hash, key, value, equalityFunction); + buckets[buckets.length] = bucket; + bucketsByHash[hash] = bucket; + } + return oldValue; + }; + + this.get = function (key) { + checkKey(key); + + var hash = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + if (bucket) { + // Check this bucket to see if it contains this key + var bucketEntry = bucket.getEntryForKey(key); + if (bucketEntry) { + // This bucket entry is the current mapping of key to value, so return the value. + return bucketEntry[1]; + } + } + return null; + }; + + this.containsKey = function (key) { + checkKey(key); + var bucketKey = hashingFunction(key); + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, bucketKey); + + return bucket ? bucket.containsKey(key) : false; + }; + + this.containsValue = function (value) { + checkValue(value); + var i = buckets.length; + while (i--) { + if (buckets[i].containsValue(value)) { + return true; + } + } + return false; + }; + + this.clear = function () { + buckets.length = 0; + bucketsByHash = {}; + }; + + this.isEmpty = function () { + return !buckets.length; + }; + + var createBucketAggregator = function (bucketFuncName) { + return function () { + var aggregated = [], i = buckets.length; + while (i--) { + buckets[i][bucketFuncName](aggregated); + } + return aggregated; + }; + }; + + this._keys = createBucketAggregator("keys"); + this._values = createBucketAggregator("values"); + this._entries = createBucketAggregator("getEntries"); + + this.values = function () { + var values = this._values(); + var i = values.length + var result = Kotlin.$new(Kotlin.ArrayList)(); + while (--i) { + result.add(values[i]); + } + return result; + }; + + this.remove = function (key) { + checkKey(key); + + var hash = hashingFunction(key), bucketIndex, oldValue = null; + + // Check if a bucket exists for the bucket key + var bucket = getBucketForHash(bucketsByHash, hash); + + if (bucket) { + // Remove entry from this bucket for this key + oldValue = bucket.removeEntryForKey(key); + if (oldValue !== null) { + // Entry was removed, so check if bucket is empty + if (!bucket.entries.length) { + // Bucket is empty, so remove it from the bucket collections + bucketIndex = searchBuckets(buckets, hash); + arrayRemoveAt(buckets, bucketIndex); + delete bucketsByHash[hash]; + } + } + } + return oldValue; + }; + + this.size = function () { + var total = 0, i = buckets.length; + while (i--) { + total += buckets[i].entries.length; + } + return total; + }; + + this.each = function (callback) { + var entries = that.entries(), i = entries.length, entry; + while (i--) { + entry = entries[i]; + callback(entry[0], entry[1]); + } + }; + + + this.putAll = function (hashtable, conflictCallback) { + var entries = hashtable.entries(); + var entry, key, value, thisValue, i = entries.length; + var hasConflictCallback = (typeof conflictCallback == FUNCTION); + while (i--) { + entry = entries[i]; + key = entry[0]; + value = entry[1]; + + // Check for a conflict. The default behaviour is to overwrite the value for an existing key + if (hasConflictCallback && (thisValue = that.get(key))) { + value = conflictCallback(key, thisValue, value); + } + that.put(key, value); + } + }; + + this.clone = function () { + var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam); + clone.putAll(that); + return clone; + }; + + this.keySet = function () { + var res = Kotlin.$new(Kotlin.HashSet)(); + var keys = this._keys(); + var i = keys.length; + while (i--) { + res.add(keys[i]); + } + return res; + }; + }; + + + Kotlin.HashTable = Hashtable; +})(); + +Kotlin.Map = Kotlin.$createClass(); + +Kotlin.HashMap = Kotlin.$createClass(Kotlin.Map, {initialize: function () { + Kotlin.HashTable.call(this); +}}); + +Kotlin.ComplexHashMap = Kotlin.HashMap; + +(function () { + var PrimitiveHashMapValuesIterator = Kotlin.$createClass(Kotlin.Iterator, { + initialize: function (map, keys) { + this.map = map; + this.keys = keys; + this.size = keys.length; + this.index = 0; + }, + next: function () { + return this.map[this.keys[this.index++]]; + }, + get_hasNext: function () { + return this.index < this.size; + } + }); + + var PrimitiveHashMapValues = Kotlin.$createClass(Kotlin.Collection, { + initialize: function (map) { + this.map = map; + }, + iterator: function () { + return Kotlin.$new(PrimitiveHashMapValuesIterator)(this.map.map, Kotlin.keys(this.map.map)); + }, + isEmpty: function () { + return this.map.$size === 0; + }, + contains: function (o) { + return this.map.containsValue(o); + } + }); + + Kotlin.PrimitiveHashMap = Kotlin.$createClass(Kotlin.Map, { + initialize: function () { + this.$size = 0; + this.map = {}; + }, + size: function () { + return this.$size; + }, + isEmpty: function () { + return this.$size === 0; + }, + containsKey: function (key) { + return this.map[key] !== undefined; + }, + containsValue: function (value) { + var map = this.map; + for (var key in map) { + if (map.hasOwnProperty(key) && map[key] === value) { + return true; + } + } + + return false; + }, + get: function (key) { + return this.map[key]; + }, + put: function (key, value) { + var prevValue = this.map[key]; + this.map[key] = value === undefined ? null : value; + if (prevValue === undefined) { + this.$size++; + } + return prevValue; + }, + remove: function (key) { + var prevValue = this.map[key]; + if (prevValue !== undefined) { + delete this.map[key]; + this.$size--; + } + return prevValue; + }, + clear: function () { + this.$size = 0; + this.map = {}; + }, + putAll: function (fromMap) { + throw Kotlin.$new(Kotlin.UnsupportedOperationException)(); + }, + keySet: function () { + var result = Kotlin.$new(Kotlin.HashSet)(); + var map = this.map; + for (var key in map) { + if (map.hasOwnProperty(key)) { + result.add(key); + } + } + + return result; + }, + values: function () { + return Kotlin.$new(PrimitiveHashMapValues)(this); + }, + toJSON: function () { + return this.map; + } + }); +}()); + +(function () { + function HashSet(hashingFunction, equalityFunction) { + var hashTable = new Kotlin.HashTable(hashingFunction, equalityFunction); + + this.add = function (o) { + hashTable.put(o, true); + }; + + this.addAll = function (arr) { + var i = arr.length; + while (i--) { + hashTable.put(arr[i], true); + } + }; + + this.values = function () { + return hashTable._keys(); + }; + + this.iterator = function () { + return Kotlin.arrayIterator(this.values()); + }; + + this.remove = function (o) { + return hashTable.remove(o) ? o : null; + }; + + this.contains = function (o) { + return hashTable.containsKey(o); + }; + + this.clear = function () { + hashTable.clear(); + }; + + this.size = function () { + return hashTable.size(); + }; + + this.isEmpty = function () { + return hashTable.isEmpty(); + }; + + this.clone = function () { + var h = new HashSet(hashingFunction, equalityFunction); + h.addAll(hashTable.keys()); + return h; + }; + + this.equals = function (o) { + if (o === null || o === undefined) return false; + if (this.size() === o.size()) { + var iter1 = this.iterator(); + var iter2 = o.iterator(); + while (true) { + var hn1 = iter1.get_hasNext(); + var hn2 = iter2.get_hasNext(); + if (hn1 != hn2) return false; + if (!hn2) + return true; + else { + var o1 = iter1.next(); + var o2 = iter2.next(); + if (!Kotlin.equals(o1, o2)) return false; + } + } + } + return false; + }; + + this.toString = function() { + var builder = "["; + var iter = this.iterator(); + var first = true; + while (iter.get_hasNext()) { + if (first) + first = false; + else + builder += ", "; + builder += iter.next(); + } + builder += "]"; + return builder; + }; + + this.intersection = function (hashSet) { + var intersection = new HashSet(hashingFunction, equalityFunction); + var values = hashSet.values(), i = values.length, val; + while (i--) { + val = values[i]; + if (hashTable.containsKey(val)) { + intersection.add(val); + } + } + return intersection; + }; + + this.union = function (hashSet) { + var union = this.clone(); + var values = hashSet.values(), i = values.length, val; + while (i--) { + val = values[i]; + if (!hashTable.containsKey(val)) { + union.add(val); + } + } + return union; + }; + + this.isSubsetOf = function (hashSet) { + var values = hashTable.keys(), i = values.length; + while (i--) { + if (!hashSet.contains(values[i])) { + return false; + } + } + return true; + }; + } + + Kotlin.HashSet = Kotlin.$createClass({initialize: function () { + HashSet.call(this); + }}); +}());