Update Range and Progression built-in implementations in js library.

No need to define special method mapping for js ranges/progressions.
Support progression companion objects' function fromClosedRange as intrinsic
This commit is contained in:
Ilya Gorbunov
2015-11-06 22:32:27 +03:00
parent 55d4d17dc3
commit af9020a010
9 changed files with 296 additions and 128 deletions
@@ -24,6 +24,10 @@ public final class RangeTest extends SingleFileTranslationTest {
super("range/");
}
public void testCreatingProgressions() throws Exception {
fooBoxTest();
}
public void testExplicitRange() throws Exception {
fooBoxTest();
}
@@ -98,25 +98,15 @@ public final class StandardClasses {
if (type == PrimitiveType.CHAR || type == PrimitiveType.LONG) continue;
String typeName = type.getTypeName().asString();
standardClasses.declare().forFQ("kotlin." + typeName + "Range").kotlinClass("NumberRange")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin." + typeName + "Progression").kotlinClass("NumberProgression")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin." + typeName + "Range").kotlinClass("NumberRange");
standardClasses.declare().forFQ("kotlin." + typeName + "Progression").kotlinClass("NumberProgression");
}
standardClasses.declare().forFQ("kotlin.LongRange").kotlinClass("LongRange")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin.LongRange").kotlinClass("LongRange");
standardClasses.declare().forFQ("kotlin.CharRange").kotlinClass("CharRange");
standardClasses.declare().forFQ("kotlin.CharRange").kotlinClass("CharRange")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin.LongProgression").kotlinClass("LongProgression")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin.CharProgression").kotlinClass("CharProgression")
.methods("iterator", "contains").properties("start", "end", "increment");
standardClasses.declare().forFQ("kotlin.LongProgression").kotlinClass("LongProgression");
standardClasses.declare().forFQ("kotlin.CharProgression").kotlinClass("CharProgression");
standardClasses.declare().forFQ("kotlin.Enum").kotlinClass("Enum");
@@ -47,6 +47,7 @@ public final class FunctionIntrinsics {
register(ArrayFIF.INSTANCE);
register(TopLevelFIF.INSTANCE);
register(NumberAndCharConversionFIF.INSTANCE$);
register(ProgressionCompanionFIF.INSTANCE);
}
private void register(@NotNull FunctionIntrinsicFactory instance) {
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2015 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.
*/
package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories
import com.google.dart.compiler.backend.js.ast.*
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic
public object ProgressionCompanionFIF : CompositeFIF() {
init {
val numberProgressionConstructor = CallProgressionConstructorIntrinsic("NumberProgression")
for (type in arrayOf(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT)) {
add(methodPattern("${type.typeName}Progression"), numberProgressionConstructor)
}
add(methodPattern("LongProgression"), CallProgressionConstructorIntrinsic("LongProgression"))
add(methodPattern("CharProgression"), CallProgressionConstructorIntrinsic("CharProgression"))
}
private fun methodPattern(builtinProgressionName: String) = pattern("kotlin", builtinProgressionName, "Companion", "fromClosedRange")
private class CallProgressionConstructorIntrinsic(val libraryProgressionName: String) : FunctionIntrinsic() {
override fun apply(receiver: JsExpression?, arguments: MutableList<JsExpression>, context: TranslationContext): JsExpression
= JsNew(JsNameRef(libraryProgressionName, Namer.KOTLIN_OBJECT_REF), arguments)
}
}
@@ -20,14 +20,6 @@ fun box(): String {
n++
assertEquals(4, n)
// http://youtrack.jetbrains.com/issue/KT-4381
// JS: fails to iterate over Double range
var nd = 0.0
for (i in 0.0 .. 10.0) {
nd += i
}
assertEquals(55.0, nd)
// Evaluation order
for (i in up("A", 0)..up("B", 5)) {
}
@@ -53,10 +45,6 @@ fun box(): String {
sLong += i
assertEquals(55L, sLong)
var sDouble = 0.0
for(i in 0L..10.0)
sDouble += i
assertEquals(55.0, sDouble)
return "OK"
}
@@ -18,11 +18,5 @@ fun box(): String {
sLong += i
assertEquals(55L, sLong)
var sDouble = 0.0
var rDouble = 0L..10.0
for(i in rDouble)
sDouble += i
assertEquals(55.0, sDouble)
return "OK"
}
+209 -94
View File
@@ -790,53 +790,128 @@
var classObject = this.constructor;
if (this instanceof classObject && other instanceof classObject) {
return this.isEmpty() && other.isEmpty() ||
(this.start === other.start && this.end === other.end && this.increment === other.increment);
(this.first === other.first && this.last === other.last && this.increment === other.increment);
}
return false;
}
Kotlin.NumberRange = Kotlin.createClassNow(null,
function (start, end) {
function isSameLongRanges(other) {
var classObject = this.constructor;
if (this instanceof classObject && other instanceof classObject) {
return this.isEmpty() && other.isEmpty() ||
(this.first.equals_za3rmp$(other.first) && this.last.equals_za3rmp$(other.last) && this.increment.equals_za3rmp$(other.increment));
}
return false;
}
// reference implementation in core/builtins/src/kotlin/internal/progressionUtil.kt
function getProgressionFinalElement(start, end, increment) {
function mod(a, b) {
var mod = a % b;
return mod >= 0 ? mod : mod + b;
}
function differenceModulo(a, b, c) {
return mod(mod(a, c) - mod(b, c), c);
}
if (increment > 0) {
return end - differenceModulo(end, start, increment);
}
else if (increment < 0) {
return end + differenceModulo(start, end, -increment);
}
else {
throw new Kotlin.IllegalArgumentException('Increment is zero.');
}
}
// reference implementation in core/builtins/src/kotlin/internal/progressionUtil.kt
function getProgressionFinalElementLong(start, end, increment) {
function mod(a, b) {
var mod = a.modulo(b);
return !mod.isNegative() ? mod : mod.add(b);
}
function differenceModulo(a, b, c) {
return mod(mod(a, c).subtract(mod(b, c)), c);
}
var diff;
if (increment.compareTo_za3rmp$(Kotlin.Long.ZERO) > 0) {
diff = differenceModulo(end, start, increment);
return diff.isZero() ? end : end.subtract(diff);
}
else if (increment.compareTo_za3rmp$(Kotlin.Long.ZERO) < 0) {
diff = differenceModulo(start, end, increment.unaryMinus());
return diff.isZero() ? end : end.add(diff);
}
else {
throw new Kotlin.IllegalArgumentException('Increment is zero.');
}
}
lazyInitClasses.NumberProgression = Kotlin.createClass(
function () {
return [Kotlin.modules['builtins'].kotlin.Iterable];
},
function (start, end, increment) {
this.start = start;
this.end = end;
this.increment = 1;
this.endInclusive = end;
this.first = start;
this.last = getProgressionFinalElement(start, end, increment);
this.increment = increment;
if (this.increment === 0)
throw new Kotlin.IllegalArgumentException('Increment must be non-zero');
}, {
contains: function (number) {
return this.start <= number && number <= this.end;
},
end: { get: function () {
return this.endInclusive;
}},
iterator: function () {
return new Kotlin.RangeIterator(this.start, this.end, this.increment);
return new Kotlin.RangeIterator(this.first, this.last, this.increment);
},
isEmpty: function () {
return this.start > this.end;
return this.increment > 0 ? this.first > this.last : this.first < this.last;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * this.start|0 + this.end|0);
hashCode: function () {
return this.isEmpty() ? -1 : 31 * (31 * this.first + this.last) + this.increment;
},
equals_za3rmp$: isSameNotNullRanges
equals_za3rmp$: isSameNotNullRanges,
toString: function () {
return this.increment > 0 ? this.first.toString() + '..' + this.last + ' step ' + this.increment : this.first.toString() + ' downTo ' + this.last + ' step ' + -this.increment;
}
});
lazyInitClasses.NumberRange = Kotlin.createClass(
function() {
return [Kotlin.modules['builtins'].kotlin.ClosedRange, Kotlin.NumberProgression]
},
function $fun(start, endInclusive) {
$fun.baseInitializer.call(this, start, endInclusive, 1);
this.start = start;
this.endInclusive = endInclusive;
}, {
end: { get: function () {
return this.endInclusive;
}},
contains_htax2k$: function (item) {
return this.start <= item && item <= this.endInclusive;
},
isEmpty: function () {
return this.start > this.endInclusive;
},
hashCode: function () {
return this.isEmpty() ? -1 : 31 * this.start + this.endInclusive;
},
equals_za3rmp$: isSameNotNullRanges,
toString: function () {
return this.start.toString() + '..' + this.endInclusive;
}
}, {
object_initializer$: function () {
return { EMPTY : new this(1, 0) };
}
});
Kotlin.NumberProgression = Kotlin.createClassNow(null,
function (start, end, increment) {
this.start = start;
this.end = end;
this.increment = increment;
}, {
iterator: function () {
return new Kotlin.RangeIterator(this.start, this.end, this.increment);
},
isEmpty: function() {
return this.increment > 0 ? this.start > this.end : this.start < this.end;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * (31 * this.start|0 + this.end|0) + this.increment|0);
},
equals_za3rmp$: isSameNotNullRanges
});
lazyInitClasses.LongRangeIterator = Kotlin.createClass(
function () {
@@ -861,48 +936,69 @@
}
});
Kotlin.LongRange = Kotlin.createClassNow(null,
function (start, end) {
this.start = start;
this.end = end;
this.increment = Kotlin.Long.ONE;
}, {
contains: function (number) {
return this.start.compare(number) <= 0 && number.compare(this.end) <= 0;
},
iterator: function () {
return new Kotlin.LongRangeIterator(this.start, this.end, this.increment);
},
isEmpty: function () {
return this.start.compare(this.end) > 0;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * this.start.toInt() + this.end.toInt());
lazyInitClasses.LongProgression = Kotlin.createClass(
function () {
return [Kotlin.modules['builtins'].kotlin.Iterable];
},
function (start, end, increment) {
this.start = start;
this.endInclusive = end;
this.first = start;
this.last = getProgressionFinalElementLong(start, end, increment);
this.increment = increment;
if (this.increment.isZero())
throw new Kotlin.IllegalArgumentException('Increment must be non-zero');
}, {
end: { get: function () {
return this.endInclusive;
}},
iterator: function () {
return new Kotlin.LongRangeIterator(this.first, this.last, this.increment);
},
equals_za3rmp$: isSameNotNullRanges
isEmpty: function() {
return this.increment.isNegative() ? this.first.compare(this.last) < 0 : this.first.compare(this.last) > 0;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * (31 * this.first.toInt() + this.last.toInt()) + this.increment.toInt());
},
equals_za3rmp$: isSameLongRanges,
toString: function () {
return !this.increment.isNegative() ? this.first.toString() + '..' + this.last + ' step ' + this.increment : this.first.toString() + ' downTo ' + this.last + ' step ' + this.increment.unaryMinus();
}
});
lazyInitClasses.LongRange = Kotlin.createClass(
function () {
return [Kotlin.modules['builtins'].kotlin.ClosedRange, Kotlin.LongProgression];
},
function $fun(start, endInclusive) {
$fun.baseInitializer.call(this, start, endInclusive, Kotlin.Long.ONE);
this.start = start;
this.endInclusive = endInclusive;
}, {
end: { get: function () {
return this.endInclusive;
}},
contains_htax2k$: function (item) {
return this.start.compareTo_za3rmp$(item) <= 0 && item.compareTo_za3rmp$(this.endInclusive) <= 0;
},
isEmpty: function () {
return this.start.compare(this.endInclusive) > 0;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * this.start.toInt() + this.endInclusive.toInt());
},
equals_za3rmp$: isSameLongRanges,
toString: function () {
return this.start.toString() + '..' + this.endInclusive;
}
}, {
object_initializer$: function () {
return { EMPTY : new this(Kotlin.Long.ONE, Kotlin.Long.ZERO) };
}
});
Kotlin.LongProgression = Kotlin.createClassNow(null,
function (start, end, increment) {
this.start = start;
this.end = end;
this.increment = increment;
}, {
iterator: function () {
return new Kotlin.LongRangeIterator(this.start, this.end, this.increment);
},
isEmpty: function() {
return this.increment.isNegative() ? this.start.compare(this.end) < 0 : this.start.compare(this.end) > 0;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * (31 * this.start.toInt() + this.end.toInt()) + this.increment.toInt());
},
equals_za3rmp$: isSameNotNullRanges
});
lazyInitClasses.CharRangeIterator = Kotlin.createClass(
function () {
@@ -918,52 +1014,71 @@
}
});
Kotlin.CharRange = Kotlin.createClassNow(null,
function (start, end) {
lazyInitClasses.CharProgression = Kotlin.createClassNow(
function () {
return [Kotlin.modules['builtins'].kotlin.Iterable];
},
function (start, end, increment) {
this.start = start;
this.endInclusive = end;
this.first = start;
this.startCode = start.charCodeAt(0);
this.end = end;
this.endCode = end.charCodeAt(0);
this.increment = 1;
this.endCode = getProgressionFinalElement(this.startCode, end.charCodeAt(0), increment);
this.last = String.fromCharCode(this.endCode);
this.increment = increment;
if (this.increment === 0)
throw new Kotlin.IllegalArgumentException('Increment must be non-zero');
}, {
contains: function (char) {
return this.start <= char && char <= this.end;
},
end: { get: function () {
return this.endInclusive;
}},
iterator: function () {
return new Kotlin.CharRangeIterator(this.startCode, this.endCode, this.increment);
},
isEmpty: function() {
return this.increment > 0 ? this.startCode > this.endCode : this.startCode < this.endCode;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * (31 * this.startCode|0 + this.endCode|0) + this.increment|0);
},
equals_za3rmp$: isSameNotNullRanges,
toString: function () {
return this.increment > 0 ? this.first.toString() + '..' + this.last + ' step ' + this.increment : this.first.toString() + ' downTo ' + this.last + ' step ' + -this.increment;
}
});
lazyInitClasses.CharRange = Kotlin.createClass(
function() {
return [Kotlin.modules['builtins'].kotlin.ClosedRange, Kotlin.CharProgression]
},
function $fun(start, endInclusive) {
$fun.baseInitializer.call(this, start, endInclusive, 1);
this.start = start;
this.endInclusive = endInclusive;
}, {
end: { get: function () {
return this.endInclusive;
}},
contains_htax2k$: function (item) {
return this.start <= item && item <= this.endInclusive;
},
isEmpty: function () {
return this.start > this.end;
return this.start > this.endInclusive;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * this.startCode|0 + this.endCode|0);
},
equals_za3rmp$: isSameNotNullRanges
equals_za3rmp$: isSameNotNullRanges,
toString: function () {
return this.start.toString() + '..' + this.endInclusive;
}
}, {
object_initializer$: function () {
return { EMPTY : new this(Kotlin.toChar(1), Kotlin.toChar(0)) };
}
});
Kotlin.CharProgression = Kotlin.createClassNow(null,
function (start, end, increment) {
this.start = start;
this.startCode = start.charCodeAt(0);
this.end = end;
this.endCode = end.charCodeAt(0);
this.increment = increment;
}, {
iterator: function () {
return new Kotlin.CharRangeIterator(this.startCode, this.endCode, this.increment);
},
isEmpty: function() {
return this.increment > 0 ? this.start > this.end : this.start < this.end;
},
hashCode: function() {
return this.isEmpty() ? -1 : (31 * (31 * this.startCode|0 + this.endCode|0) + this.increment|0);
},
equals_za3rmp$: isSameNotNullRanges
});
/**
* @interface
@@ -0,0 +1,22 @@
package foo
fun box(): Boolean {
val intProgression = IntProgression.fromClosedRange(0, 10, 2)
assertEquals(10, intProgression.last)
val longProgression = LongProgression.fromClosedRange(0, 420004200042000L, 420004200042000L)
assertEquals(420004200042000L, longProgression.last)
val charProgression = CharProgression.fromClosedRange('a', 'z', 2)
assertEquals('y', charProgression.last)
// deprecated
val byteProgression = ByteProgression.fromClosedRange(1, 127, 2)
assertEquals(127.toByte(), byteProgression.last)
val shortProgression = ShortProgression.fromClosedRange(1, 32767, 2)
assertEquals(32767.toShort(), shortProgression.last)
return true
}
@@ -22,6 +22,9 @@ public class RangeTest {
assertFalse(range.isEmpty())
assertTrue(9 in (range as ClosedRange<Int>))
assertFalse((range as ClosedRange<Int>).isEmpty())
assertTrue(1.toShort() in range)
assertTrue(1.toByte() in range)
assertTrue(1.toLong() in range)
@@ -122,6 +125,9 @@ public class RangeTest {
assertFalse(range.isEmpty())
assertTrue(9 in (range as ClosedRange<Long>))
assertFalse((range as ClosedRange<Long>).isEmpty())
assertTrue(1.toByte() in range)
assertTrue(1.toShort() in range)
assertTrue(1.toInt() in range)
@@ -155,6 +161,9 @@ public class RangeTest {
assertFalse(range.isEmpty())
assertTrue('v' in (range as ClosedRange<Char>))
assertFalse((range as ClosedRange<Char>).isEmpty())
val openRange = 'A' until 'Z'
assertTrue('Y' in openRange)
assertFalse('Z' in openRange)
@@ -239,6 +248,7 @@ public class RangeTest {
assertTrue(IntRange.EMPTY == IntRange.EMPTY)
assertEquals(IntRange.EMPTY, IntRange.EMPTY)
assertEquals(0L..42L, 0L..42L)
assertEquals(0L..4200000042000000L, 0L..4200000042000000L)
assertEquals(3 downTo 0, 3 downTo 0)
assertEquals(2..1, 1..0)