From 52344103d6b42b6a609a05705d22bb7b905d09cc Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Tue, 31 Jan 2012 20:23:15 +0400 Subject: [PATCH] Switched to using native Javascript array for Kotlin's Array instead of library class. Motivation: better interop with native libraries Index in bounds check is dropped for now. --- .idea/workspace.xml | 852 ++++++++---------- .../jetbrains/k2js/facade/K2JSTranslator.java | 1 - .../translate/context/StandardClasses.java | 13 +- .../expression/ExpressionVisitor.java | 7 +- .../k2js/translate/intrinsic/Intrinsics.java | 13 +- .../ArrayFunctionConstructorIntrinsic.java | 32 + .../array/ArrayIndicesIntrinsic.java | 31 + .../array/ArrayIteratorIntrinsic.java | 30 + .../array/ArrayNullConstructorIntrinsic.java | 9 +- .../intrinsic/array/ArraySizeIntrinsic.java | 32 + .../jetbrains/k2js/utils/GenerationUtils.java | 4 +- .../k2js/test/StandardClassesTest.java | 11 +- translator/testFiles/kotlinLib/cases/array.js | 6 +- translator/testFiles/kotlin_lib.js | 101 +-- 14 files changed, 564 insertions(+), 578 deletions(-) create mode 100644 translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java create mode 100644 translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java create mode 100644 translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java create mode 100644 translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 33ef73ecd68..ae69769127e 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -13,21 +13,20 @@ + + + + - + + + - - - - - - - - - - - + + + + @@ -85,6 +84,19 @@ - + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -199,37 +175,73 @@ - - + + - + - - + + - + - - + + - + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -239,64 +251,70 @@ - - + + - + - - + + - + - - + + - - + + + + - - + + - - + + + + - - + + - - + + + + - - - - - - - - - - + - + + + + + + + + + + @@ -314,7 +332,7 @@ - + @@ -354,22 +372,22 @@ @@ -824,6 +842,16 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + - - - - - - - - - - - - - @@ -1912,114 +1764,122 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java b/translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java index 984893c05a5..5353f614aff 100644 --- a/translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java +++ b/translator/src/org/jetbrains/k2js/facade/K2JSTranslator.java @@ -105,7 +105,6 @@ public final class K2JSTranslator { @NotNull public String generateCallToMain(@NotNull JetFile file, @NotNull String argumentString) { String namespaceName = getRootNamespaceName(file); - List arguments = parseString(argumentString); return GenerationUtils.generateCallToMain(namespaceName, arguments); } diff --git a/translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java b/translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java index fc84f2edb31..f06e53ca92b 100644 --- a/translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java +++ b/translator/src/org/jetbrains/k2js/translate/context/StandardClasses.java @@ -86,19 +86,15 @@ public final class StandardClasses { } private static void declareJetObjects(@NotNull StandardClasses standardClasses) { - standardClasses.declare().forFQ("jet.Array").kotlinClass("Array") - .properties("size", "indices"); - standardClasses.declare().forFQ("jet.Array").kotlinFunction("array"); - - standardClasses.declare().forFQ("jet.Iterator").kotlinClass("ArrayIterator") - .methods("next", "hasNext"); + standardClasses.declare().forFQ("jet.Iterator").kotlinClass("ArrayIteratorIntrinsic") + .methods("next").properties("hasNext"); standardClasses.declare().forFQ("jet.IntRange").kotlinClass("NumberRange") .methods("iterator", "contains").properties("start", "size", "end", "reversed"); - standardClasses.declare().forFQ("jet.String").kotlinClass("String"). - properties("length"); +// standardClasses.declare().forFQ("jet.String").kotlinClass("String"). +// properties("length"); standardClasses.declare().forFQ("jet.Any.toString").kotlinFunction("toString"); } @@ -162,7 +158,6 @@ public final class StandardClasses { @NotNull String... propertyNames) { for (String propertyName : propertyNames) { declareInner(classFQName, propertyName, propertyName); - } } diff --git a/translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java index 04def85bada..53809fd9178 100644 --- a/translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/expression/ExpressionVisitor.java @@ -19,6 +19,7 @@ import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator; import org.jetbrains.k2js.translate.reference.AccessTranslator; import org.jetbrains.k2js.translate.reference.CallTranslator; import org.jetbrains.k2js.translate.reference.ReferenceTranslator; +import org.jetbrains.k2js.translate.utils.BindingUtils; import org.jetbrains.k2js.translate.utils.TranslationUtils; import java.util.List; @@ -122,10 +123,14 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { + JsIf ifStatement = translateAsIfStatement(expression, context); + if (BindingUtils.isStatement(context.bindingContext(), expression)) { + return ifStatement; + } TemporaryVariable result = context.declareTemporary(context.program().getNullLiteral()); AstUtil.SaveLastExpressionMutator saveResultToTemporaryMutator = new AstUtil.SaveLastExpressionMutator(result.nameReference()); - JsNode mutatedIfStatement = AstUtil.mutateLastExpression(translateAsIfStatement(expression, context), + JsNode mutatedIfStatement = AstUtil.mutateLastExpression(ifStatement, saveResultToTemporaryMutator); JsStatement resultingStatement = AstUtil.convertToStatement(mutatedIfStatement); context.jsBlock().addStatement(resultingStatement); diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java index b87d830fbd2..fcee3e53773 100644 --- a/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/Intrinsics.java @@ -7,9 +7,7 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetStandardClasses; import org.jetbrains.jet.lang.types.JetStandardLibrary; import org.jetbrains.jet.lexer.JetToken; -import org.jetbrains.k2js.translate.intrinsic.array.ArrayGetIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.array.ArrayNullConstructorIntrinsic; -import org.jetbrains.k2js.translate.intrinsic.array.ArraySetIntrinsic; +import org.jetbrains.k2js.translate.intrinsic.array.*; import org.jetbrains.k2js.translate.intrinsic.primitive.*; import org.jetbrains.k2js.translate.intrinsic.string.LengthIntrinsic; import org.jetbrains.k2js.translate.intrinsic.tuple.TupleAccessIntrinsic; @@ -55,14 +53,23 @@ public final class Intrinsics { declareArrayIntrinsics(); } + //TODO: provide generic mechanism or refactor private void declareArrayIntrinsics() { JetScope arrayMemberScope = library.getArray().getDefaultType().getMemberScope(); FunctionDescriptor setFunction = getFunctionByName(arrayMemberScope, "set"); functionIntrinsics.put(setFunction, ArraySetIntrinsic.INSTANCE); FunctionDescriptor getFunction = getFunctionByName(arrayMemberScope, "get"); functionIntrinsics.put(getFunction, ArrayGetIntrinsic.INSTANCE); + PropertyDescriptor sizeProperty = getPropertyByName(arrayMemberScope, "size"); + functionIntrinsics.put(sizeProperty.getGetter(), ArraySizeIntrinsic.INSTANCE); + PropertyDescriptor indicesProperty = getPropertyByName(arrayMemberScope, "indices"); + functionIntrinsics.put(indicesProperty.getGetter(), ArrayIndicesIntrinsic.INSTANCE); FunctionDescriptor nullArrayConstructor = getFunctionByName(library.getLibraryScope(), "Array"); functionIntrinsics.put(nullArrayConstructor, ArrayNullConstructorIntrinsic.INSTANCE); + FunctionDescriptor iteratorFunction = getFunctionByName(arrayMemberScope, "iterator"); + functionIntrinsics.put(iteratorFunction, ArrayIteratorIntrinsic.INSTANCE); + ConstructorDescriptor arrayConstructor = library.getArray().getConstructors().iterator().next(); + functionIntrinsics.put(arrayConstructor, ArrayFunctionConstructorIntrinsic.INSTANCE); } private void declareTuplesIntrinsics() { diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java new file mode 100644 index 00000000000..80cdce7eecc --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayFunctionConstructorIntrinsic.java @@ -0,0 +1,32 @@ +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayFunctionConstructorIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, + @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver == null; + assert arguments.size() == 2; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayFromFun"); + return AstUtil.newInvocation(iteratorFunName, arguments); + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java new file mode 100644 index 00000000000..ed0d765eaea --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIndicesIntrinsic.java @@ -0,0 +1,31 @@ +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayIndicesIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 0; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayIndices"); + return AstUtil.newInvocation(iteratorFunName, receiver); + } +} \ No newline at end of file diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java new file mode 100644 index 00000000000..827b2bcccb4 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayIteratorIntrinsic.java @@ -0,0 +1,30 @@ +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArrayIteratorIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.size() == 0; + //TODO: provide better mechanism + JsNameRef iteratorFunName = AstUtil.newQualifiedNameRef("Kotlin.arrayIterator"); + return AstUtil.newInvocation(iteratorFunName, receiver); + } +} diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java index 227bb15d611..47f1f637b34 100644 --- a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArrayNullConstructorIntrinsic.java @@ -1,7 +1,8 @@ package org.jetbrains.k2js.translate.intrinsic.array; -import com.google.dart.compiler.backend.js.ast.JsArrayLiteral; import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.k2js.translate.context.TranslationContext; @@ -21,6 +22,10 @@ public enum ArrayNullConstructorIntrinsic implements FunctionIntrinsic { @Override public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, @NotNull TranslationContext context) { - return new JsArrayLiteral(); + assert receiver == null; + assert arguments.size() == 1; + //TODO: provide better mechanism + JsNameRef nullArrayFunName = AstUtil.newQualifiedNameRef("Kotlin.nullArray"); + return AstUtil.newInvocation(nullArrayFunName, arguments); } } diff --git a/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java new file mode 100644 index 00000000000..560311ccbe6 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/intrinsic/array/ArraySizeIntrinsic.java @@ -0,0 +1,32 @@ +package org.jetbrains.k2js.translate.intrinsic.array; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.k2js.translate.context.TranslationContext; +import org.jetbrains.k2js.translate.intrinsic.FunctionIntrinsic; + +import java.util.List; + +/** + * @author Pavel Talanov + */ +public enum ArraySizeIntrinsic implements FunctionIntrinsic { + + INSTANCE; + + + @NotNull + @Override + public JsExpression apply(@Nullable JsExpression receiver, @NotNull List arguments, + @NotNull TranslationContext context) { + assert receiver != null; + assert arguments.isEmpty() : "Length expression must have zero arguments."; + //TODO: provide better way + JsNameRef lengthProperty = AstUtil.newQualifiedNameRef("length"); + AstUtil.setQualifier(lengthProperty, receiver); + return lengthProperty; + } +} diff --git a/translator/src/org/jetbrains/k2js/utils/GenerationUtils.java b/translator/src/org/jetbrains/k2js/utils/GenerationUtils.java index af3f6b567e8..2e3bcc41ab9 100644 --- a/translator/src/org/jetbrains/k2js/utils/GenerationUtils.java +++ b/translator/src/org/jetbrains/k2js/utils/GenerationUtils.java @@ -11,10 +11,10 @@ public final class GenerationUtils { @NotNull public static String generateCallToMain(@NotNull String namespaceName, @NotNull List arguments) { - String constructArguments = "var args = Kotlin.array(" + arguments.size() + ");\n"; + String constructArguments = "var args = [];\n"; int index = 0; for (String argument : arguments) { - constructArguments = constructArguments + "args.set(" + index + ", \"" + argument + "\");\n"; + constructArguments = constructArguments + "args[" + index + "]= \"" + argument + "\";\n"; index++; } String callMain = namespaceName + ".main(args);\n"; diff --git a/translator/test/org/jetbrains/k2js/test/StandardClassesTest.java b/translator/test/org/jetbrains/k2js/test/StandardClassesTest.java index c3fbb148c8a..e5ba453bdbc 100644 --- a/translator/test/org/jetbrains/k2js/test/StandardClassesTest.java +++ b/translator/test/org/jetbrains/k2js/test/StandardClassesTest.java @@ -1,7 +1,6 @@ package org.jetbrains.k2js.test; import org.junit.Test; -import org.mozilla.javascript.JavaScriptException; /** * @author Pavel Talanov @@ -39,10 +38,12 @@ public class StandardClassesTest extends TranslationTest { testFooBoxIsTrue("arraySize.kt"); } - @Test(expected = JavaScriptException.class) - public void arrayThrowsExceptionOnOOBaccess() throws Exception { - testFooBoxIsTrue("arrayThrowsExceptionOnOOBaccess.kt"); - } + //TODO: this feature in not supported for some time + //TODO: support it. Probably confugurable. +// @Test(expected = JavaScriptException.class) +// public void arrayThrowsExceptionOnOOBaccess() throws Exception { +// testFooBoxIsTrue("arrayThrowsExceptionOnOOBaccess.kt"); +// } @Test public void arraysIterator() throws Exception { diff --git a/translator/testFiles/kotlinLib/cases/array.js b/translator/testFiles/kotlinLib/cases/array.js index 4a3adb7f6cc..6b6561c1b68 100644 --- a/translator/testFiles/kotlinLib/cases/array.js +++ b/translator/testFiles/kotlinLib/cases/array.js @@ -1,8 +1,6 @@ function test() { - var a = new Kotlin.Array(3, function () { + var a = Kotlin.arrayFromFun(3, function () { return 3 }); - a.set(0, "1") - a.set(2, "4") - return (a.get(0) == "1") && (a.get(2) == "4") && (a.get(1) == 3); + return (a[0] == 3) && (a[2] == 3) && (a[1] == 3); } \ No newline at end of file diff --git a/translator/testFiles/kotlin_lib.js b/translator/testFiles/kotlin_lib.js index 7f8b4e3ba28..8870fd97338 100644 --- a/translator/testFiles/kotlin_lib.js +++ b/translator/testFiles/kotlin_lib.js @@ -254,46 +254,6 @@ Kotlin.Exceptions = {} Kotlin.Exception = Kotlin.Class.create(); Kotlin.Exceptions.IndexOutOfBounds = {} - Kotlin.array = function (len) { - return new Kotlin.Array(len, function () { - return null - }); - } - Kotlin.Array = Class.create({ - initialize:function (len, f) { - this.array = []; - var i = 0; - while (i < len) { - this.array.push(f(i)); - ++i; - } - }, - get:function (index) { - if ((index < 0) || (index >= this.array.length)) { - throw Kotlin.Exceptions.IndexOutOfBounds; - } - return (this.array)[index]; - }, - set:function (index, value) { - if ((index < 0) || (index >= this.array.length)) { - throw Kotlin.Exceptions.IndexOutOfBounds; - } - (this.array)[index] = value; - }, - size:function () { - return this.array.length; - }, - //TODO: remove duplicated methods - get_size:function () { - return this.array.length; - }, - iterator:function () { - return new Kotlin.ArrayIterator(this); - }, - get_indices:function () { - return new Kotlin.NumberRange(0, this.size(), false); - } - }); Kotlin.ArrayList = Class.create({ @@ -352,19 +312,6 @@ }); - Kotlin.ArrayIterator = Class.create({ - initialize:function (array) { - this.array = array; - this.index = 0; - }, - next:function () { - return this.array.get(this.index++); - }, - hasNext:function () { - return (this.array.size() > this.index); - } - }); - Kotlin.parseInt = function (str) { return parseInt(str); @@ -502,9 +449,9 @@ Kotlin.Comparator = Kotlin.Class.create( { - initialize:function() { + initialize:function () { }, - compare:function() { + compare:function () { throw new Kotlin.AbstractFunctionInvokationError(); } } @@ -524,6 +471,50 @@ } ); + Kotlin.nullArray = function (size) { + var res = []; + var i = size; + while (i > 0) { + res[--i] = null; + } + return res; + }; + + Kotlin.arrayFromFun = function (size, initFun) { + var res = []; + var i = size; + while (i > 0) { + res[--i] = initFun(i); + } + return res; + }; + + Kotlin.arrayIndices = function (arr) { + return new Kotlin.NumberRange(0, arr.length); + }; + + var intrinsicArrayIterator = Kotlin.Class.create( + Kotlin.Iterator, + { + initialize:function (arr) { + this.arr = arr; + this.len = arr.length; + this.i = 0; + }, + hasNext:function () { + return (this.i < this.len); + }, + next:function () { + return this.arr[this.i++]; + }, + get_hasNext:function() { return this.hasNext()} + } + ); + + Kotlin.arrayIterator = function (arr) { + return new intrinsicArrayIterator(arr); + }; + Kotlin.toString = function (obj) { return obj.toString(); };