diff --git a/js/js.libraries/src/stdlib/TuplesCode.kt b/js/js.libraries/src/stdlib/TuplesCode.kt
new file mode 100644
index 00000000000..cd9cd79fac7
--- /dev/null
+++ b/js/js.libraries/src/stdlib/TuplesCode.kt
@@ -0,0 +1,25 @@
+package kotlin
+
+// TODO: make it a data class
+public class Pair (
+ public val first: A,
+ public val second: B
+) {
+ public fun component1(): A = first
+ public fun component2(): B = second
+
+ public fun toString(): String = "($first, $second)"
+
+}
+
+public class Triple (
+ public val first: A,
+ public val second: B,
+ public val third: C
+) {
+ public fun component1(): A = first
+ public fun component2(): B = second
+ public fun component3(): C = third
+
+ public fun toString(): String = "($first, $second, $third)"
+}
\ No newline at end of file
diff --git a/libraries/stdlib/src/kotlin/Tuples.kt b/libraries/stdlib/src/kotlin/Tuples.kt
new file mode 100644
index 00000000000..569a6674c4b
--- /dev/null
+++ b/libraries/stdlib/src/kotlin/Tuples.kt
@@ -0,0 +1,58 @@
+package kotlin
+
+import kotlin.nullable.hashCodeOrDefault
+import java.io.Serializable
+
+// TODO: make it a data class
+public class Pair (
+ public val first: A,
+ public val second: B
+) : Serializable {
+ public fun component1(): A = first
+ public fun component2(): B = second
+
+ override fun toString(): String = "($first, $second)"
+
+ override fun hashCode(): Int {
+ var result = first.hashCodeOrDefault(0)
+ result = 31 * result + second.hashCodeOrDefault(0)
+ return result;
+ }
+
+ override fun equals(o: Any?): Boolean {
+ if (this identityEquals o) return true;
+ if (o == null || this.javaClass != o.javaClass) return false;
+
+ val t = o as Pair<*, *>
+ return first == t.first && second == t.second;
+ }
+}
+
+public class Triple (
+ public val first: A,
+ public val second: B,
+ public val third: C
+) : Serializable {
+ public fun component1(): A = first
+ public fun component2(): B = second
+ public fun component3(): C = third
+
+ override fun toString(): String = "($first, $second, $third)"
+
+ override fun hashCode(): Int {
+ var result = first.hashCodeOrDefault(0)
+ result = 31 * result + second.hashCodeOrDefault(0)
+ result = 31 * result + third.hashCodeOrDefault(0)
+ return result;
+ }
+
+ override fun equals(o: Any?): Boolean {
+ if (this identityEquals o) return true;
+ if (o == null || this.javaClass != o.javaClass) return false;
+
+ val t = o as Triple<*, *, *>
+ return first == t.first &&
+ second == t.second &&
+ third == t.third;
+ }
+}
\ No newline at end of file
diff --git a/libraries/stdlib/test/TuplesTest.kt b/libraries/stdlib/test/TuplesTest.kt
new file mode 100644
index 00000000000..8b165a624f8
--- /dev/null
+++ b/libraries/stdlib/test/TuplesTest.kt
@@ -0,0 +1,91 @@
+package test.tuples
+
+import kotlin.test.assertTrue
+import org.junit.Test as test
+import kotlin.test.assertEquals
+
+class PairTest {
+ val p = Pair(1, "a")
+
+ test fun pairFirstAndSecond() {
+ assertTrue(p.first == 1)
+ assertTrue(p.second == "a")
+ }
+
+ test fun pairMultiAssignment() {
+ val (a, b) = p
+ assertTrue(a == 1)
+ assertTrue(b == "a")
+ }
+
+ test fun pairToString() {
+ assertTrue(p.toString() == "(1, a)")
+ }
+
+ test fun pairEquals() {
+ assertTrue(p == Pair(1, "a"))
+ assertTrue(p != Pair(2, "a"))
+ assertTrue(p != Pair(1, "b"))
+ assertTrue(!(p as Object).equals(null))
+ assertTrue((p as Any) != "")
+ }
+
+ test fun pairHashCode() {
+ assertTrue(p.hashCode() == Pair(1, "a").hashCode())
+ assertTrue(p.hashCode() != Pair(2, "a").hashCode())
+ assertTrue(Pair(null, "b").hashCode() != 0)
+ assertTrue(Pair("b", null).hashCode() != 0)
+ assertTrue(Pair(null, null).hashCode() == 0)
+ }
+
+ test fun pairHashSet() {
+ val s = hashSet(Pair(1, "a"), Pair(1, "b"), Pair(1, "a"))
+ assertTrue(s.size == 2)
+ assertTrue(s.contains(p))
+ }
+}
+
+class TripleTest {
+ val t = Triple(1, "a", 0.0)
+
+ test fun tripleFirstAndSecond() {
+ assertTrue(t.first == 1)
+ assertTrue(t.second == "a")
+ assertTrue(t.third == 0.0)
+ }
+
+ test fun tripleMultiAssignment() {
+ val (a, b, c) = t
+ assertTrue(a == 1)
+ assertTrue(b == "a")
+ assertTrue(c == 0.0)
+ }
+
+ test fun tripleToString() {
+ assertEquals("(1, a, 0.0)", t.toString())
+ }
+
+ test fun tripleEquals() {
+ assertTrue(t == Triple(1, "a", 0.0))
+ assertTrue(t != Triple(2, "a", 0.0))
+ assertTrue(t != Triple(1, "b", 0.0))
+ assertTrue(t != Triple(1, "a", 0.1))
+ assertTrue(!(t as Object).equals(null))
+ assertTrue((t as Any) != "")
+ }
+
+ test fun tripleHashCode() {
+ assertTrue(t.hashCode() == Triple(1, "a", 0.0).hashCode())
+ assertTrue(t.hashCode() != Triple(2, "a", 0.0).hashCode())
+ assertTrue(Triple(null, "b", 0.0).hashCode() != 0)
+ assertTrue(Triple("b", null, 0.0).hashCode() != 0)
+ assertTrue(Triple("b", 1, null).hashCode() != 0)
+ assertTrue(Triple(null, null, null).hashCode() == 0)
+ }
+
+ test fun tripleHashSet() {
+ val s = hashSet(Triple(1, "a", 0.0), Triple(1, "b", 0.0), Triple(1, "a", 0.0))
+ assertTrue(s.size == 2)
+ assertTrue(s.contains(t))
+ }
+}