Supporting data class for JS backend
This commit is contained in:
committed by
Zalim Bashorov
parent
535cc3eed8
commit
ab81d2e261
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.k2js.test.semantics;
|
||||
|
||||
import org.jetbrains.k2js.test.SingleFileTranslationTest;
|
||||
|
||||
public class DataClassTest extends SingleFileTranslationTest {
|
||||
public DataClassTest() {
|
||||
super("dataClass/");
|
||||
}
|
||||
|
||||
public void testComponents() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testCopy() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testEquals() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testHashcode() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testTostring() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
|
||||
public void testKeyrole() throws Exception {
|
||||
checkFooBoxIsOk();
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.psi.JetObjectDeclaration;
|
||||
import org.jetbrains.jet.lang.psi.JetParameter;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeConstructor;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
import org.jetbrains.k2js.translate.context.DefinitionPlace;
|
||||
import org.jetbrains.k2js.translate.context.Namer;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
@@ -45,8 +46,7 @@ import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.transla
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getClassDescriptor;
|
||||
import static org.jetbrains.k2js.translate.utils.BindingUtils.getPropertyDescriptorForConstructorParameter;
|
||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getContainingClass;
|
||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
|
||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getSupertypesWithoutFakes;
|
||||
import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.*;
|
||||
import static org.jetbrains.k2js.translate.utils.PsiUtils.getPrimaryConstructorParameters;
|
||||
import static org.jetbrains.k2js.translate.utils.TranslationUtils.simpleReturnFunction;
|
||||
|
||||
@@ -134,6 +134,10 @@ public final class ClassTranslator extends AbstractTranslator {
|
||||
bodyVisitor.traverseContainer(classDeclaration, declarationContext);
|
||||
mayBeAddEnumEntry(bodyVisitor.getEnumEntryList(), staticProperties, declarationContext);
|
||||
|
||||
if (KotlinBuiltIns.getInstance().isData(descriptor)) {
|
||||
new JsDataClassGenerator(classDeclaration, declarationContext, properties).generate();
|
||||
}
|
||||
|
||||
boolean hasStaticProperties = !staticProperties.isEmpty();
|
||||
if (!properties.isEmpty() || hasStaticProperties) {
|
||||
if (properties.isEmpty()) {
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2010-2014 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.k2js.translate.declaration;
|
||||
|
||||
import com.google.dart.compiler.backend.js.ast.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jet.backend.common.CodegenUtil;
|
||||
import org.jetbrains.jet.backend.common.DataClassMethodGenerator;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.psi.JetClassOrObject;
|
||||
import org.jetbrains.jet.lang.psi.JetParameter;
|
||||
import org.jetbrains.k2js.translate.context.Namer;
|
||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class JsDataClassGenerator extends DataClassMethodGenerator {
|
||||
private final TranslationContext context;
|
||||
private final List<? super JsPropertyInitializer> output;
|
||||
|
||||
JsDataClassGenerator(JetClassOrObject klass, TranslationContext context, List<? super JsPropertyInitializer> output) {
|
||||
super(klass, context.bindingContext());
|
||||
this.context = context;
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateComponentFunction(@NotNull FunctionDescriptor function, @NotNull ValueParameterDescriptor parameter) {
|
||||
JsFunction functionObject = generateJsMethod(function);
|
||||
JsExpression returnExpression = propertyAccessor(JsLiteral.THIS, context.getNameForDescriptor(parameter).toString());
|
||||
functionObject.getBody().getStatements().add(new JsReturn(returnExpression));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateCopyFunction(@NotNull FunctionDescriptor function, @NotNull List<JetParameter> constructorParameters) {
|
||||
JsFunction functionObj = generateJsMethod(function);
|
||||
JsScope funScope = functionObj.getScope();
|
||||
|
||||
assert function.getValueParameters().size() == constructorParameters.size();
|
||||
|
||||
List<JsExpression> constructorArguments = new ArrayList<JsExpression>(constructorParameters.size());
|
||||
for (int i = 0; i < constructorParameters.size(); i++) {
|
||||
JetParameter constructorParam = constructorParameters.get(i);
|
||||
JsName paramName = funScope.declareName(constructorParam.getName());
|
||||
|
||||
functionObj.getParameters().add(new JsParameter(paramName));
|
||||
|
||||
JsExpression argumentValue;
|
||||
JsExpression parameterValue = new JsNameRef(paramName);
|
||||
if (constructorParam.getValOrVarNode() == null) {
|
||||
assert !function.getValueParameters().get(i).hasDefaultValue();
|
||||
// Caller cannot rely on default value and pass undefined here.
|
||||
argumentValue = parameterValue;
|
||||
}
|
||||
else {
|
||||
JsExpression defaultCondition = JsAstUtils.equality(new JsNameRef(paramName), context.namer().getUndefinedExpression());
|
||||
argumentValue = new JsConditional(defaultCondition,
|
||||
propertyAccessor(JsLiteral.THIS, constructorParam.getName()),
|
||||
parameterValue);
|
||||
}
|
||||
constructorArguments.add(argumentValue);
|
||||
}
|
||||
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) function.getContainingDeclaration();
|
||||
ConstructorDescriptor constructor = classDescriptor.getConstructors().iterator().next();
|
||||
|
||||
JsExpression constructorRef = context.getQualifiedReference(constructor);
|
||||
|
||||
JsExpression returnExpression = new JsNew(constructorRef, constructorArguments);
|
||||
functionObj.getBody().getStatements().add(new JsReturn(returnExpression));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateToStringMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
// TODO: relax this limitation, with the data generation logic fixed.
|
||||
assert !classProperties.isEmpty();
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyToStringMethod();
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
|
||||
JsProgram jsProgram = context.program();
|
||||
JsExpression result = null;
|
||||
for (int i = 0; i < classProperties.size(); i++) {
|
||||
String name = classProperties.get(i).getName().toString();
|
||||
JsExpression literal = jsProgram.getStringLiteral((i == 0 ? (getClassDescriptor().getName() + "(") : ", ") + name + "=");
|
||||
JsExpression expr =
|
||||
new JsInvocation(new JsNameRef("toString", Namer.KOTLIN_OBJECT_REF), propertyAccessor(JsLiteral.THIS, name));
|
||||
JsExpression component = JsAstUtils.sum(literal, expr);
|
||||
if (result == null) {
|
||||
result = component;
|
||||
}
|
||||
else {
|
||||
result = JsAstUtils.sum(result, component);
|
||||
}
|
||||
}
|
||||
assert result != null;
|
||||
result = JsAstUtils.sum(result, jsProgram.getStringLiteral(")"));
|
||||
functionObj.getBody().getStatements().add(new JsReturn(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateHashCodeMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyHashCodeMethod();
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
|
||||
JsProgram jsProgram = context.program();
|
||||
List<JsStatement> statements = functionObj.getBody().getStatements();
|
||||
|
||||
JsName varName = functionObj.getScope().declareName("result");
|
||||
|
||||
int typeHash = context.getQualifiedReference(getClassDescriptor()).toString().hashCode();
|
||||
statements.add(new JsVars(new JsVars.JsVar(varName, jsProgram.getNumberLiteral(typeHash))));
|
||||
|
||||
for (PropertyDescriptor prop : classProperties) {
|
||||
// TODO: we should statically check that we can call hashCode method directly.
|
||||
JsExpression component = new JsInvocation(new JsNameRef("hashCode", Namer.KOTLIN_OBJECT_REF),
|
||||
propertyAccessor(JsLiteral.THIS, prop.getName().toString()));
|
||||
JsExpression newHashValue = JsAstUtils.sum(JsAstUtils.mul(new JsNameRef(varName), jsProgram.getNumberLiteral(31)), component);
|
||||
JsExpression assignment = JsAstUtils.assignment(new JsNameRef(varName),
|
||||
new JsBinaryOperation(JsBinaryOperator.BIT_OR, newHashValue,
|
||||
jsProgram.getNumberLiteral(0)));
|
||||
statements.add(assignment.makeStmt());
|
||||
}
|
||||
|
||||
statements.add(new JsReturn(new JsNameRef(varName)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateEqualsMethod(@NotNull List<PropertyDescriptor> classProperties) {
|
||||
assert !classProperties.isEmpty();
|
||||
FunctionDescriptor prototypeFun = CodegenUtil.getAnyEqualsMethod();
|
||||
JsFunction functionObj = generateJsMethod(prototypeFun);
|
||||
JsScope funScope = functionObj.getScope();
|
||||
|
||||
JsName paramName = funScope.declareName("other");
|
||||
functionObj.getParameters().add(new JsParameter(paramName));
|
||||
|
||||
JsExpression referenceEqual = JsAstUtils.equality(JsLiteral.THIS, new JsNameRef(paramName));
|
||||
JsExpression isNotNull = JsAstUtils.inequality(new JsNameRef(paramName), JsLiteral.NULL);
|
||||
JsExpression prototypeEqual =
|
||||
JsAstUtils.equality(new JsInvocation(new JsNameRef("getPrototypeOf", new JsNameRef("Object")), JsLiteral.THIS),
|
||||
new JsInvocation(new JsNameRef("getPrototypeOf", new JsNameRef("Object")), new JsNameRef(paramName)));
|
||||
|
||||
JsExpression fieldChain = null;
|
||||
for (PropertyDescriptor prop : classProperties) {
|
||||
String name = prop.getName().toString();
|
||||
JsExpression next = new JsInvocation(new JsNameRef("equals", Namer.KOTLIN_OBJECT_REF),
|
||||
propertyAccessor(JsLiteral.THIS, name),
|
||||
propertyAccessor(new JsNameRef(paramName), name));
|
||||
if (fieldChain == null) {
|
||||
fieldChain = next;
|
||||
}
|
||||
else {
|
||||
fieldChain = JsAstUtils.and(fieldChain, next);
|
||||
}
|
||||
}
|
||||
assert fieldChain != null;
|
||||
|
||||
JsExpression returnExpression =
|
||||
JsAstUtils.or(referenceEqual, JsAstUtils.and(isNotNull, JsAstUtils.and(prototypeEqual, fieldChain)));
|
||||
functionObj.getBody().getStatements().add(new JsReturn(returnExpression));
|
||||
}
|
||||
|
||||
private static JsExpression propertyAccessor(JsExpression object, String propertyName) {
|
||||
// Might be not accurate enough.
|
||||
return new JsNameRef(propertyName, object);
|
||||
}
|
||||
|
||||
private JsFunction generateJsMethod(@NotNull FunctionDescriptor functionDescriptor) {
|
||||
JsName functionName = context.getNameForDescriptor(functionDescriptor);
|
||||
JsScope enclosingScope = context.scope();
|
||||
JsFunction functionObject = JsAstUtils.createFunctionWithEmptyBody(enclosingScope);
|
||||
JsPropertyInitializer initializer = new JsPropertyInitializer(functionName.makeRef(), functionObject);
|
||||
output.add(initializer);
|
||||
return functionObject;
|
||||
}
|
||||
}
|
||||
+7
@@ -65,6 +65,12 @@ public final class TopLevelFIF extends CompositeFIF {
|
||||
return new JsBinaryOperation(JsBinaryOperator.REF_EQ, receiver, arguments.get(0));
|
||||
}
|
||||
};
|
||||
|
||||
@NotNull
|
||||
public static final DescriptorPredicate HASH_CODE_IN_ANY = pattern("kotlin", "Any", "hashCode");
|
||||
@NotNull
|
||||
public static final KotlinFunctionIntrinsic KOTLIN_HASH_CODE = new KotlinFunctionIntrinsic("hashCode");
|
||||
|
||||
@NotNull
|
||||
private static final FunctionIntrinsic RETURN_RECEIVER_INTRINSIC = new FunctionIntrinsic() {
|
||||
@NotNull
|
||||
@@ -156,6 +162,7 @@ public final class TopLevelFIF extends CompositeFIF {
|
||||
add(pattern("kotlin", "toString").receiverExists(), TO_STRING);
|
||||
add(pattern("kotlin", "equals").receiverExists(), KOTLIN_EQUALS);
|
||||
add(pattern("kotlin", "identityEquals").receiverExists(), IDENTITY_EQUALS);
|
||||
add(HASH_CODE_IN_ANY, KOTLIN_HASH_CODE);
|
||||
add(pattern(NamePredicate.PRIMITIVE_NUMBERS, "equals"), KOTLIN_EQUALS);
|
||||
add(pattern("String|Boolean|Char|Number.equals"), KOTLIN_EQUALS);
|
||||
add(pattern("kotlin", "arrayOfNulls"), new KotlinFunctionIntrinsic("nullArray"));
|
||||
|
||||
@@ -139,6 +139,11 @@ public final class JsAstUtils {
|
||||
return new JsBinaryOperation(JsBinaryOperator.SUB, left, right);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsBinaryOperation mul(@NotNull JsExpression left, @NotNull JsExpression right) {
|
||||
return new JsBinaryOperation(JsBinaryOperator.MUL, left, right);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JsPrefixOperation not(@NotNull JsExpression expression) {
|
||||
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package foo
|
||||
|
||||
data class Dat(val start: String, middle: String, val end: String) {
|
||||
fun getLabel() : String {
|
||||
return start + end
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEquals<T>(expected: T, actual: T) {
|
||||
if (expected != actual) throw Exception("expected: $expected, actual, $actual")
|
||||
}
|
||||
|
||||
fun assertNotEqual<T>(expected: T, actual: T) {
|
||||
if (expected == actual) throw Exception("unexpectedly equal: $expected and $actual")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val d = Dat("max", "-", "min")
|
||||
assertEquals("maxmin", d.getLabel())
|
||||
val (p1, p2) = d
|
||||
assertEquals("max", p1)
|
||||
assertEquals("min", p2)
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package foo
|
||||
|
||||
data class Dat(val start: String, middle: String, val end: String)
|
||||
|
||||
fun box(): String {
|
||||
val d1 = Dat("OO", "-", "PS")
|
||||
val d2: Dat = d1.copy(end = "K", middle = "+")
|
||||
val d3: Dat = d2.copy(start = "O", middle = "-")
|
||||
val (p1, p2) = d3
|
||||
return p1 + p2
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package foo
|
||||
|
||||
data class Holder<T>(val v: T)
|
||||
|
||||
data class Dat(val start: String, val end: String)
|
||||
|
||||
data class Dat2(val start: String, val end: String)
|
||||
|
||||
class Obj(val start: String, val end: String)
|
||||
|
||||
fun assertEquals<T>(expected: T, actual: T) {
|
||||
if (expected != actual) throw Exception("expected: $expected, actual, $actual")
|
||||
}
|
||||
|
||||
fun assertNotEqual<T>(expected: T, actual: T) {
|
||||
if (expected == actual) throw Exception("unexpectedly equal: $expected and $actual")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val d1 = Dat("a", "b")
|
||||
val d2 = Dat("a", "b")
|
||||
val d3 = Dat("a", "c")
|
||||
|
||||
val otherD1 = Dat2("a", "b")
|
||||
|
||||
assertEquals(d1, d1)
|
||||
assertEquals(d1, d2)
|
||||
assertNotEqual(d1, d3)
|
||||
|
||||
assertNotEqual(d1, otherD1)
|
||||
|
||||
var hd1 = Holder(Dat("y", "n"))
|
||||
var hd2 = Holder(Dat("y", "n"))
|
||||
var hd3 = Holder(Dat("1", "2"))
|
||||
|
||||
assertEquals(hd1, hd1)
|
||||
assertEquals(hd1, hd2)
|
||||
assertNotEqual(hd1, hd3)
|
||||
|
||||
var ho1 = Holder(Obj("+", "-"))
|
||||
var ho2 = Holder(Obj("+", "-"))
|
||||
var ho3 = Holder(Obj("*", "*"))
|
||||
|
||||
assertEquals(ho1, ho1)
|
||||
assertNotEqual(ho1, ho2)
|
||||
assertNotEqual(ho1, ho3)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package foo
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
|
||||
data class Holder<T>(val v: T)
|
||||
|
||||
data class Dat(val start: String, val end: String)
|
||||
|
||||
class Obj(val start: String, val end: String)
|
||||
|
||||
|
||||
fun assertEquals<T>(expected: T, actual: T) {
|
||||
if (expected != actual) throw Exception("expected: $expected, actual, $actual")
|
||||
}
|
||||
|
||||
fun assertSomeNotEqual<T>(c: Iterable<T>) {
|
||||
val it = c.iterator()
|
||||
val first = it.next()
|
||||
while (it.hasNext()) {
|
||||
val item: T = it.next()
|
||||
if (item != first) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw Exception("All elements are the same: $first")
|
||||
}
|
||||
|
||||
fun assertAllEqual<T>(c: Iterable<out T>) {
|
||||
val it = c.iterator()
|
||||
val first = it.next()
|
||||
while (it.hasNext()) {
|
||||
val item: T = it.next()
|
||||
assertEquals(first, item)
|
||||
}
|
||||
}
|
||||
|
||||
val hashCoder: (o: Any) -> Int = { o -> o.hashCode() }
|
||||
|
||||
val <T> wrapInH = { (t: T) -> Holder(t) }
|
||||
|
||||
fun box(): String {
|
||||
|
||||
// Check that same Dat's have the same hashcode.
|
||||
val sameDs = listOf(Dat("a", "b"), Dat("a", "b"))
|
||||
assertAllEqual(map(sameDs, hashCoder))
|
||||
|
||||
// Check that different Dat's have different hashcodes (at least some of them).
|
||||
val differentDs = listOf(Dat("a", "b"), Dat("a", "c"), Dat("a", "d"))
|
||||
assertSomeNotEqual(map(differentDs, hashCoder))
|
||||
|
||||
// Check the same on Obj's, which should be always different and with different hashcodes.
|
||||
val sameOs = listOf(Obj("a", "b"), Obj("a", "b"), Obj("a", "b"))
|
||||
val differentOs = listOf(Obj("a", "b"), Obj("a", "b"), Obj("a", "b"))
|
||||
|
||||
// Obj's are always different.
|
||||
assertSomeNotEqual(map(sameOs, hashCoder))
|
||||
assertSomeNotEqual(map(differentOs, hashCoder))
|
||||
|
||||
// Both Dat's and Obj's wrapped as Holder should retain their hashcode relations.
|
||||
val sameHDs = map(sameDs, wrapInH)
|
||||
assertAllEqual(map(sameHDs, hashCoder))
|
||||
val differentHDs = map(differentDs, wrapInH)
|
||||
assertSomeNotEqual(map(differentHDs, hashCoder))
|
||||
|
||||
val sameHOs = map(sameOs, wrapInH)
|
||||
assertSomeNotEqual(map(sameHOs, hashCoder))
|
||||
val differentHOs = map(differentOs, wrapInH)
|
||||
assertSomeNotEqual(map(differentHOs, hashCoder))
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
fun listOf<T>(vararg a: T): List<T> {
|
||||
val list = ArrayList<T>()
|
||||
for (e in a) {
|
||||
list.add(e)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun map<T, S>(c : Iterable<T>, transform : (T) -> S) : List<S> {
|
||||
val list = ArrayList<S>()
|
||||
for (t in c) {
|
||||
list.add(transform(t))
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package foo
|
||||
|
||||
data class Holder<T>(val v: T)
|
||||
|
||||
data class Dat(val start: String, val end: String)
|
||||
|
||||
class Obj(val start: String, val end: String)
|
||||
|
||||
fun assertEquals<T>(expected: T, actual: T) {
|
||||
if (expected != actual) throw Exception("expected: $expected, actual, $actual")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val setD = java.util.HashSet<Holder<Dat>>()
|
||||
setD.add(Holder(Dat("a", "b")))
|
||||
setD.add(Holder(Dat("a", "b")))
|
||||
setD.add(Holder(Dat("a", "b")))
|
||||
assertEquals(1, setD.size())
|
||||
|
||||
val setO = java.util.HashSet<Holder<Obj>>()
|
||||
setO.add(Holder(Obj("a", "b")))
|
||||
setO.add(Holder(Obj("a", "b")))
|
||||
setO.add(Holder(Obj("a", "b")))
|
||||
assertEquals(3, setO.size())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package foo
|
||||
|
||||
data class Holder<T>(val v: T)
|
||||
|
||||
data class Dat(val start: String, val end: String)
|
||||
|
||||
class Obj(val start: String, val end: String)
|
||||
|
||||
fun assertEquals<T>(expected: T, actual: T) {
|
||||
if (expected != actual) throw Exception("expected: $expected, actual, $actual")
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val d = Dat("a", "b")
|
||||
|
||||
assertEquals("Dat(start=a, end=b)", "${d}")
|
||||
|
||||
var hd = Holder(Dat("y", "n"))
|
||||
|
||||
assertEquals("Holder(v=Dat(start=y, end=n))", "${hd}")
|
||||
|
||||
var ho = Holder(Obj("+", "-"))
|
||||
|
||||
assertEquals("Holder(v=[object Object])", "${ho}")
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -48,6 +48,27 @@
|
||||
return obj1 === obj2;
|
||||
};
|
||||
|
||||
Kotlin.hashCode = function (obj) {
|
||||
if (obj == null) {
|
||||
return 0;
|
||||
}
|
||||
if ("function" == typeof obj.hashCode) {
|
||||
return obj.hashCode();
|
||||
}
|
||||
var objType = typeof obj;
|
||||
if ("object" == objType || "function" == objType) {
|
||||
return getObjectHashCode(obj);
|
||||
} else if ("number" == objType) {
|
||||
// TODO: a more elaborate code is needed for floating point values.
|
||||
return obj | 0;
|
||||
} if ("boolean" == objType) {
|
||||
return Number(obj)
|
||||
}
|
||||
|
||||
var str = String(obj);
|
||||
return getStringHashCode(str);
|
||||
};
|
||||
|
||||
Kotlin.toString = function (o) {
|
||||
if (o == null) {
|
||||
return "null";
|
||||
@@ -97,6 +118,29 @@
|
||||
};
|
||||
}
|
||||
|
||||
/** @const */
|
||||
var POW_2_32 = 4294967296;
|
||||
// TODO: consider switching to Symbol type once we are on ES6.
|
||||
/** @const */
|
||||
var OBJECT_HASH_CODE_PROPERTY_NAME = "kotlinHashCodeValue$";
|
||||
|
||||
function getObjectHashCode(obj) {
|
||||
if (!(OBJECT_HASH_CODE_PROPERTY_NAME in obj)) {
|
||||
var hash = (Math.random() * POW_2_32) | 0; // Make 32-bit singed integer.
|
||||
Object.defineProperty(obj, OBJECT_HASH_CODE_PROPERTY_NAME, { value: hash, enumerable: false });
|
||||
}
|
||||
return obj[OBJECT_HASH_CODE_PROPERTY_NAME];
|
||||
}
|
||||
|
||||
function getStringHashCode(str) {
|
||||
var hash = 0;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var code = str.charCodeAt(i);
|
||||
hash = (hash * 31 + code) | 0; // Keep it 32-bit.
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
* @template T
|
||||
|
||||
@@ -64,12 +64,12 @@
|
||||
}
|
||||
|
||||
function equals_fixedValueHasEquals(fixedValue, variableValue) {
|
||||
return fixedValue.equals(variableValue);
|
||||
return fixedValue.equals_za3rmp$(variableValue);
|
||||
}
|
||||
|
||||
function equals_fixedValueNoEquals(fixedValue, variableValue) {
|
||||
return (typeof variableValue.equals == FUNCTION) ?
|
||||
variableValue.equals(fixedValue) : (fixedValue === variableValue);
|
||||
return (typeof variableValue.equals_za3rmp$ == FUNCTION) ?
|
||||
variableValue.equals_za3rmp$(fixedValue) : (fixedValue === variableValue);
|
||||
}
|
||||
|
||||
function createKeyValCheck(kvStr) {
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
Bucket.prototype = {
|
||||
getEqualityFunction: function (searchValue) {
|
||||
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
|
||||
return (typeof searchValue.equals_za3rmp$ == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
|
||||
},
|
||||
|
||||
getEntryForKey: createBucketSearcher(ENTRY),
|
||||
@@ -687,7 +687,7 @@ Kotlin.PrimitiveHashSet = Kotlin.createClassNow(Kotlin.AbstractCollection,
|
||||
return h;
|
||||
};
|
||||
|
||||
this.equals = function (o) {
|
||||
this.equals_za3rmp$ = function (o) {
|
||||
if (o === null || o === undefined) return false;
|
||||
if (this.size() === o.size()) {
|
||||
var iter1 = this.iterator();
|
||||
|
||||
Reference in New Issue
Block a user