From eec6fd137fa079f60d429fdb4aaac8801972f68a Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Wed, 16 Nov 2011 17:44:46 +0400 Subject: [PATCH 01/22] Refactored test code a bit. --- .../jetbrains/k2js/test/KotlinLibTest.java | 35 +++++----------- .../k2js/test/RhinoPropertyTypesChecker.java | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java diff --git a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java index 894c8000bd1..bf589819f46 100644 --- a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java +++ b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -3,7 +3,6 @@ package org.jetbrains.k2js.test; import org.junit.Test; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Scriptable; import java.util.Arrays; @@ -11,8 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.junit.Assert.assertTrue; - /** * @author Talanov Pavel */ @@ -30,39 +27,27 @@ public class KotlinLibTest extends TranslationTest { return MAIN; } - protected void verifyObjectHasExpectedPropertiesOfExpectedTypes - (NativeObject object, Map> nameToClassMap) { - for (Map.Entry> entry : nameToClassMap.entrySet()) { - String name = entry.getKey(); - Class expectedClass = entry.getValue(); - assertTrue(object + " must contain key " + name, object.containsKey(name)); - assertTrue(object + "'s property " + name + " must be of type " + expectedClass, - expectedClass.isInstance(object.get(name))); - } + private void runPropertyTypeCheck(String objectName, Map> propertyToType) + throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoPropertyTypesChecker(objectName, propertyToType)); } @Test public void kotlinJsLibRunsWithRhino() throws Exception { Context context = Context.enter(); Scriptable scope = context.initStandardObjects(); - // ss runFileWithRhino(kotlinLibraryPath(), context, scope); Context.exit(); } @Test public void classObjectHasCreateMethod() throws Exception { - final Map> nameToClassMap = - new HashMap>(); - nameToClassMap.put("create", Function.class); - - runRhinoTest(Arrays.asList(kotlinLibraryPath()), - new RhinoResultChecker() { - @Override - public void runChecks(Context context, Scriptable scope) throws Exception { - NativeObject object = RhinoUtils.extractObject("Class", scope); - verifyObjectHasExpectedPropertiesOfExpectedTypes(object, nameToClassMap); - } - }); + String objectName = "Class"; + final Map> propertyToType + = new HashMap>(); + propertyToType.put("create", Function.class); + runPropertyTypeCheck(objectName, propertyToType); } + + } diff --git a/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java new file mode 100644 index 00000000000..8e25bb880c3 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java @@ -0,0 +1,41 @@ +package org.jetbrains.k2js.test; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.NativeObject; +import org.mozilla.javascript.Scriptable; + +import java.util.Map; + +import static org.junit.Assert.assertTrue; + +/** + * @author Talanov Pavel + */ +public class RhinoPropertyTypesChecker implements RhinoResultChecker { + + final private String objectName; + final private Map> propertyToType; + + public RhinoPropertyTypesChecker(String objectName, Map> propertyToType) { + this.objectName = objectName; + this.propertyToType = propertyToType; + } + + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + NativeObject object = RhinoUtils.extractObject(objectName, scope); + verifyObjectHasExpectedPropertiesOfExpectedTypes(object, propertyToType); + } + + private void verifyObjectHasExpectedPropertiesOfExpectedTypes + (NativeObject object, Map> nameToClassMap) { + for (Map.Entry> entry : nameToClassMap.entrySet()) { + String name = entry.getKey(); + Class expectedClass = entry.getValue(); + assertTrue(object + " must contain key " + name, object.containsKey(name)); + assertTrue(object + "'s property " + name + " must be of type " + expectedClass, + expectedClass.isInstance(object.get(name))); + } + } + +} From 81d7f86b935cad1cd65c22b2681ba09920e2b549 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Wed, 16 Nov 2011 19:37:03 +0400 Subject: [PATCH 02/22] Worked on kotlinJsLib and supported trait extending other trait. --- .../k2js/translate/ClassTranslator.java | 30 +- .../org/jetbrains/k2js/translate/Namer.java | 8 +- .../jetbrains/k2js/test/KotlinLibTest.java | 18 +- .../jetbrains/k2js/test/TranslationTest.java | 5 + translator/testFiles/kotlinLib/cases/trait.js | 12 + translator/testFiles/kotlin_lib.js | 21 +- translator/testFiles/prototype.js | 388 ++++++++++++++++++ 7 files changed, 453 insertions(+), 29 deletions(-) create mode 100644 translator/testFiles/kotlinLib/cases/trait.js create mode 100644 translator/testFiles/prototype.js diff --git a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java index 3857087688e..38c0987c336 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java @@ -31,34 +31,20 @@ public final class ClassTranslator extends AbstractTranslator { } @NotNull - public JsStatement translateClass(@NotNull JetClass classDeclaration) { - if (!classDeclaration.isTrait()) { - return translateAsClassWithState(classDeclaration); - } else { - return translateAsStatelessTrait(classDeclaration); - } - } - - @NotNull - private JsStatement translateAsStatelessTrait(@NotNull JetClass classDeclaration) { - JsObjectLiteral traitLiteral = translateClassDeclarations(classDeclaration); - return AstUtil.convertToStatement - (AstUtil.newAssignment(namespaceQualifiedClassNameReference(classDeclaration), traitLiteral)); - } - - @NotNull - private JsStatement translateAsClassWithState(@NotNull JetClass jetClassDeclaration) { - JsInvocation jsClassDeclaration = createMethodInvocation(); + public JsStatement translateClass(@NotNull JetClass jetClassDeclaration) { + JsInvocation jsClassDeclaration = classCreateMethodInvocation(jetClassDeclaration); addSuperclassReferences(jetClassDeclaration, jsClassDeclaration); addClassOwnDeclarations(jetClassDeclaration, jsClassDeclaration); return classDeclarationStatement(jetClassDeclaration, jsClassDeclaration); } @NotNull - private JsInvocation createMethodInvocation() { - JsInvocation jsClassDeclaration = new JsInvocation(); - jsClassDeclaration.setQualifier(Namer.creationMethodReference()); - return jsClassDeclaration; + private JsInvocation classCreateMethodInvocation(@NotNull JetClass jetClassDeclaration) { + if (jetClassDeclaration.isTrait()) { + return AstUtil.newInvocation(Namer.traitCreationMethodReference()); + } else { + return AstUtil.newInvocation(Namer.classCreationMethodReference()); + } } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/Namer.java b/translator/src/org/jetbrains/k2js/translate/Namer.java index c792d03fd70..676ff9f2a48 100644 --- a/translator/src/org/jetbrains/k2js/translate/Namer.java +++ b/translator/src/org/jetbrains/k2js/translate/Namer.java @@ -58,14 +58,18 @@ public final class Namer { return name; } + //TODO: dummy public static JsNameRef classObjectReference() { - //TODO dummy return AstUtil.newQualifiedNameRef("Class"); } - public static JsNameRef creationMethodReference() { + public static JsNameRef classCreationMethodReference() { return AstUtil.newQualifiedNameRef("Class.create"); } + public static JsNameRef traitCreationMethodReference() { + return AstUtil.newQualifiedNameRef("Trait.create"); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java index bf589819f46..249a9ba7c2c 100644 --- a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java +++ b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -42,12 +42,26 @@ public class KotlinLibTest extends TranslationTest { @Test public void classObjectHasCreateMethod() throws Exception { - String objectName = "Class"; final Map> propertyToType = new HashMap>(); propertyToType.put("create", Function.class); - runPropertyTypeCheck(objectName, propertyToType); + runPropertyTypeCheck("Class", propertyToType); } + @Test + public void traitObjectHasCreateMethod() throws Exception { + final Map> propertyToType + = new HashMap>(); + propertyToType.put("create", Function.class); + runPropertyTypeCheck("Trait", propertyToType); + } + + @Test + public void createdTraitIsJSObject() throws Exception { + final Map> propertyToType + = new HashMap>(); + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("trait.js")), + new RhinoPropertyTypesChecker("foo", propertyToType)); + } } diff --git a/translator/test/org/jetbrains/k2js/test/TranslationTest.java b/translator/test/org/jetbrains/k2js/test/TranslationTest.java index 600b54cc3b4..8263ca5c11b 100644 --- a/translator/test/org/jetbrains/k2js/test/TranslationTest.java +++ b/translator/test/org/jetbrains/k2js/test/TranslationTest.java @@ -56,6 +56,7 @@ public abstract class TranslationTest { abstract protected List generateFilenameList(String inputfile); + //TODO: refactor filename generation logic private String getOutputFilePath(String filename) { return getOutputDirectory() + convertToDotJsFile(filename); } @@ -68,6 +69,10 @@ public abstract class TranslationTest { return getInputDirectory() + filename; } + protected String cases(String filename) { + return getInputFilePath(filename); + } + protected void runFileWithRhino(String inputFile, Context context, Scriptable scope) throws Exception { FileReader reader = new FileReader(inputFile); context.evaluateReader(scope, reader, inputFile, 1, null); diff --git a/translator/testFiles/kotlinLib/cases/trait.js b/translator/testFiles/kotlinLib/cases/trait.js new file mode 100644 index 00000000000..f1efec21a20 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/trait.js @@ -0,0 +1,12 @@ +foo = {}; +(function(foo){ + foo.Test = Trait.create({addFoo:function(s){ + return s + 'FOO'; + } + }); + foo.ExtendedTest = Trait.create(foo.Test, {hooray:function(){ + return 'hooray'; + } + }); +} +(foo)); diff --git a/translator/testFiles/kotlin_lib.js b/translator/testFiles/kotlin_lib.js index 8c0e002e123..2e100bfc744 100644 --- a/translator/testFiles/kotlin_lib.js +++ b/translator/testFiles/kotlin_lib.js @@ -66,8 +66,6 @@ var Class = (function() { return function() { return ancestor[m].apply(this, arguments); }; })(property).wrap(method); - // value.valueOf = method.valueOf.bind(method); - // value.toString = method.toString.bind(method); } this.prototype[property] = value; } @@ -85,8 +83,25 @@ var Class = (function() { var Trait = (function() { + + function add(object, source) { + properties = Object.keys(source); + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i]; + var value = source[property]; + object[property] = value; + } + return this; + } + function create() { - return new Class.create(arguments); + + result = {} + for (var i = 0, length = arguments.length; i < length; i++) + { + add(result, arguments[i]); + } + return result; } return { diff --git a/translator/testFiles/prototype.js b/translator/testFiles/prototype.js new file mode 100644 index 00000000000..8de2bd31b17 --- /dev/null +++ b/translator/testFiles/prototype.js @@ -0,0 +1,388 @@ +function $A(iterable) { + if (!iterable) return []; + if ('toArray' in Object(iterable)) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +var emptyFunction = function() {} + +var Class = (function() { + + var IS_DONTENUM_BUGGY = (function(){ + for (var p in { toString: 1 }) { + if (p === 'toString') return false; + } + return true; + })(); + + function subclass() {}; + function create() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0, length = properties.length; i < length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = emptyFunction; + + klass.prototype.constructor = klass; + return klass; + } + + function addMethods(source) { + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); + + if (IS_DONTENUM_BUGGY) { + if (source.toString != Object.prototype.toString) + properties.push("toString"); + if (source.valueOf != Object.prototype.valueOf) + properties.push("valueOf"); + } + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames()[0] == "$super") { + var method = value; + value = (function(m) { + return function() { return ancestor[m].apply(this, arguments); }; + })(property).wrap(method); + + value.valueOf = method.valueOf.bind(method); + value.toString = method.toString.bind(method); + } + this.prototype[property] = value; + } + + return this; + } + + return { + create: create, + Methods: { + addMethods: addMethods + } + }; +})(); + +var Trait = (function() { + + function create() { + var traitClass = Class.create.apply(Class, arguments) + return new traitClass; + } + + return { + create: create + }; +})(); + +(function() { + + var _toString = Object.prototype.toString, + NULL_TYPE = 'Null', + UNDEFINED_TYPE = 'Undefined', + BOOLEAN_TYPE = 'Boolean', + NUMBER_TYPE = 'Number', + STRING_TYPE = 'String', + OBJECT_TYPE = 'Object', + FUNCTION_CLASS = '[object Function]', + BOOLEAN_CLASS = '[object Boolean]', + NUMBER_CLASS = '[object Number]', + STRING_CLASS = '[object String]', + ARRAY_CLASS = '[object Array]', + DATE_CLASS = '[object Date]'; + + function Type(o) { + switch(o) { + case null: return NULL_TYPE; + case (void 0): return UNDEFINED_TYPE; + } + var type = typeof o; + switch(type) { + case 'boolean': return BOOLEAN_TYPE; + case 'number': return NUMBER_TYPE; + case 'string': return STRING_TYPE; + } + return OBJECT_TYPE; + } + + function extend(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; + } + + function inspect(object) { + try { + if (isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + } + + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key], + type = typeof value; + + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); + } + + var _class = _toString.call(value); + + switch (_class) { + case NUMBER_CLASS: + case BOOLEAN_CLASS: + case STRING_CLASS: + value = value.valueOf(); + } + + switch (value) { + case null: return 'null'; + case true: return 'true'; + case false: return 'false'; + } + + type = typeof value; + switch (type) { + case 'string': + return value.inspect(true); + case 'number': + return isFinite(value) ? String(value) : 'null'; + case 'object': + + for (var i = 0, length = stack.length; i < length; i++) { + if (stack[i] === value) { throw new TypeError(); } + } + stack.push(value); + + var partial = []; + if (_class === ARRAY_CLASS) { + for (var i = 0, length = value.length; i < length; i++) { + var str = Str(i, value, stack); + partial.push(typeof str === 'undefined' ? 'null' : str); + } + partial = '[' + partial.join(',') + ']'; + } else { + var keys = Object.keys(value); + for (var i = 0, length = keys.length; i < length; i++) { + var key = keys[i], str = Str(key, value, stack); + if (typeof str !== "undefined") { + partial.push(key.inspect(true)+ ':' + str); + } + } + partial = '{' + partial.join(',') + '}'; + } + stack.pop(); + return partial; + } + } + + function stringify(object) { + return JSON.stringify(object); + } + + function toQueryString(object) { + return $H(object).toQueryString(); + } + + function toHTML(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + } + + function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } + 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; + } + + function clone(object) { + return extend({ }, object); + } + + function isElement(object) { + return !!(object && object.nodeType == 1); + } + + function isArray(object) { + return _toString.call(object) === ARRAY_CLASS; + } + + var hasNativeIsArray = (typeof Array.isArray == 'function') + && Array.isArray([]) && !Array.isArray({}); + + if (hasNativeIsArray) { + isArray = Array.isArray; + } + + function isHash(object) { + return object instanceof Hash; + } + + function isFunction(object) { + return _toString.call(object) === FUNCTION_CLASS; + } + + function isString(object) { + return _toString.call(object) === STRING_CLASS; + } + + function isNumber(object) { + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; + } + + function isUndefined(object) { + return typeof object === "undefined"; + } + + extend(Object, { + extend: extend, + inspect: inspect, + toQueryString: toQueryString, + toHTML: toHTML, + keys: Object.keys || keys, + values: values, + clone: clone, + isElement: isElement, + isArray: isArray, + isHash: isHash, + isFunction: isFunction, + isString: isString, + isNumber: isNumber, + isDate: isDate, + isUndefined: isUndefined + }); +})(); + + +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 merge(array, args) { + array = slice.call(array, 0); + return update(array, args); + } + + 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 bind(context) { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = slice.call(arguments, 1); + return function() { + var a = merge(args, arguments); + return __method.apply(context, a); + } + } + + 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 curry() { + if (!arguments.length) return this; + var __method = this, args = slice.call(arguments, 0); + return function() { + var a = merge(args, arguments); + return __method.apply(this, a); + } + } + + function delay(timeout) { + var __method = this, args = slice.call(arguments, 1); + timeout = timeout * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + } + + function defer() { + var args = update([0.01], arguments); + return this.delay.apply(this, args); + } + + function wrap(wrapper) { + var __method = this; + return function() { + var a = update([__method.bind(this)], arguments); + return wrapper.apply(this, a); + } + } + + function methodize() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + var a = update([this], arguments); + return __method.apply(null, a); + }; + } + + return { + argumentNames: argumentNames, + bind: bind, + bindAsEventListener: bindAsEventListener, + curry: curry, + delay: delay, + defer: defer, + wrap: wrap, + methodize: methodize + } +})()); From 9d848cb8a597c1caa47239a8682d1752ee7117b0 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Wed, 16 Nov 2011 19:40:28 +0400 Subject: [PATCH 03/22] Added a test for a trait extending two traits. --- .../org/jetbrains/k2js/test/TraitTest.java | 5 ++++ .../trait/cases/traitExtendsTwoTraits.kt | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 translator/testFiles/trait/cases/traitExtendsTwoTraits.kt diff --git a/translator/test/org/jetbrains/k2js/test/TraitTest.java b/translator/test/org/jetbrains/k2js/test/TraitTest.java index b08ef053be9..a153e15458c 100644 --- a/translator/test/org/jetbrains/k2js/test/TraitTest.java +++ b/translator/test/org/jetbrains/k2js/test/TraitTest.java @@ -39,4 +39,9 @@ public final class TraitTest extends AbstractClassTest { testFooBoxIsTrue("traitExtendsTrait.kt"); } + @Test + public void traitExtendsTwoTraits() throws Exception { + testFooBoxIsTrue("traitExtendsTwoTraits.kt"); + } + } diff --git a/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt b/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt new file mode 100644 index 00000000000..fa28a2a94c9 --- /dev/null +++ b/translator/testFiles/trait/cases/traitExtendsTwoTraits.kt @@ -0,0 +1,25 @@ +namespace foo + +trait A { + fun addFoo(s:String) : String { + return s + "FOO" + } +} + +trait B { + fun hooray() : String { + return "hooray" + } +} + +trait AD : A, B { + +} + +class Test() : AD { + fun eval() : String { + return addFoo(hooray()); + } +} + +fun box() = (Test().eval() == "hoorayFOO") \ No newline at end of file From fec7539f8c9b669f1309f940a79bf3f330744dfb Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 14:14:47 +0400 Subject: [PATCH 04/22] Supported (Variable is Type expression) for user defined classes. --- .../google/dart/compiler/util/AstUtil.java | 4 + .../k2js/translate/AbstractTranslator.java | 12 --- .../k2js/translate/BindingUtils.java | 34 +++++-- .../k2js/translate/ExpressionVisitor.java | 14 +++ .../org/jetbrains/k2js/translate/Namer.java | 6 +- .../k2js/translate/NamespaceTranslator.java | 2 +- .../k2js/translate/OperationTranslator.java | 22 +++-- .../k2js/translate/OperatorTable.java | 27 +++++- .../jetbrains/k2js/translate/Translation.java | 5 ++ .../translate/TypeOperationTranslator.java | 90 +++++++++++++++++++ .../jetbrains/k2js/test/KotlinLibTest.java | 29 +++++- .../org/jetbrains/k2js/test/RTTITest.java | 22 +++++ .../k2js/test/RhinoFunctionResultChecker.java | 43 +++++++-- .../k2js/test/RhinoPropertyTypesChecker.java | 2 +- .../jetbrains/k2js/test/TranslationTest.java | 8 +- .../kotlinLib/cases/isAncestorType.js | 7 ++ .../kotlinLib/cases/isComplexTest.js | 24 +++++ .../kotlinLib/cases/isNotOtherType.js | 8 ++ .../testFiles/kotlinLib/cases/isSameType.js | 6 ++ translator/testFiles/kotlin_lib.js | 20 ++++- .../testFiles/rtti/cases/isSameClass.kt | 7 ++ 21 files changed, 348 insertions(+), 44 deletions(-) create mode 100644 translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java create mode 100644 translator/test/org/jetbrains/k2js/test/RTTITest.java create mode 100644 translator/testFiles/kotlinLib/cases/isAncestorType.js create mode 100644 translator/testFiles/kotlinLib/cases/isComplexTest.js create mode 100644 translator/testFiles/kotlinLib/cases/isNotOtherType.js create mode 100644 translator/testFiles/kotlinLib/cases/isSameType.js create mode 100644 translator/testFiles/rtti/cases/isSameClass.kt diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index 2ea618c8bea..a1ba71ce077 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -268,4 +268,8 @@ public class AstUtil { result.setStatements(statements); return result; } + + public static JsPrefixOperation negation(JsExpression expression) { + return new JsPrefixOperation(JsUnaryOperator.NOT, expression); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java b/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java index 6072b4c4b23..6f44e0846db 100644 --- a/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java @@ -1,9 +1,7 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsProgram; -import com.google.dart.compiler.backend.js.ast.JsScope; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.resolve.BindingContext; /** * @author Talanov Pavel @@ -26,14 +24,4 @@ public abstract class AbstractTranslator { protected TranslationContext translationContext() { return context; } - - @NotNull - protected BindingContext bindingContext() { - return context.bindingContext(); - } - - @NotNull - protected JsScope scope() { - return context.enclosingScope(); - } } diff --git a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java index 42e183eb81b..794cd6ecb28 100644 --- a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java +++ b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java @@ -91,17 +91,23 @@ public final class BindingUtils { Collection superclassTypes = classDescriptor.getTypeConstructor().getSupertypes(); List superClassDescriptors = new ArrayList(); for (JetType type : superclassTypes) { - DeclarationDescriptor superClassDescriptor = - type.getConstructor().getDeclarationDescriptor(); - assert superClassDescriptor instanceof ClassDescriptor - : "Superclass descriptor of a type should be of type ClassDescriptor"; - if (isNotAny(superClassDescriptor)) { - superClassDescriptors.add((ClassDescriptor) superClassDescriptor); + ClassDescriptor result = getClassDescriptorForType(type); + if (isNotAny(result)) { + superClassDescriptors.add(result); } } return superClassDescriptors; } + @NotNull + static public ClassDescriptor getClassDescriptorForType(@NotNull JetType type) { + DeclarationDescriptor superClassDescriptor = + type.getConstructor().getDeclarationDescriptor(); + assert superClassDescriptor instanceof ClassDescriptor + : "Superclass descriptor of a type should be of type ClassDescriptor"; + return (ClassDescriptor) superClassDescriptor; + } + static public boolean hasAncestorClass(@NotNull BindingContext context, @NotNull JetClass classDeclaration) { List superclassDescriptors = getSuperclassDescriptors(context, classDeclaration); return (findAncestorClass(superclassDescriptors) != null); @@ -113,8 +119,22 @@ public final class BindingUtils { return isStatement; } + @NotNull + static public JetType getTypeByReference(@NotNull BindingContext context, + @NotNull JetTypeReference typeReference) { + JetType result = context.get(BindingContext.TYPE, typeReference); + assert result != null : "TypeReference should reference a type"; + return result; + } + + @NotNull + static public ClassDescriptor getClassDescriptorForTypeReference(@NotNull BindingContext context, + @NotNull JetTypeReference typeReference) { + return getClassDescriptorForType(getTypeByReference(context, typeReference)); + } + //TODO better implementation? - private static boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) { + static private boolean isNotAny(@NotNull DeclarationDescriptor superClassDescriptor) { return !superClassDescriptor.getName().equals("Any"); } diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index a72ad7771ea..a83dc2301b6 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -350,5 +350,19 @@ public final class ExpressionVisitor extends TranslatorVisitor { } +// @Override +// @NotNull +// public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, +// @NotNull TranslationContext context) { +// return Translation.typeOperationTranslator(context).translate(expression); +// } + + @Override + @NotNull + public JsNode visitIsExpression(@NotNull JetIsExpression expression, + @NotNull TranslationContext context) { + return Translation.typeOperationTranslator(context).translateIsExpression(expression); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/Namer.java b/translator/src/org/jetbrains/k2js/translate/Namer.java index 676ff9f2a48..533bfa6a411 100644 --- a/translator/src/org/jetbrains/k2js/translate/Namer.java +++ b/translator/src/org/jetbrains/k2js/translate/Namer.java @@ -8,7 +8,7 @@ import com.google.dart.compiler.util.AstUtil; */ /* - * This class is a complete dummy and needs a lot of work + * This class is a complete dummy and should completely change in the future */ public final class Namer { @@ -71,5 +71,9 @@ public final class Namer { return AstUtil.newQualifiedNameRef("Trait.create"); } + public static JsNameRef isOperationReference() { + return AstUtil.newQualifiedNameRef("isType"); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java index 492d9402374..d5f4606efea 100644 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java @@ -33,7 +33,7 @@ public final class NamespaceTranslator extends AbstractTranslator { public JsBlock translate(@NotNull JetNamespace namespace) { // TODO support multiple namespaces JsBlock block = program().getFragmentBlock(0); - JsName namespaceName = scope().declareName(Namer.getNameForNamespace(namespace.getName())); + JsName namespaceName = translationContext().enclosingScope().declareName(Namer.getNameForNamespace(namespace.getName())); block.addStatement(namespaceInitStatement(namespaceName)); TranslationContext newContext = translationContext().newNamespace(namespace); JsFunction dummyFunction = JsFunction.getAnonymousFunctionWithScope diff --git a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java index 9fd3af25a71..11420e841b4 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java @@ -80,18 +80,21 @@ public final class OperationTranslator extends AbstractTranslator { return setterCall; } - @NotNull - private JetToken getOperationToken(@NotNull JetBinaryExpression expression) { - return (JetToken) expression.getOperationToken(); - } - @NotNull private JsExpression translateAsBinaryOperation(@NotNull JetBinaryExpression expression) { JsExpression left = AstUtil.convertToExpression (Translation.translateExpression(expression.getLeft(), translationContext())); JsExpression right = translateRightExpression(expression); - JetToken jetOperationToken = getOperationToken(expression); - return new JsBinaryOperation(OperatorTable.getBinaryOperator(jetOperationToken), left, right); + JetToken token = getOperationToken(expression); + if (OperatorTable.hasCorrespondingBinaryOperator(token)) { + return new JsBinaryOperation(OperatorTable.getBinaryOperator(token), left, right); + } + if (OperatorTable.hasCorrespondingFunctionInvocation(token)) { + JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token); + functionInvocation.setArguments(Arrays.asList(left, right)); + return functionInvocation; + } + throw new AssertionError("Unsupported token encountered: " + token.toString()); } @NotNull @@ -102,5 +105,10 @@ public final class OperationTranslator extends AbstractTranslator { (Translation.translateExpression(rightExpression, translationContext())); } + @NotNull + private JetToken getOperationToken(@NotNull JetBinaryExpression expression) { + return (JetToken) expression.getOperationToken(); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java index d9970e7122a..748f86441e6 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java +++ b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java @@ -1,7 +1,10 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsBinaryOperator; +import com.google.dart.compiler.backend.js.ast.JsInvocation; +import com.google.dart.compiler.backend.js.ast.JsNameRef; import com.google.dart.compiler.backend.js.ast.JsUnaryOperator; +import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; @@ -14,9 +17,9 @@ import java.util.Map; */ public final class OperatorTable { - private static Map binaryOperatorsMap = new HashMap(); private static Map unaryOperatorsMap = new HashMap(); + private static Map operatorToFunctionNameReference = new HashMap(); static { unaryOperatorsMap.put(JetTokens.PLUSPLUS, JsUnaryOperator.INC); //++ @@ -43,6 +46,27 @@ public final class OperatorTable { binaryOperatorsMap.put(JetTokens.PERC, JsBinaryOperator.MOD); } + static { + operatorToFunctionNameReference.put(JetTokens.IS_KEYWORD, Namer.isOperationReference()); + } + + @NotNull + public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) { + return binaryOperatorsMap.containsKey(token); + } + + @NotNull + static boolean hasCorrespondingFunctionInvocation(@NotNull JetToken token) { + return operatorToFunctionNameReference.containsKey(token); + } + + @NotNull + public static JsInvocation getCorrespondingFunctionInvocation(@NotNull JetToken token) { + JsNameRef functionReference = operatorToFunctionNameReference.get(token); + assert functionReference != null : "Token should represent a function call!"; + return AstUtil.newInvocation(functionReference); + } + @NotNull public static JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; @@ -59,4 +83,5 @@ public final class OperatorTable { return (token == JetTokens.EQ); } + } diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index 5c9d7749b18..f6f4f4b22a0 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -46,6 +46,11 @@ public final class Translation { return DeclarationTranslator.newInstance(context); } + @NotNull + static public TypeOperationTranslator typeOperationTranslator(@NotNull TranslationContext context) { + return TypeOperationTranslator.newInstance(context); + } + @NotNull static public JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { return expressionTranslator(context).translate(expression); diff --git a/translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java new file mode 100644 index 00000000000..11e3aa1750a --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java @@ -0,0 +1,90 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.JsExpression; +import com.google.dart.compiler.backend.js.ast.JsName; +import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.psi.JetIsExpression; +import org.jetbrains.jet.lang.psi.JetTypePattern; +import org.jetbrains.jet.lang.psi.JetTypeReference; + +/** + * @author Talanov Pavel + */ +public final class TypeOperationTranslator extends AbstractTranslator { + + @NotNull + public static TypeOperationTranslator newInstance(@NotNull TranslationContext context) { + return new TypeOperationTranslator(context); + } + + private TypeOperationTranslator(@NotNull TranslationContext context) { + super(context); + } + +// @NotNull +// public JsExpression translate(@NotNull JetBinaryExpressionWithTypeRHS expression) { +// JsExpression left = AstUtil.convertToExpression +// (Translation.translateExpression(expression.getLeft(), translationContext())); +// JsNameRef right = getClassReference(expression); +// JetToken token = getOperationToken(expression); +// if (OperatorTable.hasCorrespondingFunctionInvocation(token)) { +// JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token); +// functionInvocation.setArguments(Arrays.asList(left, right)); +// return functionInvocation; +// } +// throw new AssertionError("Unsupported token encountered: " + token.toString()); +// } + + @NotNull + JsExpression translateIsExpression(@NotNull JetIsExpression expression) { + JsExpression left = AstUtil.convertToExpression + (Translation.translateExpression(expression.getLeftHandSide(), translationContext())); + JsExpression resultingExpression = translateTypePattern(left, (JetTypePattern) expression.getPattern()); + if (expression.isNegated()) { + return AstUtil.negation(resultingExpression); + } + return resultingExpression; + } + + @NotNull + private JsExpression translateTypePattern(@NotNull JsExpression expressionToMatch, + @NotNull JetTypePattern pattern) { + return AstUtil.newInvocation(Namer.isOperationReference(), expressionToMatch, getClassReference(pattern)); + } + + @NotNull + private JsExpression getClassReference(@NotNull JetTypePattern pattern) { + JetTypeReference typeReference = pattern.getTypeReference(); + assert typeReference != null : "Type pattern should contain a type reference"; + return getClassNameReferenceForTypeReference(typeReference); + } + +// @NotNull +// private JsNameRef getClassReference(@NotNull JetBinaryExpressionWithTypeRHS expression) { +// JetTypeReference typeReference = expression.getRight(); +// assert typeReference != null : "Binary type expression should have a right expression"; +// return getClassNameReferenceForTypeReference(typeReference); +// } + + @NotNull + private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) { + ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference + (translationContext().bindingContext(), typeReference); + //TODO should reference class by full name here + JsName className = translationContext().getNameForDescriptor(referencedClass); + return translationContext().getNamespaceQualifiedReference(className); + } + +// @NotNull +// private JetToken getOperationToken(@NotNull JetBinaryExpressionWithTypeRHS expression) { +// JetSimpleNameExpression operationExpression = expression.getOperationSign(); +// IElementType elementType = operationExpression.getReferencedNameElementType(); +// assert elementType instanceof JetToken : "Binary type operation should have IElementType of type JetToken"; +// return (JetToken) elementType; +// } + + +} diff --git a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java index 249a9ba7c2c..9fd8b52ce4f 100644 --- a/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java +++ b/translator/test/org/jetbrains/k2js/test/KotlinLibTest.java @@ -34,10 +34,12 @@ public class KotlinLibTest extends TranslationTest { @Test public void kotlinJsLibRunsWithRhino() throws Exception { - Context context = Context.enter(); - Scriptable scope = context.initStandardObjects(); - runFileWithRhino(kotlinLibraryPath(), context, scope); - Context.exit(); + runRhinoTest(Arrays.asList(kotlinLibraryPath()), new RhinoResultChecker() { + @Override + public void runChecks(Context context, Scriptable scope) throws Exception { + //do nothing + } + }); } @Test @@ -64,4 +66,23 @@ public class KotlinLibTest extends TranslationTest { new RhinoPropertyTypesChecker("foo", propertyToType)); } + @Test + public void isSameType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isSameType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + @Test + public void isAncestorType() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isAncestorType.js")), + new RhinoFunctionResultChecker("test", true)); + } + + @Test + public void isComplexTest() throws Exception { + runRhinoTest(Arrays.asList(kotlinLibraryPath(), cases("isComplexTest.js")), + new RhinoFunctionResultChecker("test", true)); + } + + } diff --git a/translator/test/org/jetbrains/k2js/test/RTTITest.java b/translator/test/org/jetbrains/k2js/test/RTTITest.java new file mode 100644 index 00000000000..517451f1a69 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/RTTITest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class RTTITest extends AbstractClassTest { + + final private static String MAIN = "rtti/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void isSameClass() throws Exception { + testFooBoxIsTrue("isSameClass.kt"); + } + +} diff --git a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java index 5b0fb36bd09..34a40a3ae17 100644 --- a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java +++ b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java @@ -1,5 +1,6 @@ package org.jetbrains.k2js.test; +import org.jetbrains.annotations.Nullable; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.NativeObject; @@ -16,27 +17,55 @@ public final class RhinoFunctionResultChecker implements RhinoResultChecker { final String functionName; final Object expectedResult; - public RhinoFunctionResultChecker(String namespaceName, String functionName, Object expectedResult) { + public RhinoFunctionResultChecker(@Nullable String namespaceName, String functionName, Object expectedResult) { this.namespaceName = namespaceName; this.functionName = functionName; this.expectedResult = expectedResult; } + public RhinoFunctionResultChecker(String functionName, Object expectedResult) { + this(null, functionName, expectedResult); + } + @Override public void runChecks(Context context, Scriptable scope) throws Exception { Object result = extractAndCallFunctionObject(namespaceName, functionName, context, scope); - assertTrue("Result is not what expected!", result.equals(expectedResult)); + assertTrue("Result is not what expected! Exprected: " + expectedResult + " Evaluated : " + result, + result.equals(expectedResult)); String report = namespaceName + "." + functionName + "() = " + Context.toString(result); System.out.println(report); } private Object extractAndCallFunctionObject(String namespaceName, String functionName, Context cx, Scriptable scope) { - NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope); - Object box = namespaceObject.get(functionName, namespaceObject); - assertTrue("Function " + functionName + " not defined in namespace " + namespaceName, box instanceof Function); + Object functionObject; + if (namespaceName != null) { + functionObject = extractFunctionFromObject(namespaceName, functionName, scope); + } else { + functionObject = extractFunctionFromGlobalScope(functionName, scope); + } + return callFunctionAndCheckResults(cx, scope, (Function) functionObject); + } + + private Object callFunctionAndCheckResults(Context cx, Scriptable scope, Function functionObject) { Object functionArgs[] = {}; - Function function = (Function) box; - return function.call(cx, scope, scope, functionArgs); + return functionObject.call(cx, scope, scope, functionArgs); + } + + private Object extractFunctionFromGlobalScope(String functionName, Scriptable scope) { + Object functionObject; + functionObject = scope.get(functionName, scope); + assertTrue("Function " + functionName + " is not defined in global scope", + functionObject instanceof Function); + return functionObject; + } + + private Object extractFunctionFromObject(String namespaceName, String functionName, Scriptable scope) { + Object functionObject; + NativeObject namespaceObject = RhinoUtils.extractObject(namespaceName, scope); + functionObject = namespaceObject.get(functionName, namespaceObject); + assertTrue("Function " + functionName + " is not defined in namespace " + namespaceName, + functionObject instanceof Function); + return functionObject; } } diff --git a/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java index 8e25bb880c3..a9e1ab2a723 100644 --- a/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java +++ b/translator/test/org/jetbrains/k2js/test/RhinoPropertyTypesChecker.java @@ -11,7 +11,7 @@ import static org.junit.Assert.assertTrue; /** * @author Talanov Pavel */ -public class RhinoPropertyTypesChecker implements RhinoResultChecker { +public final class RhinoPropertyTypesChecker implements RhinoResultChecker { final private String objectName; final private Map> propertyToType; diff --git a/translator/test/org/jetbrains/k2js/test/TranslationTest.java b/translator/test/org/jetbrains/k2js/test/TranslationTest.java index 8263ca5c11b..3a6454b59ea 100644 --- a/translator/test/org/jetbrains/k2js/test/TranslationTest.java +++ b/translator/test/org/jetbrains/k2js/test/TranslationTest.java @@ -46,12 +46,16 @@ public abstract class TranslationTest { protected void testFunctionOutput(String filename, String namespaceName, String functionName, Object expectedResult) throws Exception { + translateFile(filename); + runRhinoTest(generateFilenameList(getOutputFilePath(filename)), + new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); + } + + private void translateFile(String filename) { K2JSTranslator.Arguments args = new K2JSTranslator.Arguments(); args.src = getInputFilePath(filename); args.outputDir = getOutputFilePath(filename); K2JSTranslator.translate(args); - runRhinoTest(generateFilenameList(args.outputDir), - new RhinoFunctionResultChecker(namespaceName, functionName, expectedResult)); } abstract protected List generateFilenameList(String inputfile); diff --git a/translator/testFiles/kotlinLib/cases/isAncestorType.js b/translator/testFiles/kotlinLib/cases/isAncestorType.js new file mode 100644 index 00000000000..29fea2ff1b1 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isAncestorType.js @@ -0,0 +1,7 @@ +var A = Class.create(); +var B = Class.create(A); +var b = new B; + +test = function() { + return (isType(b, A) && isType(b, B)); +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isComplexTest.js b/translator/testFiles/kotlinLib/cases/isComplexTest.js new file mode 100644 index 00000000000..c935e77afc0 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isComplexTest.js @@ -0,0 +1,24 @@ +var A = Class.create(); +var B = Class.create(A); +var b = new B; +var C = Class.create(B); +var c = new C; +var E = Class.create(A) +var e = new E; + +test1 = function() { + b2 = b + return (isType(b, A) && isType(b, B)); +} + +test2 = function() { + return (isType(c, C) && isType(c, B) && isType(c, A) && (!isType(c, E))); +} + +test3 = function() { + return isType(e, E) && !isType(e, B) && !isType(e, C) && isType(e, A) +} + +test = function() { + return test1() && test2() && test3() +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isNotOtherType.js b/translator/testFiles/kotlinLib/cases/isNotOtherType.js new file mode 100644 index 00000000000..624f1584c89 --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isNotOtherType.js @@ -0,0 +1,8 @@ +var A = Class.create(); +var B = Class.create(A); +var C = Class.create(); +var c = new C; + +test = function() { + return ((!isType(c, A)) && !isType(c, B)); +} \ No newline at end of file diff --git a/translator/testFiles/kotlinLib/cases/isSameType.js b/translator/testFiles/kotlinLib/cases/isSameType.js new file mode 100644 index 00000000000..bb8dfb4e7db --- /dev/null +++ b/translator/testFiles/kotlinLib/cases/isSameType.js @@ -0,0 +1,6 @@ +var A = Class.create(); +var a = new A; + +test = function() { + return isType(a, A); +} \ No newline at end of file diff --git a/translator/testFiles/kotlin_lib.js b/translator/testFiles/kotlin_lib.js index 2e100bfc744..9eccea4f81b 100644 --- a/translator/testFiles/kotlin_lib.js +++ b/translator/testFiles/kotlin_lib.js @@ -6,6 +6,17 @@ function $A(iterable) { return results; } +var isType = function(object, class) { + current = object.get_class(); + while (current !== class) { + if (current === null) { + return false; + } + current = current.superclass; + } + return true; +} + var emptyFunction = function() {} var Class = (function() { @@ -32,10 +43,17 @@ var Class = (function() { } + klass.addMethods( + { + get_class : function () { + return klass; + } + }); + if (parent != null) { klass.addMethods( { - 'super_init' : function () { + super_init : function () { this.initializing = this.initializing.superclass; this.initializing.prototype.initialize.apply(this, arguments) } diff --git a/translator/testFiles/rtti/cases/isSameClass.kt b/translator/testFiles/rtti/cases/isSameClass.kt new file mode 100644 index 00000000000..49517d74b05 --- /dev/null +++ b/translator/testFiles/rtti/cases/isSameClass.kt @@ -0,0 +1,7 @@ +namespace foo + +class A() { + +} + +fun box() = (A() is A) \ No newline at end of file From 57f5b2cc7d7e302d5cedb0f3b156b05d72b5a2dc Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 14:17:17 +0400 Subject: [PATCH 05/22] Supported negated is operation. --- translator/test/org/jetbrains/k2js/test/RTTITest.java | 5 +++++ translator/testFiles/rtti/cases/notIsOtherClass.kt | 11 +++++++++++ 2 files changed, 16 insertions(+) create mode 100644 translator/testFiles/rtti/cases/notIsOtherClass.kt diff --git a/translator/test/org/jetbrains/k2js/test/RTTITest.java b/translator/test/org/jetbrains/k2js/test/RTTITest.java index 517451f1a69..b6c52390505 100644 --- a/translator/test/org/jetbrains/k2js/test/RTTITest.java +++ b/translator/test/org/jetbrains/k2js/test/RTTITest.java @@ -19,4 +19,9 @@ public class RTTITest extends AbstractClassTest { testFooBoxIsTrue("isSameClass.kt"); } + @Test + public void notIsOtherClass() throws Exception { + testFooBoxIsTrue("notIsOtherClass.kt"); + } + } diff --git a/translator/testFiles/rtti/cases/notIsOtherClass.kt b/translator/testFiles/rtti/cases/notIsOtherClass.kt new file mode 100644 index 00000000000..9aed1fe8aa9 --- /dev/null +++ b/translator/testFiles/rtti/cases/notIsOtherClass.kt @@ -0,0 +1,11 @@ +namespace foo + +open class A() { + +} + +class B() : A() { + +} + +fun box() = (A() !is B) \ No newline at end of file From ce39645ccd7ac14d8612ca98460b50d3d5aa7527 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 14:56:06 +0400 Subject: [PATCH 06/22] Added support for simple case of safe qualified access. --- .../k2js/translate/ExpressionVisitor.java | 57 +++++++++++++------ .../translate/PropertyAccessTranslator.java | 7 ++- .../k2js/test/PropertyAccessorTest.java | 5 ++ .../org/jetbrains/k2js/test/RTTITest.java | 1 - .../propertyAccess/cases/safeCall.kt | 13 +++++ 5 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 translator/testFiles/propertyAccess/cases/safeCall.kt diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index a83dc2301b6..382c31f4fe6 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -143,7 +143,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @Override @NotNull public JsNode visitCallExpression(JetCallExpression expression, TranslationContext context) { - JsExpression callee = getCallee(expression, context); + JsExpression callee = translateCallee(expression, context); List arguments = translateArgumentList(expression.getValueArguments(), context); if (isConstructorInvocation(expression, context)) { JsNew constructorCall = new JsNew(callee); @@ -165,7 +165,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { } @NotNull - private JsExpression getCallee(@NotNull JetCallExpression expression, @NotNull TranslationContext context) { + private JsExpression translateCallee(@NotNull JetCallExpression expression, @NotNull TranslationContext context) { JetExpression jetCallee = expression.getCalleeExpression(); if (jetCallee == null) { throw new AssertionError("Call expression with no callee encountered!"); @@ -291,26 +291,30 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, @NotNull TranslationContext context) { + return translateQualifiedExpression(expression, context); + } + + @NotNull + private JsNode translateQualifiedExpression(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { JsInvocation getterCall = translateAsGetterCall(expression, context); if (getterCall != null) { return getterCall; } - return translateAsQualifiedExpression(expression, context); + return translateAsQualifiedAccess(expression, context); } @Nullable - private JsInvocation translateAsGetterCall(@NotNull JetDotQualifiedExpression expression, + private JsInvocation translateAsGetterCall(@NotNull JetQualifiedExpression expression, @NotNull TranslationContext context) { return Translation.propertyAccessTranslator(context).resolveAsPropertyGet(expression); } @NotNull - private JsNode translateAsQualifiedExpression(@NotNull JetDotQualifiedExpression expression, - @NotNull TranslationContext context) { - JsExpression receiver = AstUtil.convertToExpression(expression.getReceiverExpression().accept(this, context)); - JetExpression jetSelector = expression.getSelectorExpression(); - assert jetSelector != null : "Selector should not be null in dot qualified expression."; - JsExpression selector = AstUtil.convertToExpression(jetSelector.accept(this, context)); + private JsNode translateAsQualifiedAccess(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JsExpression receiver = translateReceiver(expression, context); + JsExpression selector = translateSelector(expression, context); assert (selector instanceof JsNameRef || selector instanceof JsInvocation) : "Selector should be a name reference or a method invocation in dot qualified expression."; if (selector instanceof JsInvocation) { @@ -320,6 +324,20 @@ public final class ExpressionVisitor extends TranslatorVisitor { } } + @NotNull + private JsExpression translateSelector(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + JetExpression jetSelector = expression.getSelectorExpression(); + assert jetSelector != null : "Selector should not be null in dot qualified expression."; + return AstUtil.convertToExpression(jetSelector.accept(this, context)); + } + + @NotNull + private JsExpression translateReceiver(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { + return AstUtil.convertToExpression(expression.getReceiverExpression().accept(this, context)); + } + @NotNull private JsNode translateAsQualifiedNameReference(@NotNull JsExpression receiver, @NotNull JsNameRef selector) { selector.setQualifier(receiver); @@ -347,16 +365,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression, @NotNull TranslationContext context) { return Translation.operationTranslator(context).translatePostfixOperation(expression); - } -// @Override -// @NotNull -// public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, -// @NotNull TranslationContext context) { -// return Translation.typeOperationTranslator(context).translate(expression); -// } - @Override @NotNull public JsNode visitIsExpression(@NotNull JetIsExpression expression, @@ -364,5 +374,16 @@ public final class ExpressionVisitor extends TranslatorVisitor { return Translation.typeOperationTranslator(context).translateIsExpression(expression); } + @Override + @NotNull + public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, + @NotNull TranslationContext context) { + JsExpression receiver = translateReceiver(expression, context); + JsBinaryOperation nullCheck = new JsBinaryOperation + (JsBinaryOperator.NEQ, receiver, context.program().getNullLiteral()); + JsStatement thenStatement = AstUtil.convertToStatement(translateQualifiedExpression(expression, context)); + return new JsIf(nullCheck, thenStatement, null); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java index f77e35fecb6..21ed8515701 100644 --- a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java @@ -13,6 +13,7 @@ import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor; import org.jetbrains.jet.lang.descriptors.PropertySetterDescriptor; import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression; import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetQualifiedExpression; import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.calls.ResolvedCall; @@ -33,7 +34,7 @@ public final class PropertyAccessTranslator extends AbstractTranslator { } @Nullable - public JsInvocation resolveAsPropertyGet(@NotNull JetDotQualifiedExpression expression) { + public JsInvocation resolveAsPropertyGet(@NotNull JetQualifiedExpression expression) { JetExpression selectorExpression = expression.getSelectorExpression(); assert selectorExpression != null : "Selector should not be null."; JsName getterName = getPropertyGetterName(selectorExpression); @@ -54,9 +55,9 @@ public final class PropertyAccessTranslator extends AbstractTranslator { @NotNull private JsInvocation translateReceiverAndReturnAccessorInvocation - (@NotNull JetDotQualifiedExpression dotQualifiedExpression, @NotNull JsName accessorName) { + (@NotNull JetQualifiedExpression qualifiedExpression, @NotNull JsName accessorName) { JsNode node = Translation.expressionTranslator(translationContext()) - .translate(dotQualifiedExpression.getReceiverExpression()); + .translate(qualifiedExpression.getReceiverExpression()); JsNameRef result = accessorName.makeRef(); result.setQualifier(AstUtil.convertToExpression(node)); return AstUtil.newInvocation(result); diff --git a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java index aa410782026..1d604d9411d 100644 --- a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java +++ b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java @@ -39,6 +39,11 @@ public final class PropertyAccessorTest extends AbstractClassTest { testFooBoxIsTrue("customSetter.kt"); } + @Test + public void safeCall() throws Exception { + testFooBoxIsTrue("safeCall.kt"); + } + //TODO test // @Test // public void namespaceCustomAccessors() throws Exception { diff --git a/translator/test/org/jetbrains/k2js/test/RTTITest.java b/translator/test/org/jetbrains/k2js/test/RTTITest.java index b6c52390505..4af82cc409c 100644 --- a/translator/test/org/jetbrains/k2js/test/RTTITest.java +++ b/translator/test/org/jetbrains/k2js/test/RTTITest.java @@ -23,5 +23,4 @@ public class RTTITest extends AbstractClassTest { public void notIsOtherClass() throws Exception { testFooBoxIsTrue("notIsOtherClass.kt"); } - } diff --git a/translator/testFiles/propertyAccess/cases/safeCall.kt b/translator/testFiles/propertyAccess/cases/safeCall.kt new file mode 100644 index 00000000000..2a918b76ff8 --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/safeCall.kt @@ -0,0 +1,13 @@ +namespace foo + +class A() { + fun doSomething() { + + } +} + +fun box() : Boolean { + var a : A? = null; + a?.doSomething() + return true; +} \ No newline at end of file From 628a2334f551c86af0758f053b84fcc861fb1ea5 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 15:03:23 +0400 Subject: [PATCH 07/22] Added support for returning value from safe qualified expression. --- .../jetbrains/k2js/translate/ExpressionVisitor.java | 7 ++++--- .../org/jetbrains/k2js/test/PropertyAccessorTest.java | 5 +++++ .../propertyAccess/cases/safeCallReturnsNullIfFails.kt | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index 382c31f4fe6..d96c738341d 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -379,10 +379,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, @NotNull TranslationContext context) { JsExpression receiver = translateReceiver(expression, context); + JsNullLiteral nullLiteral = context.program().getNullLiteral(); JsBinaryOperation nullCheck = new JsBinaryOperation - (JsBinaryOperator.NEQ, receiver, context.program().getNullLiteral()); - JsStatement thenStatement = AstUtil.convertToStatement(translateQualifiedExpression(expression, context)); - return new JsIf(nullCheck, thenStatement, null); + (JsBinaryOperator.NEQ, receiver, nullLiteral); + JsExpression thenExpression = AstUtil.convertToExpression(translateQualifiedExpression(expression, context)); + return new JsConditional(nullCheck, thenExpression, nullLiteral); } diff --git a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java index 1d604d9411d..0d5a7f4d7e7 100644 --- a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java +++ b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java @@ -44,6 +44,11 @@ public final class PropertyAccessorTest extends AbstractClassTest { testFooBoxIsTrue("safeCall.kt"); } + @Test + public void safeCallReturnsNullIfFails() throws Exception { + testFooBoxIsTrue("safeCallReturnsNullIfFails.kt"); + } + //TODO test // @Test // public void namespaceCustomAccessors() throws Exception { diff --git a/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt b/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt new file mode 100644 index 00000000000..b5d44bb29bb --- /dev/null +++ b/translator/testFiles/propertyAccess/cases/safeCallReturnsNullIfFails.kt @@ -0,0 +1,10 @@ +namespace foo + +class A() { + val x = 4 +} + +fun box() : Boolean { + var a : A? = null; + return (a?.x == null); +} \ No newline at end of file From bd33b23f512028578f5ac752ecf87a1f79df1e0e Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 17:17:52 +0400 Subject: [PATCH 08/22] Added support for simple case of when expression. --- .../k2js/translate/AbstractTranslator.java | 2 +- .../k2js/translate/ExpressionVisitor.java | 7 + ...Translator.java => PatternTranslator.java} | 29 +++- .../jetbrains/k2js/translate/Translation.java | 24 +++- .../k2js/translate/WhenTranslator.java | 130 ++++++++++++++++++ .../jetbrains/k2js/test/BasicClassTest.java | 2 +- .../k2js/test/ClassInheritanceTest.java | 2 +- ...ClassTest.java => IncludeLibraryTest.java} | 2 +- .../k2js/test/PatternMatchingTest.java | 22 +++ .../k2js/test/PropertyAccessorTest.java | 2 +- .../org/jetbrains/k2js/test/RTTITest.java | 2 +- .../org/jetbrains/k2js/test/TraitTest.java | 2 +- .../patternMatching/cases/whenType.kt | 12 ++ 13 files changed, 223 insertions(+), 15 deletions(-) rename translator/src/org/jetbrains/k2js/translate/{TypeOperationTranslator.java => PatternTranslator.java} (76%) create mode 100644 translator/src/org/jetbrains/k2js/translate/WhenTranslator.java rename translator/test/org/jetbrains/k2js/test/{AbstractClassTest.java => IncludeLibraryTest.java} (80%) create mode 100644 translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java create mode 100644 translator/testFiles/patternMatching/cases/whenType.kt diff --git a/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java b/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java index 6f44e0846db..a53032a4ad7 100644 --- a/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/AbstractTranslator.java @@ -11,7 +11,7 @@ public abstract class AbstractTranslator { @NotNull private final TranslationContext context; - public AbstractTranslator(@NotNull TranslationContext context) { + protected AbstractTranslator(@NotNull TranslationContext context) { this.context = context; } diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index d96c738341d..b6c5a451ad1 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -386,5 +386,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { return new JsConditional(nullCheck, thenExpression, nullLiteral); } + @Override + @NotNull + public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, + @NotNull TranslationContext context) { + return Translation.translateWhenExpression(expression, context); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java similarity index 76% rename from translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java rename to translator/src/org/jetbrains/k2js/translate/PatternTranslator.java index 11e3aa1750a..d73984ddc2b 100644 --- a/translator/src/org/jetbrains/k2js/translate/TypeOperationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java @@ -7,20 +7,21 @@ import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.psi.JetIsExpression; +import org.jetbrains.jet.lang.psi.JetPattern; import org.jetbrains.jet.lang.psi.JetTypePattern; import org.jetbrains.jet.lang.psi.JetTypeReference; /** * @author Talanov Pavel */ -public final class TypeOperationTranslator extends AbstractTranslator { +public final class PatternTranslator extends AbstractTranslator { @NotNull - public static TypeOperationTranslator newInstance(@NotNull TranslationContext context) { - return new TypeOperationTranslator(context); + public static PatternTranslator newInstance(@NotNull TranslationContext context) { + return new PatternTranslator(context); } - private TypeOperationTranslator(@NotNull TranslationContext context) { + private PatternTranslator(@NotNull TranslationContext context) { super(context); } @@ -39,16 +40,32 @@ public final class TypeOperationTranslator extends AbstractTranslator { // } @NotNull - JsExpression translateIsExpression(@NotNull JetIsExpression expression) { + public JsExpression translateIsExpression(@NotNull JetIsExpression expression) { JsExpression left = AstUtil.convertToExpression (Translation.translateExpression(expression.getLeftHandSide(), translationContext())); - JsExpression resultingExpression = translateTypePattern(left, (JetTypePattern) expression.getPattern()); + JetPattern pattern = getPattern(expression); + JsExpression resultingExpression = translatePattern(pattern, left); if (expression.isNegated()) { return AstUtil.negation(resultingExpression); } return resultingExpression; } + @NotNull + public JsExpression translatePattern(@NotNull JetPattern pattern, @NotNull JsExpression expressionToMatch) { + if (pattern instanceof JetTypePattern) { + return translateTypePattern(expressionToMatch, (JetTypePattern) pattern); + } + throw new AssertionError("Unsupported pattern type " + pattern.getClass()); + } + + @NotNull + private JetPattern getPattern(@NotNull JetIsExpression expression) { + JetPattern pattern = expression.getPattern(); + assert pattern != null : "Pattern should not be null"; + return pattern; + } + @NotNull private JsExpression translateTypePattern(@NotNull JsExpression expressionToMatch, @NotNull JetTypePattern pattern) { diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index f6f4f4b22a0..6580d694345 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -1,8 +1,12 @@ package org.jetbrains.k2js.translate; +import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsNode; +import com.google.dart.compiler.backend.js.ast.JsStatement; +import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetWhenExpression; /** * @author Talanov Pavel @@ -47,12 +51,28 @@ public final class Translation { } @NotNull - static public TypeOperationTranslator typeOperationTranslator(@NotNull TranslationContext context) { - return TypeOperationTranslator.newInstance(context); + static public PatternTranslator typeOperationTranslator(@NotNull TranslationContext context) { + return PatternTranslator.newInstance(context); } @NotNull static public JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { return expressionTranslator(context).translate(expression); } + + //TODO: clean out similar code fragments + @NotNull + static public JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { + return AstUtil.convertToExpression(translateExpression(expression, context)); + } + + @NotNull + static public JsStatement translateAsStatement(@NotNull JetExpression expression, @NotNull TranslationContext context) { + return AstUtil.convertToStatement(translateExpression(expression, context)); + } + + @NotNull + static public JsNode translateWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + return WhenTranslator.translateWhenExpression(expression, context); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java new file mode 100644 index 00000000000..9ad7f3205ef --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -0,0 +1,130 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.*; +import com.google.dart.compiler.util.AstUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Talanov Pavel + */ +public class WhenTranslator extends AbstractTranslator { + + @NotNull + static public JsNode translateWhenExpression(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + WhenTranslator translator = new WhenTranslator(expression, context); + return translator.translate(); + } + + @NotNull + private final JetWhenExpression whenExpression; + @NotNull + private final JsExpression expressionToMatch; + @NotNull + private final JsName dummyCounterName; + + private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { + super(context); + this.whenExpression = expression; + this.expressionToMatch = translateExpressionToMatch(whenExpression); + this.dummyCounterName = context.enclosingScope().declareTemporary(); + } + + @NotNull + JsNode translate() { + return translateEntries(); + } + + private JsBlock translateEntries() { + //JsFor resultingFor = generateDummyFor(); + List entries = new ArrayList(); + for (JetWhenEntry entry : whenExpression.getEntries()) { + entries.add(translateEntry(entry)); + } + return AstUtil.newBlock(entries); + } + + @NotNull + private JsFor generateDummyFor() { + JsFor result = new JsFor(); + result.setInitVars(generateInitStatement()); + result.setIncrExpr(generateIncrementStatement()); + result.setCondition(generateConditionStatement()); + return result; + } + + @NotNull + private JsBinaryOperation generateConditionStatement() { + JsNumberLiteral entriesNumber = program().getNumberLiteral(whenExpression.getEntries().size()); + return new JsBinaryOperation(JsBinaryOperator.LT, dummyCounterName.makeRef(), entriesNumber); + } + + @NotNull + private JsPrefixOperation generateIncrementStatement() { + return new JsPrefixOperation(JsUnaryOperator.INC, dummyCounterName.makeRef()); + } + + @NotNull + private JsVars generateInitStatement() { + return AstUtil.newVar(dummyCounterName, translationContext().program().getNumberLiteral(0)); + } + + @NotNull + private JsStatement translateEntry(@NotNull JetWhenEntry entry) { + JsStatement statementToExecute = translateExpressionToExecute(entry); + if (entry.isElse()) { + return statementToExecute; + } + JsExpression condition = translateConditions(entry); + return new JsIf(condition, statementToExecute, null); + } + + @NotNull + private JsStatement translateExpressionToExecute(@NotNull JetWhenEntry entry) { + JetExpression expressionToExecute = entry.getExpression(); + assert expressionToExecute != null : "WhenEntry should have whenExpression to execute."; + return Translation.translateAsStatement(expressionToExecute, translationContext()); + } + + //TODO: ask what these conditions mean + @NotNull + private JsExpression translateConditions(@NotNull JetWhenEntry entry) { + JetWhenCondition[] conditions = entry.getConditions(); +// for (JetWhenCondition condition : conditions) { +// translateCondition(condition); +// } + return translateCondition(conditions[0]); + } + + @NotNull + private JsExpression translateCondition(@NotNull JetWhenCondition condition) { + if (condition instanceof JetWhenConditionIsPattern) { + return translatePatternCondition((JetWhenConditionIsPattern) condition); + } + throw new AssertionError("Unsupported when condition " + condition.getClass()); + } + + + @NotNull + private JsExpression translatePatternCondition(@NotNull JetWhenConditionIsPattern condition) { + return Translation.typeOperationTranslator(translationContext()). + translatePattern(getPattern(condition), expressionToMatch); + } + + @NotNull + private JetPattern getPattern(@NotNull JetWhenConditionIsPattern condition) { + JetPattern pattern = condition.getPattern(); + assert pattern != null : "Is condition should have a non null pattern."; + return pattern; + } + + @NotNull + private JsExpression translateExpressionToMatch(@NotNull JetWhenExpression expression) { + JetExpression subject = expression.getSubjectExpression(); + assert subject != null : "Subject should not be null."; + return Translation.translateAsExpression(subject, translationContext()); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/BasicClassTest.java b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java index c05c9c072f4..42f8efaaa49 100644 --- a/translator/test/org/jetbrains/k2js/test/BasicClassTest.java +++ b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public class BasicClassTest extends AbstractClassTest { +public class BasicClassTest extends IncludeLibraryTest { final private static String MAIN = "class/"; diff --git a/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java b/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java index 0d2f901d00b..247cf7e1b39 100644 --- a/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java +++ b/translator/test/org/jetbrains/k2js/test/ClassInheritanceTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class ClassInheritanceTest extends AbstractClassTest { +public final class ClassInheritanceTest extends IncludeLibraryTest { final private static String MAIN = "inheritance/"; diff --git a/translator/test/org/jetbrains/k2js/test/AbstractClassTest.java b/translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java similarity index 80% rename from translator/test/org/jetbrains/k2js/test/AbstractClassTest.java rename to translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java index a5214f7271f..4b87608a5f6 100644 --- a/translator/test/org/jetbrains/k2js/test/AbstractClassTest.java +++ b/translator/test/org/jetbrains/k2js/test/IncludeLibraryTest.java @@ -6,7 +6,7 @@ import java.util.List; /** * @author Talanov Pavel */ -public abstract class AbstractClassTest extends TranslationTest { +public abstract class IncludeLibraryTest extends TranslationTest { @Override protected List generateFilenameList(String inputFile) { diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java new file mode 100644 index 00000000000..8b73a74f9df --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public final class PatternMatchingTest extends IncludeLibraryTest { + + final private static String MAIN = "patternMatching/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void whenType() throws Exception { + testFooBoxIsTrue("whenType.kt"); + } + +} diff --git a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java index 0d5a7f4d7e7..3052216e426 100644 --- a/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java +++ b/translator/test/org/jetbrains/k2js/test/PropertyAccessorTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class PropertyAccessorTest extends AbstractClassTest { +public final class PropertyAccessorTest extends IncludeLibraryTest { final private static String MAIN = "propertyAccess/"; diff --git a/translator/test/org/jetbrains/k2js/test/RTTITest.java b/translator/test/org/jetbrains/k2js/test/RTTITest.java index 4af82cc409c..0fa4ddc4d0f 100644 --- a/translator/test/org/jetbrains/k2js/test/RTTITest.java +++ b/translator/test/org/jetbrains/k2js/test/RTTITest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public class RTTITest extends AbstractClassTest { +public class RTTITest extends IncludeLibraryTest { final private static String MAIN = "rtti/"; diff --git a/translator/test/org/jetbrains/k2js/test/TraitTest.java b/translator/test/org/jetbrains/k2js/test/TraitTest.java index a153e15458c..6546702c631 100644 --- a/translator/test/org/jetbrains/k2js/test/TraitTest.java +++ b/translator/test/org/jetbrains/k2js/test/TraitTest.java @@ -5,7 +5,7 @@ import org.junit.Test; /** * @author Talanov Pavel */ -public final class TraitTest extends AbstractClassTest { +public final class TraitTest extends IncludeLibraryTest { final private static String MAIN = "trait/"; diff --git a/translator/testFiles/patternMatching/cases/whenType.kt b/translator/testFiles/patternMatching/cases/whenType.kt new file mode 100644 index 00000000000..87cfccc929f --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenType.kt @@ -0,0 +1,12 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + when(A()) { + is A => return true; + else => return false; + } +} \ No newline at end of file From d0239085c1f18f1bb4d29db0d82b525da919f774 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 17:39:44 +0400 Subject: [PATCH 09/22] Supported another case of when expression. --- .../google/dart/compiler/util/AstUtil.java | 2 +- .../k2js/translate/PatternTranslator.java | 2 +- .../k2js/translate/WhenTranslator.java | 28 +++++++++++++++---- .../k2js/test/PatternMatchingTest.java | 5 ++++ .../k2js/test/RhinoFunctionResultChecker.java | 2 +- .../patternMatching/cases/whenNotType.kt | 12 ++++++++ 6 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 translator/testFiles/patternMatching/cases/whenNotType.kt diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index a1ba71ce077..2bc025b1c21 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -269,7 +269,7 @@ public class AstUtil { return result; } - public static JsPrefixOperation negation(JsExpression expression) { + public static JsPrefixOperation negated(JsExpression expression) { return new JsPrefixOperation(JsUnaryOperator.NOT, expression); } } diff --git a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java index d73984ddc2b..2abb504754d 100644 --- a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java @@ -46,7 +46,7 @@ public final class PatternTranslator extends AbstractTranslator { JetPattern pattern = getPattern(expression); JsExpression resultingExpression = translatePattern(pattern, left); if (expression.isNegated()) { - return AstUtil.negation(resultingExpression); + return AstUtil.negated(resultingExpression); } return resultingExpression; } diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java index 9ad7f3205ef..86777d87df2 100644 --- a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -25,6 +25,7 @@ public class WhenTranslator extends AbstractTranslator { private final JsExpression expressionToMatch; @NotNull private final JsName dummyCounterName; + private int currentEntryNumber = 0; private WhenTranslator(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) { super(context); @@ -35,16 +36,27 @@ public class WhenTranslator extends AbstractTranslator { @NotNull JsNode translate() { - return translateEntries(); + JsFor resultingFor = generateDummyFor(); + List entries = translateEntries(); + resultingFor.setBody(AstUtil.newBlock(entries)); + return resultingFor; } - private JsBlock translateEntries() { - //JsFor resultingFor = generateDummyFor(); + @NotNull + private List translateEntries() { List entries = new ArrayList(); for (JetWhenEntry entry : whenExpression.getEntries()) { - entries.add(translateEntry(entry)); + entries.add(surroundWithDummyIf(translateEntry(entry))); } - return AstUtil.newBlock(entries); + return entries; + } + + @NotNull + private JsStatement surroundWithDummyIf(@NotNull JsStatement entryStatement) { + JsExpression stepNumberEqualsCurrentEntryNumber = new JsBinaryOperation(JsBinaryOperator.EQ, + dummyCounterName.makeRef(), translationContext().program().getNumberLiteral(currentEntryNumber)); + currentEntryNumber++; + return new JsIf(stepNumberEqualsCurrentEntryNumber, entryStatement, null); } @NotNull @@ -110,8 +122,12 @@ public class WhenTranslator extends AbstractTranslator { @NotNull private JsExpression translatePatternCondition(@NotNull JetWhenConditionIsPattern condition) { - return Translation.typeOperationTranslator(translationContext()). + JsExpression patternMatchExpression = Translation.typeOperationTranslator(translationContext()). translatePattern(getPattern(condition), expressionToMatch); + if (condition.isNegated()) { + return AstUtil.negated(patternMatchExpression); + } + return patternMatchExpression; } @NotNull diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index 8b73a74f9df..2973f6999f7 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -19,4 +19,9 @@ public final class PatternMatchingTest extends IncludeLibraryTest { testFooBoxIsTrue("whenType.kt"); } + @Test + public void whenNotType() throws Exception { + testFooBoxIsTrue("whenNotType.kt"); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java index 34a40a3ae17..8be5474e5bc 100644 --- a/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java +++ b/translator/test/org/jetbrains/k2js/test/RhinoFunctionResultChecker.java @@ -30,7 +30,7 @@ public final class RhinoFunctionResultChecker implements RhinoResultChecker { @Override public void runChecks(Context context, Scriptable scope) throws Exception { Object result = extractAndCallFunctionObject(namespaceName, functionName, context, scope); - assertTrue("Result is not what expected! Exprected: " + expectedResult + " Evaluated : " + result, + assertTrue("Result is not what expected! Expected: " + expectedResult + " Evaluated : " + result, result.equals(expectedResult)); String report = namespaceName + "." + functionName + "() = " + Context.toString(result); System.out.println(report); diff --git a/translator/testFiles/patternMatching/cases/whenNotType.kt b/translator/testFiles/patternMatching/cases/whenNotType.kt new file mode 100644 index 00000000000..be7d077a352 --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenNotType.kt @@ -0,0 +1,12 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + when(A()) { + !is A => return false; + else => return true; + } +} \ No newline at end of file From 5841049683192d3d7a4d9f98de40521501d9ec0b Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 17:41:00 +0400 Subject: [PATCH 10/22] Supported another case of when expression. --- .idea/workspace.xml | 967 ++++++++++++++++++++++++++++---------------- 1 file changed, 611 insertions(+), 356 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 2b6e648cae1..423f58ca895 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,13 +1,14 @@ - + + + - @@ -58,7 +59,33 @@ - + - - + + - + + + + + + + + + + + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -135,70 +227,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -208,26 +237,53 @@ - - + + - + - - + + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -235,64 +291,37 @@ - - + + - + - - + + - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -319,22 +348,22 @@ @@ -377,22 +406,6 @@ - - - - - - - - - - - - @@ -425,6 +438,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -555,106 +800,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + localhost @@ -802,23 +1043,23 @@ + + - + - + - - @@ -852,7 +1093,7 @@ @@ -899,108 +1140,122 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 98bcfe78476d115e75572afc47994db536f92f78 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 18:05:47 +0400 Subject: [PATCH 11/22] Fixed a bug where multiple when entries could execute in one statement. --- .../jetbrains/k2js/translate/WhenTranslator.java | 7 ++++++- .../jetbrains/k2js/test/PatternMatchingTest.java | 5 +++++ .../patternMatching/cases/whenExecutesOnlyOnce.kt | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java index 86777d87df2..ae42bb5e031 100644 --- a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -91,7 +91,7 @@ public class WhenTranslator extends AbstractTranslator { return statementToExecute; } JsExpression condition = translateConditions(entry); - return new JsIf(condition, statementToExecute, null); + return new JsIf(condition, addDummyBreak(statementToExecute), null); } @NotNull @@ -119,6 +119,11 @@ public class WhenTranslator extends AbstractTranslator { throw new AssertionError("Unsupported when condition " + condition.getClass()); } + @NotNull + private JsBlock addDummyBreak(@NotNull JsStatement statement) { + return AstUtil.newBlock(statement, new JsBreak()); + } + @NotNull private JsExpression translatePatternCondition(@NotNull JetWhenConditionIsPattern condition) { diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index 2973f6999f7..a6406f84b7a 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -24,4 +24,9 @@ public final class PatternMatchingTest extends IncludeLibraryTest { testFooBoxIsTrue("whenNotType.kt"); } + @Test + public void whenExecutesOnlyOnce() throws Exception { + testFooBoxIsTrue("whenExecutesOnlyOnce.kt"); + } + } diff --git a/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt b/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt new file mode 100644 index 00000000000..3ba142f334c --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenExecutesOnlyOnce.kt @@ -0,0 +1,15 @@ +namespace foo + +class A() { + +} + +fun box() : Boolean { + var a = 0 + when(A()) { + is A => a++; + is A => a++; + else => a++; + } + return (a == 1) +} \ No newline at end of file From d4fb41377820b087eee0309eba216cab605c96c7 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 18:39:40 +0400 Subject: [PATCH 12/22] Supported variable is value pattern matching. --- .../k2js/translate/PatternTranslator.java | 23 +++++++++++++------ .../k2js/translate/WhenTranslator.java | 1 + .../k2js/test/PatternMatchingTest.java | 5 ++++ .../patternMatching/cases/whenValue.kt | 10 ++++++++ 4 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 translator/testFiles/patternMatching/cases/whenValue.kt diff --git a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java index 2abb504754d..cc18391e265 100644 --- a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java @@ -1,19 +1,15 @@ package org.jetbrains.k2js.translate; -import com.google.dart.compiler.backend.js.ast.JsExpression; -import com.google.dart.compiler.backend.js.ast.JsName; -import com.google.dart.compiler.backend.js.ast.JsNameRef; +import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; -import org.jetbrains.jet.lang.psi.JetIsExpression; -import org.jetbrains.jet.lang.psi.JetPattern; -import org.jetbrains.jet.lang.psi.JetTypePattern; -import org.jetbrains.jet.lang.psi.JetTypeReference; +import org.jetbrains.jet.lang.psi.*; /** * @author Talanov Pavel */ +//TODO: fix members order public final class PatternTranslator extends AbstractTranslator { @NotNull @@ -56,9 +52,22 @@ public final class PatternTranslator extends AbstractTranslator { if (pattern instanceof JetTypePattern) { return translateTypePattern(expressionToMatch, (JetTypePattern) pattern); } + if (pattern instanceof JetExpressionPattern) { + return translateExpressionPattern(expressionToMatch, (JetExpressionPattern) pattern); + } throw new AssertionError("Unsupported pattern type " + pattern.getClass()); } + @NotNull + private JsExpression translateExpressionPattern(JsExpression expressionToMatch, JetExpressionPattern pattern) { + JetExpression patternExpression = pattern.getExpression(); + assert patternExpression != null : "Expression patter should have an expression."; + JsExpression expressionToMatchAgainst = + Translation.translateAsExpression(patternExpression, translationContext()); + //TODO: should call equals method here + return new JsBinaryOperation(JsBinaryOperator.REF_EQ, expressionToMatch, expressionToMatchAgainst); + } + @NotNull private JetPattern getPattern(@NotNull JetIsExpression expression) { JetPattern pattern = expression.getPattern(); diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java index ae42bb5e031..cfa1c136fe7 100644 --- a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -11,6 +11,7 @@ import java.util.List; /** * @author Talanov Pavel */ +//TODO: fix members order public class WhenTranslator extends AbstractTranslator { @NotNull diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index a6406f84b7a..fa8478d4b7c 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -29,4 +29,9 @@ public final class PatternMatchingTest extends IncludeLibraryTest { testFooBoxIsTrue("whenExecutesOnlyOnce.kt"); } + @Test + public void whenValue() throws Exception { + testFooBoxIsTrue("whenValue.kt"); + } + } diff --git a/translator/testFiles/patternMatching/cases/whenValue.kt b/translator/testFiles/patternMatching/cases/whenValue.kt new file mode 100644 index 00000000000..52093a79b4c --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenValue.kt @@ -0,0 +1,10 @@ +namespace foo + +fun box() : Boolean { + var a = 4 + when(a) { + is 3 => a = 10; + is 4 => a = 20; + } + return (a == 20) +} \ No newline at end of file From ca57aee0409be52da8a2c51fb0aea137a231e7f3 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 20:19:34 +0400 Subject: [PATCH 13/22] Added tests and support for binary with type RHS expressions. --- .../k2js/translate/ExpressionVisitor.java | 8 ++++++ .../k2js/translate/OperatorTable.java | 5 ++++ .../k2js/test/PatternMatchingTest.java | 10 ++++++++ .../patternMatching/cases/whenNotValue.kt | 10 ++++++++ .../patternMatching/cases/whenValueOrType.kt | 25 +++++++++++++++++++ 5 files changed, 58 insertions(+) create mode 100644 translator/testFiles/patternMatching/cases/whenNotValue.kt create mode 100644 translator/testFiles/patternMatching/cases/whenValueOrType.kt diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index b6c5a451ad1..95d597ee496 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -394,4 +394,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { } + @Override + @NotNull + public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, + @NotNull TranslationContext context) { + return Translation.translateExpression(expression.getLeft(), context); + } + + } diff --git a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java index 748f86441e6..9eaf94f97a2 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java +++ b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java @@ -44,6 +44,11 @@ public final class OperatorTable { binaryOperatorsMap.put(JetTokens.ANDAND, JsBinaryOperator.AND); binaryOperatorsMap.put(JetTokens.EXCLEQ, JsBinaryOperator.NEQ); binaryOperatorsMap.put(JetTokens.PERC, JsBinaryOperator.MOD); + binaryOperatorsMap.put(JetTokens.PLUSEQ, JsBinaryOperator.ASG_ADD); + binaryOperatorsMap.put(JetTokens.MINUSEQ, JsBinaryOperator.ASG_SUB); + binaryOperatorsMap.put(JetTokens.DIVEQ, JsBinaryOperator.ASG_DIV); + binaryOperatorsMap.put(JetTokens.MULTEQ, JsBinaryOperator.ASG_MUL); + binaryOperatorsMap.put(JetTokens.PERCEQ, JsBinaryOperator.ASG_MOD); } static { diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index fa8478d4b7c..c0577d76191 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -34,4 +34,14 @@ public final class PatternMatchingTest extends IncludeLibraryTest { testFooBoxIsTrue("whenValue.kt"); } + @Test + public void whenNotValue() throws Exception { + testFooBoxIsTrue("whenNotValue.kt"); + } + + @Test + public void whenValueOrType() throws Exception { + testFooBoxIsTrue("whenValueOrType.kt"); + } + } diff --git a/translator/testFiles/patternMatching/cases/whenNotValue.kt b/translator/testFiles/patternMatching/cases/whenNotValue.kt new file mode 100644 index 00000000000..949ef0b068d --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenNotValue.kt @@ -0,0 +1,10 @@ +namespace foo + +fun box() : Boolean { + var a = 4 + when(a) { + !is 3 => a = 10; + !is 4 => a = 20; + } + return (a == 10) +} \ No newline at end of file diff --git a/translator/testFiles/patternMatching/cases/whenValueOrType.kt b/translator/testFiles/patternMatching/cases/whenValueOrType.kt new file mode 100644 index 00000000000..71afe50d300 --- /dev/null +++ b/translator/testFiles/patternMatching/cases/whenValueOrType.kt @@ -0,0 +1,25 @@ +namespace foo + +class A() { + +} + +class B() { + +} + +fun box() : Boolean { + var c : Int = 0 + var a = A() : Any? + var b = null : Any? + when(a) { + is null => c = 10; + is B => c = 10000 + is A => c = 20; + } + when(b) { + is null => c += 5 + is B => c += 100 + } + return (c == 25) +} \ No newline at end of file From d1c49f80c8aff9b50f3b4516a4679e0b900109e1 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Thu, 17 Nov 2011 22:28:46 +0400 Subject: [PATCH 14/22] Refactoring. --- .../google/dart/compiler/util/AstUtil.java | 4 ++ .../k2js/translate/BindingUtils.java | 7 -- .../k2js/translate/ClassBodyVisitor.java | 18 ----- .../k2js/translate/ClassTranslator.java | 4 +- .../k2js/translate/ExpressionVisitor.java | 38 ++++++----- .../k2js/translate/FunctionTranslator.java | 9 ++- .../k2js/translate/InitializerVisitor.java | 8 +-- ...va => NamespaceDeclarationTranslator.java} | 10 +-- ....java => NamespaceDeclarationVisitor.java} | 2 +- .../k2js/translate/NamespaceTranslator.java | 9 +-- .../k2js/translate/OperationTranslator.java | 11 ++- .../k2js/translate/OperatorTable.java | 12 ++-- .../k2js/translate/PatternTranslator.java | 67 +++++-------------- .../translate/PropertyAccessTranslator.java | 8 +-- .../jetbrains/k2js/translate/Translation.java | 5 +- .../k2js/translate/TranslatorVisitor.java | 6 +- .../jetbrains/k2js/test/BasicClassTest.java | 2 +- .../k2js/test/PatternMatchingTest.java | 5 ++ .../org/jetbrains/k2js/test/TupleTest.java | 22 ++++++ .../patternMatching/cases/multipleCases.kt | 17 +++++ .../testFiles/tuple/cases/twoElements.kt | 6 ++ 21 files changed, 128 insertions(+), 142 deletions(-) rename translator/src/org/jetbrains/k2js/translate/{DeclarationTranslator.java => NamespaceDeclarationTranslator.java} (52%) rename translator/src/org/jetbrains/k2js/translate/{DeclarationVisitor.java => NamespaceDeclarationVisitor.java} (95%) create mode 100644 translator/test/org/jetbrains/k2js/test/TupleTest.java create mode 100644 translator/testFiles/patternMatching/cases/multipleCases.kt create mode 100644 translator/testFiles/tuple/cases/twoElements.kt diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index 2bc025b1c21..e8a9d57d18b 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -272,4 +272,8 @@ public class AstUtil { public static JsPrefixOperation negated(JsExpression expression) { return new JsPrefixOperation(JsUnaryOperator.NOT, expression); } + + public static JsStatement newAssignmentStatement(JsNameRef nameRef, JsExpression expr) { + return convertToStatement(new JsBinaryOperation(JsBinaryOperator.ASG, nameRef, expr)); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java index 794cd6ecb28..6f8c61481d6 100644 --- a/translator/src/org/jetbrains/k2js/translate/BindingUtils.java +++ b/translator/src/org/jetbrains/k2js/translate/BindingUtils.java @@ -61,13 +61,6 @@ public final class BindingUtils { return getDescriptorForExpression(context, declaration, PropertyDescriptor.class); } - @NotNull - static public String getPropertyNameForPropertyAccessor(@NotNull BindingContext context, - @NotNull JetPropertyAccessor accessor) { - PropertyAccessorDescriptor descriptor = getPropertyAccessorDescriptor(context, accessor); - return descriptor.getCorrespondingProperty().getName(); - } - @NotNull static public PropertySetterDescriptor getPropertySetterDescriptorForProperty(@NotNull BindingContext context, @NotNull JetProperty property) { diff --git a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java b/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java index 750ac52aecc..57798ae81e9 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassBodyVisitor.java @@ -153,22 +153,4 @@ public final class ClassBodyVisitor extends TranslatorVisitor no additional actions are needed return new ArrayList(); } - -// @NotNull -// JsName getNameForGetter(@NotNull String propertyName, @NotNull TranslationContext context) { -// return getNameForAccessor(propertyName, true, context); -// } -// -// @NotNull -// JsName getNameForSetter(@NotNull String propertyName, @NotNull TranslationContext context) { -// return getNameForAccessor(propertyName, false, context); -// } -// -// @NotNull -// JsName getNameForAccessor(@NotNull String propertyName, boolean isGetter, -// @NotNull TranslationContext context) { -// return context.classScope().findExistingName(Namer.getNameForAccessor(propertyName, isGetter)); -// } - - } diff --git a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java index 38c0987c336..44e6e51ec58 100644 --- a/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/ClassTranslator.java @@ -50,8 +50,8 @@ public final class ClassTranslator extends AbstractTranslator { @NotNull private JsStatement classDeclarationStatement(@NotNull JetClass classDeclaration, @NotNull JsInvocation jsClassDeclaration) { - return AstUtil.convertToStatement(AstUtil.newAssignment - (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration)); + return AstUtil.newAssignmentStatement + (namespaceQualifiedClassNameReference(classDeclaration), jsClassDeclaration); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index 95d597ee496..bb88309f447 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -61,7 +61,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull TranslationContext context) { JetExpression returnedExpression = jetReturnExpression.getReturnedExpression(); if (returnedExpression != null) { - JsExpression jsExpression = AstUtil.convertToExpression(returnedExpression.accept(this, context)); + JsExpression jsExpression = translateAsExpression(returnedExpression, context); return new JsReturn(jsExpression); } return new JsReturn(); @@ -166,12 +166,11 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull private JsExpression translateCallee(@NotNull JetCallExpression expression, @NotNull TranslationContext context) { - JetExpression jetCallee = expression.getCalleeExpression(); - if (jetCallee == null) { + JetExpression callee = expression.getCalleeExpression(); + if (callee == null) { throw new AssertionError("Call expression with no callee encountered!"); } - JsNode jsCallee = jetCallee.accept(this, context); - return AstUtil.convertToExpression(jsCallee); + return translateAsExpression(callee, context); } @Override @@ -227,9 +226,9 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull private JsExpression translateConditionExpression(@Nullable JetExpression expression, @NotNull TranslationContext context) { - JsNode jsCondition = translateNullableExpression(expression, context); - if (jsCondition == context.program().getEmptyStmt()) { - throw new AssertionError("Empty condition clause!"); + JsExpression jsCondition = translateNullableExpression(expression, context); + if (jsCondition == null) { + throw new AssertionError("Empty condition!"); } return AstUtil.convertToExpression(jsCondition); } @@ -240,6 +239,10 @@ public final class ExpressionVisitor extends TranslatorVisitor { if (expression == null) { return null; } + return translateAsExpression(expression, context); + } + + private JsExpression translateAsExpression(JetExpression expression, TranslationContext context) { return AstUtil.convertToExpression(expression.accept(this, context)); } @@ -286,7 +289,6 @@ public final class ExpressionVisitor extends TranslatorVisitor { return null; } - //TODO method too long @Override @NotNull public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, @@ -295,8 +297,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { } @NotNull - private JsNode translateQualifiedExpression(@NotNull JetQualifiedExpression expression, - @NotNull TranslationContext context) { + private JsExpression translateQualifiedExpression(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { JsInvocation getterCall = translateAsGetterCall(expression, context); if (getterCall != null) { return getterCall; @@ -311,8 +313,8 @@ public final class ExpressionVisitor extends TranslatorVisitor { } @NotNull - private JsNode translateAsQualifiedAccess(@NotNull JetQualifiedExpression expression, - @NotNull TranslationContext context) { + private JsExpression translateAsQualifiedAccess(@NotNull JetQualifiedExpression expression, + @NotNull TranslationContext context) { JsExpression receiver = translateReceiver(expression, context); JsExpression selector = translateSelector(expression, context); assert (selector instanceof JsNameRef || selector instanceof JsInvocation) @@ -329,23 +331,23 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull TranslationContext context) { JetExpression jetSelector = expression.getSelectorExpression(); assert jetSelector != null : "Selector should not be null in dot qualified expression."; - return AstUtil.convertToExpression(jetSelector.accept(this, context)); + return translateAsExpression(jetSelector, context); } @NotNull private JsExpression translateReceiver(@NotNull JetQualifiedExpression expression, @NotNull TranslationContext context) { - return AstUtil.convertToExpression(expression.getReceiverExpression().accept(this, context)); + return translateAsExpression(expression.getReceiverExpression(), context); } @NotNull - private JsNode translateAsQualifiedNameReference(@NotNull JsExpression receiver, @NotNull JsNameRef selector) { + private JsExpression translateAsQualifiedNameReference(@NotNull JsExpression receiver, @NotNull JsNameRef selector) { selector.setQualifier(receiver); return selector; } @NotNull - private JsNode translateAsQualifiedInvocation(@NotNull JsExpression receiver, @NotNull JsInvocation selector) { + private JsExpression translateAsQualifiedInvocation(@NotNull JsExpression receiver, @NotNull JsInvocation selector) { JsExpression qualifier = selector.getQualifier(); JsNameRef nameRef = (JsNameRef) qualifier; nameRef.setQualifier(receiver); @@ -382,7 +384,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { JsNullLiteral nullLiteral = context.program().getNullLiteral(); JsBinaryOperation nullCheck = new JsBinaryOperation (JsBinaryOperator.NEQ, receiver, nullLiteral); - JsExpression thenExpression = AstUtil.convertToExpression(translateQualifiedExpression(expression, context)); + JsExpression thenExpression = translateQualifiedExpression(expression, context); return new JsConditional(nullCheck, thenExpression, nullLiteral); } diff --git a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java index ee513c04409..798f043dada 100644 --- a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java @@ -28,8 +28,8 @@ public final class FunctionTranslator extends AbstractTranslator { public JsStatement translateAsFunction(@NotNull JetNamedFunction jetFunction) { JsName functionName = translationContext().namespaceScope().declareFreshName(jetFunction.getName()); JsFunction function = generateFunctionObject(jetFunction); - return AstUtil.convertToStatement(AstUtil.newAssignment - (translationContext().getNamespaceQualifiedReference(functionName), function)); + return AstUtil.newAssignmentStatement + (translationContext().getNamespaceQualifiedReference(functionName), function); } @NotNull @@ -55,12 +55,11 @@ public final class FunctionTranslator extends AbstractTranslator { JetExpression jetBodyExpression = jetFunction.getBodyExpression(); //TODO support them ffs assert jetBodyExpression != null : "Function without body not supported at the moment"; - JsNode body = Translation.expressionTranslator - (translationContext().newFunction(jetFunction)).translate(jetBodyExpression); + JsNode body = Translation.translateExpression(jetBodyExpression, translationContext().newFunction(jetFunction)); if (jetFunction.hasBlockBody()) { return AstUtil.convertToBlock(body); } - return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression((body)))); + return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression(body))); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java index b244dc5cec3..56bb69f26ab 100644 --- a/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/InitializerVisitor.java @@ -121,8 +121,7 @@ public class InitializerVisitor extends TranslatorVisitor> { @NotNull private JsStatement translateInitializer(@NotNull JetProperty property, @NotNull TranslationContext context, @NotNull JetExpression initializer) { - JsExpression initExpression = AstUtil.convertToExpression(Translation.expressionTranslator(context) - .translate(initializer)); + JsExpression initExpression = Translation.translateAsExpression(initializer, context); return assignmentToBackingField(property, initExpression, context); } @@ -134,15 +133,14 @@ public class InitializerVisitor extends TranslatorVisitor> { JsName backingFieldName = getBackingFieldName(propertyName, context); JsNameRef backingFieldRef = backingFieldName.makeRef(); backingFieldRef.setQualifier(new JsThisRef()); - return AstUtil.convertToStatement(AstUtil.newAssignment(backingFieldRef, initExpression)); + return AstUtil.newAssignmentStatement(backingFieldRef, initExpression); } @Override @NotNull public List visitAnonymousInitializer(@NotNull JetClassInitializer initializer, @NotNull TranslationContext context) { - return Arrays.asList(AstUtil.convertToStatement - (Translation.expressionTranslator(context).translate(initializer.getBody()))); + return Arrays.asList(Translation.translateAsStatement(initializer.getBody(), context)); } @Override diff --git a/translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java similarity index 52% rename from translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java rename to translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java index 620ca020498..4ef42250b6e 100644 --- a/translator/src/org/jetbrains/k2js/translate/DeclarationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationTranslator.java @@ -7,16 +7,16 @@ import org.jetbrains.jet.lang.psi.JetDeclaration; /** * @author Talanov Pavel */ -public final class DeclarationTranslator extends AbstractTranslator { +public final class NamespaceDeclarationTranslator extends AbstractTranslator { - private DeclarationVisitor visitor = new DeclarationVisitor(); + private NamespaceDeclarationVisitor visitor = new NamespaceDeclarationVisitor(); @NotNull - public static DeclarationTranslator newInstance(@NotNull TranslationContext context) { - return new DeclarationTranslator(context); + public static NamespaceDeclarationTranslator newInstance(@NotNull TranslationContext context) { + return new NamespaceDeclarationTranslator(context); } - private DeclarationTranslator(TranslationContext context) { + private NamespaceDeclarationTranslator(TranslationContext context) { super(context); } diff --git a/translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java similarity index 95% rename from translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java rename to translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java index 2231ddf9a75..ee4dafc1451 100644 --- a/translator/src/org/jetbrains/k2js/translate/DeclarationVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java @@ -14,7 +14,7 @@ import org.jetbrains.jet.lang.psi.JetProperty; * @author Talanov Pavel */ //TODO: rework the class -public class DeclarationVisitor extends TranslatorVisitor { +public class NamespaceDeclarationVisitor extends TranslatorVisitor { @NotNull @Override diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java index d5f4606efea..511e3392928 100644 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceTranslator.java @@ -39,23 +39,24 @@ public final class NamespaceTranslator extends AbstractTranslator { JsFunction dummyFunction = JsFunction.getAnonymousFunctionWithScope (translationContext().getScopeForElement(namespace)); JsBlock namespaceDeclarations = translateDeclarations(namespace, newContext); - block.addStatement(AstUtil.convertToStatement(newNamespace(namespaceName, namespaceDeclarations, dummyFunction))); + JsInvocation namespaceExpression = newNamespace(namespaceName, namespaceDeclarations, dummyFunction); + block.addStatement(AstUtil.convertToStatement(namespaceExpression)); return block; } @NotNull private JsBlock translateDeclarations(@NotNull JetNamespace namespace, @NotNull TranslationContext newContext) { - DeclarationTranslator declarationTranslator = Translation.declarationTranslator(newContext); + NamespaceDeclarationTranslator namespaceDeclarationTranslator = Translation.declarationTranslator(newContext); JsBlock namespaceDeclarations = new JsBlock(); for (JetDeclaration declaration : namespace.getDeclarations()) { - namespaceDeclarations.addStatement(declarationTranslator.translateDeclaration(declaration)); + namespaceDeclarations.addStatement(namespaceDeclarationTranslator.translateDeclaration(declaration)); } return namespaceDeclarations; } @NotNull private JsStatement namespaceInitStatement(@NotNull JsName namespaceName) { - return AstUtil.convertToStatement(AstUtil.newAssignment(namespaceName.makeRef(), new JsObjectLiteral())); + return AstUtil.newAssignmentStatement(namespaceName.makeRef(), new JsObjectLiteral()); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java index 11420e841b4..b725ffbc856 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/OperationTranslator.java @@ -1,7 +1,6 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; -import com.google.dart.compiler.util.AstUtil; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,8 +41,7 @@ public final class OperationTranslator extends AbstractTranslator { private JsExpression translateBaseExpression(@NotNull JetUnaryExpression expression) { JetExpression baseExpression = expression.getBaseExpression(); assert baseExpression != null : "Unary operation should have a base expression"; - return AstUtil.convertToExpression - (Translation.translateExpression(baseExpression, translationContext())); + return Translation.translateAsExpression(baseExpression, translationContext()); } @NotNull @@ -63,6 +61,7 @@ public final class OperationTranslator extends AbstractTranslator { return translateAsBinaryOperation(expression); } + //TODO: think about the ways to improve logic here @Nullable public JsInvocation translateAsSetterCall(@NotNull JetBinaryExpression expression) { JetToken jetOperationToken = getOperationToken(expression); @@ -82,8 +81,7 @@ public final class OperationTranslator extends AbstractTranslator { @NotNull private JsExpression translateAsBinaryOperation(@NotNull JetBinaryExpression expression) { - JsExpression left = AstUtil.convertToExpression - (Translation.translateExpression(expression.getLeft(), translationContext())); + JsExpression left = Translation.translateAsExpression(expression.getLeft(), translationContext()); JsExpression right = translateRightExpression(expression); JetToken token = getOperationToken(expression); if (OperatorTable.hasCorrespondingBinaryOperator(token)) { @@ -101,8 +99,7 @@ public final class OperationTranslator extends AbstractTranslator { private JsExpression translateRightExpression(@NotNull JetBinaryExpression expression) { JetExpression rightExpression = expression.getRight(); assert rightExpression != null : "Binary expression should have a right expression"; - return AstUtil.convertToExpression - (Translation.translateExpression(rightExpression, translationContext())); + return Translation.translateAsExpression(rightExpression, translationContext()); } @NotNull diff --git a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java index 9eaf94f97a2..90c2010255d 100644 --- a/translator/src/org/jetbrains/k2js/translate/OperatorTable.java +++ b/translator/src/org/jetbrains/k2js/translate/OperatorTable.java @@ -55,38 +55,34 @@ public final class OperatorTable { operatorToFunctionNameReference.put(JetTokens.IS_KEYWORD, Namer.isOperationReference()); } - @NotNull public static boolean hasCorrespondingBinaryOperator(@NotNull JetToken token) { return binaryOperatorsMap.containsKey(token); } - @NotNull static boolean hasCorrespondingFunctionInvocation(@NotNull JetToken token) { return operatorToFunctionNameReference.containsKey(token); } @NotNull - public static JsInvocation getCorrespondingFunctionInvocation(@NotNull JetToken token) { + static public JsInvocation getCorrespondingFunctionInvocation(@NotNull JetToken token) { JsNameRef functionReference = operatorToFunctionNameReference.get(token); assert functionReference != null : "Token should represent a function call!"; return AstUtil.newInvocation(functionReference); } @NotNull - public static JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { + static public JsBinaryOperator getBinaryOperator(@NotNull JetToken token) { assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; return binaryOperatorsMap.get(token); } @NotNull - public static JsUnaryOperator getUnaryOperator(@NotNull JetToken token) { + static public JsUnaryOperator getUnaryOperator(@NotNull JetToken token) { assert JetTokens.OPERATIONS.contains(token) : "Token should represent an operation!"; return unaryOperatorsMap.get(token); } - public static boolean isAssignment(JetToken token) { + static public boolean isAssignment(JetToken token) { return (token == JetTokens.EQ); } - - } diff --git a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java index cc18391e265..2de2defcff8 100644 --- a/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PatternTranslator.java @@ -9,7 +9,6 @@ import org.jetbrains.jet.lang.psi.*; /** * @author Talanov Pavel */ -//TODO: fix members order public final class PatternTranslator extends AbstractTranslator { @NotNull @@ -21,24 +20,9 @@ public final class PatternTranslator extends AbstractTranslator { super(context); } -// @NotNull -// public JsExpression translate(@NotNull JetBinaryExpressionWithTypeRHS expression) { -// JsExpression left = AstUtil.convertToExpression -// (Translation.translateExpression(expression.getLeft(), translationContext())); -// JsNameRef right = getClassReference(expression); -// JetToken token = getOperationToken(expression); -// if (OperatorTable.hasCorrespondingFunctionInvocation(token)) { -// JsInvocation functionInvocation = OperatorTable.getCorrespondingFunctionInvocation(token); -// functionInvocation.setArguments(Arrays.asList(left, right)); -// return functionInvocation; -// } -// throw new AssertionError("Unsupported token encountered: " + token.toString()); -// } - @NotNull public JsExpression translateIsExpression(@NotNull JetIsExpression expression) { - JsExpression left = AstUtil.convertToExpression - (Translation.translateExpression(expression.getLeftHandSide(), translationContext())); + JsExpression left = Translation.translateAsExpression(expression.getLeftHandSide(), translationContext()); JetPattern pattern = getPattern(expression); JsExpression resultingExpression = translatePattern(pattern, left); if (expression.isNegated()) { @@ -47,6 +31,13 @@ public final class PatternTranslator extends AbstractTranslator { return resultingExpression; } + @NotNull + private JetPattern getPattern(@NotNull JetIsExpression expression) { + JetPattern pattern = expression.getPattern(); + assert pattern != null : "Pattern should not be null"; + return pattern; + } + @NotNull public JsExpression translatePattern(@NotNull JetPattern pattern, @NotNull JsExpression expressionToMatch) { if (pattern instanceof JetTypePattern) { @@ -58,23 +49,6 @@ public final class PatternTranslator extends AbstractTranslator { throw new AssertionError("Unsupported pattern type " + pattern.getClass()); } - @NotNull - private JsExpression translateExpressionPattern(JsExpression expressionToMatch, JetExpressionPattern pattern) { - JetExpression patternExpression = pattern.getExpression(); - assert patternExpression != null : "Expression patter should have an expression."; - JsExpression expressionToMatchAgainst = - Translation.translateAsExpression(patternExpression, translationContext()); - //TODO: should call equals method here - return new JsBinaryOperation(JsBinaryOperator.REF_EQ, expressionToMatch, expressionToMatchAgainst); - } - - @NotNull - private JetPattern getPattern(@NotNull JetIsExpression expression) { - JetPattern pattern = expression.getPattern(); - assert pattern != null : "Pattern should not be null"; - return pattern; - } - @NotNull private JsExpression translateTypePattern(@NotNull JsExpression expressionToMatch, @NotNull JetTypePattern pattern) { @@ -88,13 +62,6 @@ public final class PatternTranslator extends AbstractTranslator { return getClassNameReferenceForTypeReference(typeReference); } -// @NotNull -// private JsNameRef getClassReference(@NotNull JetBinaryExpressionWithTypeRHS expression) { -// JetTypeReference typeReference = expression.getRight(); -// assert typeReference != null : "Binary type expression should have a right expression"; -// return getClassNameReferenceForTypeReference(typeReference); -// } - @NotNull private JsNameRef getClassNameReferenceForTypeReference(@NotNull JetTypeReference typeReference) { ClassDescriptor referencedClass = BindingUtils.getClassDescriptorForTypeReference @@ -104,13 +71,13 @@ public final class PatternTranslator extends AbstractTranslator { return translationContext().getNamespaceQualifiedReference(className); } -// @NotNull -// private JetToken getOperationToken(@NotNull JetBinaryExpressionWithTypeRHS expression) { -// JetSimpleNameExpression operationExpression = expression.getOperationSign(); -// IElementType elementType = operationExpression.getReferencedNameElementType(); -// assert elementType instanceof JetToken : "Binary type operation should have IElementType of type JetToken"; -// return (JetToken) elementType; -// } - - + @NotNull + private JsExpression translateExpressionPattern(JsExpression expressionToMatch, JetExpressionPattern pattern) { + JetExpression patternExpression = pattern.getExpression(); + assert patternExpression != null : "Expression patter should have an expression."; + JsExpression expressionToMatchAgainst = + Translation.translateAsExpression(patternExpression, translationContext()); + //TODO: should call equals method here + return new JsBinaryOperation(JsBinaryOperator.REF_EQ, expressionToMatch, expressionToMatchAgainst); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java index 21ed8515701..048b2a11fc1 100644 --- a/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/PropertyAccessTranslator.java @@ -1,9 +1,9 @@ package org.jetbrains.k2js.translate; +import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsInvocation; import com.google.dart.compiler.backend.js.ast.JsName; import com.google.dart.compiler.backend.js.ast.JsNameRef; -import com.google.dart.compiler.backend.js.ast.JsNode; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -56,10 +56,10 @@ public final class PropertyAccessTranslator extends AbstractTranslator { @NotNull private JsInvocation translateReceiverAndReturnAccessorInvocation (@NotNull JetQualifiedExpression qualifiedExpression, @NotNull JsName accessorName) { - JsNode node = Translation.expressionTranslator(translationContext()) - .translate(qualifiedExpression.getReceiverExpression()); + JsExpression qualifier = Translation.translateAsExpression + (qualifiedExpression.getReceiverExpression(), translationContext()); JsNameRef result = accessorName.makeRef(); - result.setQualifier(AstUtil.convertToExpression(node)); + result.setQualifier(qualifier); return AstUtil.newInvocation(result); } diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index 6580d694345..a3e3d6c3f05 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -46,8 +46,8 @@ public final class Translation { } @NotNull - static public DeclarationTranslator declarationTranslator(@NotNull TranslationContext context) { - return DeclarationTranslator.newInstance(context); + static public NamespaceDeclarationTranslator declarationTranslator(@NotNull TranslationContext context) { + return NamespaceDeclarationTranslator.newInstance(context); } @NotNull @@ -60,7 +60,6 @@ public final class Translation { return expressionTranslator(context).translate(expression); } - //TODO: clean out similar code fragments @NotNull static public JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { return AstUtil.convertToExpression(translateExpression(expression, context)); diff --git a/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java b/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java index aacc912ce4f..26c34290dc2 100644 --- a/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/TranslatorVisitor.java @@ -2,7 +2,6 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsName; -import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; @@ -45,8 +44,7 @@ public class TranslatorVisitor extends JetVisitor { JsExpression jsInitExpression = null; JetExpression initializer = declaration.getInitializer(); if (initializer != null) { - jsInitExpression = AstUtil.convertToExpression(Translation.expressionTranslator(context) - .translate(initializer)); + jsInitExpression = Translation.translateAsExpression(initializer, context); } return jsInitExpression; } @@ -67,6 +65,6 @@ public class TranslatorVisitor extends JetVisitor { if (jetExpression == null) { throw new AssertionError("Argument with no expression encountered!"); } - return AstUtil.convertToExpression(Translation.translateExpression(jetExpression, context)); + return Translation.translateAsExpression(jetExpression, context); } } diff --git a/translator/test/org/jetbrains/k2js/test/BasicClassTest.java b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java index 42f8efaaa49..efcad3444ce 100644 --- a/translator/test/org/jetbrains/k2js/test/BasicClassTest.java +++ b/translator/test/org/jetbrains/k2js/test/BasicClassTest.java @@ -24,7 +24,7 @@ public class BasicClassTest extends IncludeLibraryTest { testFooBoxIsTrue("methodDeclarationAndCall.kt"); } - //TODO: test excluded. Wait for bugfix and implement functionality + //TODO: wait for bugfix and implement properties as consructor parameter declaration @Test public void constructorWithParameter() throws Exception { testFooBoxIsTrue("constructorWithParameter.kt"); diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index c0577d76191..a1031833734 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -44,4 +44,9 @@ public final class PatternMatchingTest extends IncludeLibraryTest { testFooBoxIsTrue("whenValueOrType.kt"); } + @Test + public void multipleCases() throws Exception { + testFooBoxIsTrue("multipleCases.kt"); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/TupleTest.java b/translator/test/org/jetbrains/k2js/test/TupleTest.java new file mode 100644 index 00000000000..4e8fab2ad21 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/TupleTest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +/** + * @author Talanov Pavel + */ +public class TupleTest extends IncludeLibraryTest { + + final private static String MAIN = "tuple/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + //TODO: excluded because Tuples are not implemented +// @Test +// public void twoElements() throws Exception { +// testFooBoxIsTrue("twoElements.kt"); +// } + +} + diff --git a/translator/testFiles/patternMatching/cases/multipleCases.kt b/translator/testFiles/patternMatching/cases/multipleCases.kt new file mode 100644 index 00000000000..9b6ef9a3bda --- /dev/null +++ b/translator/testFiles/patternMatching/cases/multipleCases.kt @@ -0,0 +1,17 @@ +namespace foo + +fun box() : Boolean { + val c = 3 + val d = 5 + var z = 0 + when(c) { + is 5, is 3 => z++; + else => z = -1000; + } + + when(d) { + is 5, is 3 => z++; + else => z = -1000; + } + return c == 2 +} \ No newline at end of file diff --git a/translator/testFiles/tuple/cases/twoElements.kt b/translator/testFiles/tuple/cases/twoElements.kt new file mode 100644 index 00000000000..8a8524ca4d4 --- /dev/null +++ b/translator/testFiles/tuple/cases/twoElements.kt @@ -0,0 +1,6 @@ +namespace foo + +fun box() : Boolean { + val a : (Int, Int) = (1, 2) + return (a._1 == 1) + } \ No newline at end of file From 95ab245f1540e4e225ef889f49199224d51dbf7c Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 12:38:06 +0400 Subject: [PATCH 15/22] Added support for multiple cases in one when entry. --- .../k2js/translate/WhenTranslator.java | 33 ++++++++++++++++--- .../k2js/test/PatternMatchingTest.java | 2 +- .../patternMatching/cases/multipleCases.kt | 4 +-- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java index cfa1c136fe7..b0de57c306f 100644 --- a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -3,6 +3,7 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; @@ -105,13 +106,35 @@ public class WhenTranslator extends AbstractTranslator { //TODO: ask what these conditions mean @NotNull private JsExpression translateConditions(@NotNull JetWhenEntry entry) { - JetWhenCondition[] conditions = entry.getConditions(); -// for (JetWhenCondition condition : conditions) { -// translateCondition(condition); -// } - return translateCondition(conditions[0]); + List conditions = new ArrayList(); + for (JetWhenCondition condition : entry.getConditions()) { + conditions.add(translateCondition(condition)); + } + return anyOfThemIsTrue(conditions); } + @NotNull + private JsExpression anyOfThemIsTrue(List conditions) { + assert !conditions.isEmpty() : "When entry (not else) should have at least one condition"; + JsExpression current = null; + for (JsExpression condition : conditions) { + current = addCaseCondition(current, condition); + } + assert current != null; + return current; + } + + @NotNull + private JsExpression addCaseCondition(@Nullable JsExpression current, @NotNull JsExpression condition) { + if (current == null) { + current = condition; + } else { + current = new JsBinaryOperation(JsBinaryOperator.OR, current, condition); + } + return current; + } + + @NotNull private JsExpression translateCondition(@NotNull JetWhenCondition condition) { if (condition instanceof JetWhenConditionIsPattern) { diff --git a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java index a1031833734..74d0996ba70 100644 --- a/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java +++ b/translator/test/org/jetbrains/k2js/test/PatternMatchingTest.java @@ -46,7 +46,7 @@ public final class PatternMatchingTest extends IncludeLibraryTest { @Test public void multipleCases() throws Exception { - testFooBoxIsTrue("multipleCases.kt"); + testFunctionOutput("multipleCases.kt", "foo", "box", 2.0); } } diff --git a/translator/testFiles/patternMatching/cases/multipleCases.kt b/translator/testFiles/patternMatching/cases/multipleCases.kt index 9b6ef9a3bda..b75ebe8d8d3 100644 --- a/translator/testFiles/patternMatching/cases/multipleCases.kt +++ b/translator/testFiles/patternMatching/cases/multipleCases.kt @@ -1,6 +1,6 @@ namespace foo -fun box() : Boolean { +fun box() : Int { val c = 3 val d = 5 var z = 0 @@ -13,5 +13,5 @@ fun box() : Boolean { is 5, is 3 => z++; else => z = -1000; } - return c == 2 + return z } \ No newline at end of file From 37ea3c75caaa84edfe87f3818570ec520b1a3e3d Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 13:12:47 +0400 Subject: [PATCH 16/22] Split expression tests into different categories. --- .../k2js/translate/ExpressionVisitor.java | 15 +++ .../k2js/test/AbstractExpressionTest.java | 22 ++++ .../jetbrains/k2js/test/ConditionalTest.java | 31 +++++ .../jetbrains/k2js/test/ExpressionTest.java | 122 ------------------ .../org/jetbrains/k2js/test/FunctionTest.java | 26 ++++ .../org/jetbrains/k2js/test/MiscTest.java | 23 ++++ .../jetbrains/k2js/test/OperationTest.java | 45 +++++++ .../org/jetbrains/k2js/test/StringTest.java | 26 ++++ .../jetbrains/k2js/test/TranslationTest.java | 40 +++--- .../org/jetbrains/k2js/test/WhileTest.java | 35 +++++ translator/testFiles/expression/cases/test.kt | 9 -- .../{ => conditional}/cases/elseif.kt | 0 .../expression/{ => conditional}/cases/if.kt | 0 .../cases/ifElseAsExpression.kt | 0 .../cases/functionUsedBeforeDeclaration.kt | 0 .../cases/functionWithTwoParametersCall.kt | 0 .../{ => misc}/cases/localProperty.jet | 0 .../{ => operation}/cases/assign.jet | 0 .../{ => operation}/cases/comparison.kt | 0 .../{ => operation}/cases/notBoolean.kt | 0 .../cases/positiveAndNegativeNumbers.kt | 0 .../cases/postfixIntOperations.kt | 0 .../cases/prefixIntOperations.kt | 0 .../{ => string}/cases/stringAssignment.kt | 0 .../{ => string}/cases/stringConstant.kt | 0 .../expression/{ => while}/cases/doWhile.kt | 0 .../expression/{ => while}/cases/doWhile2.kt | 0 .../expression/{ => while}/cases/while.kt | 0 .../expression/{ => while}/cases/while2.kt | 0 29 files changed, 245 insertions(+), 149 deletions(-) create mode 100644 translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/ConditionalTest.java delete mode 100644 translator/test/org/jetbrains/k2js/test/ExpressionTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/FunctionTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/MiscTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/OperationTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/StringTest.java create mode 100644 translator/test/org/jetbrains/k2js/test/WhileTest.java delete mode 100644 translator/testFiles/expression/cases/test.kt rename translator/testFiles/expression/{ => conditional}/cases/elseif.kt (100%) rename translator/testFiles/expression/{ => conditional}/cases/if.kt (100%) rename translator/testFiles/expression/{ => conditional}/cases/ifElseAsExpression.kt (100%) rename translator/testFiles/expression/{ => function}/cases/functionUsedBeforeDeclaration.kt (100%) rename translator/testFiles/expression/{ => function}/cases/functionWithTwoParametersCall.kt (100%) rename translator/testFiles/expression/{ => misc}/cases/localProperty.jet (100%) rename translator/testFiles/expression/{ => operation}/cases/assign.jet (100%) rename translator/testFiles/expression/{ => operation}/cases/comparison.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/notBoolean.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/positiveAndNegativeNumbers.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/postfixIntOperations.kt (100%) rename translator/testFiles/expression/{ => operation}/cases/prefixIntOperations.kt (100%) rename translator/testFiles/expression/{ => string}/cases/stringAssignment.kt (100%) rename translator/testFiles/expression/{ => string}/cases/stringConstant.kt (100%) rename translator/testFiles/expression/{ => while}/cases/doWhile.kt (100%) rename translator/testFiles/expression/{ => while}/cases/doWhile2.kt (100%) rename translator/testFiles/expression/{ => while}/cases/while.kt (100%) rename translator/testFiles/expression/{ => while}/cases/while2.kt (100%) diff --git a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java index bb88309f447..5ab654e34c5 100644 --- a/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/ExpressionVisitor.java @@ -400,8 +400,23 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, @NotNull TranslationContext context) { + // we actually do not care for types in js return Translation.translateExpression(expression.getLeft(), context); } + @Override + @NotNull + public JsNode visitBreakExpression(@NotNull JetBreakExpression expression, + @NotNull TranslationContext context) { + return new JsBreak(); + } + + @Override + @NotNull + public JsNode visitContinueExpression(@NotNull JetContinueExpression expression, + @NotNull TranslationContext context) { + return new JsContinue(); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java new file mode 100644 index 00000000000..bb96c99a92a --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/AbstractExpressionTest.java @@ -0,0 +1,22 @@ +package org.jetbrains.k2js.test; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Talanov Pavel + */ +public abstract class AbstractExpressionTest extends TranslationTest { + + private final String SUITE = "expression/"; + + @Override + protected List generateFilenameList(String inputFile) { + return Arrays.asList(inputFile); + } + + @Override + protected String suiteDirectoryName() { + return SUITE; + } +} diff --git a/translator/test/org/jetbrains/k2js/test/ConditionalTest.java b/translator/test/org/jetbrains/k2js/test/ConditionalTest.java new file mode 100644 index 00000000000..a7124ad6923 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/ConditionalTest.java @@ -0,0 +1,31 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class ConditionalTest extends AbstractExpressionTest { + final private static String MAIN = "conditional/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void ifElseAsExpression() throws Exception { + testFooBoxIsTrue("ifElseAsExpression.kt"); + } + + @Test + public void ifElse() throws Exception { + testFunctionOutput("if.kt", "foo", "box", 5); + } + + //TODO: test fails due to isStatement issue, include when issue is solved or implement another solution +// @Test +// public void ifElseIf() throws Exception { +// testFunctionOutput("elseif.kt", "foo", "box", 5); +// } +} diff --git a/translator/test/org/jetbrains/k2js/test/ExpressionTest.java b/translator/test/org/jetbrains/k2js/test/ExpressionTest.java deleted file mode 100644 index 2415c9a6ef7..00000000000 --- a/translator/test/org/jetbrains/k2js/test/ExpressionTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.jetbrains.k2js.test; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -/** - * @author Talanov Pavel - *

- * This class tests basic language features and constructs such as constants, local variables, simple loops, - * conditional clauses etc. - */ -public final class ExpressionTest extends TranslationTest { - - private static final String MAIN = "expression/"; - - protected String mainDirectory() { - return MAIN; - } - - @Override - protected List generateFilenameList(String inputFile) { - return Arrays.asList(inputFile); - } - - @Test - public void currentTest() throws Exception { - testFooBoxIsTrue("test.kt"); - } - - @Test - public void testAssign() throws Exception { - testFunctionOutput("assign.jet", "foo", "f", 2.0); - } - - @Test - public void namespaceProperties() throws Exception { - testFunctionOutput("localProperty.jet", "foo", "box", 50); - } - - @Test - public void comparison() throws Exception { - testFooBoxIsTrue("comparison.kt"); - } - - @Test - public void ifElse() throws Exception { - testFunctionOutput("if.kt", "foo", "box", 5); - } - //TODO: test fails due to isStatement issue, include when issue is solved or implement another solution -// @Test -// public void ifElseIf() throws Exception { -// testFunctionOutput("elseif.kt", "foo", "box", 5); -// } - - @Test - public void whileSimpleTest() throws Exception { - testFooBoxIsTrue("while.kt"); - } - - @Test - public void doWhileSimpleTest() throws Exception { - testFooBoxIsTrue("doWhile.kt"); - } - - @Test - public void doWhileExecutesAtLeastOnce() throws Exception { - testFooBoxIsTrue("doWhile2.kt"); - } - - @Test - public void whileDoesntExecuteEvenOnceIfConditionIsFalse() throws Exception { - testFooBoxIsTrue("while2.kt"); - } - - @Test - public void stringConstant() throws Exception { - testFooBoxIsTrue("stringConstant.kt"); - } - - @Test - public void stringAssignment() throws Exception { - testFooBoxIsTrue("stringAssignment.kt"); - } - - @Test - public void functionUsedBeforeDeclaration() throws Exception { - testFooBoxIsTrue("functionUsedBeforeDeclaration.kt"); - } - - @Test - public void functionWithTwoParametersCall() throws Exception { - testFooBoxIsTrue("functionWithTwoParametersCall.kt"); - } - - @Test - public void prefixIntOperations() throws Exception { - testFooBoxIsTrue("prefixIntOperations.kt"); - } - - @Test - public void postfixIntOperations() throws Exception { - testFooBoxIsTrue("postfixIntOperations.kt"); - } - - @Test - public void notBoolean() throws Exception { - testFooBoxIsTrue("notBoolean.kt"); - } - - @Test - public void positiveAndNegativeNumbers() throws Exception { - testFooBoxIsTrue("positiveAndNegativeNumbers.kt"); - } - - @Test - public void ifElseAsExpression() throws Exception { - testFooBoxIsTrue("ifElseAsExpression.kt"); - } - -} diff --git a/translator/test/org/jetbrains/k2js/test/FunctionTest.java b/translator/test/org/jetbrains/k2js/test/FunctionTest.java new file mode 100644 index 00000000000..ad96579343a --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/FunctionTest.java @@ -0,0 +1,26 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class FunctionTest extends AbstractExpressionTest { + + final private static String MAIN = "function/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void functionUsedBeforeDeclaration() throws Exception { + testFooBoxIsTrue("functionUsedBeforeDeclaration.kt"); + } + + @Test + public void functionWithTwoParametersCall() throws Exception { + testFooBoxIsTrue("functionWithTwoParametersCall.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/MiscTest.java b/translator/test/org/jetbrains/k2js/test/MiscTest.java new file mode 100644 index 00000000000..da01678566a --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/MiscTest.java @@ -0,0 +1,23 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + *

+ * This class contains tests that do not fall in any particular category + * most probably because that functionality has very little support + */ +public class MiscTest extends AbstractExpressionTest { + final private static String MAIN = "misc/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void namespaceProperties() throws Exception { + testFunctionOutput("localProperty.jet", "foo", "box", 50); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/OperationTest.java b/translator/test/org/jetbrains/k2js/test/OperationTest.java new file mode 100644 index 00000000000..92be83c7be0 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/OperationTest.java @@ -0,0 +1,45 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class OperationTest extends AbstractExpressionTest { + final private static String MAIN = "operation/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void prefixIntOperations() throws Exception { + testFooBoxIsTrue("prefixIntOperations.kt"); + } + + @Test + public void postfixIntOperations() throws Exception { + testFooBoxIsTrue("postfixIntOperations.kt"); + } + + @Test + public void notBoolean() throws Exception { + testFooBoxIsTrue("notBoolean.kt"); + } + + @Test + public void positiveAndNegativeNumbers() throws Exception { + testFooBoxIsTrue("positiveAndNegativeNumbers.kt"); + } + + @Test + public void assign() throws Exception { + testFunctionOutput("assign.jet", "foo", "f", 2.0); + } + + @Test + public void comparison() throws Exception { + testFooBoxIsTrue("comparison.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/StringTest.java b/translator/test/org/jetbrains/k2js/test/StringTest.java new file mode 100644 index 00000000000..fa8a9b09d02 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/StringTest.java @@ -0,0 +1,26 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public class StringTest extends AbstractExpressionTest { + + final private static String MAIN = "string/"; + + @Override + protected String mainDirectory() { + return MAIN; + } + + @Test + public void stringConstant() throws Exception { + testFooBoxIsTrue("stringConstant.kt"); + } + + @Test + public void stringAssignment() throws Exception { + testFooBoxIsTrue("stringAssignment.kt"); + } +} diff --git a/translator/test/org/jetbrains/k2js/test/TranslationTest.java b/translator/test/org/jetbrains/k2js/test/TranslationTest.java index 3a6454b59ea..80b4e3eef0c 100644 --- a/translator/test/org/jetbrains/k2js/test/TranslationTest.java +++ b/translator/test/org/jetbrains/k2js/test/TranslationTest.java @@ -1,7 +1,6 @@ package org.jetbrains.k2js.test; import org.jetbrains.k2js.K2JSTranslator; -import org.junit.Before; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -19,29 +18,34 @@ public abstract class TranslationTest { final private static String OUT = "out/"; final private String KOTLIN_JS_LIB = TEST_FILES + "kotlin_lib.js"; - protected String testFilesDirectory; - protected String testCasesDirectory; - protected String outputDirectory; - - @Before - public void setUpClass() { - testCasesDirectory = CASES; - outputDirectory = OUT; - testFilesDirectory = TEST_FILES + mainDirectory(); - } - protected abstract String mainDirectory(); protected String kotlinLibraryPath() { return KOTLIN_JS_LIB; } - private String getOutputDirectory() { - return testFilesDirectory + outputDirectory; + private String casesDirectoryName() { + return CASES; } - private String getInputDirectory() { - return testFilesDirectory + testCasesDirectory; + private String outDirectoryName() { + return OUT; + } + + private String testFilesPath() { + return TEST_FILES + suiteDirectoryName() + mainDirectory(); + } + + protected String suiteDirectoryName() { + return ""; + } + + private String getOutputPath() { + return testFilesPath() + outDirectoryName(); + } + + private String getInputPath() { + return testFilesPath() + casesDirectoryName(); } protected void testFunctionOutput(String filename, String namespaceName, @@ -62,7 +66,7 @@ public abstract class TranslationTest { //TODO: refactor filename generation logic private String getOutputFilePath(String filename) { - return getOutputDirectory() + convertToDotJsFile(filename); + return getOutputPath() + convertToDotJsFile(filename); } private String convertToDotJsFile(String filename) { @@ -70,7 +74,7 @@ public abstract class TranslationTest { } private String getInputFilePath(String filename) { - return getInputDirectory() + filename; + return getInputPath() + filename; } protected String cases(String filename) { diff --git a/translator/test/org/jetbrains/k2js/test/WhileTest.java b/translator/test/org/jetbrains/k2js/test/WhileTest.java new file mode 100644 index 00000000000..c7b0c03d0a1 --- /dev/null +++ b/translator/test/org/jetbrains/k2js/test/WhileTest.java @@ -0,0 +1,35 @@ +package org.jetbrains.k2js.test; + +import org.junit.Test; + +/** + * @author Talanov Pavel + */ +public final class WhileTest extends AbstractExpressionTest { + + private static final String MAIN = "while/"; + + protected String mainDirectory() { + return MAIN; + } + + @Test + public void whileSimpleTest() throws Exception { + testFooBoxIsTrue("while.kt"); + } + + @Test + public void doWhileSimpleTest() throws Exception { + testFooBoxIsTrue("doWhile.kt"); + } + + @Test + public void doWhileExecutesAtLeastOnce() throws Exception { + testFooBoxIsTrue("doWhile2.kt"); + } + + @Test + public void whileDoesntExecuteEvenOnceIfConditionIsFalse() throws Exception { + testFooBoxIsTrue("while2.kt"); + } +} diff --git a/translator/testFiles/expression/cases/test.kt b/translator/testFiles/expression/cases/test.kt deleted file mode 100644 index 98ee81ba1ea..00000000000 --- a/translator/testFiles/expression/cases/test.kt +++ /dev/null @@ -1,9 +0,0 @@ -namespace foo - -fun box() : Boolean { - val a = 2; - val b = 3; - var c = 4; - return (a < c) -} - diff --git a/translator/testFiles/expression/cases/elseif.kt b/translator/testFiles/expression/conditional/cases/elseif.kt similarity index 100% rename from translator/testFiles/expression/cases/elseif.kt rename to translator/testFiles/expression/conditional/cases/elseif.kt diff --git a/translator/testFiles/expression/cases/if.kt b/translator/testFiles/expression/conditional/cases/if.kt similarity index 100% rename from translator/testFiles/expression/cases/if.kt rename to translator/testFiles/expression/conditional/cases/if.kt diff --git a/translator/testFiles/expression/cases/ifElseAsExpression.kt b/translator/testFiles/expression/conditional/cases/ifElseAsExpression.kt similarity index 100% rename from translator/testFiles/expression/cases/ifElseAsExpression.kt rename to translator/testFiles/expression/conditional/cases/ifElseAsExpression.kt diff --git a/translator/testFiles/expression/cases/functionUsedBeforeDeclaration.kt b/translator/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt similarity index 100% rename from translator/testFiles/expression/cases/functionUsedBeforeDeclaration.kt rename to translator/testFiles/expression/function/cases/functionUsedBeforeDeclaration.kt diff --git a/translator/testFiles/expression/cases/functionWithTwoParametersCall.kt b/translator/testFiles/expression/function/cases/functionWithTwoParametersCall.kt similarity index 100% rename from translator/testFiles/expression/cases/functionWithTwoParametersCall.kt rename to translator/testFiles/expression/function/cases/functionWithTwoParametersCall.kt diff --git a/translator/testFiles/expression/cases/localProperty.jet b/translator/testFiles/expression/misc/cases/localProperty.jet similarity index 100% rename from translator/testFiles/expression/cases/localProperty.jet rename to translator/testFiles/expression/misc/cases/localProperty.jet diff --git a/translator/testFiles/expression/cases/assign.jet b/translator/testFiles/expression/operation/cases/assign.jet similarity index 100% rename from translator/testFiles/expression/cases/assign.jet rename to translator/testFiles/expression/operation/cases/assign.jet diff --git a/translator/testFiles/expression/cases/comparison.kt b/translator/testFiles/expression/operation/cases/comparison.kt similarity index 100% rename from translator/testFiles/expression/cases/comparison.kt rename to translator/testFiles/expression/operation/cases/comparison.kt diff --git a/translator/testFiles/expression/cases/notBoolean.kt b/translator/testFiles/expression/operation/cases/notBoolean.kt similarity index 100% rename from translator/testFiles/expression/cases/notBoolean.kt rename to translator/testFiles/expression/operation/cases/notBoolean.kt diff --git a/translator/testFiles/expression/cases/positiveAndNegativeNumbers.kt b/translator/testFiles/expression/operation/cases/positiveAndNegativeNumbers.kt similarity index 100% rename from translator/testFiles/expression/cases/positiveAndNegativeNumbers.kt rename to translator/testFiles/expression/operation/cases/positiveAndNegativeNumbers.kt diff --git a/translator/testFiles/expression/cases/postfixIntOperations.kt b/translator/testFiles/expression/operation/cases/postfixIntOperations.kt similarity index 100% rename from translator/testFiles/expression/cases/postfixIntOperations.kt rename to translator/testFiles/expression/operation/cases/postfixIntOperations.kt diff --git a/translator/testFiles/expression/cases/prefixIntOperations.kt b/translator/testFiles/expression/operation/cases/prefixIntOperations.kt similarity index 100% rename from translator/testFiles/expression/cases/prefixIntOperations.kt rename to translator/testFiles/expression/operation/cases/prefixIntOperations.kt diff --git a/translator/testFiles/expression/cases/stringAssignment.kt b/translator/testFiles/expression/string/cases/stringAssignment.kt similarity index 100% rename from translator/testFiles/expression/cases/stringAssignment.kt rename to translator/testFiles/expression/string/cases/stringAssignment.kt diff --git a/translator/testFiles/expression/cases/stringConstant.kt b/translator/testFiles/expression/string/cases/stringConstant.kt similarity index 100% rename from translator/testFiles/expression/cases/stringConstant.kt rename to translator/testFiles/expression/string/cases/stringConstant.kt diff --git a/translator/testFiles/expression/cases/doWhile.kt b/translator/testFiles/expression/while/cases/doWhile.kt similarity index 100% rename from translator/testFiles/expression/cases/doWhile.kt rename to translator/testFiles/expression/while/cases/doWhile.kt diff --git a/translator/testFiles/expression/cases/doWhile2.kt b/translator/testFiles/expression/while/cases/doWhile2.kt similarity index 100% rename from translator/testFiles/expression/cases/doWhile2.kt rename to translator/testFiles/expression/while/cases/doWhile2.kt diff --git a/translator/testFiles/expression/cases/while.kt b/translator/testFiles/expression/while/cases/while.kt similarity index 100% rename from translator/testFiles/expression/cases/while.kt rename to translator/testFiles/expression/while/cases/while.kt diff --git a/translator/testFiles/expression/cases/while2.kt b/translator/testFiles/expression/while/cases/while2.kt similarity index 100% rename from translator/testFiles/expression/cases/while2.kt rename to translator/testFiles/expression/while/cases/while2.kt From 16c6e8bee535ed94fdff6b7d37567a61424fe30a Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 13:19:23 +0400 Subject: [PATCH 17/22] Added support for break expressions in while/do while. --- .../test/org/jetbrains/k2js/test/WhileTest.java | 10 ++++++++++ .../expression/while/cases/breakDoWhile.kt | 13 +++++++++++++ .../testFiles/expression/while/cases/breakWhile.kt | 13 +++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 translator/testFiles/expression/while/cases/breakDoWhile.kt create mode 100644 translator/testFiles/expression/while/cases/breakWhile.kt diff --git a/translator/test/org/jetbrains/k2js/test/WhileTest.java b/translator/test/org/jetbrains/k2js/test/WhileTest.java index c7b0c03d0a1..0111b622ca7 100644 --- a/translator/test/org/jetbrains/k2js/test/WhileTest.java +++ b/translator/test/org/jetbrains/k2js/test/WhileTest.java @@ -32,4 +32,14 @@ public final class WhileTest extends AbstractExpressionTest { public void whileDoesntExecuteEvenOnceIfConditionIsFalse() throws Exception { testFooBoxIsTrue("while2.kt"); } + + @Test + public void breakWhile() throws Exception { + testFooBoxIsTrue("breakWhile.kt"); + } + + @Test + public void breakDoWhile() throws Exception { + testFooBoxIsTrue("breakDoWhile.kt"); + } } diff --git a/translator/testFiles/expression/while/cases/breakDoWhile.kt b/translator/testFiles/expression/while/cases/breakDoWhile.kt new file mode 100644 index 00000000000..b69bec71763 --- /dev/null +++ b/translator/testFiles/expression/while/cases/breakDoWhile.kt @@ -0,0 +1,13 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + do { + if (i == 3) { + break; + } + ++i; + } while ( i < 100) + + return i == 3 +} \ No newline at end of file diff --git a/translator/testFiles/expression/while/cases/breakWhile.kt b/translator/testFiles/expression/while/cases/breakWhile.kt new file mode 100644 index 00000000000..c0b2a84a810 --- /dev/null +++ b/translator/testFiles/expression/while/cases/breakWhile.kt @@ -0,0 +1,13 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + while ( i < 100) { + if (i == 3) { + break; + } + ++i; + } + + return i == 3 +} \ No newline at end of file From f06fa3c295851fb40745f52c41bcaf23b527169e Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 13:24:18 +0400 Subject: [PATCH 18/22] Added support for continue in while/do while. --- .../test/org/jetbrains/k2js/test/WhileTest.java | 10 ++++++++++ .../expression/while/cases/continueDoWhile.kt | 15 +++++++++++++++ .../expression/while/cases/continueWhile.kt | 15 +++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 translator/testFiles/expression/while/cases/continueDoWhile.kt create mode 100644 translator/testFiles/expression/while/cases/continueWhile.kt diff --git a/translator/test/org/jetbrains/k2js/test/WhileTest.java b/translator/test/org/jetbrains/k2js/test/WhileTest.java index 0111b622ca7..d31f87aaf99 100644 --- a/translator/test/org/jetbrains/k2js/test/WhileTest.java +++ b/translator/test/org/jetbrains/k2js/test/WhileTest.java @@ -42,4 +42,14 @@ public final class WhileTest extends AbstractExpressionTest { public void breakDoWhile() throws Exception { testFooBoxIsTrue("breakDoWhile.kt"); } + + @Test + public void continueWhile() throws Exception { + testFooBoxIsTrue("continueWhile.kt"); + } + + @Test + public void continueDoWhile() throws Exception { + testFooBoxIsTrue("continueDoWhile.kt"); + } } diff --git a/translator/testFiles/expression/while/cases/continueDoWhile.kt b/translator/testFiles/expression/while/cases/continueDoWhile.kt new file mode 100644 index 00000000000..a514a8e70ca --- /dev/null +++ b/translator/testFiles/expression/while/cases/continueDoWhile.kt @@ -0,0 +1,15 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + var b = true + do { + ++i; + if (i >= 1) { + continue; + } + b =false; + } while (i < 100) + + return b +} \ No newline at end of file diff --git a/translator/testFiles/expression/while/cases/continueWhile.kt b/translator/testFiles/expression/while/cases/continueWhile.kt new file mode 100644 index 00000000000..0094978a9b5 --- /dev/null +++ b/translator/testFiles/expression/while/cases/continueWhile.kt @@ -0,0 +1,15 @@ +namespace foo + +fun box() : Boolean { + var i = 0 + var b = true + while (i < 100) { + ++i; + if (i >= 1) { + continue; + } + b =false; + } + + return b +} \ No newline at end of file From 694902103d48642f119b64c58a05c6e58f9a0e46 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 16:12:14 +0400 Subject: [PATCH 19/22] Rewrote name resolving logic. Improved DeclarationExtractor and renamed to Declarations. --- ...rationExtractor.java => Declarations.java} | 15 ++- .../k2js/declarations/ExtractionVisitor.java | 66 +++++++++--- .../k2js/translate/BindingUtils.java | 6 ++ .../k2js/translate/ExpressionVisitor.java | 65 +++-------- .../k2js/translate/FunctionTranslator.java | 61 +++++++---- .../k2js/translate/GenerationState.java | 11 +- .../NamespaceDeclarationVisitor.java | 2 +- .../k2js/translate/ReferenceTranslator.java | 102 ++++++++++++++++++ .../jetbrains/k2js/translate/Translation.java | 7 +- .../k2js/translate/TranslationContext.java | 48 +++++---- .../k2js/translate/WhenTranslator.java | 2 +- .../org/jetbrains/k2js/test/FunctionTest.java | 5 + .../function/cases/functionLiteral.kt | 8 ++ 13 files changed, 285 insertions(+), 113 deletions(-) rename translator/src/org/jetbrains/k2js/declarations/{DeclarationExtractor.java => Declarations.java} (74%) create mode 100644 translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java create mode 100644 translator/testFiles/expression/function/cases/functionLiteral.kt diff --git a/translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java b/translator/src/org/jetbrains/k2js/declarations/Declarations.java similarity index 74% rename from translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java rename to translator/src/org/jetbrains/k2js/declarations/Declarations.java index c9d9dd2c13c..4aa19b139ce 100644 --- a/translator/src/org/jetbrains/k2js/declarations/DeclarationExtractor.java +++ b/translator/src/org/jetbrains/k2js/declarations/Declarations.java @@ -11,19 +11,22 @@ import java.util.Map; /** * @author Talanov Pavel */ -public final class DeclarationExtractor { +public final class Declarations { private final Map descriptorToScopeMap = new HashMap(); private final Map descriptorToNameMap = new HashMap(); - public DeclarationExtractor() { + private Declarations() { } - public void extractDeclarations(@NotNull DeclarationDescriptor descriptor, JsScope rootScope) { - ExtractionVisitor visitor = new ExtractionVisitor(this); + @NotNull + static public Declarations extractDeclarations(@NotNull DeclarationDescriptor descriptor, JsScope rootScope) { + Declarations declarations = new Declarations(); + ExtractionVisitor visitor = new ExtractionVisitor(declarations); descriptor.accept(visitor, rootScope); + return declarations; } @NotNull @@ -40,6 +43,10 @@ public final class DeclarationExtractor { return name; } + public boolean isDeclared(@NotNull DeclarationDescriptor descriptor) { + return descriptorToNameMap.containsKey(descriptor); + } + /*package*/ void putScope(@NotNull DeclarationDescriptor descriptor, @NotNull JsScope scope) { descriptorToScopeMap.put(descriptor, scope); } diff --git a/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java b/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java index 02880fa7949..4e16bf9dea6 100644 --- a/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java +++ b/translator/src/org/jetbrains/k2js/declarations/ExtractionVisitor.java @@ -13,32 +13,58 @@ import org.jetbrains.k2js.translate.Namer; public final class ExtractionVisitor extends DeclarationDescriptorVisitor { @NotNull - private final DeclarationExtractor extractor; + private final Declarations declarations; - /*package*/ ExtractionVisitor(@NotNull DeclarationExtractor extractor) { - this.extractor = extractor; + /*package*/ ExtractionVisitor(@NotNull Declarations declarations) { + this.declarations = declarations; } - @Override public Void visitClassDescriptor(@NotNull ClassDescriptor descriptor, @NotNull JsScope enclosingScope) { + JsScope classScope = extractClassDeclarations(descriptor, enclosingScope); + visitClassMembers(descriptor, classScope); + return null; + } + + @NotNull + private JsScope extractClassDeclarations(@NotNull ClassDescriptor descriptor, @NotNull JsScope enclosingScope) { String className = descriptor.getName(); - extractor.putName(descriptor, enclosingScope.declareName(className)); + declarations.putName(descriptor, enclosingScope.declareName(className)); JsScope classScope = new JsScope(enclosingScope, "class " + className); - extractor.putScope(descriptor, classScope); + declarations.putScope(descriptor, classScope); + return classScope; + } + + private void visitClassMembers(@NotNull ClassDescriptor descriptor, @NotNull JsScope classScope) { + visitClassConstructor(descriptor, classScope); for (DeclarationDescriptor memberDescriptor : descriptor.getDefaultType().getMemberScope().getAllDescriptors()) { memberDescriptor.accept(this, classScope); } + } + + private void visitClassConstructor(@NotNull ClassDescriptor descriptor, @NotNull JsScope classScope) { + for (ConstructorDescriptor constructorDescriptor : descriptor.getConstructors()) { + constructorDescriptor.accept(this, classScope); + } + } + + //TODO: think about the ways to make this less hacky + //For now constructor just references the name of the class. Initialize method scope is defined independently. + @Override + public Void visitConstructorDescriptor(@NotNull ConstructorDescriptor descriptor, @NotNull JsScope enclosingScope) { + String className = descriptor.getContainingDeclaration().getName(); + JsName alreadyDeclaredClassName = enclosingScope.findExistingName(className); + declarations.putName(descriptor, alreadyDeclaredClassName); return null; } @Override public Void visitFunctionDescriptor(@NotNull FunctionDescriptor descriptor, @NotNull JsScope enclosingScope) { String functionName = descriptor.getName(); - extractor.putName(descriptor, enclosingScope.declareName(functionName)); + declarations.putName(descriptor, enclosingScope.declareName(functionName)); JsScope functionScope = new JsScope(enclosingScope, "function " + functionName); - extractor.putScope(descriptor, functionScope); + declarations.putScope(descriptor, functionScope); return null; } @@ -59,23 +85,35 @@ public final class ExtractionVisitor extends DeclarationDescriptorVisitor { return Translation.operationTranslator(context).translate(expression); } - - //TODO support other cases - @Override - @NotNull - public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, - @NotNull TranslationContext context) { - - //TODO: this is only a hack for now - // Problem is that namespace properties do not generate getters and setter actually so they must be referenced - // by name - JsName referencedName = getReferencedName(expression, context); - if (referencedName != null) { - if (requiresNamespaceQualifier(context, referencedName)) { - return context.getNamespaceQualifiedReference(referencedName); - } - JsNameRef nameRef = referencedName.makeRef(); - if (requiresThisQualifier(expression, context)) { - nameRef.setQualifier(new JsThisRef()); - } - return nameRef; - } - - JsInvocation getterCall = Translation.propertyAccessTranslator(context).resolveAsPropertyGet(expression); - if (getterCall != null) { - return getterCall; - } - throw new AssertionError("Undefined name in this scope: " + expression.getReferencedName()); - } - - private boolean requiresNamespaceQualifier(TranslationContext context, JsName referencedName) { - return context.namespaceScope().ownsName(referencedName); - } - - private boolean requiresThisQualifier(@NotNull JetSimpleNameExpression expression, - @NotNull TranslationContext context) { - boolean isClassMember = context.classScope().ownsName(getReferencedName(expression, context)); - boolean isBackingFieldAccess = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; - return isClassMember || isBackingFieldAccess; - } - - @Nullable - private JsName getReferencedName(@NotNull JetSimpleNameExpression expression, @NotNull TranslationContext context) { - String referencedName = expression.getReferencedName(); - return context.enclosingScope().findExistingName(referencedName); - } - @Override @NotNull // assume it is a local variable declaration @@ -182,9 +135,16 @@ public final class ExpressionVisitor extends TranslatorVisitor { } else { return translateAsConditionalExpression(expression, context); } - } + @Override + @NotNull + public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, + @NotNull TranslationContext context) { + return Translation.referenceTranslator(context).translateSimpleName(expression); + } + + @NotNull private JsIf translateAsIfStatement(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { @@ -373,7 +333,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull public JsNode visitIsExpression(@NotNull JetIsExpression expression, @NotNull TranslationContext context) { - return Translation.typeOperationTranslator(context).translateIsExpression(expression); + return Translation.patternTranslator(context).translateIsExpression(expression); } @Override @@ -418,5 +378,12 @@ public final class ExpressionVisitor extends TranslatorVisitor { return new JsContinue(); } + @Override + @NotNull + public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, + @NotNull TranslationContext context) { + return Translation.functionTranslator(context).translateAsLiteral(expression.getFunctionLiteral()); + } + } diff --git a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java index 798f043dada..510bc34c3ab 100644 --- a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java @@ -3,9 +3,7 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; import com.sun.istack.internal.NotNull; -import org.jetbrains.jet.lang.psi.JetExpression; -import org.jetbrains.jet.lang.psi.JetNamedFunction; -import org.jetbrains.jet.lang.psi.JetParameter; +import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; import java.util.List; @@ -25,43 +23,68 @@ public final class FunctionTranslator extends AbstractTranslator { } @NotNull - public JsStatement translateAsFunction(@NotNull JetNamedFunction jetFunction) { - JsName functionName = translationContext().namespaceScope().declareFreshName(jetFunction.getName()); - JsFunction function = generateFunctionObject(jetFunction); + public JsStatement translateAsFunctionDeclaration(@NotNull JetNamedFunction expression) { + JsName functionName = translationContext().getNameForElement(expression); + JsFunction function = generateFunctionObject(expression); return AstUtil.newAssignmentStatement (translationContext().getNamespaceQualifiedReference(functionName), function); } @NotNull - JsPropertyInitializer translateAsMethod(@NotNull JetNamedFunction jetFunction) { - JsName functionName = translationContext().namespaceScope().declareFreshName(jetFunction.getName()); - JsFunction function = generateFunctionObject(jetFunction); + JsPropertyInitializer translateAsMethod(@NotNull JetNamedFunction expression) { + JsName functionName = translationContext().getNameForElement(expression); + JsFunction function = generateFunctionObject(expression); return new JsPropertyInitializer(functionName.makeRef(), function); } @NotNull - private JsFunction generateFunctionObject(@NotNull JetNamedFunction jetFunction) { - JsFunction result = JsFunction.getAnonymousFunctionWithScope - (translationContext().getScopeForElement(jetFunction)); + JsFunction translateAsLiteral(@NotNull JetFunctionLiteral expression) { + return generateFunctionObject(expression); + } + + @NotNull + private JsFunction generateFunctionObject(@NotNull JetFunction jetFunction) { + JsFunction result = createFunctionObject(jetFunction); List jsParameters = translateParameters(jetFunction.getValueParameters(), result.getScope()); - JsNode jsBody = translateBody(jetFunction); + JsNode jsBody = translateBody(jetFunction, result.getScope()); result.setParameters(jsParameters); result.setBody(AstUtil.convertToBlock(jsBody)); return result; } + private JsFunction createFunctionObject(JetFunction function) { + if (function instanceof JetNamedFunction) { + return JsFunction.getAnonymousFunctionWithScope + (translationContext().getScopeForElement(function)); + } + if (function instanceof JetFunctionLiteral) { + return new JsFunction(translationContext().enclosingScope()); + } + throw new AssertionError("Unsupported type of function."); + } + @NotNull - private JsNode translateBody(@NotNull JetNamedFunction jetFunction) { - JetExpression jetBodyExpression = jetFunction.getBodyExpression(); - //TODO support them ffs - assert jetBodyExpression != null : "Function without body not supported at the moment"; - JsNode body = Translation.translateExpression(jetBodyExpression, translationContext().newFunction(jetFunction)); - if (jetFunction.hasBlockBody()) { + private JsStatement translateBody(@NotNull JetFunction function, @NotNull JsScope functionScope) { + JetExpression jetBodyExpression = function.getBodyExpression(); + //TODO decide if there are cases where this assert is illegal + assert jetBodyExpression != null : "Function without body not supported"; + JsNode body = Translation.translateExpression(jetBodyExpression, functionBodyContext(function, functionScope)); + if (function.hasBlockBody()) { return AstUtil.convertToBlock(body); } return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression(body))); } + private TranslationContext functionBodyContext(JetFunction function, JsScope functionScope) { + if (function instanceof JetNamedFunction) { + return translationContext().newFunctionDeclaration((JetNamedFunction) function); + } + if (function instanceof JetFunctionLiteral) { + return translationContext().newFunctionLiteral(functionScope); + } + throw new AssertionError("Unsupported type of function."); + } + @NotNull private List translateParameters(@NotNull List jetParameters, @NotNull JsScope functionScope) { diff --git a/translator/src/org/jetbrains/k2js/translate/GenerationState.java b/translator/src/org/jetbrains/k2js/translate/GenerationState.java index a04e6143a95..453369144ec 100644 --- a/translator/src/org/jetbrains/k2js/translate/GenerationState.java +++ b/translator/src/org/jetbrains/k2js/translate/GenerationState.java @@ -4,10 +4,11 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.JsProgram; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.psi.JetNamespace; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.k2js.declarations.DeclarationExtractor; +import org.jetbrains.k2js.declarations.Declarations; import java.util.List; @@ -18,14 +19,14 @@ public final class GenerationState { } //TODO method too long - public JsProgram compileCorrectNamespaces(BindingContext bindingContext, List namespaces) { + @NotNull + public JsProgram compileCorrectNamespaces(@NotNull BindingContext bindingContext, @NotNull List namespaces) { //TODO hardcoded JsProgram result = new JsProgram("main"); JetNamespace namespace = namespaces.get(0); NamespaceDescriptor descriptor = BindingUtils.getNamespaceDescriptor(bindingContext, namespace); - DeclarationExtractor extractor = new DeclarationExtractor(); - extractor.extractDeclarations(descriptor, result.getRootScope()); - Translation.namespaceTranslator(TranslationContext.rootContext(result, bindingContext, extractor)) + Declarations declarations = Declarations.extractDeclarations(descriptor, result.getRootScope()); + Translation.namespaceTranslator(TranslationContext.rootContext(result, bindingContext, declarations)) .generateAst(namespace); return result; } diff --git a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java index ee4dafc1451..be783500ff6 100644 --- a/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java +++ b/translator/src/org/jetbrains/k2js/translate/NamespaceDeclarationVisitor.java @@ -41,7 +41,7 @@ public class NamespaceDeclarationVisitor extends TranslatorVisitor @NotNull @Override public JsStatement visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) { - return AstUtil.convertToStatement(Translation.functionTranslator(context).translateAsFunction(expression)); + return AstUtil.convertToStatement(Translation.functionTranslator(context).translateAsFunctionDeclaration(expression)); } } diff --git a/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java b/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java new file mode 100644 index 00000000000..12cda448210 --- /dev/null +++ b/translator/src/org/jetbrains/k2js/translate/ReferenceTranslator.java @@ -0,0 +1,102 @@ +package org.jetbrains.k2js.translate; + +import com.google.dart.compiler.backend.js.ast.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.psi.JetSimpleNameExpression; +import org.jetbrains.jet.lexer.JetTokens; + +/** + * @author Talanov Pavel + */ +public class ReferenceTranslator extends AbstractTranslator { + + @NotNull + static public ReferenceTranslator newInstance(@NotNull TranslationContext context) { + return new ReferenceTranslator(context); + } + + private ReferenceTranslator(@NotNull TranslationContext context) { + super(context); + } + + @NotNull + JsExpression translateSimpleName(@NotNull JetSimpleNameExpression expression) { + //TODO: this is only a hack for now + // Problem is that namespace properties do not generate getters and setter actually so they must be referenced + // by name + JsExpression result; + result = resolveAsGlobalReference(expression); + if (result != null) { + return result; + } + result = resolveAsLocalReference(expression); + if (result != null) { + return result; + } + JsInvocation getterCall = + Translation.propertyAccessTranslator(translationContext()).resolveAsPropertyGet(expression); + if (getterCall != null) { + return getterCall; + } + throw new AssertionError("Undefined name in this scope: " + expression.getReferencedName()); + + } + + @Nullable + private JsExpression resolveAsGlobalReference(@NotNull JetSimpleNameExpression expression) { + DeclarationDescriptor referencedDescriptor = + BindingUtils.getDescriptorForReferenceExpression(translationContext().bindingContext(), expression); + if (referencedDescriptor == null) { + return null; + } + if (!translationContext().isDeclared(referencedDescriptor)) { + return null; + } + JsName referencedName = translationContext().getNameForDescriptor(referencedDescriptor); + return generateCorrectReference(expression, referencedName); + } + + @Nullable + private JsExpression resolveAsLocalReference(@NotNull JetSimpleNameExpression expression) { + JsName localReferencedName = getLocalReferencedName(expression); + if (localReferencedName == null) { + return null; + } + return generateCorrectReference(expression, localReferencedName); + } + + @NotNull + private JsNameRef generateCorrectReference(@NotNull JetSimpleNameExpression expression, + @NotNull JsName referencedName) { + JsNameRef result; + if (requiresNamespaceQualifier(referencedName)) { + result = translationContext().getNamespaceQualifiedReference(referencedName); + } else { + result = referencedName.makeRef(); + if (requiresThisQualifier(expression)) { + result.setQualifier(new JsThisRef()); + } + } + return result; + } + + private boolean requiresNamespaceQualifier(@NotNull JsName referencedName) { + return translationContext().namespaceScope().ownsName(referencedName); + } + + private boolean requiresThisQualifier(@NotNull JetSimpleNameExpression expression) { + boolean isClassMember = translationContext().classScope().ownsName(getLocalReferencedName(expression)); + boolean isBackingFieldAccess = expression.getReferencedNameElementType() == JetTokens.FIELD_IDENTIFIER; + return isClassMember || isBackingFieldAccess; + } + + @Nullable + private JsName getLocalReferencedName(@NotNull JetSimpleNameExpression expression) { + String referencedName = expression.getReferencedName(); + return translationContext().enclosingScope().findExistingName(referencedName); + } + + +} diff --git a/translator/src/org/jetbrains/k2js/translate/Translation.java b/translator/src/org/jetbrains/k2js/translate/Translation.java index a3e3d6c3f05..ee9cf594979 100644 --- a/translator/src/org/jetbrains/k2js/translate/Translation.java +++ b/translator/src/org/jetbrains/k2js/translate/Translation.java @@ -51,10 +51,15 @@ public final class Translation { } @NotNull - static public PatternTranslator typeOperationTranslator(@NotNull TranslationContext context) { + static public PatternTranslator patternTranslator(@NotNull TranslationContext context) { return PatternTranslator.newInstance(context); } + @NotNull + static public ReferenceTranslator referenceTranslator(@NotNull TranslationContext context) { + return ReferenceTranslator.newInstance(context); + } + @NotNull static public JsNode translateExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) { return expressionTranslator(context).translate(expression); diff --git a/translator/src/org/jetbrains/k2js/translate/TranslationContext.java b/translator/src/org/jetbrains/k2js/translate/TranslationContext.java index 7ae20bc999b..76244da762f 100644 --- a/translator/src/org/jetbrains/k2js/translate/TranslationContext.java +++ b/translator/src/org/jetbrains/k2js/translate/TranslationContext.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.k2js.declarations.DeclarationExtractor; +import org.jetbrains.k2js.declarations.Declarations; /** * @author Talanov Pavel @@ -35,7 +35,7 @@ public final class TranslationContext { @NotNull public static TranslationContext rootContext(@NotNull JsProgram program, @NotNull BindingContext bindingContext, - @NotNull DeclarationExtractor extractor) { + @NotNull Declarations extractor) { JsScope rootScope = program.getRootScope(); Scopes scopes = new Scopes(rootScope, rootScope, rootScope); return new TranslationContext(null, program, bindingContext, scopes, extractor); @@ -50,17 +50,17 @@ public final class TranslationContext { @Nullable private final JsName namespaceName; @NotNull - private final DeclarationExtractor extractor; + private final Declarations declarations; private TranslationContext(@Nullable JsName namespaceName, @NotNull JsProgram program, @NotNull BindingContext bindingContext, @NotNull Scopes scopes, - @NotNull DeclarationExtractor extractor) { + @NotNull Declarations declarations) { this.program = program; this.bindingContext = bindingContext; this.namespaceName = namespaceName; this.scopes = scopes; - this.extractor = extractor; + this.declarations = declarations; } @NotNull @@ -70,17 +70,17 @@ public final class TranslationContext { @NotNull public TranslationContext newNamespace(@NotNull NamespaceDescriptor descriptor) { - JsScope namespaceScope = extractor.getScope(descriptor); + JsScope namespaceScope = declarations.getScope(descriptor); JsName namespaceName = scopes.enclosingScope.findExistingName(descriptor.getName()); Scopes newScopes = new Scopes(namespaceScope, namespaceScope, namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull public TranslationContext newBlock() { Scopes newScopes = new Scopes(new JsScope (scopes.enclosingScope, "Scope for a block"), scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull @@ -90,9 +90,9 @@ public final class TranslationContext { @NotNull public TranslationContext newClass(@NotNull ClassDescriptor descriptor) { - JsScope classScope = extractor.getScope(descriptor); + JsScope classScope = declarations.getScope(descriptor); Scopes newScopes = new Scopes(classScope, classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @NotNull @@ -102,26 +102,32 @@ public final class TranslationContext { @NotNull public TranslationContext newPropertyAccess(@NotNull PropertyAccessorDescriptor descriptor) { - return newFunction(descriptor); + return newFunctionDeclaration(descriptor); } @NotNull - public TranslationContext newFunction(@NotNull JetNamedFunction declaration) { - return newFunction(BindingUtils.getFunctionDescriptor(bindingContext, declaration)); + public TranslationContext newFunctionDeclaration(@NotNull JetNamedFunction declaration) { + return newFunctionDeclaration(BindingUtils.getFunctionDescriptor(bindingContext, declaration)); } @NotNull - public TranslationContext newFunction(@NotNull FunctionDescriptor descriptor) { - JsScope functionScope = extractor.getScope(descriptor); + public TranslationContext newFunctionDeclaration(@NotNull FunctionDescriptor descriptor) { + JsScope functionScope = declarations.getScope(descriptor); Scopes newScopes = new Scopes(functionScope, scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); + } + + @NotNull + public TranslationContext newFunctionLiteral(@NotNull JsScope correspondingScope) { + Scopes newScopes = new Scopes(correspondingScope, scopes.classScope, scopes.namespaceScope); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } // Note: Should be used if and only if scope has no corresponding descriptor @NotNull public TranslationContext newEnclosingScope(@NotNull JsScope enclosingScope) { Scopes newScopes = new Scopes(enclosingScope, scopes.classScope, scopes.namespaceScope); - return new TranslationContext(namespaceName, program, bindingContext, newScopes, extractor); + return new TranslationContext(namespaceName, program, bindingContext, newScopes, declarations); } @@ -165,7 +171,7 @@ public final class TranslationContext { @NotNull public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { - return extractor.getScope(descriptor); + return declarations.getScope(descriptor); } @NotNull @@ -176,7 +182,7 @@ public final class TranslationContext { @NotNull public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { - return extractor.getName(descriptor); + return declarations.getName(descriptor); } @NotNull @@ -191,4 +197,8 @@ public final class TranslationContext { assert descriptor != null : "Element should have a descriptor"; return descriptor; } + + public boolean isDeclared(@NotNull DeclarationDescriptor descriptor) { + return declarations.isDeclared(descriptor); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java index b0de57c306f..9f71f74af2e 100644 --- a/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/WhenTranslator.java @@ -151,7 +151,7 @@ public class WhenTranslator extends AbstractTranslator { @NotNull private JsExpression translatePatternCondition(@NotNull JetWhenConditionIsPattern condition) { - JsExpression patternMatchExpression = Translation.typeOperationTranslator(translationContext()). + JsExpression patternMatchExpression = Translation.patternTranslator(translationContext()). translatePattern(getPattern(condition), expressionToMatch); if (condition.isNegated()) { return AstUtil.negated(patternMatchExpression); diff --git a/translator/test/org/jetbrains/k2js/test/FunctionTest.java b/translator/test/org/jetbrains/k2js/test/FunctionTest.java index ad96579343a..e37f46b64eb 100644 --- a/translator/test/org/jetbrains/k2js/test/FunctionTest.java +++ b/translator/test/org/jetbrains/k2js/test/FunctionTest.java @@ -23,4 +23,9 @@ public class FunctionTest extends AbstractExpressionTest { public void functionWithTwoParametersCall() throws Exception { testFooBoxIsTrue("functionWithTwoParametersCall.kt"); } + + @Test + public void functionLiteral() throws Exception { + testFooBoxIsTrue("functionLiteral.kt"); + } } diff --git a/translator/testFiles/expression/function/cases/functionLiteral.kt b/translator/testFiles/expression/function/cases/functionLiteral.kt new file mode 100644 index 00000000000..b0eefc30f42 --- /dev/null +++ b/translator/testFiles/expression/function/cases/functionLiteral.kt @@ -0,0 +1,8 @@ +namespace foo + +fun box() : Boolean { + var sum = 0 + val addFive = {(a: Int) => a + 5 } + sum = addFive(sum) + return sum == 5 +} \ No newline at end of file From 7cdfccf2d8b99553f830794499354ca78dd6503d Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 16:45:53 +0400 Subject: [PATCH 20/22] Added support for basic case of function literal. --- .../google/dart/compiler/util/AstUtil.java | 5 +++ .../k2js/translate/FunctionTranslator.java | 43 ++++++++++++++----- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/js/src/com/google/dart/compiler/util/AstUtil.java b/js/src/com/google/dart/compiler/util/AstUtil.java index e8a9d57d18b..b238bd4e9b1 100644 --- a/js/src/com/google/dart/compiler/util/AstUtil.java +++ b/js/src/com/google/dart/compiler/util/AstUtil.java @@ -276,4 +276,9 @@ public class AstUtil { public static JsStatement newAssignmentStatement(JsNameRef nameRef, JsExpression expr) { return convertToStatement(new JsBinaryOperation(JsBinaryOperator.ASG, nameRef, expr)); } + + public static JsExpression extractExpressionFromStatement(JsStatement statement) { + assert statement instanceof JsExprStmt : "Cannot extract exprssion form statement: " + statement; + return (((JsExprStmt) statement).getExpression()); + } } diff --git a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java index 510bc34c3ab..9d39c124d24 100644 --- a/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java +++ b/translator/src/org/jetbrains/k2js/translate/FunctionTranslator.java @@ -2,7 +2,7 @@ package org.jetbrains.k2js.translate; import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.util.AstUtil; -import com.sun.istack.internal.NotNull; +import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; import java.util.ArrayList; @@ -45,10 +45,8 @@ public final class FunctionTranslator extends AbstractTranslator { @NotNull private JsFunction generateFunctionObject(@NotNull JetFunction jetFunction) { JsFunction result = createFunctionObject(jetFunction); - List jsParameters = translateParameters(jetFunction.getValueParameters(), result.getScope()); - JsNode jsBody = translateBody(jetFunction, result.getScope()); - result.setParameters(jsParameters); - result.setBody(AstUtil.convertToBlock(jsBody)); + result.setParameters(translateParameters(jetFunction.getValueParameters(), result.getScope())); + result.setBody(translateBody(jetFunction, result.getScope())); return result; } @@ -64,18 +62,41 @@ public final class FunctionTranslator extends AbstractTranslator { } @NotNull - private JsStatement translateBody(@NotNull JetFunction function, @NotNull JsScope functionScope) { + private JsBlock translateBody(@NotNull JetFunction function, @NotNull JsScope functionScope) { JetExpression jetBodyExpression = function.getBodyExpression(); //TODO decide if there are cases where this assert is illegal assert jetBodyExpression != null : "Function without body not supported"; JsNode body = Translation.translateExpression(jetBodyExpression, functionBodyContext(function, functionScope)); - if (function.hasBlockBody()) { - return AstUtil.convertToBlock(body); - } - return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression(body))); + return wrapWithReturnIfNeeded(body, !function.hasBlockBody()); } - private TranslationContext functionBodyContext(JetFunction function, JsScope functionScope) { + @NotNull + private JsBlock wrapWithReturnIfNeeded(@NotNull JsNode body, boolean needsReturn) { + if (!needsReturn) { + return AstUtil.convertToBlock(body); + } + if (body instanceof JsExpression) { + return AstUtil.convertToBlock(new JsReturn(AstUtil.convertToExpression(body))); + } + if (body instanceof JsBlock) { + JsBlock bodyBlock = (JsBlock) body; + addReturnToBlockStatement(bodyBlock); + return bodyBlock; + } + throw new AssertionError("Invalid node as function body"); + } + + private void addReturnToBlockStatement(@NotNull JsBlock bodyBlock) { + List statements = bodyBlock.getStatements(); + int lastIndex = statements.size() - 1; + JsStatement lastStatement = statements.get(lastIndex); + statements.set(lastIndex, + new JsReturn(AstUtil.extractExpressionFromStatement(lastStatement))); + } + + @NotNull + private TranslationContext functionBodyContext(@NotNull JetFunction function, + @NotNull JsScope functionScope) { if (function instanceof JetNamedFunction) { return translationContext().newFunctionDeclaration((JetNamedFunction) function); } From 28763128beb63fe6a18982889b9a7cb41b10e6b2 Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 16:56:23 +0400 Subject: [PATCH 21/22] Added tests for simple closure examples. --- .../org/jetbrains/k2js/test/FunctionTest.java | 10 ++++++++++ .../expression/function/cases/adderClosure.kt | 9 +++++++++ .../expression/function/cases/loopClosure.kt | 17 +++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 translator/testFiles/expression/function/cases/adderClosure.kt create mode 100644 translator/testFiles/expression/function/cases/loopClosure.kt diff --git a/translator/test/org/jetbrains/k2js/test/FunctionTest.java b/translator/test/org/jetbrains/k2js/test/FunctionTest.java index e37f46b64eb..75632d5a5ad 100644 --- a/translator/test/org/jetbrains/k2js/test/FunctionTest.java +++ b/translator/test/org/jetbrains/k2js/test/FunctionTest.java @@ -28,4 +28,14 @@ public class FunctionTest extends AbstractExpressionTest { public void functionLiteral() throws Exception { testFooBoxIsTrue("functionLiteral.kt"); } + + @Test + public void adderClosure() throws Exception { + testFooBoxIsTrue("adderClosure.kt"); + } + + @Test + public void loopClosure() throws Exception { + testFooBoxIsTrue("loopClosure.kt"); + } } diff --git a/translator/testFiles/expression/function/cases/adderClosure.kt b/translator/testFiles/expression/function/cases/adderClosure.kt new file mode 100644 index 00000000000..06e2ea8f116 --- /dev/null +++ b/translator/testFiles/expression/function/cases/adderClosure.kt @@ -0,0 +1,9 @@ +namespace foo + +fun box() : Boolean { + var sum = 0 + val adder = {(a: Int) => sum += a } + adder(3) + adder(2) + return sum == 5 +} \ No newline at end of file diff --git a/translator/testFiles/expression/function/cases/loopClosure.kt b/translator/testFiles/expression/function/cases/loopClosure.kt new file mode 100644 index 00000000000..5c5a1e5615d --- /dev/null +++ b/translator/testFiles/expression/function/cases/loopClosure.kt @@ -0,0 +1,17 @@ +namespace foo + +var b = 0 + +fun loop(var times : Int) { + while(times > 0) { + val u : fun(value : Int) : Unit = { + b++ + } + u(times--) + } +} + +fun box() : Boolean { + loop(5) + return b == 5 +} \ No newline at end of file From ebf29263f1345ef7d7f291a69c30007cb33b1b9b Mon Sep 17 00:00:00 2001 From: Pavel Talanov Date: Fri, 18 Nov 2011 17:32:53 +0400 Subject: [PATCH 22/22] Added some tests from jet project. --- .../org/jetbrains/k2js/test/FunctionTest.java | 16 ++++++++++++++++ .../org/jetbrains/k2js/test/TraitTest.java | 6 ++++++ .../jetbrains/k2js/test/TranslationTest.java | 5 +++++ .../function/cases/closureWithParameter.jet | 9 +++++++++ .../cases/closureWithParameterAndBoxing.jet | 9 +++++++++ .../cases/functionLiteralAsParameter.kt | 10 ++++++++++ .../testFiles/trait/cases/funDelegation.jet | 19 +++++++++++++++++++ 7 files changed, 74 insertions(+) create mode 100644 translator/testFiles/expression/function/cases/closureWithParameter.jet create mode 100644 translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet create mode 100644 translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt create mode 100644 translator/testFiles/trait/cases/funDelegation.jet diff --git a/translator/test/org/jetbrains/k2js/test/FunctionTest.java b/translator/test/org/jetbrains/k2js/test/FunctionTest.java index 75632d5a5ad..3b41d6d0a41 100644 --- a/translator/test/org/jetbrains/k2js/test/FunctionTest.java +++ b/translator/test/org/jetbrains/k2js/test/FunctionTest.java @@ -38,4 +38,20 @@ public class FunctionTest extends AbstractExpressionTest { public void loopClosure() throws Exception { testFooBoxIsTrue("loopClosure.kt"); } + + @Test + public void functionLiteralAsParameter() throws Exception { + testFooBoxIsTrue("functionLiteralAsParameter.kt"); + } + + @Test + public void closureWithParameter() throws Exception { + testFooBoxIsOk("closureWithParameter.jet"); + } + + @Test + public void closureWithParameterAndBoxing() throws Exception { + testFooBoxIsOk("closureWithParameterAndBoxing.jet"); + } + } diff --git a/translator/test/org/jetbrains/k2js/test/TraitTest.java b/translator/test/org/jetbrains/k2js/test/TraitTest.java index 6546702c631..d8346238200 100644 --- a/translator/test/org/jetbrains/k2js/test/TraitTest.java +++ b/translator/test/org/jetbrains/k2js/test/TraitTest.java @@ -44,4 +44,10 @@ public final class TraitTest extends IncludeLibraryTest { testFooBoxIsTrue("traitExtendsTwoTraits.kt"); } + @Test + public void funDelegation() throws Exception { + testFooBoxIsOk("funDelegation.jet"); + } + + } diff --git a/translator/test/org/jetbrains/k2js/test/TranslationTest.java b/translator/test/org/jetbrains/k2js/test/TranslationTest.java index 80b4e3eef0c..ae1ddbd0b58 100644 --- a/translator/test/org/jetbrains/k2js/test/TranslationTest.java +++ b/translator/test/org/jetbrains/k2js/test/TranslationTest.java @@ -100,4 +100,9 @@ public abstract class TranslationTest { protected void testFooBoxIsTrue(String filename) throws Exception { testFunctionOutput(filename, "foo", "box", true); } + + protected void testFooBoxIsOk(String filename) throws Exception { + testFunctionOutput(filename, "foo", "box", "OK"); + } + } diff --git a/translator/testFiles/expression/function/cases/closureWithParameter.jet b/translator/testFiles/expression/function/cases/closureWithParameter.jet new file mode 100644 index 00000000000..8674e078051 --- /dev/null +++ b/translator/testFiles/expression/function/cases/closureWithParameter.jet @@ -0,0 +1,9 @@ +namespace foo + +fun box() : String { + return apply( "OK", {(arg: String) => arg } ) +} + +fun apply(arg : String, f : fun (p:String) : String) : String { + return f(arg) +} diff --git a/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet b/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet new file mode 100644 index 00000000000..3b05060982d --- /dev/null +++ b/translator/testFiles/expression/function/cases/closureWithParameterAndBoxing.jet @@ -0,0 +1,9 @@ +namespace foo + +fun box() : String { + return if (apply( 5, {(arg: Int) => arg + 13 } ) == 18) "OK" else "fail" +} + +fun apply(arg : Int, f : fun (p:Int) : Int) : Int { + return f(arg) +} diff --git a/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt b/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt new file mode 100644 index 00000000000..3ec397a9127 --- /dev/null +++ b/translator/testFiles/expression/function/cases/functionLiteralAsParameter.kt @@ -0,0 +1,10 @@ +namespace foo + +fun apply(f : fun(Int) : Int, t : Int) : Int { + return f(t) +} + + +fun box() : Boolean { + return apply({(a: Int) => a + 5 }, 3) == 8 +} \ No newline at end of file diff --git a/translator/testFiles/trait/cases/funDelegation.jet b/translator/testFiles/trait/cases/funDelegation.jet new file mode 100644 index 00000000000..8a5258d2cee --- /dev/null +++ b/translator/testFiles/trait/cases/funDelegation.jet @@ -0,0 +1,19 @@ +namespace foo + +open class Base() { + fun n(n : Int) : Int = n + 1 +} + +trait Abstract {} + +class Derived1() : Base(), Abstract {} +class Derived2() : Abstract, Base() {} + +fun test(s : Base) : Boolean = s.n(238) == 239 + +fun box() : String { + if (!test(Base())) return "Fail #1" + if (!test(Derived1())) return "Fail #2" + if (!test(Derived2())) return "Fail #3" + return "OK" +} \ No newline at end of file