diff --git a/core-lib/es5/src/main/java/def/js/Float32Array.java b/core-lib/es5/src/main/java/def/js/Float32Array.java index ad8173ba..315d3f85 100644 --- a/core-lib/es5/src/main/java/def/js/Float32Array.java +++ b/core-lib/es5/src/main/java/def/js/Float32Array.java @@ -4,11 +4,11 @@ import jsweet.util.function.Function4; * A typed array of 32-bit float values. The contents are initialized to 0. If the requested number * of bytes could not be allocated an exception is raised. */ -public class Float32Array extends def.js.Object implements Iterable { +public class Float32Array extends def.js.Object implements Iterable { /** * The size in bytes of each element in the array. */ - public final double BYTES_PER_ELEMENT=0; + public final int BYTES_PER_ELEMENT=0; /** * The ArrayBuffer instance referenced by the array. */ @@ -16,11 +16,11 @@ public class Float32Array extends def.js.Object implements Iterable array, double offset); + native public void set(ArrayLike array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Float32Array slice(double start, double end); + native public Float32Array slice(int start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -205,7 +205,7 @@ public class Float32Array extends def.js.Object implements Iterable array){} - public Float32Array(ArrayBuffer buffer, double byteOffset, double length){} + public Float32Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -240,7 +241,7 @@ public class Float32Array extends def.js.Object implements Iterable iterator(); + native public java.util.Iterator iterator(); protected Float32Array(){} } diff --git a/core-lib/es5/src/main/java/def/js/Float64Array.java b/core-lib/es5/src/main/java/def/js/Float64Array.java index 310d47c5..02d976c0 100644 --- a/core-lib/es5/src/main/java/def/js/Float64Array.java +++ b/core-lib/es5/src/main/java/def/js/Float64Array.java @@ -8,7 +8,7 @@ public class Float64Array extends def.js.Object implements Iterable array, double offset); + native public void set(ArrayLike array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Float64Array slice(double start, double end); + native public Float64Array slice(int start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -205,7 +205,7 @@ public class Float64Array extends def.js.Object implements Iterable array){} - public Float64Array(ArrayBuffer buffer, double byteOffset, double length){} + public Float64Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -240,7 +241,7 @@ public class Float64Array extends def.js.Object implements Iterable { +public class Uint32Array extends def.js.Object implements Iterable { /** * The size in bytes of each element in the array. */ - public final double BYTES_PER_ELEMENT=0; + public final int BYTES_PER_ELEMENT=0; /** * The ArrayBuffer instance referenced by the array. */ @@ -16,11 +16,11 @@ public class Uint32Array extends def.js.Object implements Iterable callbackfn, double initialValue); + native public double reduce(Function4 callbackfn, int initialValue); /** * Calls the specified callback function for all the elements in an array. The return value of * the callback function is the accumulated result, and is provided as an argument in the next @@ -150,7 +150,7 @@ public class Uint32Array extends def.js.Object implements Iterable callbackfn, double initialValue); + native public double reduceRight(Function4 callbackfn, int initialValue); /** * Calls the specified callback function for all the elements in an array, in descending order. * The return value of the callback function is the accumulated result, and is provided as an @@ -171,19 +171,19 @@ public class Uint32Array extends def.js.Object implements Iterable array, double offset); + native public void set(ArrayLike array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Uint32Array slice(double start, double end); + native public Uint32Array slice(int start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -205,7 +205,7 @@ public class Uint32Array extends def.js.Object implements Iterable array){} - public Uint32Array(ArrayBuffer buffer, double byteOffset, double length){} + public Uint32Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -240,7 +241,7 @@ public class Uint32Array extends def.js.Object implements Iterable iterator(); + native public java.util.Iterator iterator(); protected Uint32Array(){} } diff --git a/core-lib/es5/src/main/java/jsweet/util/Lang.java b/core-lib/es5/src/main/java/jsweet/util/Lang.java index b0c2715e..c65f0992 100644 --- a/core-lib/es5/src/main/java/jsweet/util/Lang.java +++ b/core-lib/es5/src/main/java/jsweet/util/Lang.java @@ -382,7 +382,7 @@ public final class Lang { native public static Integer integer(def.js.Number number); /** - * Casts back a JavaScript number to a Java integer. + * Casts a JavaScript number to a Java double. */ native public static Double number(def.js.Number number); @@ -702,6 +702,6 @@ public final class Lang { * * @see def.js.Globals#eval(String) */ - public static native String $insert(String typescriptString); + public static native T $insert(String typescriptString); } diff --git a/core-lib/es6/src/main/java/def/js/Float32Array.java b/core-lib/es6/src/main/java/def/js/Float32Array.java index b203bfdb..b7d921dd 100644 --- a/core-lib/es6/src/main/java/def/js/Float32Array.java +++ b/core-lib/es6/src/main/java/def/js/Float32Array.java @@ -5,11 +5,11 @@ import jsweet.util.function.Function4; * of bytes could not be allocated an exception is raised. */ @jsweet.lang.SyntacticIterable -public class Float32Array extends Iterable { +public class Float32Array extends Iterable { /** * The size in bytes of each element in the array. */ - public double BYTES_PER_ELEMENT; + public int BYTES_PER_ELEMENT; /** * The ArrayBuffer instance referenced by the array. */ @@ -17,11 +17,11 @@ public class Float32Array extends Iterable { /** * The length in bytes of the array. */ - public double byteLength; + public int byteLength; /** * The offset in bytes of the array. */ - public double byteOffset; + public int byteOffset; /** * Returns the this object after copying a section of the array identified by start and end * to the same array starting at position target @@ -31,7 +31,7 @@ public class Float32Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Float32Array copyWithin(double target, double start, double end); + native public Float32Array copyWithin(double target, int start, int end); /** * Returns an array of key, value pairs for every entry in the array */ @@ -53,7 +53,7 @@ public class Float32Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Float32Array fill(double value, double start, double end); + native public Float32Array fill(double value, int start, int end); /** * Returns the elements of an array that meet the condition specified in a callback function. * @param callbackfn A function that accepts up to three arguments. The filter method calls @@ -96,7 +96,7 @@ public class Float32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double indexOf(double searchElement, double fromIndex); + native public int indexOf(double searchElement, int fromIndex); /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the @@ -113,11 +113,11 @@ public class Float32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double lastIndexOf(double searchElement, double fromIndex); + native public int lastIndexOf(double searchElement, int fromIndex); /** * The length of the array. */ - public double length; + public int length; /** * Calls a defined callback function on each element of an array, and returns an array that * contains the results. @@ -180,19 +180,19 @@ public class Float32Array extends Iterable { * @param index The index of the location to set. * @param value The value to set. */ - native public void set(double index, double value); + native public void set(int index, double value); /** * Sets a value or an array of values. * @param array A typed or untyped array of values to set. * @param offset The index in the current array at which the values are to be written. */ - native public void set(Float32Array array, double offset); + native public void set(Float32Array array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Float32Array slice(double start, double end); + native public Float32Array slice(int start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -214,17 +214,18 @@ public class Float32Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Float32Array subarray(double begin, double end); + native public Float32Array subarray(int begin, int end); /** * Returns an list of values in the array */ native public IterableIterator values(); - native public java.lang.Double $get(double index); + native public java.lang.Float $get(int index); + native public void $set(int index, java.lang.Float value); public static Float32Array prototype; - public Float32Array(double length){} + public Float32Array(int length){} public Float32Array(Float32Array array){} public Float32Array(double[] array){} - public Float32Array(ArrayBuffer buffer, double byteOffset, double length){} + public Float32Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -246,7 +247,7 @@ public class Float32Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Float32Array copyWithin(double target, double start); + native public Float32Array copyWithin(double target, int start); /** * Determines whether all the members of an array satisfy the specified test. * @param callbackfn A function that accepts up to three arguments. The every method calls @@ -264,7 +265,7 @@ public class Float32Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Float32Array fill(double value, double start); + native public Float32Array fill(double value, int start); /** * Returns the this object after filling the section identified by start and end with value * @param value value to fill array section with @@ -372,7 +373,7 @@ public class Float32Array extends Iterable { * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Float32Array slice(double start); + native public Float32Array slice(int start); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. @@ -400,8 +401,8 @@ public class Float32Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Float32Array subarray(double begin); - public Float32Array(ArrayBuffer buffer, double byteOffset){} + native public Float32Array subarray(int begin); + public Float32Array(ArrayBuffer buffer, int byteOffset){} public Float32Array(ArrayBuffer buffer){} /** * Creates an array from an array-like or iterable object. @@ -461,7 +462,7 @@ public class Float32Array extends Iterable { native public static Float32Array from(Double[] arrayLike); /** From Iterable, to allow foreach loop (do not use directly). */ @jsweet.lang.Erased - native public java.util.Iterator iterator(); + native public java.util.Iterator iterator(); protected Float32Array(){} } diff --git a/core-lib/es6/src/main/java/def/js/Float64Array.java b/core-lib/es6/src/main/java/def/js/Float64Array.java index 26a7e960..ed2e078d 100644 --- a/core-lib/es6/src/main/java/def/js/Float64Array.java +++ b/core-lib/es6/src/main/java/def/js/Float64Array.java @@ -9,7 +9,7 @@ public class Float64Array extends Iterable { /** * The size in bytes of each element in the array. */ - public double BYTES_PER_ELEMENT; + public int BYTES_PER_ELEMENT; /** * The ArrayBuffer instance referenced by the array. */ @@ -17,11 +17,11 @@ public class Float64Array extends Iterable { /** * The length in bytes of the array. */ - public double byteLength; + public int byteLength; /** * The offset in bytes of the array. */ - public double byteOffset; + public int byteOffset; /** * Returns the this object after copying a section of the array identified by start and end * to the same array starting at position target @@ -31,7 +31,7 @@ public class Float64Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Float64Array copyWithin(double target, double start, double end); + native public Float64Array copyWithin(double target, int start, int end); /** * Returns an array of key, value pairs for every entry in the array */ @@ -53,7 +53,7 @@ public class Float64Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Float64Array fill(double value, double start, double end); + native public Float64Array fill(double value, int start, int end); /** * Returns the elements of an array that meet the condition specified in a callback function. * @param callbackfn A function that accepts up to three arguments. The filter method calls @@ -96,7 +96,7 @@ public class Float64Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double indexOf(double searchElement, double fromIndex); + native public double indexOf(double searchElement, int fromIndex); /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the @@ -113,11 +113,11 @@ public class Float64Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double lastIndexOf(double searchElement, double fromIndex); + native public double lastIndexOf(double searchElement, int fromIndex); /** * The length of the array. */ - public double length; + public int length; /** * Calls a defined callback function on each element of an array, and returns an array that * contains the results. @@ -180,19 +180,19 @@ public class Float64Array extends Iterable { * @param index The index of the location to set. * @param value The value to set. */ - native public void set(double index, double value); + native public void set(int index, double value); /** * Sets a value or an array of values. * @param array A typed or untyped array of values to set. * @param offset The index in the current array at which the values are to be written. */ - native public void set(Float64Array array, double offset); + native public void set(Float64Array array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Float64Array slice(double start, double end); + native public Float64Array slice(double start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -214,17 +214,18 @@ public class Float64Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Float64Array subarray(double begin, double end); + native public Float64Array subarray(int begin, int end); /** * Returns an list of values in the array */ native public IterableIterator values(); - native public java.lang.Double $get(double index); + native public java.lang.Double $get(int index); + native public void $set(int index, java.lang.Double value); public static Float64Array prototype; - public Float64Array(double length){} + public Float64Array(int length){} public Float64Array(Float64Array array){} public Float64Array(double[] array){} - public Float64Array(ArrayBuffer buffer, double byteOffset, double length){} + public Float64Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -246,7 +247,7 @@ public class Float64Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Float64Array copyWithin(double target, double start); + native public Float64Array copyWithin(double target, int start); /** * Determines whether all the members of an array satisfy the specified test. * @param callbackfn A function that accepts up to three arguments. The every method calls @@ -264,7 +265,7 @@ public class Float64Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Float64Array fill(double value, double start); + native public Float64Array fill(double value, int start); /** * Returns the this object after filling the section identified by start and end with value * @param value value to fill array section with @@ -400,8 +401,8 @@ public class Float64Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Float64Array subarray(double begin); - public Float64Array(ArrayBuffer buffer, double byteOffset){} + native public Float64Array subarray(int begin); + public Float64Array(ArrayBuffer buffer, int byteOffset){} public Float64Array(ArrayBuffer buffer){} /** * Creates an array from an array-like or iterable object. diff --git a/core-lib/es6/src/main/java/def/js/Iterable.java b/core-lib/es6/src/main/java/def/js/Iterable.java index a8df17fa..1706921b 100644 --- a/core-lib/es6/src/main/java/def/js/Iterable.java +++ b/core-lib/es6/src/main/java/def/js/Iterable.java @@ -1,5 +1,5 @@ package def.js; @jsweet.lang.Interface -public abstract class Iterable extends def.js.Object { +public abstract class Iterable extends def.js.Object implements java.lang.Iterable { } diff --git a/core-lib/es6/src/main/java/def/js/Uint32Array.java b/core-lib/es6/src/main/java/def/js/Uint32Array.java index 3c997d2e..548728ed 100644 --- a/core-lib/es6/src/main/java/def/js/Uint32Array.java +++ b/core-lib/es6/src/main/java/def/js/Uint32Array.java @@ -5,11 +5,11 @@ import jsweet.util.function.Function4; * requested number of bytes could not be allocated an exception is raised. */ @jsweet.lang.SyntacticIterable -public class Uint32Array extends Iterable { +public class Uint32Array extends Iterable { /** * The size in bytes of each element in the array. */ - public double BYTES_PER_ELEMENT; + public int BYTES_PER_ELEMENT; /** * The ArrayBuffer instance referenced by the array. */ @@ -17,11 +17,11 @@ public class Uint32Array extends Iterable { /** * The length in bytes of the array. */ - public double byteLength; + public int byteLength; /** * The offset in bytes of the array. */ - public double byteOffset; + public int byteOffset; /** * Returns the this object after copying a section of the array identified by start and end * to the same array starting at position target @@ -31,7 +31,7 @@ public class Uint32Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Uint32Array copyWithin(double target, double start, double end); + native public Uint32Array copyWithin(double target, int start, int end); /** * Returns an array of key, value pairs for every entry in the array */ @@ -53,7 +53,7 @@ public class Uint32Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Uint32Array fill(double value, double start, double end); + native public Uint32Array fill(int value, int start, int end); /** * Returns the elements of an array that meet the condition specified in a callback function. * @param callbackfn A function that accepts up to three arguments. The filter method calls @@ -96,7 +96,7 @@ public class Uint32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double indexOf(double searchElement, double fromIndex); + native public double indexOf(int searchElement, int fromIndex); /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the @@ -113,11 +113,11 @@ public class Uint32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double lastIndexOf(double searchElement, double fromIndex); + native public double lastIndexOf(int searchElement, int fromIndex); /** * The length of the array. */ - public double length; + public int length; /** * Calls a defined callback function on each element of an array, and returns an array that * contains the results. @@ -180,19 +180,19 @@ public class Uint32Array extends Iterable { * @param index The index of the location to set. * @param value The value to set. */ - native public void set(double index, double value); + native public void set(int index, int value); /** * Sets a value or an array of values. * @param array A typed or untyped array of values to set. * @param offset The index in the current array at which the values are to be written. */ - native public void set(Uint32Array array, double offset); + native public void set(Uint32Array array, int offset); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Uint32Array slice(double start, double end); + native public Uint32Array slice(int start, int end); /** * Determines whether the specified callback function returns true for any element of an array. * @param callbackfn A function that accepts up to three arguments. The some method calls the @@ -214,17 +214,18 @@ public class Uint32Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Uint32Array subarray(double begin, double end); + native public Uint32Array subarray(int begin, int end); /** * Returns an list of values in the array */ native public IterableIterator values(); - native public java.lang.Double $get(double index); + native public java.lang.Double $get(int index); + native public void $set(int index, java.lang.Integer value); public static Uint32Array prototype; - public Uint32Array(double length){} + public Uint32Array(int length){} public Uint32Array(Uint32Array array){} public Uint32Array(double[] array){} - public Uint32Array(ArrayBuffer buffer, double byteOffset, double length){} + public Uint32Array(ArrayBuffer buffer, int byteOffset, int length){} /** * Returns a new array from a set of elements. * @param items A set of elements to include in the new array object. @@ -246,7 +247,7 @@ public class Uint32Array extends Iterable { * is treated as length+end. * @param end If not specified, length of the this object is used as its default value. */ - native public Uint32Array copyWithin(double target, double start); + native public Uint32Array copyWithin(double target, int start); /** * Determines whether all the members of an array satisfy the specified test. * @param callbackfn A function that accepts up to three arguments. The every method calls @@ -264,7 +265,7 @@ public class Uint32Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Uint32Array fill(double value, double start); + native public Uint32Array fill(int value, int start); /** * Returns the this object after filling the section identified by start and end with value * @param value value to fill array section with @@ -273,7 +274,7 @@ public class Uint32Array extends Iterable { * @param end index to stop filling the array at. If end is negative, it is treated as * length+end. */ - native public Uint32Array fill(double value); + native public Uint32Array fill(int value); /** * Returns the elements of an array that meet the condition specified in a callback function. * @param callbackfn A function that accepts up to three arguments. The filter method calls @@ -316,7 +317,7 @@ public class Uint32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double indexOf(double searchElement); + native public int indexOf(int searchElement); /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the @@ -329,7 +330,7 @@ public class Uint32Array extends Iterable { * @param fromIndex The array index at which to begin the search. If fromIndex is omitted, the * search starts at index 0. */ - native public double lastIndexOf(double searchElement); + native public int lastIndexOf(int searchElement); /** * Calls a defined callback function on each element of an array, and returns an array that * contains the results. @@ -372,7 +373,7 @@ public class Uint32Array extends Iterable { * @param start The beginning of the specified portion of the array. * @param end The end of the specified portion of the array. */ - native public Uint32Array slice(double start); + native public Uint32Array slice(int start); /** * Returns a section of an array. * @param start The beginning of the specified portion of the array. @@ -400,8 +401,8 @@ public class Uint32Array extends Iterable { * @param begin The index of the beginning of the array. * @param end The index of the end of the array. */ - native public Uint32Array subarray(double begin); - public Uint32Array(ArrayBuffer buffer, double byteOffset){} + native public Uint32Array subarray(int begin); + public Uint32Array(ArrayBuffer buffer, int byteOffset){} public Uint32Array(ArrayBuffer buffer){} /** * Creates an array from an array-like or iterable object. @@ -461,7 +462,7 @@ public class Uint32Array extends Iterable { native public static Uint32Array from(Double[] arrayLike); /** From Iterable, to allow foreach loop (do not use directly). */ @jsweet.lang.Erased - native public java.util.Iterator iterator(); + native public java.util.Iterator iterator(); protected Uint32Array(){} } diff --git a/core-lib/es6/src/main/java/jsweet/util/Lang.java b/core-lib/es6/src/main/java/jsweet/util/Lang.java index 72f98026..b3b3c070 100644 --- a/core-lib/es6/src/main/java/jsweet/util/Lang.java +++ b/core-lib/es6/src/main/java/jsweet/util/Lang.java @@ -382,7 +382,7 @@ public final class Lang { native public static Integer integer(def.js.Number number); /** - * Casts back a JavaScript number to a Java integer. + * Casts a JavaScript number to a Java double. */ native public static Double number(def.js.Number number); @@ -702,6 +702,6 @@ public final class Lang { * * @see def.js.Globals#eval(String) */ - public static native String $insert(String typescriptString); + public static native T $insert(String typescriptString); } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java index ed7565a4..847cbb7c 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/Java2TypeScriptTranslator.java @@ -150,4818 +150,4830 @@ import com.sun.tools.javac.util.Name; */ public class Java2TypeScriptTranslator extends AbstractTreePrinter { - public static final String PARENT_CLASS_FIELD_NAME = "__parent"; - public static final String INTERFACES_FIELD_NAME = "__interfaces"; - public static final String STATIC_INITIALIZATION_SUFFIX = "_$LI$"; - public static final String CLASS_NAME_IN_CONSTRUCTOR = "__class"; - public static final String ANONYMOUS_PREFIX = "$"; - public static final String ENUM_WRAPPER_CLASS_SUFFIX = "_$WRAPPER"; - public static final String ENUM_WRAPPER_CLASS_WRAPPERS = "_$wrappers"; - public static final String ENUM_WRAPPER_CLASS_NAME = "_$name"; - public static final String ENUM_WRAPPER_CLASS_ORDINAL = "_$ordinal"; - public static final String VAR_DECL_KEYWORD = "let"; - public static final String BODY_MARKER = "{{body}}"; - public static final String BASE_INDENT_MARKER = "{{baseIndent}}"; - public static final String INDENT_MARKER = "{{indent}}"; - public static final String METHOD_NAME_MARKER = "{{methodName}}"; - public static final String CLASS_NAME_MARKER = "{{className}}"; - public static final String GENERATOR_PREFIX = "__generator_"; + public static final String PARENT_CLASS_FIELD_NAME = "__parent"; + public static final String INTERFACES_FIELD_NAME = "__interfaces"; + public static final String STATIC_INITIALIZATION_SUFFIX = "_$LI$"; + public static final String CLASS_NAME_IN_CONSTRUCTOR = "__class"; + public static final String ANONYMOUS_PREFIX = "$"; + public static final String ENUM_WRAPPER_CLASS_SUFFIX = "_$WRAPPER"; + public static final String ENUM_WRAPPER_CLASS_WRAPPERS = "_$wrappers"; + public static final String ENUM_WRAPPER_CLASS_NAME = "_$name"; + public static final String ENUM_WRAPPER_CLASS_ORDINAL = "_$ordinal"; + public static final String VAR_DECL_KEYWORD = "let"; + public static final String BODY_MARKER = "{{body}}"; + public static final String BASE_INDENT_MARKER = "{{baseIndent}}"; + public static final String INDENT_MARKER = "{{indent}}"; + public static final String METHOD_NAME_MARKER = "{{methodName}}"; + public static final String CLASS_NAME_MARKER = "{{className}}"; + public static final String GENERATOR_PREFIX = "__generator_"; - protected static Logger logger = Logger.getLogger(Java2TypeScriptTranslator.class); + protected static Logger logger = Logger.getLogger(Java2TypeScriptTranslator.class); - public enum ComparisonMode { - FORCE_STRICT, STRICT, LOOSE; + public enum ComparisonMode { + FORCE_STRICT, STRICT, LOOSE; + } + + private final Stack comparisonModeStack = new Stack<>(); + + public void enterComparisonMode(ComparisonMode comparisonMode) { + comparisonModeStack.push(comparisonMode); + } + + public void exitComparisonMode() { + comparisonModeStack.pop(); + } + + private ComparisonMode getComparisonMode() { + if (comparisonModeStack.isEmpty()) { + return ComparisonMode.STRICT; + } else { + if (comparisonModeStack.peek() == ComparisonMode.STRICT) { + return ComparisonMode.FORCE_STRICT; + } else { + return ComparisonMode.LOOSE; + } } + } - private final Stack comparisonModeStack = new Stack<>(); + private static class ClassScope { + private String name; - public void enterComparisonMode(ComparisonMode comparisonMode) { - comparisonModeStack.push(comparisonMode); + private boolean interfaceScope = false; + + private boolean enumScope = false; + + private boolean isComplexEnum = false; + + private boolean enumWrapperClassScope = false; + + private boolean removedSuperclass = false; + + private boolean declareClassScope; + + private boolean skipTypeAnnotations = false; + + private boolean defaultMethodScope = false; + + private boolean eraseVariableTypes = false; + + private boolean hasDeclaredConstructor = false; + + private boolean innerClass = false; + + private boolean innerClassNotStatic = false; + + private boolean hasInnerClass = false; + + private List anonymousClasses = new ArrayList<>(); + + private List anonymousClassesConstructors = new ArrayList<>(); + + private List> finalVariables = new ArrayList<>(); + + private boolean hasConstructorOverloadWithSuperClass; + + private List fieldsWithInitializers = new ArrayList<>(); + + private List inlinedConstructorArgs = null; + + private List localClasses = new ArrayList<>(); + + // to be accessed in the parent scope + private boolean isAnonymousClass = false; + // to be accessed in the parent scope + private boolean isInnerClass = false; + // to be accessed in the parent scope + private boolean isLocalClass = false; + + } + + private Stack scope = new Stack<>(); + + private boolean isAnnotationScope = false; + + private boolean isDefinitionScope = false; + + private boolean isTopLevelScope() { + return getIndent() == 0; + } + + private ClassScope getScope() { + return scope.peek(); + } + + private ClassScope getScope(int i) { + return scope.get(scope.size() - 1 - i); + } + + public void enterScope() { + scope.push(new ClassScope()); + } + + public void exitScope() { + scope.pop(); + } + + /** + * Creates a new TypeScript translator. + * + * @param adapter + * an object that can tune various aspects of the TypeScript code + * generation + * @param logHandler + * the handler for logging and error reporting + * @param context + * the AST scanning context + * @param compilationUnit + * the compilation unit to be translated + * @param fillSourceMap + * if true, the printer generates the source maps, for debugging + * purpose + */ + public Java2TypeScriptTranslator(PrinterAdapter adapter, TranspilationHandler logHandler, JSweetContext context, + JCCompilationUnit compilationUnit, boolean fillSourceMap) { + super(logHandler, context, compilationUnit, adapter, fillSourceMap); + } + + private static java.util.List> statementsWithNoSemis = Arrays + .asList(new Class[] { JCIf.class, JCForLoop.class, JCEnhancedForLoop.class, JCSwitch.class }); + + private static String mapConstructorType(String typeName) { + if (CONSTRUCTOR_TYPE_MAPPING.containsKey(typeName)) { + return CONSTRUCTOR_TYPE_MAPPING.get(typeName); + } else { + return typeName; } + } - public void exitComparisonMode() { - comparisonModeStack.pop(); + public static final Map TYPE_MAPPING; + static { + Map mapping = new HashMap<>(); + mapping.put("java.lang.String", "String"); + mapping.put("java.lang.Number", "Number"); + mapping.put("java.lang.Integer", "Number"); + mapping.put("java.lang.Float", "Number"); + mapping.put("java.lang.Double", "Number"); + mapping.put("java.lang.Short", "Number"); + mapping.put("java.lang.Character", "String"); + mapping.put("java.lang.Byte", "Number"); + mapping.put("java.lang.Boolean", "Boolean"); + mapping.put("java.lang.Long", "Number"); + mapping.put("int", "Number"); + mapping.put("float", "Number"); + mapping.put("double", "Number"); + mapping.put("short", "Number"); + mapping.put("char", "String"); + mapping.put("boolean", "Boolean"); + mapping.put("byte", "Number"); + mapping.put("long", "Number"); + TYPE_MAPPING = Collections.unmodifiableMap(mapping); + } + + private static final Map CONSTRUCTOR_TYPE_MAPPING; + static { + Map mapping = new HashMap<>(); + mapping.put("string", "String"); + mapping.put("number", "Number"); + mapping.put("boolean", "Boolean"); + mapping.put("any", "Object"); + CONSTRUCTOR_TYPE_MAPPING = Collections.unmodifiableMap(mapping); + } + + private JCMethodDecl mainMethod; + + private PackageSymbol topLevelPackage; + + private void useModule(boolean require, PackageSymbol targetPackage, JCTree sourceTree, String targetName, + String moduleName, Symbol sourceElement) { + if (context.useModules) { + context.packageDependencies.add(targetPackage); + context.packageDependencies.add(compilationUnit.packge); + context.packageDependencies.addEdge(compilationUnit.packge, targetPackage); } + context.registerUsedModule(moduleName); + Set importedNames = context.getImportedNames(compilationUnit.getSourceFile().getName()); + if (!importedNames.contains(targetName)) { + if (context.useModules) { + // TODO: when using several qualified Globals classes, we need + // to disambiguate (Globals__1, Globals__2, ....) + // TODO: IDEA FOR MODULES AND FULLY QUALIFIED NAMES + // when using a fully qualified name in the code, we need an + // import, which can be named after something like + // qualName.replace(".", "_")... this would work in the general + // case... - private ComparisonMode getComparisonMode() { - if (comparisonModeStack.isEmpty()) { - return ComparisonMode.STRICT; + boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName); + if (fullImport) { + if (context.useRequireForModules) { + print("import " + targetName + " = require(\"" + moduleName + "\"); ").println(); + } else { + print("import * as " + targetName + " from '" + moduleName + "'; ").println(); + } } else { - if (comparisonModeStack.peek() == ComparisonMode.STRICT) { - return ComparisonMode.FORCE_STRICT; + print("import { " + targetName + " } from '" + moduleName + "'; ").println(); + } + } + context.registerImportedName(compilationUnit.getSourceFile().getName(), sourceElement, targetName); + } + } + + private boolean checkRootPackageParent(JCCompilationUnit topLevel, PackageSymbol rootPackage, + PackageSymbol parentPackage) { + if (parentPackage == null) { + return true; + } + if (!context.options.isNoRootDirectories() || context.options.isBundle()) { + return true; + } + if (context.isRootPackage(parentPackage)) { + report(topLevel.getPackageName(), JSweetProblem.ENCLOSED_ROOT_PACKAGES, + rootPackage.getQualifiedName().toString(), parentPackage.getQualifiedName().toString()); + return false; + } + for (Symbol s : parentPackage.getEnclosedElements()) { + if (s instanceof ClassSymbol) { + if (Util.isSourceElement(s)) { + report(topLevel.getPackageName(), JSweetProblem.CLASS_OUT_OF_ROOT_PACKAGE_SCOPE, + s.getQualifiedName().toString(), rootPackage.getQualifiedName().toString()); + return false; + } + } + } + return checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) parentPackage.owner); + } + + @Override + public void visitTopLevel(JCCompilationUnit topLevel) { + + if (context.isPackageErased(topLevel.packge)) { + return; + } + boolean noDefs = true; + for (JCTree def : topLevel.defs) { + if (def instanceof JCClassDecl) { + if (!context.isIgnored(((JCClassDecl) def))) { + noDefs = false; + } + } + } + // do not print the compilation unit at all if no defs are to be printed + if (!context.bundleMode && noDefs) { + return; + } + + isDefinitionScope = topLevel.packge.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + "."); + + if (context.hasAnnotationType(topLevel.packge, JSweetConfig.ANNOTATION_MODULE)) { + context.addExportedElement( + context.getAnnotationValue(topLevel.packge, JSweetConfig.ANNOTATION_MODULE, null), topLevel.packge, + getCompilationUnit()); + } + + PackageSymbol rootPackage = context.getFirstEnclosingRootPackage(topLevel.packge); + if (rootPackage != null) { + if (!checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) rootPackage.owner)) { + return; + } + } + context.importedTopPackages.clear(); + context.rootPackages.add(rootPackage); + // TODO: check relaxing @Root + // if (context.useModules && context.rootPackages.size() > 1) { + // if (!context.reportedMultipleRootPackages) { + // report(topLevel.getPackageName(), + // JSweetProblem.MULTIPLE_ROOT_PACKAGES_NOT_ALLOWED_WITH_MODULES, + // context.rootPackages.toString()); + // context.reportedMultipleRootPackages = true; + // } + // return; + // } + + topLevelPackage = context.getTopLevelPackage(topLevel.packge); + if (topLevelPackage != null) { + context.topLevelPackageNames.add(topLevelPackage.getQualifiedName().toString()); + } + + footer.delete(0, footer.length()); + + setCompilationUnit(topLevel); + + String packge = topLevel.packge.toString(); + + boolean globalModule = JSweetConfig.GLOBALS_PACKAGE_NAME.equals(packge) + || packge.endsWith("." + JSweetConfig.GLOBALS_PACKAGE_NAME); + String rootRelativePackageName = ""; + if (!globalModule) { + rootRelativePackageName = getRootRelativeName(topLevel.packge); + if (rootRelativePackageName.length() == 0) { + globalModule = true; + } + } + + List packageSegments = new ArrayList(Arrays.asList(rootRelativePackageName.split("\\."))); + packageSegments.retainAll(JSweetConfig.TS_TOP_LEVEL_KEYWORDS); + if (!packageSegments.isEmpty()) { + report(topLevel.getPackageName(), JSweetProblem.PACKAGE_NAME_CONTAINS_KEYWORD, packageSegments); + } + + // generate requires by looking up imported external modules + + for (JCImport importDecl : topLevel.getImports()) { + + TreeScanner importedModulesScanner = new TreeScanner() { + @Override + public void scan(JCTree tree) { + if (tree instanceof JCFieldAccess) { + JCFieldAccess qualified = (JCFieldAccess) tree; + if (qualified.sym != null) { + // regular import case (qualified.sym is a package) + if (context.hasAnnotationType(qualified.sym, JSweetConfig.ANNOTATION_MODULE)) { + String actualName = context.getAnnotationValue(qualified.sym, + JSweetConfig.ANNOTATION_MODULE, null); + useModule(true, null, importDecl, qualified.name.toString(), actualName, + ((PackageSymbol) qualified.sym)); + } } else { - return ComparisonMode.LOOSE; + // static import case (imported fields and methods) + if (qualified.selected instanceof JCFieldAccess) { + JCFieldAccess qualifier = (JCFieldAccess) qualified.selected; + if (qualifier.sym != null) { + try { + for (Symbol importedMember : qualifier.sym.getEnclosedElements()) { + if (qualified.name.equals(importedMember.getSimpleName())) { + if (context.hasAnnotationType(importedMember, + JSweetConfig.ANNOTATION_MODULE)) { + String actualName = context.getAnnotationValue(importedMember, + JSweetConfig.ANNOTATION_MODULE, null); + useModule(true, null, importDecl, + importedMember.getSimpleName().toString(), actualName, + importedMember); + break; + } + } + } + } catch (Exception e) { + // TODO: sometimes, + // getEnclosedElement + // fails because of string types + // (find + // out why if possible) + e.printStackTrace(); + } + } + } } + } + super.scan(tree); } + }; + importedModulesScanner.scan(importDecl.qualid); } - private static class ClassScope { - private String name; - - private boolean interfaceScope = false; - - private boolean enumScope = false; - - private boolean isComplexEnum = false; - - private boolean enumWrapperClassScope = false; - - private boolean removedSuperclass = false; - - private boolean declareClassScope; - - private boolean skipTypeAnnotations = false; - - private boolean defaultMethodScope = false; - - private boolean eraseVariableTypes = false; - - private boolean hasDeclaredConstructor = false; - - private boolean innerClass = false; - - private boolean innerClassNotStatic = false; - - private boolean hasInnerClass = false; - - private List anonymousClasses = new ArrayList<>(); - - private List anonymousClassesConstructors = new ArrayList<>(); - - private List> finalVariables = new ArrayList<>(); - - private boolean hasConstructorOverloadWithSuperClass; - - private List fieldsWithInitializers = new ArrayList<>(); - - private List inlinedConstructorArgs = null; - - private List localClasses = new ArrayList<>(); - - // to be accessed in the parent scope - private boolean isAnonymousClass = false; - // to be accessed in the parent scope - private boolean isInnerClass = false; - // to be accessed in the parent scope - private boolean isLocalClass = false; + for (JCImport importDecl : topLevel.getImports()) { + if (importDecl.qualid instanceof JCFieldAccess) { + JCFieldAccess qualified = (JCFieldAccess) importDecl.qualid; + String importedName = qualified.name.toString(); + if (importDecl.isStatic() && (qualified.selected instanceof JCFieldAccess)) { + qualified = (JCFieldAccess) qualified.selected; + } + if (qualified.sym instanceof ClassSymbol) { + ClassSymbol importedClass = (ClassSymbol) qualified.sym; + if (Util.isSourceElement(importedClass) && !importedClass.getQualifiedName().toString() + .startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { + String importedModule = importedClass.sourcefile.getName(); + if (importedModule.equals(compilationUnit.sourcefile.getName())) { + continue; + } + String pathToImportedClass = Util.getRelativePath( + new File(compilationUnit.sourcefile.getName()).getParent(), importedModule); + pathToImportedClass = pathToImportedClass.substring(0, pathToImportedClass.length() - 5); + if (!pathToImportedClass.startsWith(".")) { + pathToImportedClass = "./" + pathToImportedClass; + } + Symbol symbol = qualified.sym.getEnclosingElement(); + while (!(symbol instanceof PackageSymbol)) { + importedName = symbol.getSimpleName().toString(); + symbol = symbol.getEnclosingElement(); + } + if (symbol != null) { + useModule(false, (PackageSymbol) symbol, importDecl, importedName, + pathToImportedClass.replace('\\', '/'), null); + } + } + } + } } - private Stack scope = new Stack<>(); + if (context.useModules) { + TreeScanner usedTypesScanner = new TreeScanner() { - private boolean isAnnotationScope = false; + private void checkType(TypeSymbol type) { + if (type instanceof ClassSymbol) { + if (type.getEnclosingElement().equals(compilationUnit.packge)) { + String importedModule = ((ClassSymbol) type).sourcefile.getName(); + if (!importedModule.equals(compilationUnit.sourcefile.getName())) { + importedModule = "./" + new File(importedModule).getName(); + importedModule = importedModule.substring(0, importedModule.length() - 5); + useModule(false, (PackageSymbol) type.getEnclosingElement(), null, + type.getSimpleName().toString(), importedModule, null); + } + } + } + } - private boolean isDefinitionScope = false; + @Override + public void scan(JCTree t) { + if (t != null && t.type != null && t.type.tsym instanceof ClassSymbol) { + if (!(t instanceof JCTypeApply)) { + checkType(t.type.tsym); + } + } + super.scan(t); + } - private boolean isTopLevelScope() { - return getIndent() == 0; + }; + usedTypesScanner.scan(compilationUnit); } - private ClassScope getScope() { - return scope.peek(); + // require root modules when using fully qualified names or reserved + // keywords + TreeScanner inlinedModuleScanner = new TreeScanner() { + Stack stack = new Stack<>(); + + public void scan(JCTree t) { + if (t != null) { + stack.push(t); + try { + super.scan(t); + } finally { + stack.pop(); + } + } + } + + @SuppressWarnings("unchecked") + public T getParent(Class type) { + for (int i = this.stack.size() - 2; i >= 0; i--) { + if (type.isAssignableFrom(this.stack.get(i).getClass())) { + return (T) this.stack.get(i); + } + } + return null; + } + + public void visitIdent(JCIdent identifier) { + if (identifier.sym instanceof PackageSymbol) { + // ignore packages in imports + if (getParent(JCImport.class) != null) { + return; + } + boolean isSourceType = false; + for (int i = stack.size() - 2; i >= 0; i--) { + JCTree tree = stack.get(i); + if (!(tree instanceof JCFieldAccess)) { + break; + } else { + JCFieldAccess fa = (JCFieldAccess) tree; + if ((fa.sym instanceof ClassSymbol) && Util.isSourceElement(fa.sym)) { + isSourceType = true; + break; + } + } + } + if (!isSourceType) { + return; + } + PackageSymbol identifierPackage = (PackageSymbol) identifier.sym; + String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, identifierPackage); + if (pathToModulePackage == null) { + return; + } + File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); + if (!identifierPackage.getSimpleName().toString() + .equals(compilationUnit.packge.getSimpleName().toString())) { + useModule(false, identifierPackage, identifier, identifierPackage.getSimpleName().toString(), + moduleFile.getPath().replace('\\', '/'), null); + } + } else if (identifier.sym instanceof ClassSymbol) { + if (JSweetConfig.GLOBALS_PACKAGE_NAME + .equals(identifier.sym.getEnclosingElement().getSimpleName().toString())) { + String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, + identifier.sym.getEnclosingElement()); + if (pathToModulePackage == null) { + return; + } + File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); + if (!identifier.sym.getEnclosingElement() + .equals(compilationUnit.packge.getSimpleName().toString())) { + useModule(false, (PackageSymbol) identifier.sym.getEnclosingElement(), identifier, + JSweetConfig.GLOBALS_PACKAGE_NAME, moduleFile.getPath().replace('\\', '/'), null); + } + } + } + } + + @Override + public void visitApply(JCMethodInvocation invocation) { + // TODO: same for static variables + if (invocation.meth instanceof JCIdent + && JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(invocation.meth.toString().toLowerCase())) { + PackageSymbol invocationPackage = (PackageSymbol) ((JCIdent) invocation.meth).sym + .getEnclosingElement().getEnclosingElement(); + String rootRelativeInvocationPackageName = getRootRelativeName(invocationPackage); + if (rootRelativeInvocationPackageName.indexOf('.') == -1) { + super.visitApply(invocation); + return; + } + String targetRootPackageName = rootRelativeInvocationPackageName.substring(0, + rootRelativeInvocationPackageName.indexOf('.')); + String pathToReachRootPackage = Util.getRelativePath( + "/" + compilationUnit.packge.getQualifiedName().toString().replace('.', '/'), + "/" + targetRootPackageName); + if (pathToReachRootPackage == null) { + super.visitApply(invocation); + return; + } + File moduleFile = new File(new File(pathToReachRootPackage), JSweetConfig.MODULE_FILE_NAME); + if (!invocationPackage.toString().equals(compilationUnit.packge.getSimpleName().toString())) { + useModule(false, invocationPackage, invocation, targetRootPackageName, + moduleFile.getPath().replace('\\', '/'), null); + } + } + super.visitApply(invocation); + } + + }; + // TODO: change the way qualified names are handled (because of new + // module organization) + // inlinedModuleScanner.scan(compilationUnit); + + if (!globalModule && !context.useModules) { + printIndent(); + if (isDefinitionScope) { + print("declare "); + } + print("namespace ").print(rootRelativePackageName).print(" {").startIndent().println(); } - private ClassScope getScope(int i) { - return scope.get(scope.size() - 1 - i); + for (JCTree def : topLevel.defs) { + mainMethod = null; + + printIndent(); + int pos = getCurrentPosition(); + print(def); + if (getCurrentPosition() == pos) { + removeLastIndent(); + continue; + } + println().println(); + } + if (!globalModule && !context.useModules) { + removeLastChar().endIndent().printIndent().print("}").println(); } - public void enterScope() { - scope.push(new ClassScope()); + if (footer.length() > 0) { + println().print(footer.toString()); } - public void exitScope() { - scope.pop(); + globalModule = false; + + } + + private void printDocComment(JCTree element, boolean indent) { + if (compilationUnit != null && compilationUnit.docComments != null) { + Comment comment = compilationUnit.docComments.getComment(element); + String commentText = JSDoc.adaptDocComment(context, getCompilationUnit(), element, + comment == null ? null : comment.getText()); + + Element elt = null; + if (element instanceof JCVariableDecl) { + elt = ((JCVariableDecl) element).sym; + } else if (element instanceof JCMethodDecl) { + elt = ((JCMethodDecl) element).sym; + } else if (element instanceof JCClassDecl) { + elt = ((JCClassDecl) element).sym; + } + if (elt != null) { + commentText = getAdapter().adaptDocComment(elt, commentText); + } + + List lines = new ArrayList<>(); + if (commentText != null) { + lines.addAll(Arrays.asList(commentText.split("\n"))); + } + if (!lines.isEmpty()) { + if (indent) { + printIndent(); + } + print("/**").println(); + for (String line : lines) { + printIndent().print(" * ").print(line.trim()).println(); + } + removeLastChar(); + println().printIndent().print(" ").print("*/").println(); + if (!indent) { + printIndent(); + } + } } + } - /** - * Creates a new TypeScript translator. - * - * @param adapter - * an object that can tune various aspects of the TypeScript code - * generation - * @param logHandler - * the handler for logging and error reporting - * @param context - * the AST scanning context - * @param compilationUnit - * the compilation unit to be translated - * @param fillSourceMap - * if true, the printer generates the source maps, for debugging - * purpose - */ - public Java2TypeScriptTranslator(PrinterAdapter adapter, TranspilationHandler logHandler, JSweetContext context, - JCCompilationUnit compilationUnit, boolean fillSourceMap) { - super(logHandler, context, compilationUnit, adapter, fillSourceMap); - } - - private static java.util.List> statementsWithNoSemis = Arrays - .asList(new Class[] { JCIf.class, JCForLoop.class, JCEnhancedForLoop.class, JCSwitch.class }); - - private static String mapConstructorType(String typeName) { - if (CONSTRUCTOR_TYPE_MAPPING.containsKey(typeName)) { - return CONSTRUCTOR_TYPE_MAPPING.get(typeName); + private void printAnonymousClassTypeArgs(JCNewClass newClass) { + if ((newClass.clazz instanceof JCTypeApply)) { + JCTypeApply tapply = (JCTypeApply) newClass.clazz; + if (tapply.getTypeArguments() != null && !tapply.getTypeArguments().isEmpty()) { + boolean printed = false; + print("<"); + for (JCExpression targ : tapply.getTypeArguments()) { + if (targ.type.tsym instanceof TypeVariableSymbol) { + printed = true; + print(targ).print(", "); + } + } + if (printed) { + removeLastChars(2); + print(">"); } else { - return typeName; + removeLastChar(); } + } } + } - public static final Map TYPE_MAPPING; - static { - Map mapping = new HashMap<>(); - mapping.put("java.lang.String", "String"); - mapping.put("java.lang.Number", "Number"); - mapping.put("java.lang.Integer", "Number"); - mapping.put("java.lang.Float", "Number"); - mapping.put("java.lang.Double", "Number"); - mapping.put("java.lang.Short", "Number"); - mapping.put("java.lang.Character", "String"); - mapping.put("java.lang.Byte", "Number"); - mapping.put("java.lang.Boolean", "Boolean"); - mapping.put("java.lang.Long", "Number"); - mapping.put("int", "Number"); - mapping.put("float", "Number"); - mapping.put("double", "Number"); - mapping.put("short", "Number"); - mapping.put("char", "String"); - mapping.put("boolean", "Boolean"); - mapping.put("byte", "Number"); - mapping.put("long", "Number"); - TYPE_MAPPING = Collections.unmodifiableMap(mapping); + private boolean isAnonymousClass() { + return scope.size() > 1 && getScope(1).isAnonymousClass; + } + + private boolean isInnerClass() { + return scope.size() > 1 && getScope(1).isInnerClass; + } + + private boolean isLocalClass() { + return scope.size() > 1 && getScope(1).isLocalClass; + } + + /** + * A flags that indicates that this adapter is printing type parameters. + */ + private boolean inTypeParameters = false; + + /** + * A flags that indicates that this adapter is not substituting types. + */ + private boolean disableTypeSubstitution = false; + + protected final AbstractTreePrinter substituteAndPrintType(JCTree typeTree) { + return substituteAndPrintType(typeTree, false, inTypeParameters, true, disableTypeSubstitution); + } + + private AbstractTreePrinter printArguments(List arguments) { + int i = 1; + for (JCExpression argument : arguments) { + printArgument(argument, i++).print(", "); } - - private static final Map CONSTRUCTOR_TYPE_MAPPING; - static { - Map mapping = new HashMap<>(); - mapping.put("string", "String"); - mapping.put("number", "Number"); - mapping.put("boolean", "Boolean"); - mapping.put("any", "Object"); - CONSTRUCTOR_TYPE_MAPPING = Collections.unmodifiableMap(mapping); + if (arguments.size() > 0) { + removeLastChars(2); } + return this; + } - private JCMethodDecl mainMethod; + private AbstractTreePrinter printArgument(JCExpression argument, int i) { + print("p" + i + ": "); + substituteAndPrintType(argument, false, false, true, false); + return this; + } - private PackageSymbol topLevelPackage; - - private void useModule(boolean require, PackageSymbol targetPackage, JCTree sourceTree, String targetName, - String moduleName, Symbol sourceElement) { - if (context.useModules) { - context.packageDependencies.add(targetPackage); - context.packageDependencies.add(compilationUnit.packge); - context.packageDependencies.addEdge(compilationUnit.packge, targetPackage); - } - context.registerUsedModule(moduleName); - Set importedNames = context.getImportedNames(compilationUnit.getSourceFile().getName()); - if (!importedNames.contains(targetName)) { - if (context.useModules) { - // TODO: when using several qualified Globals classes, we need - // to disambiguate (Globals__1, Globals__2, ....) - // TODO: IDEA FOR MODULES AND FULLY QUALIFIED NAMES - // when using a fully qualified name in the code, we need an - // import, which can be named after something like - // qualName.replace(".", "_")... this would work in the general - // case... - - boolean fullImport = require || GLOBALS_CLASS_NAME.equals(targetName); - if (fullImport) { - if (context.useRequireForModules) { - print("import " + targetName + " = require(\"" + moduleName + "\"); ").println(); - } else { - print("import * as " + targetName + " from '" + moduleName + "'; ").println(); - } - } else { - print("import { " + targetName + " } from '" + moduleName + "'; ").println(); - } - } - context.registerImportedName(compilationUnit.getSourceFile().getName(), sourceElement, targetName); - } + private AbstractTreePrinter substituteAndPrintType(JCTree typeTree, boolean arrayComponent, + boolean inTypeParameters, boolean completeRawTypes, boolean disableSubstitution) { + if (typeTree.type.tsym instanceof TypeVariableSymbol) { + if (getAdapter().typeVariablesToErase.contains(typeTree.type.tsym)) { + return print("any"); + } } - - private boolean checkRootPackageParent(JCCompilationUnit topLevel, PackageSymbol rootPackage, - PackageSymbol parentPackage) { - if (parentPackage == null) { - return true; + if (!disableSubstitution) { + if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_ERASED)) { + return print("any"); + } + if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_OBJECT_TYPE)) { + // TODO: in case of object types, we should replace with the org + // object type... + return print("any"); + } + String typeFullName = typeTree.type.getModelType().toString(); // typeTree.type.tsym.getQualifiedName().toString(); + if (Runnable.class.getName().equals(typeFullName)) { + if (arrayComponent) { + print("("); } - if (!context.options.isNoRootDirectories() || context.options.isBundle()) { - return true; - } - if (context.isRootPackage(parentPackage)) { - report(topLevel.getPackageName(), JSweetProblem.ENCLOSED_ROOT_PACKAGES, - rootPackage.getQualifiedName().toString(), parentPackage.getQualifiedName().toString()); - return false; - } - for (Symbol s : parentPackage.getEnclosedElements()) { - if (s instanceof ClassSymbol) { - if (Util.isSourceElement(s)) { - report(topLevel.getPackageName(), JSweetProblem.CLASS_OUT_OF_ROOT_PACKAGE_SCOPE, - s.getQualifiedName().toString(), rootPackage.getQualifiedName().toString()); - return false; - } - } - } - return checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) parentPackage.owner); - } - - @Override - public void visitTopLevel(JCCompilationUnit topLevel) { - - if (context.isPackageErased(topLevel.packge)) { - return; - } - boolean noDefs = true; - for (JCTree def : topLevel.defs) { - if (def instanceof JCClassDecl) { - if (!context.isIgnored(((JCClassDecl) def))) { - noDefs = false; - } - } - } - // do not print the compilation unit at all if no defs are to be printed - if (!context.bundleMode && noDefs) { - return; - } - - isDefinitionScope = topLevel.packge.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + "."); - - if (context.hasAnnotationType(topLevel.packge, JSweetConfig.ANNOTATION_MODULE)) { - context.addExportedElement( - context.getAnnotationValue(topLevel.packge, JSweetConfig.ANNOTATION_MODULE, null), topLevel.packge, - getCompilationUnit()); - } - - PackageSymbol rootPackage = context.getFirstEnclosingRootPackage(topLevel.packge); - if (rootPackage != null) { - if (!checkRootPackageParent(topLevel, rootPackage, (PackageSymbol) rootPackage.owner)) { - return; - } - } - context.importedTopPackages.clear(); - context.rootPackages.add(rootPackage); - // TODO: check relaxing @Root - // if (context.useModules && context.rootPackages.size() > 1) { - // if (!context.reportedMultipleRootPackages) { - // report(topLevel.getPackageName(), - // JSweetProblem.MULTIPLE_ROOT_PACKAGES_NOT_ALLOWED_WITH_MODULES, - // context.rootPackages.toString()); - // context.reportedMultipleRootPackages = true; - // } - // return; - // } - - topLevelPackage = context.getTopLevelPackage(topLevel.packge); - if (topLevelPackage != null) { - context.topLevelPackageNames.add(topLevelPackage.getQualifiedName().toString()); - } - - footer.delete(0, footer.length()); - - setCompilationUnit(topLevel); - - String packge = topLevel.packge.toString(); - - boolean globalModule = JSweetConfig.GLOBALS_PACKAGE_NAME.equals(packge) - || packge.endsWith("." + JSweetConfig.GLOBALS_PACKAGE_NAME); - String rootRelativePackageName = ""; - if (!globalModule) { - rootRelativePackageName = getRootRelativeName(topLevel.packge); - if (rootRelativePackageName.length() == 0) { - globalModule = true; - } - } - - List packageSegments = new ArrayList(Arrays.asList(rootRelativePackageName.split("\\."))); - packageSegments.retainAll(JSweetConfig.TS_TOP_LEVEL_KEYWORDS); - if (!packageSegments.isEmpty()) { - report(topLevel.getPackageName(), JSweetProblem.PACKAGE_NAME_CONTAINS_KEYWORD, packageSegments); - } - - // generate requires by looking up imported external modules - - for (JCImport importDecl : topLevel.getImports()) { - - TreeScanner importedModulesScanner = new TreeScanner() { - @Override - public void scan(JCTree tree) { - if (tree instanceof JCFieldAccess) { - JCFieldAccess qualified = (JCFieldAccess) tree; - if (qualified.sym != null) { - // regular import case (qualified.sym is a package) - if (context.hasAnnotationType(qualified.sym, JSweetConfig.ANNOTATION_MODULE)) { - String actualName = context.getAnnotationValue(qualified.sym, - JSweetConfig.ANNOTATION_MODULE, null); - useModule(true, null, importDecl, qualified.name.toString(), actualName, - ((PackageSymbol) qualified.sym)); - } - } else { - // static import case (imported fields and methods) - if (qualified.selected instanceof JCFieldAccess) { - JCFieldAccess qualifier = (JCFieldAccess) qualified.selected; - if (qualifier.sym != null) { - try { - for (Symbol importedMember : qualifier.sym.getEnclosedElements()) { - if (qualified.name.equals(importedMember.getSimpleName())) { - if (context.hasAnnotationType(importedMember, - JSweetConfig.ANNOTATION_MODULE)) { - String actualName = context.getAnnotationValue(importedMember, - JSweetConfig.ANNOTATION_MODULE, null); - useModule(true, null, importDecl, - importedMember.getSimpleName().toString(), actualName, - importedMember); - break; - } - } - } - } catch (Exception e) { - // TODO: sometimes, - // getEnclosedElement - // fails because of string types - // (find - // out why if possible) - e.printStackTrace(); - } - } - } - } - } - super.scan(tree); - } - }; - importedModulesScanner.scan(importDecl.qualid); - } - - for (JCImport importDecl : topLevel.getImports()) { - if (importDecl.qualid instanceof JCFieldAccess) { - JCFieldAccess qualified = (JCFieldAccess) importDecl.qualid; - String importedName = qualified.name.toString(); - if (importDecl.isStatic() && (qualified.selected instanceof JCFieldAccess)) { - qualified = (JCFieldAccess) qualified.selected; - } - if (qualified.sym instanceof ClassSymbol) { - ClassSymbol importedClass = (ClassSymbol) qualified.sym; - if (Util.isSourceElement(importedClass) && !importedClass.getQualifiedName().toString() - .startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { - String importedModule = importedClass.sourcefile.getName(); - if (importedModule.equals(compilationUnit.sourcefile.getName())) { - continue; - } - String pathToImportedClass = Util.getRelativePath( - new File(compilationUnit.sourcefile.getName()).getParent(), importedModule); - pathToImportedClass = pathToImportedClass.substring(0, pathToImportedClass.length() - 5); - if (!pathToImportedClass.startsWith(".")) { - pathToImportedClass = "./" + pathToImportedClass; - } - - Symbol symbol = qualified.sym.getEnclosingElement(); - while (!(symbol instanceof PackageSymbol)) { - importedName = symbol.getSimpleName().toString(); - symbol = symbol.getEnclosingElement(); - } - if (symbol != null) { - useModule(false, (PackageSymbol) symbol, importDecl, importedName, - pathToImportedClass.replace('\\', '/'), null); - } - } - } - } - } - - if (context.useModules) { - TreeScanner usedTypesScanner = new TreeScanner() { - - private void checkType(TypeSymbol type) { - if (type instanceof ClassSymbol) { - if (type.getEnclosingElement().equals(compilationUnit.packge)) { - String importedModule = ((ClassSymbol) type).sourcefile.getName(); - if (!importedModule.equals(compilationUnit.sourcefile.getName())) { - importedModule = "./" + new File(importedModule).getName(); - importedModule = importedModule.substring(0, importedModule.length() - 5); - useModule(false, (PackageSymbol) type.getEnclosingElement(), null, - type.getSimpleName().toString(), importedModule, null); - } - } - } - } - - @Override - public void scan(JCTree t) { - if (t != null && t.type != null && t.type.tsym instanceof ClassSymbol) { - if (!(t instanceof JCTypeApply)) { - checkType(t.type.tsym); - } - } - super.scan(t); - } - - }; - usedTypesScanner.scan(compilationUnit); - } - - // require root modules when using fully qualified names or reserved - // keywords - TreeScanner inlinedModuleScanner = new TreeScanner() { - Stack stack = new Stack<>(); - - public void scan(JCTree t) { - if (t != null) { - stack.push(t); - try { - super.scan(t); - } finally { - stack.pop(); - } - } - } - - @SuppressWarnings("unchecked") - public T getParent(Class type) { - for (int i = this.stack.size() - 2; i >= 0; i--) { - if (type.isAssignableFrom(this.stack.get(i).getClass())) { - return (T) this.stack.get(i); - } - } - return null; - } - - public void visitIdent(JCIdent identifier) { - if (identifier.sym instanceof PackageSymbol) { - // ignore packages in imports - if (getParent(JCImport.class) != null) { - return; - } - boolean isSourceType = false; - for (int i = stack.size() - 2; i >= 0; i--) { - JCTree tree = stack.get(i); - if (!(tree instanceof JCFieldAccess)) { - break; - } else { - JCFieldAccess fa = (JCFieldAccess) tree; - if ((fa.sym instanceof ClassSymbol) && Util.isSourceElement(fa.sym)) { - isSourceType = true; - break; - } - } - } - if (!isSourceType) { - return; - } - PackageSymbol identifierPackage = (PackageSymbol) identifier.sym; - String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, identifierPackage); - if (pathToModulePackage == null) { - return; - } - File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); - if (!identifierPackage.getSimpleName().toString() - .equals(compilationUnit.packge.getSimpleName().toString())) { - useModule(false, identifierPackage, identifier, identifierPackage.getSimpleName().toString(), - moduleFile.getPath().replace('\\', '/'), null); - } - } else if (identifier.sym instanceof ClassSymbol) { - if (JSweetConfig.GLOBALS_PACKAGE_NAME - .equals(identifier.sym.getEnclosingElement().getSimpleName().toString())) { - String pathToModulePackage = Util.getRelativePath(compilationUnit.packge, - identifier.sym.getEnclosingElement()); - if (pathToModulePackage == null) { - return; - } - File moduleFile = new File(new File(pathToModulePackage), JSweetConfig.MODULE_FILE_NAME); - if (!identifier.sym.getEnclosingElement() - .equals(compilationUnit.packge.getSimpleName().toString())) { - useModule(false, (PackageSymbol) identifier.sym.getEnclosingElement(), identifier, - JSweetConfig.GLOBALS_PACKAGE_NAME, moduleFile.getPath().replace('\\', '/'), null); - } - } - } - } - - @Override - public void visitApply(JCMethodInvocation invocation) { - // TODO: same for static variables - if (invocation.meth instanceof JCIdent - && JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(invocation.meth.toString().toLowerCase())) { - PackageSymbol invocationPackage = (PackageSymbol) ((JCIdent) invocation.meth).sym - .getEnclosingElement().getEnclosingElement(); - String rootRelativeInvocationPackageName = getRootRelativeName(invocationPackage); - if (rootRelativeInvocationPackageName.indexOf('.') == -1) { - super.visitApply(invocation); - return; - } - String targetRootPackageName = rootRelativeInvocationPackageName.substring(0, - rootRelativeInvocationPackageName.indexOf('.')); - String pathToReachRootPackage = Util.getRelativePath( - "/" + compilationUnit.packge.getQualifiedName().toString().replace('.', '/'), - "/" + targetRootPackageName); - if (pathToReachRootPackage == null) { - super.visitApply(invocation); - return; - } - File moduleFile = new File(new File(pathToReachRootPackage), JSweetConfig.MODULE_FILE_NAME); - if (!invocationPackage.toString().equals(compilationUnit.packge.getSimpleName().toString())) { - useModule(false, invocationPackage, invocation, targetRootPackageName, - moduleFile.getPath().replace('\\', '/'), null); - } - } - super.visitApply(invocation); - } - - }; - // TODO: change the way qualified names are handled (because of new - // module organization) - // inlinedModuleScanner.scan(compilationUnit); - - if (!globalModule && !context.useModules) { - printIndent(); - if (isDefinitionScope) { - print("declare "); - } - print("namespace ").print(rootRelativePackageName).print(" {").startIndent().println(); - } - - for (JCTree def : topLevel.defs) { - mainMethod = null; - - printIndent(); - int pos = getCurrentPosition(); - print(def); - if (getCurrentPosition() == pos) { - removeLastIndent(); - continue; - } - println().println(); - } - if (!globalModule && !context.useModules) { - removeLastChar().endIndent().printIndent().print("}").println(); - } - - if (footer.length() > 0) { - println().print(footer.toString()); - } - - globalModule = false; - - } - - private void printDocComment(JCTree element, boolean indent) { - if (compilationUnit != null && compilationUnit.docComments != null) { - Comment comment = compilationUnit.docComments.getComment(element); - String commentText = JSDoc.adaptDocComment(context, getCompilationUnit(), element, - comment == null ? null : comment.getText()); - - Element elt = null; - if (element instanceof JCVariableDecl) { - elt = ((JCVariableDecl) element).sym; - } else if (element instanceof JCMethodDecl) { - elt = ((JCMethodDecl) element).sym; - } else if (element instanceof JCClassDecl) { - elt = ((JCClassDecl) element).sym; - } - if (elt != null) { - commentText = getAdapter().adaptDocComment(elt, commentText); - } - - List lines = new ArrayList<>(); - if (commentText != null) { - lines.addAll(Arrays.asList(commentText.split("\n"))); - } - if (!lines.isEmpty()) { - if (indent) { - printIndent(); - } - print("/**").println(); - for (String line : lines) { - printIndent().print(" * ").print(line.trim()).println(); - } - removeLastChar(); - println().printIndent().print(" ").print("*/").println(); - if (!indent) { - printIndent(); - } - } - } - } - - private void printAnonymousClassTypeArgs(JCNewClass newClass) { - if ((newClass.clazz instanceof JCTypeApply)) { - JCTypeApply tapply = (JCTypeApply) newClass.clazz; - if (tapply.getTypeArguments() != null && !tapply.getTypeArguments().isEmpty()) { - boolean printed = false; - print("<"); - for (JCExpression targ : tapply.getTypeArguments()) { - if (targ.type.tsym instanceof TypeVariableSymbol) { - printed = true; - print(targ).print(", "); - } - } - if (printed) { - removeLastChars(2); - print(">"); - } else { - removeLastChar(); - } - } - } - } - - private boolean isAnonymousClass() { - return scope.size() > 1 && getScope(1).isAnonymousClass; - } - - private boolean isInnerClass() { - return scope.size() > 1 && getScope(1).isInnerClass; - } - - private boolean isLocalClass() { - return scope.size() > 1 && getScope(1).isLocalClass; - } - - /** - * A flags that indicates that this adapter is printing type parameters. - */ - private boolean inTypeParameters = false; - - /** - * A flags that indicates that this adapter is not substituting types. - */ - private boolean disableTypeSubstitution = false; - - protected final AbstractTreePrinter substituteAndPrintType(JCTree typeTree) { - return substituteAndPrintType(typeTree, false, inTypeParameters, true, disableTypeSubstitution); - } - - private AbstractTreePrinter printArguments(List arguments) { - int i = 1; - for (JCExpression argument : arguments) { - printArgument(argument, i++).print(", "); - } - if (arguments.size() > 0) { - removeLastChars(2); + print("() => void"); + if (arrayComponent) { + print(")"); } return this; - } - - private AbstractTreePrinter printArgument(JCExpression argument, int i) { - print("p" + i + ": "); - substituteAndPrintType(argument, false, false, true, false); - return this; - } - - private AbstractTreePrinter substituteAndPrintType(JCTree typeTree, boolean arrayComponent, - boolean inTypeParameters, boolean completeRawTypes, boolean disableSubstitution) { - if (typeTree.type.tsym instanceof TypeVariableSymbol) { - if (getAdapter().typeVariablesToErase.contains(typeTree.type.tsym)) { - return print("any"); - } + } + if (typeTree instanceof JCTypeApply) { + JCTypeApply typeApply = ((JCTypeApply) typeTree); + String typeName = typeApply.clazz.toString(); + String mappedTypeName = context.getTypeMappingTarget(typeName); + if (mappedTypeName != null && mappedTypeName.endsWith("<>")) { + print(typeName.substring(0, mappedTypeName.length() - 2)); + return this; } - if (!disableSubstitution) { - if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_ERASED)) { - return print("any"); - } - if (context.hasAnnotationType(typeTree.type.tsym, ANNOTATION_OBJECT_TYPE)) { - // TODO: in case of object types, we should replace with the org - // object type... - return print("any"); - } - String typeFullName = typeTree.type.getModelType().toString(); // typeTree.type.tsym.getQualifiedName().toString(); - if (Runnable.class.getName().equals(typeFullName)) { - if (arrayComponent) { - print("("); - } - print("() => void"); - if (arrayComponent) { - print(")"); - } - return this; - } - if (typeTree instanceof JCTypeApply) { - JCTypeApply typeApply = ((JCTypeApply) typeTree); - String typeName = typeApply.clazz.toString(); - String mappedTypeName = context.getTypeMappingTarget(typeName); - if (mappedTypeName != null && mappedTypeName.endsWith("<>")) { - print(typeName.substring(0, mappedTypeName.length() - 2)); - return this; - } - if (typeFullName.startsWith(TUPLE_CLASSES_PACKAGE + ".")) { - print("["); - for (JCExpression argument : typeApply.arguments) { - substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false) - .print(","); - } - if (typeApply.arguments.length() > 0) { - removeLastChar(); - } - print("]"); - return this; - } - if (typeFullName.startsWith(UNION_CLASS_NAME)) { - print("("); - for (JCExpression argument : typeApply.arguments) { - print("("); - substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false); - print(")"); - print("|"); - } - if (typeApply.arguments.length() > 0) { - removeLastChar(); - } - print(")"); - return this; - } - if (typeFullName.startsWith(UTIL_PACKAGE + ".") || typeFullName.startsWith("java.util.function.")) { - if (typeName.endsWith("Consumer") || typeName.startsWith("Consumer")) { - if (arrayComponent) { - print("("); - } - print("("); - if (typeName.startsWith("Int") || typeName.startsWith("Long") - || typeName.startsWith("Double")) { - print("p0 : number"); - } else { - printArguments(typeApply.arguments); - } - print(") => void"); - if (arrayComponent) { - print(")"); - } - return this; - } else if (typeName.endsWith("Function") || typeName.startsWith("Function")) { - if (arrayComponent) { - print("("); - } - print("("); - if (typeName.startsWith("Int") || typeName.startsWith("Long") - || typeName.startsWith("Double")) { - print("p0 : number"); - } else { - printArguments(typeApply.arguments.subList(0, typeApply.arguments.length() - 1)); - } - print(") => "); - substituteAndPrintType(typeApply.arguments.get(typeApply.arguments.length() - 1), - arrayComponent, inTypeParameters, completeRawTypes, false); - if (arrayComponent) { - print(")"); - } - return this; - } else if (typeName.endsWith("Supplier") || typeName.startsWith("Supplier")) { - if (arrayComponent) { - print("("); - } - print("("); - print(") => "); - if (typeName.startsWith("Int") || typeName.startsWith("Long") - || typeName.startsWith("Double")) { - print("number"); - } else { - substituteAndPrintType(typeApply.arguments.get(0), arrayComponent, inTypeParameters, - completeRawTypes, false); - } - if (arrayComponent) { - print(")"); - } - return this; - } else if (typeName.endsWith("Predicate")) { - if (arrayComponent) { - print("("); - } - print("("); - if (typeName.startsWith("Int") || typeName.startsWith("Long") - || typeName.startsWith("Double")) { - print("p0 : number"); - } else { - printArguments(typeApply.arguments); - } - print(") => boolean"); - if (arrayComponent) { - print(")"); - } - return this; - } else if (typeName.endsWith("Operator")) { - if (arrayComponent) { - print("("); - } - print("("); - printArgument(typeApply.arguments.head, 1); - if (typeName.startsWith("Binary")) { - print(", "); - printArgument(typeApply.arguments.head, 2); - } - print(") => "); - substituteAndPrintType(typeApply.arguments.head, arrayComponent, inTypeParameters, - completeRawTypes, false); - if (arrayComponent) { - print(")"); - } - return this; - } - } - if (typeFullName.startsWith(Class.class.getName() + "<")) { - return print("any"); - } + if (typeFullName.startsWith(TUPLE_CLASSES_PACKAGE + ".")) { + print("["); + for (JCExpression argument : typeApply.arguments) { + substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false) + .print(","); + } + if (typeApply.arguments.length() > 0) { + removeLastChar(); + } + print("]"); + return this; + } + if (typeFullName.startsWith(UNION_CLASS_NAME)) { + print("("); + for (JCExpression argument : typeApply.arguments) { + print("("); + substituteAndPrintType(argument, arrayComponent, inTypeParameters, completeRawTypes, false); + print(")"); + print("|"); + } + if (typeApply.arguments.length() > 0) { + removeLastChar(); + } + print(")"); + return this; + } + if (typeFullName.startsWith(UTIL_PACKAGE + ".") || typeFullName.startsWith("java.util.function.")) { + if (typeName.endsWith("Consumer") || typeName.startsWith("Consumer")) { + if (arrayComponent) { + print("("); + } + print("("); + if (typeName.startsWith("Int") || typeName.startsWith("Long") + || typeName.startsWith("Double")) { + print("p0 : number"); } else { - if (!(typeTree instanceof JCArrayTypeTree) && typeFullName.startsWith("java.util.function.")) { - // case of a raw functional type (programmer's mistake) - return print("any"); - } - String mappedType = context.getTypeMappingTarget(typeFullName); - if (mappedType != null) { - if (mappedType.endsWith("<>")) { - print(mappedType.substring(0, mappedType.length() - 2)); - } else { - print(mappedType); - if (completeRawTypes && !typeTree.type.tsym.getTypeParameters().isEmpty() - && !context.getTypeMappingTarget(typeFullName).equals("any")) { - printAnyTypeArguments(typeTree.type.tsym.getTypeParameters().size()); - } - } - return this; - } + printArguments(typeApply.arguments); } - for (BiFunction mapping : context.getFunctionalTypeMappings()) { - Object mapped = mapping.apply(new ExtendedElementSupport(typeTree), typeFullName); - if (mapped instanceof String) { - print((String) mapped); - return this; - } else if (mapped instanceof JCTree) { - substituteAndPrintType((JCTree) mapped); - return this; - } else if (mapped instanceof TypeMirror) { - print(getAdapter().getMappedType((TypeMirror) mapped)); - return this; - } - } - } - - if (typeTree instanceof JCTypeApply) { - JCTypeApply typeApply = ((JCTypeApply) typeTree); - substituteAndPrintType(typeApply.clazz, arrayComponent, inTypeParameters, false, disableSubstitution); - if (!typeApply.arguments.isEmpty() && !"any".equals(getLastPrintedString(3)) - && !"Object".equals(getLastPrintedString(6))) { - print("<"); - for (JCExpression argument : typeApply.arguments) { - substituteAndPrintType(argument, arrayComponent, false, completeRawTypes, false).print(", "); - } - if (typeApply.arguments.length() > 0) { - removeLastChars(2); - } - print(">"); + print(") => void"); + if (arrayComponent) { + print(")"); } return this; - } else if (typeTree instanceof JCWildcard) { - JCWildcard wildcard = ((JCWildcard) typeTree); - String name = context.getWildcardName(wildcard); - if (name == null) { - return print("any"); - } else { - print(name); - if (inTypeParameters) { - print(" extends "); - return substituteAndPrintType(wildcard.getBound(), arrayComponent, false, completeRawTypes, - disableSubstitution); - } else { - return this; - } + } else if (typeName.endsWith("Function") || typeName.startsWith("Function")) { + if (arrayComponent) { + print("("); } - } else { - if (typeTree instanceof JCArrayTypeTree) { - return substituteAndPrintType(((JCArrayTypeTree) typeTree).elemtype, true, inTypeParameters, - completeRawTypes, disableSubstitution).print("[]"); - } - if (completeRawTypes && typeTree.type.tsym.getTypeParameters() != null - && !typeTree.type.tsym.getTypeParameters().isEmpty()) { - // raw type case (Java warning) - print(typeTree); - print("<"); - for (int i = 0; i < typeTree.type.tsym.getTypeParameters().length(); i++) { - print("any, "); - } - removeLastChars(2); - print(">"); - return this; - } else { - return print(typeTree); - } - } - - } - - @Override - public void visitClassDef(JCClassDecl classdecl) { - if (context.isIgnored(classdecl)) { - getAdapter().afterType(classdecl.sym); - return; - } - String name = classdecl.getSimpleName().toString(); - if (!scope.isEmpty() && getScope().anonymousClasses.contains(classdecl)) { - name = getScope().name + ANONYMOUS_PREFIX + getScope().anonymousClasses.indexOf(classdecl); - } - - JCTree testParent = getFirstParent(JCClassDecl.class, JCMethodDecl.class); - if (testParent != null && testParent instanceof JCMethodDecl) { - if (!isLocalClass()) { - getScope().localClasses.add(classdecl); - return; - } - } - - enterScope(); - getScope().name = name; - - JCClassDecl parent = getParent(JCClassDecl.class); - List parentTypeVars = new ArrayList<>(); - if (parent != null) { - getScope().innerClass = true; - if (!classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { - getScope().innerClassNotStatic = true; - if (parent.getTypeParameters() != null) { - parentTypeVars.addAll(parent.getTypeParameters().stream().map(t -> (TypeVariableSymbol) t.type.tsym) - .collect(Collectors.toList())); - getAdapter().typeVariablesToErase.addAll(parentTypeVars); - } - } - } - getScope().declareClassScope = context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_AMBIENT) - || isDefinitionScope; - getScope().interfaceScope = false; - getScope().removedSuperclass = false; - getScope().enumScope = false; - getScope().enumWrapperClassScope = false; - - HashSet> defaultMethods = null; - boolean globals = JSweetConfig.GLOBALS_CLASS_NAME.equals(classdecl.name.toString()); - if (globals && classdecl.extending != null) { - report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS); - } - List implementedInterfaces = new ArrayList<>(); - - if (!globals) { - if (classdecl.extending != null && JSweetConfig.GLOBALS_CLASS_NAME - .equals(classdecl.extending.type.tsym.getSimpleName().toString())) { - report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_BE_SUBCLASSED); - return; - } - printDocComment(classdecl, false); - print(classdecl.mods); - if (!isTopLevelScope() || context.useModules || isAnonymousClass() || isInnerClass() || isLocalClass()) { - print("export "); - } - if (context.isInterface(classdecl.sym)) { - print("interface "); - getScope().interfaceScope = true; - } else { - if (classdecl.getKind() == Kind.ENUM) { - if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { - print("declare "); - } - if (scope.size() > 1 && getScope(1).isComplexEnum) { - if (Util.hasAbstractMethod(classdecl.sym)) { - print("abstract "); - } - print("class "); - getScope().enumWrapperClassScope = true; - } else { - print("enum "); - getScope().enumScope = true; - } - } else { - if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { - print("declare "); - } - defaultMethods = new HashSet<>(); - Util.findDefaultMethodsInType(defaultMethods, context, classdecl.sym); - if (classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { - print("abstract "); - } - print("class "); - } - } - - print(name + (getScope().enumWrapperClassScope ? ENUM_WRAPPER_CLASS_SUFFIX : "")); - - if (classdecl.typarams != null && classdecl.typarams.size() > 0) { - print("<").printArgList(null, classdecl.typarams).print(">"); - } else if (isAnonymousClass() && classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { - JCNewClass newClass = getScope(1).anonymousClassesConstructors - .get(getScope(1).anonymousClasses.indexOf(classdecl)); - printAnonymousClassTypeArgs(newClass); - } - String mixin = null; - if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN)) { - mixin = context.getAnnotationValue(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN, null); - for (Compound c : classdecl.sym.getAnnotationMirrors()) { - if (JSweetConfig.ANNOTATION_MIXIN.equals(c.type.toString())) { - String targetName = getRootRelativeName(((Attribute.Class) c.values.head.snd).classType.tsym); - String mixinName = getRootRelativeName(classdecl.sym); - if (!mixinName.equals(targetName)) { - report(classdecl, JSweetProblem.WRONG_MIXIN_NAME, mixinName, targetName); - } else { - if (((Attribute.Class) c.values.head.snd).classType.tsym.equals(classdecl.sym)) { - report(classdecl, JSweetProblem.SELF_MIXIN_TARGET, mixinName); - } - } - } - } - } - - boolean extendsInterface = false; - if (classdecl.extending != null) { - - boolean removeIterable = false; - if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE) - && classdecl.extending.type.tsym.getQualifiedName().toString() - .equals(Iterable.class.getName())) { - removeIterable = true; - } - - if (!removeIterable && !JSweetConfig.isJDKReplacementMode() - && !(JSweetConfig.OBJECT_CLASSNAME.equals(classdecl.extending.type.toString()) - || Object.class.getName().equals(classdecl.extending.type.toString())) - && !(mixin != null && mixin.equals(classdecl.extending.type.toString())) && !(getAdapter() - .eraseSuperClass(classdecl.sym, (ClassSymbol) classdecl.extending.type.tsym))) { - if (!getScope().interfaceScope && context.isInterface(classdecl.extending.type.tsym)) { - extendsInterface = true; - print(" implements "); - implementedInterfaces.add(classdecl.extending.type); - } else { - print(" extends "); - } - if (getScope().enumWrapperClassScope && getScope(1).anonymousClasses.contains(classdecl)) { - print(classdecl.extending.toString() + ENUM_WRAPPER_CLASS_SUFFIX); - } else { - disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); - substituteAndPrintType(classdecl.extending); - disableTypeSubstitution = false; - } - if (context.classesWithWrongConstructorOverload.contains(classdecl.sym)) { - getScope().hasConstructorOverloadWithSuperClass = true; - } - } else { - getScope().removedSuperclass = true; - } - } - - if (classdecl.implementing != null && !classdecl.implementing.isEmpty() && !getScope().enumScope) { - List implementing = new ArrayList<>(classdecl.implementing); - - if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE)) { - for (JCExpression itf : classdecl.implementing) { - if (itf.type.tsym.getQualifiedName().toString().equals(Iterable.class.getName())) { - implementing.remove(itf); - } - } - } - // erase Java interfaces - for (JCExpression itf : classdecl.implementing) { - if (context.isFunctionalType(itf.type.tsym) - || getAdapter().eraseSuperInterface(classdecl.sym, (ClassSymbol) itf.type.tsym)) { - implementing.remove(itf); - } - } - - if (!implementing.isEmpty()) { - if (!extendsInterface) { - if (getScope().interfaceScope) { - print(" extends "); - } else { - print(" implements "); - } - } else { - print(", "); - } - for (JCExpression itf : implementing) { - disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); - substituteAndPrintType(itf); - disableTypeSubstitution = false; - implementedInterfaces.add(itf.type); - print(", "); - } - removeLastChars(2); - } - } - print(" {").println().startIndent(); - } - - if (getScope().innerClassNotStatic && !getScope().interfaceScope && !getScope().enumScope) { - printIndent().print("public " + PARENT_CLASS_FIELD_NAME + ": any;").println(); - } - - if (defaultMethods != null && !defaultMethods.isEmpty()) { - getScope().defaultMethodScope = true; - for (Entry entry : defaultMethods) { - if (!(entry.getValue().type instanceof MethodType)) { - continue; - } - MethodSymbol s = Util.findMethodDeclarationInType(context.types, classdecl.sym, - entry.getValue().getName().toString(), (MethodType) entry.getValue().type); - if (s == null || s == entry.getValue().sym) { - getAdapter().typeVariablesToErase - .addAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); - printIndent().print(entry.getValue()).println(); - getAdapter().typeVariablesToErase - .removeAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); - } - } - getScope().defaultMethodScope = false; - } - - if (getScope().enumScope) { - printIndent(); - } - if (globals) { - removeLastIndent(); - } - - if (!getScope().interfaceScope && classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { - List methods = new ArrayList<>(); - for (Type t : implementedInterfaces) { - context.grabMethodsToBeImplemented(methods, t.tsym); - } - Map signatures = new HashMap<>(); - for (MethodSymbol meth : methods) { - if (meth.type instanceof MethodType) { - MethodSymbol s = Util.findMethodDeclarationInType(getContext().types, classdecl.sym, - meth.getSimpleName().toString(), (MethodType) meth.type, true); - if (Object.class.getName().equals(s.getEnclosingElement().toString())) { - s = null; - } - boolean printDefaultImplementation = false; - if (s != null) { - if (!s.getEnclosingElement().equals(classdecl.sym)) { - if (!(s.isDefault() || (!context.isInterface((TypeSymbol) s.getEnclosingElement()) - && !s.getModifiers().contains(Modifier.ABSTRACT)))) { - printDefaultImplementation = true; - } - } - } - - if (printDefaultImplementation) { - Overload o = context.getOverload(classdecl.sym, meth); - if (o != null && o.methods.size() > 1 && !o.isValid) { - if (!meth.type.equals(o.coreMethod.type)) { - printDefaultImplementation = false; - } - } - } - if (s == null || printDefaultImplementation) { - String signature = getContext().types.erasure(meth.type).toString(); - if (!(signatures.containsKey(meth.name) && signatures.get(meth.name).equals(signature))) { - printDefaultImplementation(meth); - signatures.put(meth.name, signature); - } - } - } - } - } - - for (JCTree def : classdecl.defs) { - if (def instanceof JCClassDecl) { - getScope().hasInnerClass = true; - } - if (def instanceof JCVariableDecl) { - JCVariableDecl var = (JCVariableDecl) def; - if (!var.sym.isStatic() && var.init != null) { - getScope().fieldsWithInitializers.add((JCVariableDecl) def); - } - } - } - - if (!globals && !context.isInterface(classdecl.sym) && context.getStaticInitializerCount(classdecl.sym) > 0) { - printIndent().print("static __static_initialized : boolean = false;").println(); - int liCount = context.getStaticInitializerCount(classdecl.sym); - String prefix = classdecl.getSimpleName().toString() + "."; - printIndent().print("static __static_initialize() { "); - print("if(!" + prefix + "__static_initialized) { "); - print(prefix + "__static_initialized = true; "); - for (int i = 0; i < liCount; i++) { - print(prefix + "__static_initializer_" + i + "(); "); - } - print("} }").println().println(); - String qualifiedClassName = getQualifiedTypeName(classdecl.sym, globals); - context.addTopFooterStatement( - (isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".__static_initialize();")); - } - - boolean hasUninitializedFields = false; - - for (JCTree def : classdecl.defs) { - if (getScope().interfaceScope && ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) - || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic()))) { - // static interface members are printed in a namespace - continue; - } - if (getScope().interfaceScope && def instanceof JCMethodDecl) { - // object method should not be defined otherwise they will have - // to be implemented - if (Util.isOverridingBuiltInJavaObjectMethod(((JCMethodDecl) def).sym)) { - continue; - } - } - if (def instanceof JCClassDecl) { - // inner types are be printed in a namespace - continue; - } - if (def instanceof JCVariableDecl) { - if (getScope().enumScope && ((JCVariableDecl) def).type.tsym != classdecl.type.tsym) { - getScope().isComplexEnum = true; - continue; - } - if (!((JCVariableDecl) def).getModifiers().getFlags().contains(Modifier.STATIC) - && ((JCVariableDecl) def).init == null) { - hasUninitializedFields = true; - } - } - if (def instanceof JCBlock && !((JCBlock) def).isStatic()) { - hasUninitializedFields = true; - } - if (!getScope().enumScope) { - printIndent(); - } - int pos = getCurrentPosition(); - print(def); - if (getCurrentPosition() == pos) { - if (!getScope().enumScope) { - removeLastIndent(); - } - continue; - } - if (def instanceof JCVariableDecl) { - if (getScope().enumScope) { - print(", "); - } else { - print(";").println().println(); - } - } else { - println().println(); - } - } - - if (!getScope().hasDeclaredConstructor - && !(getScope().interfaceScope || getScope().enumScope || getScope().declareClassScope)) { - Set interfaces = new HashSet<>(); - context.grabSupportedInterfaceNames(interfaces, classdecl.sym); - if (!interfaces.isEmpty() || getScope().innerClassNotStatic || hasUninitializedFields) { - printIndent().print("constructor("); - boolean hasArgs = false; - if (getScope().innerClassNotStatic) { - print(PARENT_CLASS_FIELD_NAME + ": any"); - hasArgs = true; - } - int anonymousClassIndex = scope.size() > 1 ? getScope(1).anonymousClasses.indexOf(classdecl) : -1; - if (anonymousClassIndex != -1) { - for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).args - .length(); i++) { - if (!hasArgs) { - hasArgs = true; - } else { - print(", "); - } - print("__arg" + i + ": any"); - } - for (VarSymbol v : getScope(1).finalVariables.get(anonymousClassIndex)) { - if (!hasArgs) { - hasArgs = true; - } else { - print(", "); - } - print("private " + v.getSimpleName() + ": any"); - } - } - - print(") {").startIndent().println(); - if (classdecl.extending != null && !getScope().removedSuperclass - && !context.isInterface(classdecl.extending.type.tsym)) { - printIndent().print("super("); - if (getScope().innerClassNotStatic) { - TypeSymbol s = classdecl.extending.type.tsym; - boolean hasArg = false; - if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { - print(PARENT_CLASS_FIELD_NAME); - hasArg = true; - } - if (anonymousClassIndex != -1) { - for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).args - .length(); i++) { - if (hasArg) { - print(", "); - } else { - hasArg = true; - } - print("__arg" + i); - } - } - } - print(");").println(); - } - printInstanceInitialization(classdecl, null); - endIndent().printIndent().print("}").println().println(); - } - } - - removeLastChar(); - - if (getScope().enumWrapperClassScope && !getScope(1).anonymousClasses.contains(classdecl)) { - printIndent().print("public name() : string { return this." + ENUM_WRAPPER_CLASS_NAME + "; }").println(); - printIndent().print("public ordinal() : number { return this." + ENUM_WRAPPER_CLASS_ORDINAL + "; }") - .println(); - } - - if (getScope().enumScope) { - removeLastChar().println(); - } - - if (!globals) { - endIndent().printIndent().print("}"); - if (getContext().options.isSupportGetClass() && !getScope().interfaceScope && !getScope().declareClassScope - && !getScope().enumScope && !(getScope().enumWrapperClassScope && classdecl.sym.isAnonymous())) { - if (!classdecl.sym.isAnonymous()) { - println().printIndent().print(classdecl.sym.getSimpleName().toString()) - .print("[\"" + CLASS_NAME_IN_CONSTRUCTOR + "\"] = ") - .print("\"" + context.getRootRelativeName(null, classdecl.sym) + "\";"); - } - Set interfaces = new HashSet<>(); - context.grabSupportedInterfaceNames(interfaces, classdecl.sym); - if (!interfaces.isEmpty()) { - println().printIndent() - .print(getScope().enumWrapperClassScope ? classdecl.sym.getSimpleName().toString() : name) - .print("[\"" + INTERFACES_FIELD_NAME + "\"] = "); - print("["); - for (String itf : interfaces) { - print("\"").print(itf).print("\","); - } - removeLastChar(); - print("];").println(); - } - if (!getScope().enumWrapperClassScope) { - println(); - } - } - } - - // enum class for complex enum - if (getScope().isComplexEnum) { - println().println().printIndent(); - visitClassDef(classdecl); - } - - // inner, anonymous and local classes in a namespace - // ====================== - // print valid inner classes - boolean nameSpace = false; - for (JCTree def : classdecl.defs) { - if (def instanceof JCClassDecl) { - JCClassDecl cdef = (JCClassDecl) def; - if (context.isIgnored(cdef)) { - continue; - } - if (!nameSpace) { - nameSpace = true; - println().println().printIndent(); - if (!isTopLevelScope() || context.useModules) { - print("export "); - } else { - if (isDefinitionScope) { - print("declare "); - } - } - print("namespace ").print(name).print(" {").startIndent(); - } - getScope().isInnerClass = true; - println().println().printIndent().print(cdef); - getScope().isInnerClass = false; - } - } - // print anonymous classes - for (JCClassDecl cdef : getScope().anonymousClasses) { - if (!nameSpace) { - nameSpace = true; - println().println().printIndent(); - if (!isTopLevelScope() || context.useModules) { - print("export "); - } - print("namespace ").print(name).print(" {").startIndent(); - } - getScope().isAnonymousClass = true; - println().println().printIndent().print(cdef); - getScope().isAnonymousClass = false; - } - // print local classes - for (JCClassDecl cdef : getScope().localClasses) { - if (!nameSpace) { - nameSpace = true; - println().println().printIndent(); - if (!isTopLevelScope() || context.useModules) { - print("export "); - } - print("namespace ").print(name).print(" {").startIndent(); - } - getScope().isLocalClass = true; - println().println().printIndent().print(cdef); - getScope().isLocalClass = false; - } - if (nameSpace) { - println().endIndent().printIndent().print("}").println(); - } - // end of namespace ================================================= - - if (getScope().enumScope && getScope().isComplexEnum && !getScope().anonymousClasses.contains(classdecl)) { - println().printIndent().print(classdecl.sym.getSimpleName().toString()) - .print("[\"" + ENUM_WRAPPER_CLASS_WRAPPERS + "\"] = ["); - int index = 0; - for (JCTree tree : classdecl.defs) { - if (tree instanceof JCVariableDecl && ((JCVariableDecl) tree).type.equals(classdecl.type)) { - JCVariableDecl varDecl = (JCVariableDecl) tree; - // enum fields are not part of the enum auxiliary class but - // will initialize the enum values - JCNewClass newClass = (JCNewClass) varDecl.init; - JCClassDecl clazz = classdecl; - try { - int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); - if (anonymousClassIndex >= 0) { - print("new ") - .print(clazz.getSimpleName().toString() + "." + clazz.getSimpleName().toString() - + ANONYMOUS_PREFIX + anonymousClassIndex + ENUM_WRAPPER_CLASS_SUFFIX) - .print("("); - } else { - print("new ").print(clazz.getSimpleName().toString() + ENUM_WRAPPER_CLASS_SUFFIX) - .print("("); - } - print("" + (index++) + ", "); - print("\"" + varDecl.sym.name.toString() + "\""); - if (!newClass.args.isEmpty()) { - print(", "); - } - printArgList(null, newClass.args).print(")"); - print(", "); - } catch (Exception e) { - logger.error(e.getMessage(), e); - } - } - } - removeLastChars(2); - print("];").println(); - } - - if (getScope().interfaceScope) { - // print static members of interfaces - nameSpace = false; - for (JCTree def : classdecl.defs) { - if ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) - || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic())) { - if (def instanceof JCVariableDecl && context.hasAnnotationType(((JCVariableDecl) def).sym, - ANNOTATION_STRING_TYPE, JSweetConfig.ANNOTATION_ERASED)) { - continue; - } - if (!nameSpace) { - nameSpace = true; - println().println().printIndent(); - - if (getIndent() != 0 || context.useModules) { - print("export "); - } else { - if (isDefinitionScope) { - print("declare "); - } - } - - print("namespace ").print(classdecl.getSimpleName().toString()).print(" {").startIndent(); - } - println().println().printIndent().print(def); - if (def instanceof JCVariableDecl) { - print(";"); - } - } - } - if (nameSpace) { - println().endIndent().printIndent().print("}").println(); - } - } - - if (mainMethod != null && mainMethod.getParameters().size() < 2 - && mainMethod.sym.getEnclosingElement().equals(classdecl.sym)) { - String mainClassName = getQualifiedTypeName(classdecl.sym, globals); - String mainMethodQualifier = mainClassName; - if (!isBlank(mainClassName)) { - mainMethodQualifier = mainClassName + "."; - } - context.entryFiles.add(new File(compilationUnit.sourcefile.getName())); - context.addFooterStatement(mainMethodQualifier + JSweetConfig.MAIN_FUNCTION_NAME + "(" - + (mainMethod.getParameters().isEmpty() ? "" : "null") + ");"); - } - - getAdapter().typeVariablesToErase.removeAll(parentTypeVars); - exitScope(); - - getAdapter().afterType(classdecl.sym); - - } - - private void printDefaultImplementation(MethodSymbol method) { - printIndent().print("public abstract ").print(method.getSimpleName().toString()); - print("("); - if (method.getParameters() != null && !method.getParameters().isEmpty()) { - for (VarSymbol var : method.getParameters()) { - print(var.name.toString()).print(": any"); - print(", "); - } - removeLastChars(2); - } - print(")"); - print(": any;").println(); - } - - private String getTSMethodName(JCMethodDecl methodDecl) { - String name = context.getActualName(methodDecl.sym); - switch (name) { - case "": - return "constructor"; - case JSweetConfig.ANONYMOUS_FUNCTION_NAME: - case JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME: - return ""; - case JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME: - case JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME: - if (context.deprecatedApply) { - return ""; - } else { - return name; - } - case JSweetConfig.NEW_FUNCTION_NAME: - return "new"; - default: - return name; - } - } - - private boolean printCoreMethodDelegate = false; - - protected boolean isDebugMode(JCMethodDecl methodDecl) { - return methodDecl != null && !constructor && context.options.isDebugMode() - && !(context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_NO_DEBUG) || context - .hasAnnotationType(methodDecl.sym.getEnclosingElement(), JSweetConfig.ANNOTATION_NO_DEBUG)); - } - - boolean constructor = false; - - @Override - public void visitMethodDef(JCMethodDecl methodDecl) { - - if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { - // erased elements are ignored - return; - } - JCClassDecl parent = (JCClassDecl) getParent(); - - if (parent != null && methodDecl.pos == parent.pos && !getScope().enumWrapperClassScope) { - return; - } - - if (JSweetConfig.INDEXED_GET_FUCTION_NAME.equals(methodDecl.getName().toString()) - && methodDecl.getParameters().size() == 1) { - print("[").print(methodDecl.getParameters().head).print("]: "); - substituteAndPrintType(methodDecl.restype).print(";"); - return; - } - - constructor = methodDecl.sym.isConstructor(); - if (getScope().enumScope) { - if (constructor) { - if (parent != null && parent.pos != methodDecl.pos) { - getScope().isComplexEnum = true; - } - } else { - getScope().isComplexEnum = true; - } - return; - } - - Overload overload = null; - boolean inOverload = false; - boolean inCoreWrongOverload = false; - if (parent != null) { - overload = context.getOverload(parent.sym, methodDecl.sym); - inOverload = overload != null && overload.methods.size() > 1; - if (inOverload) { - if (!overload.isValid) { - if (!printCoreMethodDelegate) { - if (overload.coreMethod.equals(methodDecl)) { - inCoreWrongOverload = true; - if (!context.isInterface(parent.sym) && !methodDecl.sym.isConstructor() - && parent.sym.equals(overload.coreMethod.sym.getEnclosingElement())) { - printCoreMethodDelegate = true; - visitMethodDef(overload.coreMethod); - println().println().printIndent(); - printCoreMethodDelegate = false; - } - } else { - if (methodDecl.sym.isConstructor()) { - return; - } - boolean addCoreMethod = false; - addCoreMethod = !overload.printed - && overload.coreMethod.sym.getEnclosingElement() != parent.sym - && (!overload.coreMethod.sym.getModifiers().contains(Modifier.ABSTRACT) - || context.isInterface(parent.sym) - || !context.types.isSubtype(parent.sym.type, - overload.coreMethod.sym.getEnclosingElement().type)); - if (!overload.printed && !addCoreMethod && overload.coreMethod.type instanceof MethodType) { - addCoreMethod = Util.findMethodDeclarationInType(context.types, parent.sym, - methodDecl.getName().toString(), (MethodType) overload.coreMethod.type) == null; - } - if (addCoreMethod) { - visitMethodDef(overload.coreMethod); - overload.printed = true; - if (!context.isInterface(parent.sym)) { - println().println().printIndent(); - } - } - if (context.isInterface(parent.sym)) { - return; - } - } - } - } else { - if (!overload.coreMethod.equals(methodDecl)) { - return; - } - } - } - } - - boolean ambient = context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); - - if (inOverload && !inCoreWrongOverload && (ambient || isDefinitionScope)) { - // do not generate method stubs for definitions - return; - } - - if (isDebugMode(methodDecl)) { - printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); - print(getTSMethodName(methodDecl)).print("("); - printArgList(null, methodDecl.params); - print(") : "); - substituteAndPrintType(methodDecl.getReturnType()); - print(" {").println(); - startIndent().printIndent(); - if (!context.types.isSameType(context.symtab.voidType, methodDecl.sym.getReturnType())) { - print("return "); - } - print("__debug_exec('" + parent.sym.getQualifiedName() + "', '" + methodDecl.getName() + "', "); - if (!methodDecl.params.isEmpty()) { - print("["); - for (JCVariableDecl param : methodDecl.params) { - print("'" + param.getName() + "', "); - } - removeLastChars(2); - print("]"); - } else { - print("undefined"); - } - print(", this, arguments, "); - if (methodDecl.sym.isStatic()) { - print(methodDecl.sym.getEnclosingElement().getSimpleName().toString()); - } else { - print("this"); - } - print("." + GENERATOR_PREFIX + getTSMethodName(methodDecl) + "("); - for (JCVariableDecl param : methodDecl.params) { - print(context.getActualName(param.sym) + ", "); - } - if (!methodDecl.params.isEmpty()) { - removeLastChars(2); - } - print("));"); - println().endIndent().printIndent(); - print("}").println().println().printIndent(); - } - - int jsniLine = -1; - String[] content = null; - - print(methodDecl.mods); - - if (methodDecl.mods.getFlags().contains(Modifier.NATIVE)) { - if (!getScope().declareClassScope && !ambient && !getScope().interfaceScope) { - content = getGetSource(getCompilationUnit()); - if (content != null) { - int line = 0; - if (methodDecl.getParameters() != null && !methodDecl.getParameters().isEmpty()) { - line = diagnosticSource.getLineNumber(methodDecl.getParameters().last().getStartPosition()) - 1; - } else { - line = diagnosticSource.getLineNumber(methodDecl.getStartPosition()) - 1; - } - if (content[line].contains("/*-{")) { - jsniLine = line; - } else { - if (content[line + 1].contains("/*-{")) { - jsniLine = line + 1; - } - } - } - if (jsniLine == -1) { - report(methodDecl, methodDecl.name, JSweetProblem.NATIVE_MODIFIER_IS_NOT_ALLOWED, methodDecl.name); - } - } - } else { - if (getScope().declareClassScope && !constructor && !getScope().interfaceScope - && !methodDecl.mods.getFlags().contains(Modifier.DEFAULT)) { - report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, - parent == null ? "" : parent.name); - } - } - - if (methodDecl.name.toString().equals("constructor")) { - report(methodDecl, methodDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); - } - if (parent != null) { - VarSymbol v = Util.findFieldDeclaration(parent.sym, methodDecl.name); - if (v != null && context.getFieldNameMapping(v) == null) { - if (isDefinitionScope) { - return; - } else { - report(methodDecl, methodDecl.name, JSweetProblem.METHOD_CONFLICTS_FIELD, methodDecl.name, v.owner); - } - } - } - if (JSweetConfig.MAIN_FUNCTION_NAME.equals(methodDecl.name.toString()) - && methodDecl.mods.getFlags().contains(Modifier.STATIC) - && !context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_DISABLED)) { - // ignore main methods in inner classes - if (scope.size() == 1) { - mainMethod = methodDecl; - } - } - - boolean globals = parent == null ? false : JSweetConfig.GLOBALS_CLASS_NAME.equals(parent.name.toString()); - globals = globals || (getScope().interfaceScope && methodDecl.mods.getFlags().contains(Modifier.STATIC)); - printDocComment(methodDecl, false); - if (parent == null) { - print("function "); - } else if (globals) { - if (constructor && methodDecl.sym.isPrivate() && methodDecl.getParameters().isEmpty()) { - return; - } - if (constructor) { - report(methodDecl, methodDecl.name, JSweetProblem.GLOBAL_CONSTRUCTOR_DEF); - return; - } - - if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { - report(methodDecl, methodDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); - return; - } - - if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { - getContext().addExportedElement( - context.getAnnotationValue(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), - methodDecl.sym, getCompilationUnit()); - } - - if (context.useModules) { - if (!methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { - print("export "); - } - } else { - if (!isTopLevelScope()) { - print("export "); - } - } - if (ambient || (getIndent() == 0 && isDefinitionScope)) { - print("declare "); - } - print("function "); - } else { - printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); - if (ambient) { - report(methodDecl, methodDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, methodDecl.name); - } - } - if (parent == null || !context.isFunctionalType(parent.sym)) { - if (isDebugMode(methodDecl)) { - print("*").print(GENERATOR_PREFIX); - } - if (inOverload && !overload.isValid && !inCoreWrongOverload) { - print(getOverloadMethodName(methodDecl.sym)); - } else { - String tsMethodName = getTSMethodName(methodDecl); - if (doesMemberNameRequireQuotes(tsMethodName)) { - print("'" + tsMethodName + "'"); - } else { - print(tsMethodName); - } - } - } - if ((methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) - || (getContext().getWildcards(methodDecl.sym) != null)) { - inTypeParameters = true; - print("<"); - if (methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) { - printArgList(null, methodDecl.typarams); - if (getContext().getWildcards(methodDecl.sym) != null) { - print(", "); - } - } - if (getContext().getWildcards(methodDecl.sym) != null) { - printArgList(null, getContext().getWildcards(methodDecl.sym), this::substituteAndPrintType); - } - print(">"); - inTypeParameters = false; - } - print("("); - if (inCoreWrongOverload) { - getScope().eraseVariableTypes = true; - } - boolean paramPrinted = false; - if (getScope().innerClassNotStatic && methodDecl.sym.isConstructor()) { - print(PARENT_CLASS_FIELD_NAME + ": any, "); - paramPrinted = true; - } - if (constructor && getScope().enumWrapperClassScope) { - print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_ORDINAL + " : number, "); - print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_NAME + " : string"); - if (!methodDecl.getParameters().isEmpty()) { - print(", "); - } - } - int i = 0; - for (JCVariableDecl param : methodDecl.getParameters()) { - print(param); - if (inOverload && overload.isValid && overload.defaultValues.get(i) != null) { - print(" = ").print(overload.defaultValues.get(i)); - } - print(", "); - i++; - paramPrinted = true; - } - if (inCoreWrongOverload) { - getScope().eraseVariableTypes = false; - } - if (paramPrinted) { - removeLastChars(2); - } - print(")"); - if (inCoreWrongOverload && !methodDecl.sym.isConstructor()) { - print(" : any"); - } else { - if (methodDecl.restype != null && methodDecl.restype.type.getTag() != TypeTag.VOID) { - print(" : "); - substituteAndPrintType(methodDecl.restype); - } - } - if (inCoreWrongOverload && context.isInterface(parent.sym)) { - print(";"); - return; - } - if (methodDecl.getBody() == null && !(inCoreWrongOverload && !getScope().declareClassScope) - || (methodDecl.mods.getFlags().contains(Modifier.DEFAULT) && !getScope().defaultMethodScope)) { - if (!getScope().interfaceScope && methodDecl.getModifiers().getFlags().contains(Modifier.ABSTRACT) - && inOverload && !overload.isValid) { - print(" {"); - // runtime error if we go there... - print(" throw new Error('cannot invoke abstract overloaded method... check your argument(s) type(s)'); "); - print("}"); - } else if (jsniLine != -1) { - int line = jsniLine; - print(" {").println().startIndent(); - String jsniCode = content[line].substring(content[line].indexOf("/*-{") + 4).trim(); - StringBuilder jsni = new StringBuilder(); - if (!StringUtils.isEmpty(jsniCode)) { - jsni.append(jsniCode); - jsni.append("\n"); - } - line++; - while (!content[line].contains("}-*/")) { - jsniCode = content[line++].trim(); - jsni.append(jsniCode); - jsni.append("\n"); - } - jsniCode = content[line].substring(0, content[line].indexOf("}-*/")).trim(); - if (!StringUtils.isEmpty(jsniCode)) { - jsni.append(jsniCode); - jsni.append("\n"); - } - if (!StringUtils.isEmpty(jsni)) { - jsni.deleteCharAt(jsni.length() - 1); - } - String mergedCode = parseJSNI(jsni.toString()); - for (String s : mergedCode.split("\\n")) { - printIndent().print(s).println(); - } - endIndent().printIndent().print("}"); - } else { - print(";"); - } - } else { - if (getScope().interfaceScope) { - if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { - report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, - parent == null ? "" : parent.name); - } - } - if (getScope().declareClassScope) { - if (!constructor || (methodDecl.getBody() != null && methodDecl.getBody().getStatements().isEmpty())) { - report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, - parent == null ? "" : parent.name); - } - print(";"); - } else { - if (inCoreWrongOverload) { - print(" {").println().startIndent().printIndent(); - - boolean wasPrinted = false; - for (i = 0; i < overload.methods.size(); i++) { - JCMethodDecl method = overload.methods.get(i); - if (context.isInterface((ClassSymbol) method.sym.getEnclosingElement()) - && !method.getModifiers().getFlags().contains(Modifier.DEFAULT)) { - continue; - } - if (!Util.isParent(parent.sym, (ClassSymbol) method.sym.getEnclosingElement())) { - continue; - } - if (wasPrinted) { - print(" else "); - } - wasPrinted = true; - print("if("); - printMethodParamsTest(overload, method); - print(") "); - if (method.sym.isConstructor()) { - printInlinedMethod(overload, method, methodDecl.getParameters()); - } else { - if (parent.sym != method.sym.getEnclosingElement() - && context.getOverload((ClassSymbol) method.sym.getEnclosingElement(), - method.sym).coreMethod == method) { - print("{").println().startIndent().printIndent(); - - String tsMethodAccess = getTSMemberAccess(getTSMethodName(methodDecl), true); - print("super" + tsMethodAccess); - print("("); - for (int j = 0; j < method.getParameters().size(); j++) { - print(avoidJSKeyword(overload.coreMethod.getParameters().get(j).name.toString())) - .print(", "); - } - if (!method.getParameters().isEmpty()) { - removeLastChars(2); - } - print(");"); - println().endIndent().printIndent().print("}"); - } else { - print("{").println().startIndent().printIndent(); - // temporary cast to any because of Java - // generics - // bug - print("return "); - if (method.sym.isStatic()) { - print(getQualifiedTypeName(parent.sym, false).toString()); - } else { - print("this"); - } - print(".").print(getOverloadMethodName(method.sym)).print("("); - for (int j = 0; j < method.getParameters().size(); j++) { - print(avoidJSKeyword(overload.coreMethod.getParameters().get(j).name.toString())) - .print(", "); - } - if (!method.getParameters().isEmpty()) { - removeLastChars(2); - } - print(");"); - println().endIndent().printIndent().print("}"); - } - } - } - print(" else throw new Error('invalid overload');"); - endIndent().println().printIndent().print("}"); - } else { - print(" ").print("{").println().startIndent(); - - String replacedBody = null; - if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_REPLACE)) { - replacedBody = (String) context.getAnnotationValue(methodDecl.sym, - JSweetConfig.ANNOTATION_REPLACE, null); - } - int position = getCurrentPosition(); - if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { - enter(methodDecl.getBody()); - if (!methodDecl.getBody().stats.isEmpty() - && methodDecl.getBody().stats.head.toString().startsWith("super(")) { - printBlockStatement(methodDecl.getBody().stats.head); - if (parent != null) { - printInstanceInitialization(parent, methodDecl.sym); - } - printBlockStatements(methodDecl.getBody().stats.tail); - } else { - if (parent != null) { - printInstanceInitialization(parent, methodDecl.sym); - } - printBlockStatements(methodDecl.getBody().stats); - } - exit(); - if (replacedBody != null) { - String orgBody = getOutput().substring(position); - removeLastChars(getCurrentPosition() - position); - replacedBody = replacedBody.replace(BODY_MARKER, orgBody) - .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) - .replace(METHOD_NAME_MARKER, methodDecl.getName().toString()) - .replace(CLASS_NAME_MARKER, parent.sym.getQualifiedName().toString()); - } - } - if (replacedBody != null) { - printIndent().print(replacedBody).println(); - } - endIndent().printIndent().print("}"); - } - } - } - } - - protected void printMethodModifiers(JCMethodDecl methodDecl, JCClassDecl parent, boolean constructor, - boolean inOverload, Overload overload) { - if (methodDecl.mods.getFlags().contains(Modifier.PUBLIC) - || (inOverload && overload.coreMethod.equals(methodDecl))) { - if (!getScope().interfaceScope) { - print("public "); - } - } - if (methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { - if (!constructor) { - if (!getScope().innerClass) { - if (!getScope().interfaceScope) { - if (!(inOverload && overload.coreMethod.equals(methodDecl) || getScope().hasInnerClass)) { - print("private "); - } - } else { - if (!(inOverload && overload.coreMethod.equals(methodDecl))) { - report(methodDecl, methodDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, - methodDecl.name, parent == null ? "" : parent.name); - } - } - } - } - } - if (methodDecl.mods.getFlags().contains(Modifier.STATIC)) { - if (!getScope().interfaceScope) { - print("static "); - } - } - if (methodDecl.mods.getFlags().contains(Modifier.ABSTRACT)) { - if (!getScope().interfaceScope && !inOverload) { - print("abstract "); - } - } - } - - protected void printVariableInitialization(JCClassDecl clazz, JCVariableDecl var) { - String name = var.getName().toString(); - if (context.getFieldNameMapping(var.sym) != null) { - name = context.getFieldNameMapping(var.sym); - } else { - name = getIdentifier(var.sym); - } - if (getScope().innerClassNotStatic && !Util.isConstantOrNullField(var)) { - if (doesMemberNameRequireQuotes(name)) { - printIndent().print("this['").print(name).print("'] = "); - } else { - printIndent().print("this.").print(name).print(" = "); - } - substituteAndPrintAssignedExpression(var.type, var.init); - print(";").println(); - } else if (var.init == null) { - if (doesMemberNameRequireQuotes(name)) { - printIndent().print("this['").print(name).print("'] = ").print(Util.getTypeInitialValue(var.type)) - .print(";").println(); - } else { - printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(var.type)) - .print(";").println(); - } - } - } - - protected void printInstanceInitialization(JCClassDecl clazz, MethodSymbol method) { - if (getContext().options.isInterfaceTracking() && method == null || method.isConstructor()) { - getScope().hasDeclaredConstructor = true; - if (getScope().innerClassNotStatic) { - printIndent().print("this." + PARENT_CLASS_FIELD_NAME + " = " + PARENT_CLASS_FIELD_NAME + ";") - .println(); - } - for (JCTree member : clazz.defs) { - if (member instanceof JCVariableDecl) { - JCVariableDecl var = (JCVariableDecl) member; - if (!var.sym.isStatic() && !context.hasAnnotationType(var.sym, JSweetConfig.ANNOTATION_ERASED)) { - printVariableInitialization(clazz, var); - } - } else if (member instanceof JCBlock) { - JCBlock block = (JCBlock) member; - if (!block.isStatic()) { - printIndent().print("(() => {").startIndent().println(); - printBlockStatements(block.stats); - endIndent().printIndent().print("})();").println(); - } - } - } - } - } - - private String parseJSNI(String jsniCode) { - return jsniCode.replaceAll("@[^:]*::[\\n]?([a-zA-Z_$][a-zA-Z\\d_$]*)[\\n]?\\([^)]*\\)", "$1") - .replaceAll("@[^:]*::\\n?([a-zA-Z_$][a-zA-Z\\d_$]*)", "$1"); - } - - private void printInlinedMethod(Overload overload, JCMethodDecl method, List args) { - print("{").println().startIndent(); - printIndent().print(VAR_DECL_KEYWORD + " __args = Array.prototype.slice.call(arguments);").println(); - for (int j = 0; j < method.getParameters().size(); j++) { - if (args.get(j) instanceof JCVariableDecl) { - if (method.getParameters().get(j).name.equals(((JCVariableDecl) args.get(j)).name)) { - continue; - } else { - printIndent().print(VAR_DECL_KEYWORD + " ") - .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") - .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "").print(" = ") - .print("__args[" + j + "]").print(";").println(); - } - } else { - if (method.getParameters().get(j).name.toString().equals(args.get(j).toString())) { - continue; - } else { - getScope().inlinedConstructorArgs = method.getParameters().stream().map(p -> p.sym.name.toString()) - .collect(Collectors.toList()); - printIndent().print(VAR_DECL_KEYWORD + " ") - .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") - .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "").print(" = ") - .print(args.get(j)).print(";").println(); - getScope().inlinedConstructorArgs = null; - } - } - } - if (method.getBody() != null) { - boolean skipFirst = false; - boolean initialized = false; - if (!method.getBody().stats.isEmpty() && method.getBody().stats.get(0).toString().startsWith("this(")) { - skipFirst = true; - JCMethodInvocation inv = (JCMethodInvocation) ((JCExpressionStatement) method.getBody().stats - .get(0)).expr; - MethodSymbol ms = Util.findMethodDeclarationInType(context.types, - (TypeSymbol) overload.coreMethod.sym.getEnclosingElement(), inv); - for (JCMethodDecl md : overload.methods) { - if (md.sym.equals(ms)) { - printIndent(); - initialized = true; - printInlinedMethod(overload, md, inv.args); - println(); - } - } - - } - String replacedBody = null; - if (context.hasAnnotationType(method.sym, JSweetConfig.ANNOTATION_REPLACE)) { - replacedBody = (String) context.getAnnotationValue(method.sym, JSweetConfig.ANNOTATION_REPLACE, null); - } - int position = getCurrentPosition(); - if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { - enter(method.getBody()); - com.sun.tools.javac.util.List stats = skipFirst ? method.getBody().stats.tail - : method.getBody().stats; - if (!stats.isEmpty() && stats.head.toString().startsWith("super(")) { - printBlockStatement(stats.head); - printFieldInitializations(); - if (!initialized) { - printInstanceInitialization(getParent(JCClassDecl.class), method.sym); - } - if (!stats.tail.isEmpty()) { - printIndent().print("((").print(") => {").startIndent().println(); - printBlockStatements(stats.tail); - endIndent().printIndent().print("})(").print(");").println(); - } - } else { - if (!initialized) { - printInstanceInitialization(getParent(JCClassDecl.class), method.sym); - } - if (!stats.isEmpty() || !method.sym.isConstructor()) { - printIndent(); - } - if (!method.sym.isConstructor()) { - print("return "); - } - if (!stats.isEmpty() || !method.sym.isConstructor()) { - print("((").print(") => {").startIndent().println(); - printBlockStatements(stats); - endIndent().printIndent().print("})(").print(");").println(); - } - } - exit(); - if (replacedBody != null) { - getIndent(); - printIndent(); - String orgBody = getOutput().substring(position); - removeLastChars(getCurrentPosition() - position); - replacedBody = replacedBody.replace(BODY_MARKER, orgBody) - .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) - .replace(METHOD_NAME_MARKER, method.getName().toString()) - .replace(CLASS_NAME_MARKER, method.sym.getEnclosingElement().getQualifiedName().toString()); - } - } - if (replacedBody != null) { - printIndent().print(replacedBody).println(); - } - } else { - String returnValue = Util.getTypeInitialValue(method.sym.getReturnType()); - if (returnValue != null) { - print(" return ").print(returnValue).print("; "); - } - } - endIndent().printIndent().print("}"); - } - - private void printFieldInitializations() { - JCClassDecl clazz = getParent(JCClassDecl.class); - for (JCTree t : clazz.getMembers()) { - if (t instanceof JCVariableDecl && !getScope().fieldsWithInitializers.contains(t)) { - JCVariableDecl field = (JCVariableDecl) t; - if (!field.sym.isStatic() && !context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { - String name = getIdentifier(field.sym); - if (context.getFieldNameMapping(field.sym) != null) { - name = context.getFieldNameMapping(field.sym); - } - printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(field.type)) - .print(";").println(); - } - } - } - for (JCVariableDecl field : getScope().fieldsWithInitializers) { - if (context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { - continue; - } - String name = getIdentifier(field.sym); - if (context.getFieldNameMapping(field.sym) != null) { - name = context.getFieldNameMapping(field.sym); - } - printIndent().print("this.").print(name).print(" = "); - if (!substituteAssignedExpression(field.type, field.init)) { - print(field.init); - } - print(";").println(); - } - } - - protected void printBlockStatements(List statements) { - for (JCStatement statement : statements) { - if (context.options.isDebugMode()) { - JCMethodDecl methodDecl = getParent(JCMethodDecl.class); - if (isDebugMode(methodDecl)) { - int s = statement.getStartPosition(); - int e = statement.getEndPosition(diagnosticSource.getEndPosTable()); - if (e == -1) { - e = s; - } - printIndent().print("yield { row: ").print("" + diagnosticSource.getLineNumber(s)) - .print(", column: " + diagnosticSource.getColumnNumber(s, false)).print(", statement: \""); - print(StringEscapeUtils.escapeJson(statement.toString())).print("\""); - - final Stack> locals = new Stack<>(); - try { - new TreeScanner() { - public void scan(JCTree tree) { - if (tree == statement) { - throw new RuntimeException(); - } - boolean contextChange = false; - if (tree instanceof JCBlock || tree instanceof JCEnhancedForLoop - || tree instanceof JCLambda || tree instanceof JCForLoop - || tree instanceof JCDoWhileLoop) { - locals.push(new ArrayList<>()); - contextChange = true; - } - if (tree instanceof JCVariableDecl) { - locals.peek().add(((JCVariableDecl) tree).name.toString()); - } - super.scan(tree); - if (contextChange) { - locals.pop(); - } - } - - }.scan(methodDecl.body); - } catch (Exception end) { - // swallow - } - List accessibleLocals = new ArrayList<>(); - for (List l : locals) { - accessibleLocals.addAll(l); - } - if (!accessibleLocals.isEmpty()) { - print(", locals: "); - print("{"); - for (String local : accessibleLocals) { - print("" + local + ": " + local + ", "); - } - removeLastChars(2); - print("}"); - } - print(" };").println(); - } - } - printBlockStatement(statement); - } - } - - private void printBlockStatement(JCStatement statement) { - printIndent(); - int pos = getCurrentPosition(); - print(statement); - if (getCurrentPosition() == pos) { - removeLastIndent(); - return; - } - if (!statementsWithNoSemis.contains(statement.getClass())) { - if (statement instanceof JCLabeledStatement) { - if (!statementsWithNoSemis.contains(((JCLabeledStatement) statement).body.getClass())) { - print(";"); - } - } else { - print(";"); - } - } - println(); - } - - private String getOverloadMethodName(MethodSymbol method) { - if (method.isConstructor()) { - return "constructor"; - } - StringBuilder sb = new StringBuilder(method.getSimpleName().toString()); - sb.append("$"); - for (VarSymbol p : method.getParameters()) { - sb.append(context.types.erasure(p.type).toString().replace('.', '_').replace("[]", "_A")); - sb.append("$"); - } - if (!method.getParameters().isEmpty()) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - private void printMethodParamsTest(Overload overload, JCMethodDecl m) { - int i = 0; - for (; i < m.getParameters().size(); i++) { print("("); - printInstanceOf(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()), null, - m.getParameters().get(i).type); - print(" || ") - .print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()) + " === null") - .print(")"); - print(" && "); - } - for (; i < overload.coreMethod.getParameters().size(); i++) { - print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString())).print(" === undefined"); - print(" && "); - } - removeLastChars(4); - } - - @Override - public void visitBlock(JCBlock block) { - JCTree parent = getParent(); - boolean globals = (parent instanceof JCClassDecl) - && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); - boolean initializer = (parent instanceof JCClassDecl) && !globals; - int static_i = 0; - if (initializer) { - if (getScope().interfaceScope) { - report(block, JSweetProblem.INVALID_INITIALIZER_IN_INTERFACE, ((JCClassDecl) parent).name); - } - if (!block.isStatic()) { - // non-static blocks are initialized in the constructor - return; - } - for (JCTree m : ((JCClassDecl) parent).getMembers()) { - if (m instanceof JCBlock) { - if (((JCBlock) m).isStatic()) { - if (block == m) { - print("static __static_initializer_" + static_i + "() "); - break; - } - static_i++; - } - } - } - } - if (!globals) { - print("{").println().startIndent(); - } - - printBlockStatements(block.stats); - - if (!globals) { - endIndent().printIndent().print("}"); - } - } - - private String avoidJSKeyword(String name) { - if (JSweetConfig.JS_KEYWORDS.contains(name)) { - name = JSweetConfig.JS_KEYWORD_PREFIX + name; - } - return name; - } - - @Override - public void visitVarDef(JCVariableDecl varDecl) { - if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { - // erased elements are ignored - return; - } - if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_STRING_TYPE)) { - // string type fields are ignored - return; - } - - if (getScope().enumScope) { - print(varDecl.name.toString()); - if (varDecl.init instanceof JCNewClass) { - JCNewClass newClass = (JCNewClass) varDecl.init; - if (newClass.def != null) { - initAnonymousClass(newClass); - } - } - } else { - JCTree parent = getParent(); - - if (getScope().enumWrapperClassScope && varDecl.type.equals(parent.type)) { - return; - } - - String name = getIdentifier(varDecl.sym); - if (context.getFieldNameMapping(varDecl.sym) != null) { - name = context.getFieldNameMapping(varDecl.sym); - } - - boolean confictInDefinitionScope = false; - - if (parent instanceof JCClassDecl) { - MethodSymbol m = Util.findMethodDeclarationInType(context.types, ((JCClassDecl) parent).sym, name, - null); - if (m != null) { - if (!isDefinitionScope) { - report(varDecl, varDecl.name, JSweetProblem.FIELD_CONFLICTS_METHOD, name, m.owner); - } else { - confictInDefinitionScope = true; - } - } - if (!getScope().interfaceScope && name.equals("constructor")) { - report(varDecl, varDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); - } + if (typeName.startsWith("Int") || typeName.startsWith("Long") + || typeName.startsWith("Double")) { + print("p0 : number"); } else { - if (context.bundleMode) { - if (context.importedTopPackages.contains(name)) { - name = "__var_" + name; - } - } - if (JSweetConfig.JS_KEYWORDS.contains(name)) { - report(varDecl, varDecl.name, JSweetProblem.JS_KEYWORD_CONFLICT, name, name); - name = JSweetConfig.JS_KEYWORD_PREFIX + name; - } + printArguments(typeApply.arguments.subList(0, typeApply.arguments.length() - 1)); } - - boolean globals = (parent instanceof JCClassDecl) - && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); - - if (globals && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { - report(varDecl, varDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); - return; + print(") => "); + substituteAndPrintType(typeApply.arguments.get(typeApply.arguments.length() - 1), + arrayComponent, inTypeParameters, completeRawTypes, false); + if (arrayComponent) { + print(")"); } - - globals = globals || (parent instanceof JCClassDecl && (((JCClassDecl) parent).sym.isInterface() - || getScope().interfaceScope && varDecl.sym.isStatic())); - - if (parent instanceof JCClassDecl) { - printDocComment(varDecl, false); + return this; + } else if (typeName.endsWith("Supplier") || typeName.startsWith("Supplier")) { + if (arrayComponent) { + print("("); } - - print(varDecl.mods); - - if (!globals && parent instanceof JCClassDecl) { - if (varDecl.mods.getFlags().contains(Modifier.PUBLIC)) { - if (!getScope().interfaceScope) { - print("public "); - } - } - if (varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { - if (!getScope().interfaceScope) { - if (!getScope().innerClass && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { - // cannot keep private fields because they may be - // accessed in an inner class - print("/*private*/ "); - } - } else { - report(varDecl, varDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, varDecl.name, - ((JCClassDecl) parent).name); - } - } - - if (varDecl.mods.getFlags().contains(Modifier.STATIC)) { - if (!getScope().interfaceScope) { - print("static "); - } - } - } - if (!getScope().interfaceScope && parent instanceof JCClassDecl) { - if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)) { - report(varDecl, varDecl.name, JSweetProblem.USELESS_OPTIONAL_ANNOTATION, varDecl.name, - ((JCClassDecl) parent).name); - } - } - boolean ambient = context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); - if (globals || !(parent instanceof JCClassDecl || parent instanceof JCMethodDecl - || parent instanceof JCLambda)) { - if (globals) { - if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { - getContext().addExportedElement( - context.getAnnotationValue(varDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), - varDecl.sym, getCompilationUnit()); - } - if (context.useModules) { - if (!varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { - print("export "); - } - } else { - if (!isTopLevelScope()) { - print("export "); - } - } - if (ambient || (isTopLevelScope() && isDefinitionScope)) { - print("declare "); - } - } - if (!(inArgListTail && (parent instanceof JCForLoop))) { - if (isDefinitionScope) { - print("var "); - } else { - print(VAR_DECL_KEYWORD + " "); - } - } - } else { - if (ambient) { - report(varDecl, varDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, varDecl.name); - } - } - - if (Util.isVarargs(varDecl)) { - print("..."); - } - - if (doesMemberNameRequireQuotes(name)) { - print("'" + name + "'"); - } else { - print(name); - } - - if (!Util.isVarargs(varDecl) && (getScope().eraseVariableTypes || (getScope().interfaceScope - && context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)))) { - print("?"); - } - if (!getScope().skipTypeAnnotations && !getScope().enumWrapperClassScope) { - if (typeChecker.checkType(varDecl, varDecl.name, varDecl.vartype)) { - print(" : "); - if (confictInDefinitionScope) { - print("any"); - } else { - if (getScope().eraseVariableTypes) { - print("any"); - if (Util.isVarargs(varDecl)) { - print("[]"); - } - } else { - if (context.hasAnnotationType(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE)) { - print("\""); - print(context.getAnnotationValue(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE, - varDecl.vartype.type.tsym.name.toString()).toString()); - print("\""); - } else { - substituteAndPrintType(varDecl.vartype); - } - } - } - } - } - if (context.lazyInitializedStatics.contains(varDecl.sym) && !getScope().enumWrapperClassScope) { - JCClassDecl clazz = (JCClassDecl) parent; - String prefix = clazz.getSimpleName().toString(); - if (GLOBALS_CLASS_NAME.equals(prefix)) { - prefix = ""; - } else { - prefix += "."; - } - print("; "); - if (globals) { - if (!isTopLevelScope()) { - print("export "); - } - print("function "); - } else { - print("public static "); - } - print(name).print(STATIC_INITIALIZATION_SUFFIX + "() : "); - substituteAndPrintType(varDecl.vartype); - print(" { "); - int liCount = context.getStaticInitializerCount(clazz.sym); - if (liCount > 0) { - if (!globals) { - print(prefix + "__static_initialize(); "); - } - } - if (varDecl.init != null && !isDefinitionScope) { - print("if(" + prefix).print(name).print(" == null) ").print(prefix).print(name).print(" = "); - if (getScope().enumWrapperClassScope) { - JCNewClass newClass = (JCNewClass) varDecl.init; - print("new ").print(clazz.getSimpleName().toString()).print("(") - .printArgList(null, newClass.args).print(")"); - } else { - if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { - print(varDecl.init); - } - } - print("; "); - } - print("return ").print(prefix).print(name).print("; }"); - if (!globals) { - String qualifiedClassName = getQualifiedTypeName(clazz.sym, globals); - context.addTopFooterStatement((isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".") + name - + STATIC_INITIALIZATION_SUFFIX + "();"); - } - } else { - if (varDecl.init != null && !isDefinitionScope) { - if (!(parent instanceof JCClassDecl && getScope().innerClassNotStatic && !varDecl.sym.isStatic() - && !Util.isConstantOrNullField(varDecl))) { - if (!globals && parent instanceof JCClassDecl && getScope().interfaceScope) { - report(varDecl, varDecl.name, JSweetProblem.INVALID_FIELD_INITIALIZER_IN_INTERFACE, - varDecl.name, ((JCClassDecl) parent).name); - } else { - if (!(getScope().hasConstructorOverloadWithSuperClass - && getScope().fieldsWithInitializers.contains(varDecl))) { - print(" = "); - if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { - print(varDecl.init); - } - } - } - } - } - - // var initialization is not allowed in definition - if (!isDefinitionScope && !(ambient || (isTopLevelScope() && isDefinitionScope)) - && varDecl.sym.isStatic() && varDecl.init == null) { - print(" = ").print(Util.getTypeInitialValue(varDecl.sym.type)); - } - } - } - } - - private String getTSMemberAccess(String memberName, boolean hasSelector) { - if (doesMemberNameRequireQuotes(memberName)) { - // TODO : hasSelector should not be false by now for member with - // special chars for now but we should handle node case (window - // isn't something) => replace with global context - return (hasSelector ? "" : "window") + "['" + memberName + "']"; - } else { - return (hasSelector ? "." : "") + memberName; - } - } - - private boolean doesMemberNameRequireQuotes(String name) { - for (char c : name.toCharArray()) { - if (TS_IDENTIFIER_FORBIDDEN_CHARS.contains(c)) { - return true; - } - } - return false; - } - - @Override - public void visitParens(JCParens parens) { - print("("); - super.visitParens(parens); - print(")"); - } - - @Override - public void visitImport(JCImport importDecl) { - String qualId = importDecl.getQualifiedIdentifier().toString(); - if (qualId.endsWith("*") && !(qualId.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME + ".*") - || qualId.equals(JSweetConfig.UTIL_CLASSNAME + ".*"))) { - report(importDecl, JSweetProblem.WILDCARD_IMPORT); - return; - } - String adaptedQualId = getAdapter().needsImport(new ImportElementSupport(importDecl), qualId); - if (adaptedQualId != null && adaptedQualId.contains(".")) { - if (importDecl.isStatic() && !qualId.contains("." + JSweetConfig.GLOBALS_CLASS_NAME + ".") - && !qualId.contains("." + JSweetConfig.STRING_TYPES_INTERFACE_NAME + ".")) { - if (!context.bundleMode) { - print(VAR_DECL_KEYWORD + " ").print(qualId.substring(qualId.lastIndexOf('.') + 1)).print(": any = ") - .print(qualId).print(";"); - } - } else { - String[] namePath; - if (context.useModules && importDecl.isStatic()) { - namePath = qualId.split("\\."); - } else { - namePath = adaptedQualId.split("\\."); - } - String name = namePath[namePath.length - 1]; - if (context.useModules) { - if (!adaptedQualId.startsWith(GLOBALS_PACKAGE_NAME)) { - if (!context.getImportedNames(compilationUnit.getSourceFile().getName()).contains(name)) { - print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); - context.registerImportedName(compilationUnit.getSourceFile().getName(), null, name); - } - } - } else { - if (topLevelPackage == null) { - if (context.globalImports.contains(name)) { - // Tsc global package does allow multiple import - // with - // the same name in the global namespace (bug?) - return; - } - context.globalImports.add(name); - } - if (context.bundleMode) { - // in bundle mode, we do not use imports to minimize - // dependencies - // (imports create unavoidable dependencies!) - context.importedTopPackages.add(namePath[0]); - } else { - print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); - } - } - } - } - - } - - @Override - public void visitSelect(JCFieldAccess fieldAccess) { - if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(fieldAccess))) { - if (fieldAccess.selected.type.tsym instanceof PackageSymbol) { - if (context.isRootPackage(fieldAccess.selected.type.tsym)) { - if (fieldAccess.type != null && fieldAccess.type.tsym != null) { - printIdentifier(fieldAccess.type.tsym); - } else { - // TODO: see if it breaks something - print(fieldAccess.name.toString()); - } - return; - } - } - - if ("class".equals(fieldAccess.name.toString())) { - if (fieldAccess.type instanceof Type.ClassType - && context.isInterface(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) { - print("\"").print(context - .getRootRelativeJavaName(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) - .print("\""); - } else { - print(fieldAccess.selected); - } - } else if ("this".equals(fieldAccess.name.toString()) && getScope().innerClassNotStatic) { - print("this." + PARENT_CLASS_FIELD_NAME); - } else if ("this".equals(fieldAccess.name.toString())) { - print("this"); - } else { - String selected = fieldAccess.selected.toString(); - if (!selected.equals(GLOBALS_CLASS_NAME)) { - if (selected.equals("super") && (fieldAccess.sym instanceof VarSymbol)) { - print("this."); - } else { - boolean accessSubstituted = false; - if (fieldAccess.sym instanceof VarSymbol) { - VarSymbol varSym = (VarSymbol) fieldAccess.sym; - if (varSym.isStatic() && varSym.owner.isInterface() - && varSym.owner != Util.getSymbol(fieldAccess.selected)) { - accessSubstituted = true; - if (context.useModules) { - // TODO: we assume it has been imported, but - // it is clearly not always the case (to be - // tested) - print(varSym.owner.getSimpleName().toString()).print("."); - } else { - print(context.getRootRelativeName(null, varSym.owner)).print("."); - } - } - } - if (!accessSubstituted) { - print(fieldAccess.selected).print("."); - } - } - } - - String fieldName = null; - if (fieldAccess.sym instanceof VarSymbol && context.getFieldNameMapping(fieldAccess.sym) != null) { - fieldName = context.getFieldNameMapping(fieldAccess.sym); - } else { - fieldName = getIdentifier(fieldAccess.sym); - } - if (doesMemberNameRequireQuotes(fieldName)) { - if (getLastPrintedChar() == '.') { - removeLastChar(); - print("['").print(fieldName).print("']"); - } else { - print("this['").print(fieldName).print("']"); - } - } else { - print(fieldName); - } - if (fieldAccess.sym instanceof VarSymbol && !fieldAccess.sym.owner.isEnum() - && context.lazyInitializedStatics.contains(fieldAccess.sym)) { - if (!staticInitializedAssignment) { - print(STATIC_INITIALIZATION_SUFFIX + "()"); - } - } - } - } - } - - private JCImport getStaticGlobalImport(String methName) { - if (getCompilationUnit() == null) { - return null; - } - for (JCImport i : getCompilationUnit().getImports()) { - if (i.staticImport) { - if (i.qualid.toString().endsWith(JSweetConfig.GLOBALS_CLASS_NAME + "." + methName)) { - return i; - } - } - } - return null; - } - - private String getStaticContainerFullName(JCImport importDecl) { - if (importDecl.getQualifiedIdentifier() instanceof JCFieldAccess) { - JCFieldAccess fa = (JCFieldAccess) importDecl.getQualifiedIdentifier(); - String name = context.getRootRelativeJavaName(fa.selected.type.tsym); - // function is a top-level global function (no need to import) - if (JSweetConfig.GLOBALS_CLASS_NAME.equals(name)) { - return null; - } - boolean globals = name.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME); - if (globals) { - name = name.substring(0, name.length() - JSweetConfig.GLOBALS_CLASS_NAME.length() - 1); - } - // function belong to the current package (no need to import) - if (compilationUnit.packge.getQualifiedName().toString().startsWith(name)) { - return null; - } - return name; - } - - return null; - } - - private long applyTargetRefCounter = 0; - - @Override - public void visitApply(JCMethodInvocation inv) { - - boolean debugMode = false; - if (context.options.isDebugMode()) { - if (Util.getSymbol(inv.meth) instanceof MethodSymbol) { - MethodSymbol methodSymbol = (MethodSymbol) Util.getSymbol(inv.meth); - if (!methodSymbol.isConstructor() && Util.isSourceElement(methodSymbol)) { - debugMode = true; - } - } - } - if (debugMode) { - print("__debug_result(yield "); - } - - if (!getAdapter().substituteMethodInvocation(new MethodInvocationElementSupport(inv))) { - String meth = inv.meth.toString(); - String methName = meth.substring(meth.lastIndexOf('.') + 1); - if (methName.equals("super") && getScope().removedSuperclass) { - return; - } - - boolean applyVarargs = true; - if (JSweetConfig.NEW_FUNCTION_NAME.equals(methName)) { - print("new "); - applyVarargs = false; - } - - boolean anonymous = isAnonymousMethod(methName); - boolean targetIsThisOrStaticImported = meth.equals(methName) || meth.equals("this." + methName); - - MethodType type = inv.meth.type instanceof MethodType ? (MethodType) inv.meth.type : null; - MethodSymbol methSym = null; - String methodName = null; - boolean keywordHandled = false; - if (targetIsThisOrStaticImported) { - JCImport staticImport = getStaticGlobalImport(methName); - if (staticImport == null) { - JCClassDecl p = getParent(JCClassDecl.class); - methSym = p == null ? null : Util.findMethodDeclarationInType(context.types, p.sym, methName, type); - if (methSym != null) { - typeChecker.checkApply(inv, methSym); - if (!methSym.isStatic()) { - if (!meth.startsWith("this.")) { - print("this"); - if (!anonymous) { - print("."); - } - } - } else { - if (meth.startsWith("this.") && methSym.isStatic()) { - report(inv, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, methSym.getSimpleName()); - } - if (!JSweetConfig.GLOBALS_CLASS_NAME.equals(methSym.owner.getSimpleName().toString())) { - print("" + methSym.owner.getSimpleName()); - if (methSym.owner.isEnum()) { - print(ENUM_WRAPPER_CLASS_SUFFIX); - } - if (!anonymous) { - print("."); - } - } - } - } else { - if (getScope().defaultMethodScope) { - TypeSymbol target = Util.getStaticImportTarget( - getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), - methName); - if (target != null) { - print(getRootRelativeName(target) + "."); - } - } else { - TypeSymbol target = Util.getStaticImportTarget(compilationUnit, methName); - if (target != null) { - print(getRootRelativeName(target) + "."); - } - } - - if (getScope().innerClass) { - JCClassDecl parent = getParent(JCClassDecl.class); - int level = 0; - MethodSymbol method = null; - if (parent != null) { - while (getScope(level++).innerClass) { - parent = getParent(JCClassDecl.class, parent); - if ((method = Util.findMethodDeclarationInType(context.types, parent.sym, methName, - type)) != null) { - break; - } - } - } - if (method != null) { - if (method.isStatic()) { - print(method.getEnclosingElement().getSimpleName().toString() + "."); - } else { - print("this."); - for (int i = 0; i < level; i++) { - print(PARENT_CLASS_FIELD_NAME + "."); - } - if (anonymous) { - removeLastChar(); - } - } - } - } - - } - } else { - JCFieldAccess staticFieldAccess = (JCFieldAccess) staticImport.qualid; - methSym = Util.findMethodDeclarationInType(context.types, staticFieldAccess.selected.type.tsym, - methName, type); - if (methSym != null) { - Map vars = new HashMap<>(); - Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); - if (vars.containsKey(methSym.getSimpleName().toString())) { - report(inv, JSweetProblem.HIDDEN_INVOCATION, methSym.getSimpleName()); - } - if (context.bundleMode && methSym.owner.getSimpleName().toString().equals(GLOBALS_CLASS_NAME) - && methSym.owner.owner != null - && !methSym.owner.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { - String prefix = getRootRelativeName(methSym.owner.owner); - if (!StringUtils.isEmpty(prefix)) { - print(getRootRelativeName(methSym.owner.owner) + "."); - } - } - } - if (JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(context.getActualName(methSym))) { - String targetClass = getStaticContainerFullName(staticImport); - if (!isBlank(targetClass)) { - print(targetClass); - print("."); - keywordHandled = true; - } - if (JSweetConfig.isLibPath(methSym.getEnclosingElement().getQualifiedName().toString())) { - methodName = methName.toLowerCase(); - } - } - } - } else { - if (inv.meth instanceof JCFieldAccess) { - JCExpression selected = ((JCFieldAccess) inv.meth).selected; - if (context.isFunctionalType(selected.type.tsym)) { - anonymous = true; - } - methSym = Util.findMethodDeclarationInType(context.types, selected.type.tsym, methName, type); - if (methSym != null) { - typeChecker.checkApply(inv, methSym); - } - } - } - - boolean isStatic = methSym == null || methSym.isStatic(); - if (!Util.hasVarargs(methSym) // - || !inv.args.isEmpty() && (inv.args.last().type.getKind() != TypeKind.ARRAY - // we dont use apply if var args type differ - || !context.types.erasure(((ArrayType) inv.args.last().type).elemtype).equals(context.types - .erasure(((ArrayType) methSym.getParameters().last().type).elemtype)))) { - applyVarargs = false; - } - - String targetVarName = null; - if (anonymous) { - if (inv.meth instanceof JCFieldAccess) { - JCExpression selected = ((JCFieldAccess) inv.meth).selected; - print(selected); - } - } else { - // method with name - if (inv.meth instanceof JCFieldAccess && applyVarargs && !targetIsThisOrStaticImported && !isStatic) { - targetVarName = "this['__jswref_" + (applyTargetRefCounter++) + "']"; - print("("); - print(targetVarName + " = "); - print(((JCFieldAccess) inv.meth).selected); - print(")"); - - String accessedMemberName; - if (keywordHandled) { - accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); - } else { - if (methSym == null) { - methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; - } - if (methSym != null) { - accessedMemberName = context.getActualName(methSym); - } else { - accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); - } - } - print(getTSMemberAccess(accessedMemberName, true)); - } else if (methodName != null) { - print(getTSMemberAccess(methodName, removeLastChar('.'))); - } else { - if (keywordHandled) { - print(inv.meth); - } else { - if (methSym == null && inv.meth instanceof JCFieldAccess - && ((JCFieldAccess) inv.meth).sym instanceof MethodSymbol) { - methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; - } - if (methSym != null && inv.meth instanceof JCFieldAccess) { - JCExpression selected = ((JCFieldAccess) inv.meth).selected; - if (!GLOBALS_CLASS_NAME.equals(selected.type.tsym.getSimpleName().toString())) { - print(selected).print("."); - } else { - if (context.useModules) { - if (!((ClassSymbol) selected.type.tsym).sourcefile.getName() - .equals(getCompilationUnit().sourcefile.getName())) { - // TODO: when using several qualified - // Globals classes, we - // need to disambiguate (use qualified - // name with - // underscores) - print(GLOBALS_CLASS_NAME).print("."); - } - } - - Map vars = new HashMap<>(); - Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); - if (vars.containsKey(methName)) { - report(inv, JSweetProblem.HIDDEN_INVOCATION, methName); - } - } - } - if (methSym != null) { - if (context.isInvalidOverload(methSym) && !methSym.getParameters().isEmpty() - && !Util.hasTypeParameters(methSym) && !Util.hasVarargs(methSym) - && getParent(JCMethodDecl.class) != null - && !getParent(JCMethodDecl.class).sym.isDefault()) { - if (context.isInterface((TypeSymbol) methSym.getEnclosingElement())) { - removeLastChar('.'); - print("['" + getOverloadMethodName(methSym) + "']"); - } else { - print(getOverloadMethodName(methSym)); - } - } else { - print(getTSMemberAccess(context.getActualName(methSym), removeLastChar('.'))); - } - } else { - print(inv.meth); - } - } - } - } - - if (applyVarargs) { - print(".apply"); - } else { - if (inv.typeargs != null && !inv.typeargs.isEmpty()) { - print("<"); - for (JCExpression argument : inv.typeargs) { - substituteAndPrintType(argument).print(","); - } - removeLastChar(); - print(">"); - } else { - // force type arguments to any because they are inferred to - // {} by default - if (methSym != null && !methSym.getTypeParameters().isEmpty()) { - ClassSymbol target = (ClassSymbol) methSym.getEnclosingElement(); - if (!target.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { - // invalid overload type parameters are erased - Overload overload = context.getOverload(target, methSym); - boolean inOverload = overload != null && overload.methods.size() > 1; - if (!(inOverload && !overload.isValid)) { - printAnyTypeArguments(methSym.getTypeParameters().size()); - } - } - } - } - } - print("("); - - if (applyVarargs) { - String contextVar = "null"; - if (targetIsThisOrStaticImported) { - contextVar = "this"; - } else if (targetVarName != null) { - contextVar = targetVarName; - } - - print(contextVar + ", "); - if (inv.args.size() > 1) { - print("["); - } - } - - int argsLength = applyVarargs ? inv.args.size() - 1 : inv.args.size(); - - if (getScope().innerClassNotStatic && "super".equals(methName)) { - TypeSymbol s = getParent(JCClassDecl.class).extending.type.tsym; - if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { - print(PARENT_CLASS_FIELD_NAME); - if (argsLength > 0) { - print(", "); - } - } - } - - if (getScope().enumWrapperClassScope && isAnonymousClass() && "super".equals(methName)) { - print(ENUM_WRAPPER_CLASS_ORDINAL + ", " + ENUM_WRAPPER_CLASS_NAME); - if (argsLength > 0) { - print(", "); - } - } - - for (int i = 0; i < argsLength; i++) { - JCExpression arg = inv.args.get(i); - if (inv.meth.type != null) { - List argTypes = ((MethodType) inv.meth.type).argtypes; - Type paramType = i < argTypes.size() ? argTypes.get(i) : argTypes.get(argTypes.size() - 1); - if (!substituteAssignedExpression(paramType, arg)) { - print(arg); - } - } else { - // this should never happen but we fall back just in case - print(arg); - } - if (i < argsLength - 1) { - print(", "); - } - } - - if (applyVarargs) { - if (inv.args.size() > 1) { - // we cast array to any[] to avoid concat error on - // different - // types - print("].concat("); - } - - print(inv.args.last()); - - if (inv.args.size() > 1) { - print(")"); - } - } - - print(")"); - } - if (debugMode) { - print(")"); - } - - } - - private boolean isAnonymousMethod(String methName) { - boolean anonymous = JSweetConfig.ANONYMOUS_FUNCTION_NAME.equals(methName) - || JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME.equals(methName) - || (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME.equals(methName)) - || (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME.equals(methName)) - || JSweetConfig.NEW_FUNCTION_NAME.equals(methName); - return anonymous; - } - - @Override - public void visitIdent(JCIdent ident) { - String name = ident.toString(); - - if (getScope().inlinedConstructorArgs != null) { - if (ident.sym instanceof VarSymbol && getScope().inlinedConstructorArgs.contains(name)) { - print("__args[" + getScope().inlinedConstructorArgs.indexOf(name) + "]"); - return; - } - } - - if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(ident))) { - boolean lazyInitializedStatic = false; - // add this of class name if ident is a field - if (ident.sym instanceof VarSymbol && !ident.sym.name.equals(context.names._this) - && !ident.sym.name.equals(context.names._super)) { - VarSymbol varSym = (VarSymbol) ident.sym; // findFieldDeclaration(currentClass, - // ident.name); - if (varSym != null) { - if (varSym.owner instanceof ClassSymbol) { - if (context.getFieldNameMapping(varSym) != null) { - name = context.getFieldNameMapping(varSym); - } else { - name = getIdentifier(varSym); - } - if (!varSym.getModifiers().contains(Modifier.STATIC)) { - print("this."); - JCClassDecl parent = getParent(JCClassDecl.class); - int level = 0; - boolean foundInParent = false; - while (getScope(level++).innerClassNotStatic) { - parent = getParent(JCClassDecl.class, parent); - if (parent != null && varSym.owner == parent.sym) { - foundInParent = true; - break; - } - } - if (foundInParent) { - for (int i = 0; i < level; i++) { - print(PARENT_CLASS_FIELD_NAME + "."); - } - } - } else { - if (context.lazyInitializedStatics.contains(varSym)) { - lazyInitializedStatic = true; - } - if (!varSym.owner.getQualifiedName().toString().endsWith("." + GLOBALS_CLASS_NAME)) { - if (context.bundleMode && !varSym.owner.equals(getParent(JCClassDecl.class).sym)) { - String prefix = context.getRootRelativeName(null, varSym.owner); - if (!StringUtils.isEmpty(prefix)) { - print(context.getRootRelativeName(null, varSym.owner) + "."); - } - } else { - if (!varSym.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { - print(varSym.owner.getSimpleName() + "."); - } - } - } else { - if (context.bundleMode) { - String prefix = context.getRootRelativeName(null, varSym.owner); - prefix = prefix.substring(0, prefix.length() - GLOBALS_CLASS_NAME.length()); - if (!prefix.equals(GLOBALS_PACKAGE_NAME + ".") - && !prefix.endsWith("." + GLOBALS_PACKAGE_NAME + ".")) { - print(prefix); - } - } - } - } - } else { - if (varSym.owner instanceof MethodSymbol && isAnonymousClass() - && getScope(1).finalVariables - .get(getScope(1).anonymousClasses.indexOf(getParent(JCClassDecl.class))) - .contains(varSym)) { - print("this."); - } else { - if (context.bundleMode && varSym.owner instanceof MethodSymbol) { - if (context.importedTopPackages.contains(name)) { - name = "__var_" + name; - } - } - if (JSweetConfig.JS_KEYWORDS.contains(name)) { - name = JSweetConfig.JS_KEYWORD_PREFIX + name; - } - } - } - } - } - if (ident.sym instanceof ClassSymbol) { - ClassSymbol clazz = (ClassSymbol) ident.sym; - boolean prefixAdded = false; - if (getScope().defaultMethodScope) { - if (Util.isImported(getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), - clazz)) { - print(getRootRelativeName(clazz.getEnclosingElement()) + "."); - prefixAdded = true; - } - } - // add parent class name if ident is an inner class of the - // current class - if (!prefixAdded && clazz.getEnclosingElement() instanceof ClassSymbol) { - if (context.useModules) { - print(clazz.getEnclosingElement().getSimpleName() + "."); - prefixAdded = true; - } else { - // if the class has not been imported, we need to add - // the containing class prefix - if (!getCompilationUnit().getImports().stream() - .map(i -> i.qualid.type == null ? null : i.qualid.type.tsym) - .anyMatch(t -> t == clazz)) { - print(clazz.getEnclosingElement().getSimpleName() + "."); - prefixAdded = true; - } - } - } - if (!prefixAdded && context.bundleMode && !clazz.equals(getParent(JCClassDecl.class).sym)) { - print(getRootRelativeName(clazz)); - } else { - print(name); - } + print(") => "); + if (typeName.startsWith("Int") || typeName.startsWith("Long") + || typeName.startsWith("Double")) { + print("number"); } else { - if (doesMemberNameRequireQuotes(name)) { - if (getLastPrintedChar() == '.') { - removeLastChar(); - print("['").print(name).print("']"); - } else { - print("this['").print(name).print("']"); - } - } else { - print(name); - } - if (lazyInitializedStatic) { - if (!staticInitializedAssignment) { - print(STATIC_INITIALIZATION_SUFFIX + "()"); - } - } + substituteAndPrintType(typeApply.arguments.get(0), arrayComponent, inTypeParameters, + completeRawTypes, false); } - } - } - - @Override - public void visitTypeApply(JCTypeApply typeApply) { - substituteAndPrintType(typeApply); - } - - private int initAnonymousClass(JCNewClass newClass) { - int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); - if (anonymousClassIndex == -1) { - anonymousClassIndex = getScope().anonymousClasses.size(); - getScope().anonymousClasses.add(newClass.def); - getScope().anonymousClassesConstructors.add(newClass); - LinkedHashSet finalVars = new LinkedHashSet<>(); - getScope().finalVariables.add(finalVars); - new TreeScanner() { - public void visitIdent(JCIdent var) { - if (var.sym != null && (var.sym instanceof VarSymbol)) { - VarSymbol varSymbol = (VarSymbol) var.sym; - if (varSymbol.getEnclosingElement() instanceof MethodSymbol && varSymbol.getEnclosingElement() - .getEnclosingElement() == getParent(JCClassDecl.class).sym) { - finalVars.add((VarSymbol) var.sym); - } - } - } - }.visitClassDef(newClass.def); - - } - return anonymousClassIndex; - } - - @Override - public void visitNewClass(JCNewClass newClass) { - ClassSymbol clazz = ((ClassSymbol) newClass.clazz.type.tsym); - if (clazz.getSimpleName().toString().equals(JSweetConfig.GLOBALS_CLASS_NAME)) { - report(newClass, JSweetProblem.GLOBAL_CANNOT_BE_INSTANTIATED); - return; - } - if (getScope().localClasses.stream().map(c -> c.type).anyMatch(t -> t.equals(newClass.type))) { - print("new ").print(getScope().name + ".").print(newClass.clazz.toString()); - print("(").printConstructorArgList(newClass, true).print(")"); - return; - } - boolean isInterface = context.isInterface(clazz); - if (newClass.def != null || isInterface) { - if (context.isAnonymousClass(newClass)) { - int anonymousClassIndex = initAnonymousClass(newClass); - print("new ").print(getScope().name + "." + getScope().name + ANONYMOUS_PREFIX + anonymousClassIndex); - if (newClass.def.getModifiers().getFlags().contains(Modifier.STATIC)) { - printAnonymousClassTypeArgs(newClass); - } - print("(").printConstructorArgList(newClass, false).print(")"); - return; + if (arrayComponent) { + print(")"); } - - if (isInterface - || context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { - if (isInterface) { - print(""); - } - - Set interfaces = new HashSet<>(); - if (getContext().options.isInterfaceTracking()) { - context.grabSupportedInterfaceNames(interfaces, clazz); - if (!interfaces.isEmpty()) { - print("Object.defineProperty("); - } - } - print("{").println().startIndent(); - boolean statementPrinted = false; - boolean initializationBlockFound = false; - if (newClass.def != null) { - for (JCTree m : newClass.def.getMembers()) { - if (m instanceof JCBlock) { - initializationBlockFound = true; - List initializedVars = new ArrayList<>(); - for (JCTree s : ((JCBlock) m).stats) { - boolean currentStatementPrinted = false; - if (s instanceof JCExpressionStatement - && ((JCExpressionStatement) s).expr instanceof JCAssign) { - JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; - VarSymbol var = null; - if (assignment.lhs instanceof JCFieldAccess) { - var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); - printIndent().print(var.getSimpleName().toString()); - } else if (assignment.lhs instanceof JCIdent) { - var = Util.findFieldDeclaration(clazz, ((JCIdent) assignment.lhs).name); - printIndent().print(assignment.lhs.toString()); - } else { - continue; - } - initializedVars.add(var); - print(": ").print(assignment.rhs).print(",").println(); - currentStatementPrinted = true; - statementPrinted = true; - } else if (s instanceof JCExpressionStatement - && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { - JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; - MethodInvocationElement invocationElement = (MethodInvocationElement) ExtendedElementFactory.INSTANCE - .create(invocation); - if (invocationElement.getMethodName() - .equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) { - if (invocation.getArguments().size() == 3) { - if ("this".equals(invocation.getArguments().get(0).toString())) { - printIndent().print(invocation.args.tail.head).print(": ") - .print(invocation.args.tail.tail.head).print(",").println(); - } - currentStatementPrinted = true; - statementPrinted = true; - } else { - printIndent().print(invocation.args.head).print(": ") - .print(invocation.args.tail.head).print(",").println(); - currentStatementPrinted = true; - statementPrinted = true; - } - } - } - if (!currentStatementPrinted) { - report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); - } - } - for (Symbol s : clazz.getEnclosedElements()) { - if (s instanceof VarSymbol) { - if (!initializedVars.contains(s)) { - if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { - report(m, JSweetProblem.UNINITIALIZED_FIELD, s); - } - } - } - } - } - if (m instanceof JCMethodDecl) { - JCMethodDecl method = (JCMethodDecl) m; - if (!method.sym.isConstructor()) { - printIndent().print(method.getName() + ": ("); - for (JCVariableDecl param : method.getParameters()) { - print(param.getName() + ", "); - } - if (!method.getParameters().isEmpty()) { - removeLastChars(2); - } - print(") => "); - print(method.body); - print(",").println(); - statementPrinted = true; - } - } - } - if (statementPrinted) { - removeLastChars(2); - } - - } - if (!statementPrinted && !initializationBlockFound) { - for (Symbol s : clazz.getEnclosedElements()) { - if (s instanceof VarSymbol) { - if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { - report(newClass, JSweetProblem.UNINITIALIZED_FIELD, s); - } - } - } - } - - println().endIndent().printIndent().print("}"); - if (getContext().options.isInterfaceTracking()) { - if (!interfaces.isEmpty()) { - print(", '" + INTERFACES_FIELD_NAME + "', { configurable: true, value: "); - print("["); - for (String i : interfaces) { - print("\"").print(i).print("\","); - } - removeLastChar(); - print("]"); - print(" })"); - } - } + return this; + } else if (typeName.endsWith("Predicate")) { + if (arrayComponent) { + print("("); + } + print("("); + if (typeName.startsWith("Int") || typeName.startsWith("Long") + || typeName.startsWith("Double")) { + print("p0 : number"); } else { - - // ((target : DataStruct3) => { - // target['i'] = 1; - // target['s2'] = ""; - // return target - // })(new DataStruct3()); - - print("((target:").print(newClass.clazz).print(") => {").println().startIndent(); - for (JCTree m : newClass.def.getMembers()) { - if (m instanceof JCBlock) { - for (JCTree s : ((JCBlock) m).stats) { - boolean currentStatementPrinted = false; - if (s instanceof JCExpressionStatement - && ((JCExpressionStatement) s).expr instanceof JCAssign) { - JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; - VarSymbol var = null; - if (assignment.lhs instanceof JCFieldAccess) { - var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); - printIndent().print("target['").print(var.getSimpleName().toString()).print("']"); - } else if (assignment.lhs instanceof JCIdent) { - printIndent().print("target['").print(assignment.lhs.toString()).print("']"); - } else { - continue; - } - print(" = ").print(assignment.rhs).print(";").println(); - currentStatementPrinted = true; - } else if (s instanceof JCExpressionStatement - && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { - JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; - MethodInvocationElement invocationElement = (MethodInvocationElement) ExtendedElementFactory.INSTANCE - .create(invocation); - if (invocationElement.getMethodName().equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) { - if (invocation.getArguments().size() == 3) { - if ("this".equals(invocation.getArguments().get(0).toString())) { - printIndent().print("target[").print(invocation.args.tail.head).print("]") - .print(" = ").print(invocation.args.tail.tail.head).print(";") - .println(); - } - currentStatementPrinted = true; - } else { - printIndent().print("target[").print(invocation.args.head).print("]") - .print(" = ").print(invocation.args.tail.head).print(";").println(); - currentStatementPrinted = true; - } - } - } - if (!currentStatementPrinted) { - report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); - } - } - } - } - printIndent().print("return target;").println(); - println().endIndent().printIndent().print("})("); - print("new ").print(newClass.clazz).print("(").printArgList(null, newClass.args).print("))"); + printArguments(typeApply.arguments); } - } else { - if (context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { - print("{}"); - } else { - if (!getAdapter().substituteNewClass(new NewClassElementSupport(newClass))) { - String mappedType = context.getTypeMappingTarget(newClass.clazz.type.toString()); - if (typeChecker.checkType(newClass, null, newClass.clazz)) { - - boolean applyVarargs = true; - MethodSymbol methSym = (MethodSymbol) newClass.constructor; - if (newClass.args.size() == 0 || !Util.hasVarargs(methSym) // - || newClass.args.last().type.getKind() != TypeKind.ARRAY - // we dont use apply if var args type differ - || !context.types.erasure(((ArrayType) newClass.args.last().type).elemtype) - .equals(context.types - .erasure(((ArrayType) methSym.getParameters().last().type).elemtype))) { - applyVarargs = false; - } - if (applyVarargs) { - // this is necessary in case the user defines a - // Function class that hides the global Function - // class - context.addGlobalsMapping("Function", "__Function"); - print("new (__Function.prototype.bind.apply("); - if (mappedType != null) { - print(mapConstructorType(mappedType)); - } else { - print(newClass.clazz); - } - print(", [null"); - for (int i = 0; i < newClass.args.length() - 1; i++) { - print(", ").print(newClass.args.get(i)); - } - print("].concat(").print(newClass.args.last()).print(")))"); - } else { - if (newClass.clazz instanceof JCTypeApply) { - JCTypeApply typeApply = (JCTypeApply) newClass.clazz; - mappedType = context.getTypeMappingTarget(typeApply.clazz.type.toString()); - print("new "); - if (mappedType != null) { - print(mapConstructorType(mappedType)); - } else { - print(typeApply.clazz); - } - if (!typeApply.arguments.isEmpty()) { - print("<").printTypeArgList(typeApply.arguments).print(">"); - } else { - // erase types since the diamond (<>) - // operator - // does not exists in TypeScript - printAnyTypeArguments( - ((ClassSymbol) newClass.clazz.type.tsym).getTypeParameters().length()); - } - print("(").printConstructorArgList(newClass, false).print(")"); - } else { - print("new "); - if (mappedType != null) { - print(mapConstructorType(mappedType)); - } else { - print(newClass.clazz); - } - print("(").printConstructorArgList(newClass, false).print(")"); - } - } - } - } + print(") => boolean"); + if (arrayComponent) { + print(")"); } + return this; + } else if (typeName.endsWith("Operator")) { + if (arrayComponent) { + print("("); + } + print("("); + printArgument(typeApply.arguments.head, 1); + if (typeName.startsWith("Binary")) { + print(", "); + printArgument(typeApply.arguments.head, 2); + } + print(") => "); + substituteAndPrintType(typeApply.arguments.head, arrayComponent, inTypeParameters, + completeRawTypes, false); + if (arrayComponent) { + print(")"); + } + return this; + } } - + if (typeFullName.startsWith(Class.class.getName() + "<")) { + return print("any"); + } + } else { + if (!(typeTree instanceof JCArrayTypeTree) && typeFullName.startsWith("java.util.function.")) { + // case of a raw functional type (programmer's mistake) + return print("any"); + } + String mappedType = context.getTypeMappingTarget(typeFullName); + if (mappedType != null) { + if (mappedType.endsWith("<>")) { + print(mappedType.substring(0, mappedType.length() - 2)); + } else { + print(mappedType); + if (completeRawTypes && !typeTree.type.tsym.getTypeParameters().isEmpty() + && !context.getTypeMappingTarget(typeFullName).equals("any")) { + printAnyTypeArguments(typeTree.type.tsym.getTypeParameters().size()); + } + } + return this; + } + } + for (BiFunction mapping : context.getFunctionalTypeMappings()) { + Object mapped = mapping.apply(new ExtendedElementSupport(typeTree), typeFullName); + if (mapped instanceof String) { + print((String) mapped); + return this; + } else if (mapped instanceof JCTree) { + substituteAndPrintType((JCTree) mapped); + return this; + } else if (mapped instanceof TypeMirror) { + print(getAdapter().getMappedType((TypeMirror) mapped)); + return this; + } + } } - public void printAnyTypeArguments(int count) { + if (typeTree instanceof JCTypeApply) { + JCTypeApply typeApply = ((JCTypeApply) typeTree); + substituteAndPrintType(typeApply.clazz, arrayComponent, inTypeParameters, false, disableSubstitution); + if (!typeApply.arguments.isEmpty() && !"any".equals(getLastPrintedString(3)) + && !"Object".equals(getLastPrintedString(6))) { print("<"); - for (int i = 0; i < count; i++) { - print("any, "); + for (JCExpression argument : typeApply.arguments) { + substituteAndPrintType(argument, arrayComponent, false, completeRawTypes, false).print(", "); } - if (count > 0) { - removeLastChars(2); + if (typeApply.arguments.length() > 0) { + removeLastChars(2); } print(">"); - + } + return this; + } else if (typeTree instanceof JCWildcard) { + JCWildcard wildcard = ((JCWildcard) typeTree); + String name = context.getWildcardName(wildcard); + if (name == null) { + return print("any"); + } else { + print(name); + if (inTypeParameters) { + print(" extends "); + return substituteAndPrintType(wildcard.getBound(), arrayComponent, false, completeRawTypes, + disableSubstitution); + } else { + return this; + } + } + } else { + if (typeTree instanceof JCArrayTypeTree) { + return substituteAndPrintType(((JCArrayTypeTree) typeTree).elemtype, true, inTypeParameters, + completeRawTypes, disableSubstitution).print("[]"); + } + if (completeRawTypes && typeTree.type.tsym.getTypeParameters() != null + && !typeTree.type.tsym.getTypeParameters().isEmpty()) { + // raw type case (Java warning) + print(typeTree); + print("<"); + for (int i = 0; i < typeTree.type.tsym.getTypeParameters().length(); i++) { + print("any, "); + } + removeLastChars(2); + print(">"); + return this; + } else { + return print(typeTree); + } } - @Override - public AbstractTreePrinter printConstructorArgList(JCNewClass newClass, boolean localClass) { - boolean printed = false; - if (localClass || (getScope().anonymousClasses.contains(newClass.def) - && !newClass.def.getModifiers().getFlags().contains(Modifier.STATIC))) { - print("this"); - if (!newClass.args.isEmpty()) { - print(", "); + } + + @Override + public void visitClassDef(JCClassDecl classdecl) { + if (context.isIgnored(classdecl)) { + getAdapter().afterType(classdecl.sym); + return; + } + String name = classdecl.getSimpleName().toString(); + if (!scope.isEmpty() && getScope().anonymousClasses.contains(classdecl)) { + name = getScope().name + ANONYMOUS_PREFIX + getScope().anonymousClasses.indexOf(classdecl); + } + + JCTree testParent = getFirstParent(JCClassDecl.class, JCMethodDecl.class); + if (testParent != null && testParent instanceof JCMethodDecl) { + if (!isLocalClass()) { + getScope().localClasses.add(classdecl); + return; + } + } + + enterScope(); + getScope().name = name; + + JCClassDecl parent = getParent(JCClassDecl.class); + List parentTypeVars = new ArrayList<>(); + if (parent != null) { + getScope().innerClass = true; + if (!classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { + getScope().innerClassNotStatic = true; + if (parent.getTypeParameters() != null) { + parentTypeVars.addAll(parent.getTypeParameters().stream().map(t -> (TypeVariableSymbol) t.type.tsym) + .collect(Collectors.toList())); + getAdapter().typeVariablesToErase.addAll(parentTypeVars); + } + } + } + getScope().declareClassScope = context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_AMBIENT) + || isDefinitionScope; + getScope().interfaceScope = false; + getScope().removedSuperclass = false; + getScope().enumScope = false; + getScope().enumWrapperClassScope = false; + + HashSet> defaultMethods = null; + boolean globals = JSweetConfig.GLOBALS_CLASS_NAME.equals(classdecl.name.toString()); + if (globals && classdecl.extending != null) { + report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_HAVE_SUPERCLASS); + } + List implementedInterfaces = new ArrayList<>(); + + if (!globals) { + if (classdecl.extending != null && JSweetConfig.GLOBALS_CLASS_NAME + .equals(classdecl.extending.type.tsym.getSimpleName().toString())) { + report(classdecl, JSweetProblem.GLOBALS_CLASS_CANNOT_BE_SUBCLASSED); + return; + } + printDocComment(classdecl, false); + print(classdecl.mods); + if (!isTopLevelScope() || context.useModules || isAnonymousClass() || isInnerClass() || isLocalClass()) { + print("export "); + } + if (context.isInterface(classdecl.sym)) { + print("interface "); + getScope().interfaceScope = true; + } else { + if (classdecl.getKind() == Kind.ENUM) { + if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { + print("declare "); + } + if (scope.size() > 1 && getScope(1).isComplexEnum) { + if (Util.hasAbstractMethod(classdecl.sym)) { + print("abstract "); } - printed = true; - } else if ((newClass.clazz.type.tsym.getEnclosingElement() instanceof ClassSymbol - && !newClass.clazz.type.tsym.getModifiers().contains(Modifier.STATIC))) { - print("this"); - JCClassDecl parent = getParent(JCClassDecl.class); - ClassSymbol parentSymbol = parent == null ? null : parent.sym; - if (newClass.clazz.type.tsym.getEnclosingElement() != parentSymbol) { - print("." + PARENT_CLASS_FIELD_NAME); + print("class "); + getScope().enumWrapperClassScope = true; + } else { + print("enum "); + getScope().enumScope = true; + } + } else { + if (getScope().declareClassScope && !(getIndent() != 0 && isDefinitionScope)) { + print("declare "); + } + defaultMethods = new HashSet<>(); + Util.findDefaultMethodsInType(defaultMethods, context, classdecl.sym); + if (classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { + print("abstract "); + } + print("class "); + } + } + + print(name + (getScope().enumWrapperClassScope ? ENUM_WRAPPER_CLASS_SUFFIX : "")); + + if (classdecl.typarams != null && classdecl.typarams.size() > 0) { + print("<").printArgList(null, classdecl.typarams).print(">"); + } else if (isAnonymousClass() && classdecl.getModifiers().getFlags().contains(Modifier.STATIC)) { + JCNewClass newClass = getScope(1).anonymousClassesConstructors + .get(getScope(1).anonymousClasses.indexOf(classdecl)); + printAnonymousClassTypeArgs(newClass); + } + String mixin = null; + if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN)) { + mixin = context.getAnnotationValue(classdecl.sym, JSweetConfig.ANNOTATION_MIXIN, null); + for (Compound c : classdecl.sym.getAnnotationMirrors()) { + if (JSweetConfig.ANNOTATION_MIXIN.equals(c.type.toString())) { + String targetName = getRootRelativeName(((Attribute.Class) c.values.head.snd).classType.tsym); + String mixinName = getRootRelativeName(classdecl.sym); + if (!mixinName.equals(targetName)) { + report(classdecl, JSweetProblem.WRONG_MIXIN_NAME, mixinName, targetName); + } else { + if (((Attribute.Class) c.values.head.snd).classType.tsym.equals(classdecl.sym)) { + report(classdecl, JSweetProblem.SELF_MIXIN_TARGET, mixinName); + } } - if (!newClass.args.isEmpty()) { - print(", "); - } - printed = true; + } + } + } + + boolean extendsInterface = false; + if (classdecl.extending != null) { + + boolean removeIterable = false; + if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE) + && classdecl.extending.type.tsym.getQualifiedName().toString() + .equals(Iterable.class.getName())) { + removeIterable = true; } - MethodType t = (MethodType) newClass.constructorType; + if (!removeIterable && !JSweetConfig.isJDKReplacementMode() + && !(JSweetConfig.OBJECT_CLASSNAME.equals(classdecl.extending.type.toString()) + || Object.class.getName().equals(classdecl.extending.type.toString())) + && !(mixin != null && mixin.equals(classdecl.extending.type.toString())) && !(getAdapter() + .eraseSuperClass(classdecl.sym, (ClassSymbol) classdecl.extending.type.tsym))) { + if (!getScope().interfaceScope && context.isInterface(classdecl.extending.type.tsym)) { + extendsInterface = true; + print(" implements "); + implementedInterfaces.add(classdecl.extending.type); + } else { + print(" extends "); + } + if (getScope().enumWrapperClassScope && getScope(1).anonymousClasses.contains(classdecl)) { + print(classdecl.extending.toString() + ENUM_WRAPPER_CLASS_SUFFIX); + } else { + disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); + substituteAndPrintType(classdecl.extending); + disableTypeSubstitution = false; + } + if (context.classesWithWrongConstructorOverload.contains(classdecl.sym)) { + getScope().hasConstructorOverloadWithSuperClass = true; + } + } else { + getScope().removedSuperclass = true; + } + } - printArgList(t == null ? null : t.argtypes, newClass.args); - int index = getScope().anonymousClasses.indexOf(newClass.def); - if (index >= 0 && !getScope().finalVariables.get(index).isEmpty()) { - if (printed || !newClass.args.isEmpty()) { - print(", "); + if (classdecl.implementing != null && !classdecl.implementing.isEmpty() && !getScope().enumScope) { + List implementing = new ArrayList<>(classdecl.implementing); + + if (context.hasAnnotationType(classdecl.sym, JSweetConfig.ANNOTATION_SYNTACTIC_ITERABLE)) { + for (JCExpression itf : classdecl.implementing) { + if (itf.type.tsym.getQualifiedName().toString().equals(Iterable.class.getName())) { + implementing.remove(itf); } - for (VarSymbol v : getScope().finalVariables.get(index)) { - print(v.getSimpleName().toString()); - print(", "); + } + } + // erase Java interfaces + for (JCExpression itf : classdecl.implementing) { + if (context.isFunctionalType(itf.type.tsym) + || getAdapter().eraseSuperInterface(classdecl.sym, (ClassSymbol) itf.type.tsym)) { + implementing.remove(itf); + } + } + + if (!implementing.isEmpty()) { + if (!extendsInterface) { + if (getScope().interfaceScope) { + print(" extends "); + } else { + print(" implements "); + } + } else { + print(", "); + } + for (JCExpression itf : implementing) { + disableTypeSubstitution = !getAdapter().isSubstituteSuperTypes(); + substituteAndPrintType(itf); + disableTypeSubstitution = false; + implementedInterfaces.add(itf.type); + print(", "); + } + removeLastChars(2); + } + } + print(" {").println().startIndent(); + } + + if (getScope().innerClassNotStatic && !getScope().interfaceScope && !getScope().enumScope) { + printIndent().print("public " + PARENT_CLASS_FIELD_NAME + ": any;").println(); + } + + if (defaultMethods != null && !defaultMethods.isEmpty()) { + getScope().defaultMethodScope = true; + for (Entry entry : defaultMethods) { + if (!(entry.getValue().type instanceof MethodType)) { + continue; + } + MethodSymbol s = Util.findMethodDeclarationInType(context.types, classdecl.sym, + entry.getValue().getName().toString(), (MethodType) entry.getValue().type); + if (s == null || s == entry.getValue().sym) { + getAdapter().typeVariablesToErase + .addAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); + printIndent().print(entry.getValue()).println(); + getAdapter().typeVariablesToErase + .removeAll(((ClassSymbol) s.getEnclosingElement()).getTypeParameters()); + } + } + getScope().defaultMethodScope = false; + } + + if (getScope().enumScope) { + printIndent(); + } + if (globals) { + removeLastIndent(); + } + + if (!getScope().interfaceScope && classdecl.getModifiers().getFlags().contains(Modifier.ABSTRACT)) { + List methods = new ArrayList<>(); + for (Type t : implementedInterfaces) { + context.grabMethodsToBeImplemented(methods, t.tsym); + } + Map signatures = new HashMap<>(); + for (MethodSymbol meth : methods) { + if (meth.type instanceof MethodType) { + MethodSymbol s = Util.findMethodDeclarationInType(getContext().types, classdecl.sym, + meth.getSimpleName().toString(), (MethodType) meth.type, true); + if (Object.class.getName().equals(s.getEnclosingElement().toString())) { + s = null; + } + boolean printDefaultImplementation = false; + if (s != null) { + if (!s.getEnclosingElement().equals(classdecl.sym)) { + if (!(s.isDefault() || (!context.isInterface((TypeSymbol) s.getEnclosingElement()) + && !s.getModifiers().contains(Modifier.ABSTRACT)))) { + printDefaultImplementation = true; + } + } + } + + if (printDefaultImplementation) { + Overload o = context.getOverload(classdecl.sym, meth); + if (o != null && o.methods.size() > 1 && !o.isValid) { + if (!meth.type.equals(o.coreMethod.type)) { + printDefaultImplementation = false; + } + } + } + if (s == null || printDefaultImplementation) { + String signature = getContext().types.erasure(meth.type).toString(); + if (!(signatures.containsKey(meth.name) && signatures.get(meth.name).equals(signature))) { + printDefaultImplementation(meth); + signatures.put(meth.name, signature); + } + } + } + } + } + + for (JCTree def : classdecl.defs) { + if (def instanceof JCClassDecl) { + getScope().hasInnerClass = true; + } + if (def instanceof JCVariableDecl) { + JCVariableDecl var = (JCVariableDecl) def; + if (!var.sym.isStatic() && var.init != null) { + getScope().fieldsWithInitializers.add((JCVariableDecl) def); + } + } + } + + if (!globals && !context.isInterface(classdecl.sym) && context.getStaticInitializerCount(classdecl.sym) > 0) { + printIndent().print("static __static_initialized : boolean = false;").println(); + int liCount = context.getStaticInitializerCount(classdecl.sym); + String prefix = classdecl.getSimpleName().toString() + "."; + printIndent().print("static __static_initialize() { "); + print("if(!" + prefix + "__static_initialized) { "); + print(prefix + "__static_initialized = true; "); + for (int i = 0; i < liCount; i++) { + print(prefix + "__static_initializer_" + i + "(); "); + } + print("} }").println().println(); + String qualifiedClassName = getQualifiedTypeName(classdecl.sym, globals); + context.addTopFooterStatement( + (isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".__static_initialize();")); + } + + boolean hasUninitializedFields = false; + + for (JCTree def : classdecl.defs) { + if (getScope().interfaceScope && ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) + || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic()))) { + // static interface members are printed in a namespace + continue; + } + if (getScope().interfaceScope && def instanceof JCMethodDecl) { + // object method should not be defined otherwise they will have + // to be implemented + if (Util.isOverridingBuiltInJavaObjectMethod(((JCMethodDecl) def).sym)) { + continue; + } + } + if (def instanceof JCClassDecl) { + // inner types are be printed in a namespace + continue; + } + if (def instanceof JCVariableDecl) { + if (getScope().enumScope && ((JCVariableDecl) def).type.tsym != classdecl.type.tsym) { + getScope().isComplexEnum = true; + continue; + } + if (!((JCVariableDecl) def).getModifiers().getFlags().contains(Modifier.STATIC) + && ((JCVariableDecl) def).init == null) { + hasUninitializedFields = true; + } + } + if (def instanceof JCBlock && !((JCBlock) def).isStatic()) { + hasUninitializedFields = true; + } + if (!getScope().enumScope) { + printIndent(); + } + int pos = getCurrentPosition(); + print(def); + if (getCurrentPosition() == pos) { + if (!getScope().enumScope) { + removeLastIndent(); + } + continue; + } + if (def instanceof JCVariableDecl) { + if (getScope().enumScope) { + print(", "); + } else { + print(";").println().println(); + } + } else { + println().println(); + } + } + + if (!getScope().hasDeclaredConstructor + && !(getScope().interfaceScope || getScope().enumScope || getScope().declareClassScope)) { + Set interfaces = new HashSet<>(); + context.grabSupportedInterfaceNames(interfaces, classdecl.sym); + if (!interfaces.isEmpty() || getScope().innerClassNotStatic || hasUninitializedFields) { + printIndent().print("constructor("); + boolean hasArgs = false; + if (getScope().innerClassNotStatic) { + print(PARENT_CLASS_FIELD_NAME + ": any"); + hasArgs = true; + } + int anonymousClassIndex = scope.size() > 1 ? getScope(1).anonymousClasses.indexOf(classdecl) : -1; + if (anonymousClassIndex != -1) { + for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).args + .length(); i++) { + if (!hasArgs) { + hasArgs = true; + } else { + print(", "); + } + print("__arg" + i + ": any"); + } + for (VarSymbol v : getScope(1).finalVariables.get(anonymousClassIndex)) { + if (!hasArgs) { + hasArgs = true; + } else { + print(", "); + } + print("private " + v.getSimpleName() + ": any"); + } + } + + print(") {").startIndent().println(); + if (classdecl.extending != null && !getScope().removedSuperclass + && !context.isInterface(classdecl.extending.type.tsym)) { + printIndent().print("super("); + if (getScope().innerClassNotStatic) { + TypeSymbol s = classdecl.extending.type.tsym; + boolean hasArg = false; + if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { + print(PARENT_CLASS_FIELD_NAME); + hasArg = true; + } + if (anonymousClassIndex != -1) { + for (int i = 0; i < getScope(1).anonymousClassesConstructors.get(anonymousClassIndex).args + .length(); i++) { + if (hasArg) { + print(", "); + } else { + hasArg = true; + } + print("__arg" + i); + } + } + } + print(");").println(); + } + printInstanceInitialization(classdecl, null); + endIndent().printIndent().print("}").println().println(); + } + } + + removeLastChar(); + + if (getScope().enumWrapperClassScope && !getScope(1).anonymousClasses.contains(classdecl)) { + printIndent().print("public name() : string { return this." + ENUM_WRAPPER_CLASS_NAME + "; }").println(); + printIndent().print("public ordinal() : number { return this." + ENUM_WRAPPER_CLASS_ORDINAL + "; }") + .println(); + } + + if (getScope().enumScope) { + removeLastChar().println(); + } + + if (!globals) { + endIndent().printIndent().print("}"); + if (getContext().options.isSupportGetClass() && !getScope().interfaceScope && !getScope().declareClassScope + && !getScope().enumScope && !(getScope().enumWrapperClassScope && classdecl.sym.isAnonymous())) { + if (!classdecl.sym.isAnonymous()) { + println().printIndent().print(classdecl.sym.getSimpleName().toString()) + .print("[\"" + CLASS_NAME_IN_CONSTRUCTOR + "\"] = ") + .print("\"" + context.getRootRelativeName(null, classdecl.sym) + "\";"); + } + Set interfaces = new HashSet<>(); + context.grabSupportedInterfaceNames(interfaces, classdecl.sym); + if (!interfaces.isEmpty()) { + println().printIndent() + .print(getScope().enumWrapperClassScope ? classdecl.sym.getSimpleName().toString() : name) + .print("[\"" + INTERFACES_FIELD_NAME + "\"] = "); + print("["); + for (String itf : interfaces) { + print("\"").print(itf).print("\","); + } + removeLastChar(); + print("];").println(); + } + if (!getScope().enumWrapperClassScope) { + println(); + } + } + } + + // enum class for complex enum + if (getScope().isComplexEnum) { + println().println().printIndent(); + visitClassDef(classdecl); + } + + // inner, anonymous and local classes in a namespace + // ====================== + // print valid inner classes + boolean nameSpace = false; + for (JCTree def : classdecl.defs) { + if (def instanceof JCClassDecl) { + JCClassDecl cdef = (JCClassDecl) def; + if (context.isIgnored(cdef)) { + continue; + } + if (!nameSpace) { + nameSpace = true; + println().println().printIndent(); + if (!isTopLevelScope() || context.useModules) { + print("export "); + } else { + if (isDefinitionScope) { + print("declare "); + } + } + print("namespace ").print(name).print(" {").startIndent(); + } + getScope().isInnerClass = true; + println().println().printIndent().print(cdef); + getScope().isInnerClass = false; + } + } + // print anonymous classes + for (JCClassDecl cdef : getScope().anonymousClasses) { + if (!nameSpace) { + nameSpace = true; + println().println().printIndent(); + if (!isTopLevelScope() || context.useModules) { + print("export "); + } + print("namespace ").print(name).print(" {").startIndent(); + } + getScope().isAnonymousClass = true; + println().println().printIndent().print(cdef); + getScope().isAnonymousClass = false; + } + // print local classes + for (JCClassDecl cdef : getScope().localClasses) { + if (!nameSpace) { + nameSpace = true; + println().println().printIndent(); + if (!isTopLevelScope() || context.useModules) { + print("export "); + } + print("namespace ").print(name).print(" {").startIndent(); + } + getScope().isLocalClass = true; + println().println().printIndent().print(cdef); + getScope().isLocalClass = false; + } + if (nameSpace) { + println().endIndent().printIndent().print("}").println(); + } + // end of namespace ================================================= + + if (getScope().enumScope && getScope().isComplexEnum && !getScope().anonymousClasses.contains(classdecl)) { + println().printIndent().print(classdecl.sym.getSimpleName().toString()) + .print("[\"" + ENUM_WRAPPER_CLASS_WRAPPERS + "\"] = ["); + int index = 0; + for (JCTree tree : classdecl.defs) { + if (tree instanceof JCVariableDecl && ((JCVariableDecl) tree).type.equals(classdecl.type)) { + JCVariableDecl varDecl = (JCVariableDecl) tree; + // enum fields are not part of the enum auxiliary class but + // will initialize the enum values + JCNewClass newClass = (JCNewClass) varDecl.init; + JCClassDecl clazz = classdecl; + try { + int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); + if (anonymousClassIndex >= 0) { + print("new ") + .print(clazz.getSimpleName().toString() + "." + clazz.getSimpleName().toString() + + ANONYMOUS_PREFIX + anonymousClassIndex + ENUM_WRAPPER_CLASS_SUFFIX) + .print("("); + } else { + print("new ").print(clazz.getSimpleName().toString() + ENUM_WRAPPER_CLASS_SUFFIX) + .print("("); + } + print("" + (index++) + ", "); + print("\"" + varDecl.sym.name.toString() + "\""); + if (!newClass.args.isEmpty()) { + print(", "); + } + printArgList(null, newClass.args).print(")"); + print(", "); + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + } + } + removeLastChars(2); + print("];").println(); + } + + if (getScope().interfaceScope) { + // print static members of interfaces + nameSpace = false; + for (JCTree def : classdecl.defs) { + if ((def instanceof JCMethodDecl && ((JCMethodDecl) def).sym.isStatic()) + || (def instanceof JCVariableDecl && ((JCVariableDecl) def).sym.isStatic())) { + if (def instanceof JCVariableDecl && context.hasAnnotationType(((JCVariableDecl) def).sym, + ANNOTATION_STRING_TYPE, JSweetConfig.ANNOTATION_ERASED)) { + continue; + } + if (!nameSpace) { + nameSpace = true; + println().println().printIndent(); + + if (getIndent() != 0 || context.useModules) { + print("export "); + } else { + if (isDefinitionScope) { + print("declare "); + } + } + + print("namespace ").print(classdecl.getSimpleName().toString()).print(" {").startIndent(); + } + println().println().printIndent().print(def); + if (def instanceof JCVariableDecl) { + print(";"); + } + } + } + if (nameSpace) { + println().endIndent().printIndent().print("}").println(); + } + } + + if (mainMethod != null && mainMethod.getParameters().size() < 2 + && mainMethod.sym.getEnclosingElement().equals(classdecl.sym)) { + String mainClassName = getQualifiedTypeName(classdecl.sym, globals); + String mainMethodQualifier = mainClassName; + if (!isBlank(mainClassName)) { + mainMethodQualifier = mainClassName + "."; + } + context.entryFiles.add(new File(compilationUnit.sourcefile.getName())); + context.addFooterStatement(mainMethodQualifier + JSweetConfig.MAIN_FUNCTION_NAME + "(" + + (mainMethod.getParameters().isEmpty() ? "" : "null") + ");"); + } + + getAdapter().typeVariablesToErase.removeAll(parentTypeVars); + exitScope(); + + getAdapter().afterType(classdecl.sym); + + } + + private void printDefaultImplementation(MethodSymbol method) { + printIndent().print("public abstract ").print(method.getSimpleName().toString()); + print("("); + if (method.getParameters() != null && !method.getParameters().isEmpty()) { + for (VarSymbol var : method.getParameters()) { + print(var.name.toString()).print(": any"); + print(", "); + } + removeLastChars(2); + } + print(")"); + print(": any;").println(); + } + + private String getTSMethodName(JCMethodDecl methodDecl) { + String name = context.getActualName(methodDecl.sym); + switch (name) { + case "": + return "constructor"; + case JSweetConfig.ANONYMOUS_FUNCTION_NAME: + case JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME: + return ""; + case JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME: + case JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME: + if (context.deprecatedApply) { + return ""; + } else { + return name; + } + case JSweetConfig.NEW_FUNCTION_NAME: + return "new"; + default: + return name; + } + } + + private boolean printCoreMethodDelegate = false; + + protected boolean isDebugMode(JCMethodDecl methodDecl) { + return methodDecl != null && !constructor && context.options.isDebugMode() + && !(context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_NO_DEBUG) || context + .hasAnnotationType(methodDecl.sym.getEnclosingElement(), JSweetConfig.ANNOTATION_NO_DEBUG)); + } + + boolean constructor = false; + + @Override + public void visitMethodDef(JCMethodDecl methodDecl) { + + if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { + // erased elements are ignored + return; + } + JCClassDecl parent = (JCClassDecl) getParent(); + + if (parent != null && methodDecl.pos == parent.pos && !getScope().enumWrapperClassScope) { + return; + } + + if (JSweetConfig.INDEXED_GET_FUCTION_NAME.equals(methodDecl.getName().toString()) + && methodDecl.getParameters().size() == 1) { + print("[").print(methodDecl.getParameters().head).print("]: "); + substituteAndPrintType(methodDecl.restype).print(";"); + return; + } + + constructor = methodDecl.sym.isConstructor(); + if (getScope().enumScope) { + if (constructor) { + if (parent != null && parent.pos != methodDecl.pos) { + getScope().isComplexEnum = true; + } + } else { + getScope().isComplexEnum = true; + } + return; + } + + Overload overload = null; + boolean inOverload = false; + boolean inCoreWrongOverload = false; + if (parent != null) { + overload = context.getOverload(parent.sym, methodDecl.sym); + inOverload = overload != null && overload.methods.size() > 1; + if (inOverload) { + if (!overload.isValid) { + if (!printCoreMethodDelegate) { + if (overload.coreMethod.equals(methodDecl)) { + inCoreWrongOverload = true; + if (!context.isInterface(parent.sym) && !methodDecl.sym.isConstructor() + && parent.sym.equals(overload.coreMethod.sym.getEnclosingElement())) { + printCoreMethodDelegate = true; + visitMethodDef(overload.coreMethod); + println().println().printIndent(); + printCoreMethodDelegate = false; + } + } else { + if (methodDecl.sym.isConstructor()) { + return; + } + boolean addCoreMethod = false; + addCoreMethod = !overload.printed + && overload.coreMethod.sym.getEnclosingElement() != parent.sym + && (!overload.coreMethod.sym.getModifiers().contains(Modifier.ABSTRACT) + || context.isInterface(parent.sym) + || !context.types.isSubtype(parent.sym.type, + overload.coreMethod.sym.getEnclosingElement().type)); + if (!overload.printed && !addCoreMethod && overload.coreMethod.type instanceof MethodType) { + addCoreMethod = Util.findMethodDeclarationInType(context.types, parent.sym, + methodDecl.getName().toString(), (MethodType) overload.coreMethod.type) == null; + } + if (addCoreMethod) { + visitMethodDef(overload.coreMethod); + overload.printed = true; + if (!context.isInterface(parent.sym)) { + println().println().printIndent(); + } + } + if (context.isInterface(parent.sym)) { + return; + } + } + } + } else { + if (!overload.coreMethod.equals(methodDecl)) { + return; + } + } + } + } + + boolean ambient = context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); + + if (inOverload && !inCoreWrongOverload && (ambient || isDefinitionScope)) { + // do not generate method stubs for definitions + return; + } + + if (isDebugMode(methodDecl)) { + printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); + print(getTSMethodName(methodDecl)).print("("); + printArgList(null, methodDecl.params); + print(") : "); + substituteAndPrintType(methodDecl.getReturnType()); + print(" {").println(); + startIndent().printIndent(); + if (!context.types.isSameType(context.symtab.voidType, methodDecl.sym.getReturnType())) { + print("return "); + } + print("__debug_exec('" + parent.sym.getQualifiedName() + "', '" + methodDecl.getName() + "', "); + if (!methodDecl.params.isEmpty()) { + print("["); + for (JCVariableDecl param : methodDecl.params) { + print("'" + param.getName() + "', "); + } + removeLastChars(2); + print("]"); + } else { + print("undefined"); + } + print(", this, arguments, "); + if (methodDecl.sym.isStatic()) { + print(methodDecl.sym.getEnclosingElement().getSimpleName().toString()); + } else { + print("this"); + } + print("." + GENERATOR_PREFIX + getTSMethodName(methodDecl) + "("); + for (JCVariableDecl param : methodDecl.params) { + print(context.getActualName(param.sym) + ", "); + } + if (!methodDecl.params.isEmpty()) { + removeLastChars(2); + } + print("));"); + println().endIndent().printIndent(); + print("}").println().println().printIndent(); + } + + int jsniLine = -1; + String[] content = null; + + print(methodDecl.mods); + + if (methodDecl.mods.getFlags().contains(Modifier.NATIVE)) { + if (!getScope().declareClassScope && !ambient && !getScope().interfaceScope) { + content = getGetSource(getCompilationUnit()); + if (content != null) { + int line = 0; + if (methodDecl.getParameters() != null && !methodDecl.getParameters().isEmpty()) { + line = diagnosticSource.getLineNumber(methodDecl.getParameters().last().getStartPosition()) - 1; + } else { + line = diagnosticSource.getLineNumber(methodDecl.getStartPosition()) - 1; + } + if (content[line].contains("/*-{")) { + jsniLine = line; + } else { + if (content[line + 1].contains("/*-{")) { + jsniLine = line + 1; + } + } + } + if (jsniLine == -1) { + report(methodDecl, methodDecl.name, JSweetProblem.NATIVE_MODIFIER_IS_NOT_ALLOWED, methodDecl.name); + } + } + } else { + if (getScope().declareClassScope && !constructor && !getScope().interfaceScope + && !methodDecl.mods.getFlags().contains(Modifier.DEFAULT)) { + report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, + parent == null ? "" : parent.name); + } + } + + if (methodDecl.name.toString().equals("constructor")) { + report(methodDecl, methodDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); + } + if (parent != null) { + VarSymbol v = Util.findFieldDeclaration(parent.sym, methodDecl.name); + if (v != null && context.getFieldNameMapping(v) == null) { + if (isDefinitionScope) { + return; + } else { + report(methodDecl, methodDecl.name, JSweetProblem.METHOD_CONFLICTS_FIELD, methodDecl.name, v.owner); + } + } + } + if (JSweetConfig.MAIN_FUNCTION_NAME.equals(methodDecl.name.toString()) + && methodDecl.mods.getFlags().contains(Modifier.STATIC) + && !context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_DISABLED)) { + // ignore main methods in inner classes + if (scope.size() == 1) { + mainMethod = methodDecl; + } + } + + boolean globals = parent == null ? false : JSweetConfig.GLOBALS_CLASS_NAME.equals(parent.name.toString()); + globals = globals || (getScope().interfaceScope && methodDecl.mods.getFlags().contains(Modifier.STATIC)); + printDocComment(methodDecl, false); + if (parent == null) { + print("function "); + } else if (globals) { + if (constructor && methodDecl.sym.isPrivate() && methodDecl.getParameters().isEmpty()) { + return; + } + if (constructor) { + report(methodDecl, methodDecl.name, JSweetProblem.GLOBAL_CONSTRUCTOR_DEF); + return; + } + + if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { + report(methodDecl, methodDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); + return; + } + + if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { + getContext().addExportedElement( + context.getAnnotationValue(methodDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), + methodDecl.sym, getCompilationUnit()); + } + + if (context.useModules) { + if (!methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { + print("export "); + } + } else { + if (!isTopLevelScope()) { + print("export "); + } + } + if (ambient || (getIndent() == 0 && isDefinitionScope)) { + print("declare "); + } + print("function "); + } else { + printMethodModifiers(methodDecl, parent, constructor, inOverload, overload); + if (ambient) { + report(methodDecl, methodDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, methodDecl.name); + } + } + if (parent == null || !context.isFunctionalType(parent.sym)) { + if (isDebugMode(methodDecl)) { + print("*").print(GENERATOR_PREFIX); + } + if (inOverload && !overload.isValid && !inCoreWrongOverload) { + print(getOverloadMethodName(methodDecl.sym)); + } else { + String tsMethodName = getTSMethodName(methodDecl); + if (doesMemberNameRequireQuotes(tsMethodName)) { + print("'" + tsMethodName + "'"); + } else { + print(tsMethodName); + } + } + } + if ((methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) + || (getContext().getWildcards(methodDecl.sym) != null)) { + inTypeParameters = true; + print("<"); + if (methodDecl.typarams != null && !methodDecl.typarams.isEmpty()) { + printArgList(null, methodDecl.typarams); + if (getContext().getWildcards(methodDecl.sym) != null) { + print(", "); + } + } + if (getContext().getWildcards(methodDecl.sym) != null) { + printArgList(null, getContext().getWildcards(methodDecl.sym), this::substituteAndPrintType); + } + print(">"); + inTypeParameters = false; + } + print("("); + if (inCoreWrongOverload) { + getScope().eraseVariableTypes = true; + } + boolean paramPrinted = false; + if (getScope().innerClassNotStatic && methodDecl.sym.isConstructor()) { + print(PARENT_CLASS_FIELD_NAME + ": any, "); + paramPrinted = true; + } + if (constructor && getScope().enumWrapperClassScope) { + print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_ORDINAL + " : number, "); + print((isAnonymousClass() ? "" : "protected ") + ENUM_WRAPPER_CLASS_NAME + " : string"); + if (!methodDecl.getParameters().isEmpty()) { + print(", "); + } + } + int i = 0; + for (JCVariableDecl param : methodDecl.getParameters()) { + print(param); + if (inOverload && overload.isValid && overload.defaultValues.get(i) != null) { + print(" = ").print(overload.defaultValues.get(i)); + } + print(", "); + i++; + paramPrinted = true; + } + if (inCoreWrongOverload) { + getScope().eraseVariableTypes = false; + } + if (paramPrinted) { + removeLastChars(2); + } + print(")"); + if (inCoreWrongOverload && !methodDecl.sym.isConstructor()) { + print(" : any"); + } else { + if (methodDecl.restype != null && methodDecl.restype.type.getTag() != TypeTag.VOID) { + print(" : "); + substituteAndPrintType(methodDecl.restype); + } + } + if (inCoreWrongOverload && context.isInterface(parent.sym)) { + print(";"); + return; + } + if (methodDecl.getBody() == null && !(inCoreWrongOverload && !getScope().declareClassScope) + || (methodDecl.mods.getFlags().contains(Modifier.DEFAULT) && !getScope().defaultMethodScope)) { + if (!getScope().interfaceScope && methodDecl.getModifiers().getFlags().contains(Modifier.ABSTRACT) + && inOverload && !overload.isValid) { + print(" {"); + // runtime error if we go there... + print(" throw new Error('cannot invoke abstract overloaded method... check your argument(s) type(s)'); "); + print("}"); + } else if (jsniLine != -1) { + int line = jsniLine; + print(" {").println().startIndent(); + String jsniCode = content[line].substring(content[line].indexOf("/*-{") + 4).trim(); + StringBuilder jsni = new StringBuilder(); + if (!StringUtils.isEmpty(jsniCode)) { + jsni.append(jsniCode); + jsni.append("\n"); + } + line++; + while (!content[line].contains("}-*/")) { + jsniCode = content[line++].trim(); + jsni.append(jsniCode); + jsni.append("\n"); + } + jsniCode = content[line].substring(0, content[line].indexOf("}-*/")).trim(); + if (!StringUtils.isEmpty(jsniCode)) { + jsni.append(jsniCode); + jsni.append("\n"); + } + if (!StringUtils.isEmpty(jsni)) { + jsni.deleteCharAt(jsni.length() - 1); + } + String mergedCode = parseJSNI(jsni.toString()); + for (String s : mergedCode.split("\\n")) { + printIndent().print(s).println(); + } + endIndent().printIndent().print("}"); + } else { + print(";"); + } + } else { + if (getScope().interfaceScope) { + if (!methodDecl.mods.getFlags().contains(Modifier.STATIC)) { + report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, + parent == null ? "" : parent.name); + } + } + if (getScope().declareClassScope) { + if (!constructor || (methodDecl.getBody() != null && methodDecl.getBody().getStatements().isEmpty())) { + report(methodDecl, methodDecl.name, JSweetProblem.INVALID_METHOD_BODY_IN_INTERFACE, methodDecl.name, + parent == null ? "" : parent.name); + } + print(";"); + } else { + if (inCoreWrongOverload) { + print(" {").println().startIndent().printIndent(); + + boolean wasPrinted = false; + for (i = 0; i < overload.methods.size(); i++) { + JCMethodDecl method = overload.methods.get(i); + if (context.isInterface((ClassSymbol) method.sym.getEnclosingElement()) + && !method.getModifiers().getFlags().contains(Modifier.DEFAULT)) { + continue; + } + if (!Util.isParent(parent.sym, (ClassSymbol) method.sym.getEnclosingElement())) { + continue; + } + if (wasPrinted) { + print(" else "); + } + wasPrinted = true; + print("if("); + printMethodParamsTest(overload, method); + print(") "); + if (method.sym.isConstructor()) { + printInlinedMethod(overload, method, methodDecl.getParameters()); + } else { + if (parent.sym != method.sym.getEnclosingElement() + && context.getOverload((ClassSymbol) method.sym.getEnclosingElement(), + method.sym).coreMethod == method) { + print("{").println().startIndent().printIndent(); + + String tsMethodAccess = getTSMemberAccess(getTSMethodName(methodDecl), true); + print("super" + tsMethodAccess); + print("("); + for (int j = 0; j < method.getParameters().size(); j++) { + print(avoidJSKeyword(overload.coreMethod.getParameters().get(j).name.toString())) + .print(", "); + } + if (!method.getParameters().isEmpty()) { + removeLastChars(2); + } + print(");"); + println().endIndent().printIndent().print("}"); + } else { + print("{").println().startIndent().printIndent(); + // temporary cast to any because of Java + // generics + // bug + print("return "); + if (method.sym.isStatic()) { + print(getQualifiedTypeName(parent.sym, false).toString()); + } else { + print("this"); + } + print(".").print(getOverloadMethodName(method.sym)).print("("); + for (int j = 0; j < method.getParameters().size(); j++) { + print(avoidJSKeyword(overload.coreMethod.getParameters().get(j).name.toString())) + .print(", "); + } + if (!method.getParameters().isEmpty()) { + removeLastChars(2); + } + print(");"); + println().endIndent().printIndent().print("}"); + } + } + } + print(" else throw new Error('invalid overload');"); + endIndent().println().printIndent().print("}"); + } else { + print(" ").print("{").println().startIndent(); + + String replacedBody = null; + if (context.hasAnnotationType(methodDecl.sym, JSweetConfig.ANNOTATION_REPLACE)) { + replacedBody = (String) context.getAnnotationValue(methodDecl.sym, + JSweetConfig.ANNOTATION_REPLACE, null); + } + int position = getCurrentPosition(); + if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { + enter(methodDecl.getBody()); + if (!methodDecl.getBody().stats.isEmpty() + && methodDecl.getBody().stats.head.toString().startsWith("super(")) { + printBlockStatement(methodDecl.getBody().stats.head); + if (parent != null) { + printInstanceInitialization(parent, methodDecl.sym); + } + printBlockStatements(methodDecl.getBody().stats.tail); + } else { + if (parent != null) { + printInstanceInitialization(parent, methodDecl.sym); + } + printBlockStatements(methodDecl.getBody().stats); + } + exit(); + if (replacedBody != null) { + String orgBody = getOutput().substring(position); + removeLastChars(getCurrentPosition() - position); + replacedBody = replacedBody.replace(BODY_MARKER, orgBody) + .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) + .replace(METHOD_NAME_MARKER, methodDecl.getName().toString()) + .replace(CLASS_NAME_MARKER, parent.sym.getQualifiedName().toString()); + } + } + if (replacedBody != null) { + printIndent().print(replacedBody).println(); + } + endIndent().printIndent().print("}"); + } + } + } + } + + protected void printMethodModifiers(JCMethodDecl methodDecl, JCClassDecl parent, boolean constructor, + boolean inOverload, Overload overload) { + if (methodDecl.mods.getFlags().contains(Modifier.PUBLIC) + || (inOverload && overload.coreMethod.equals(methodDecl))) { + if (!getScope().interfaceScope) { + print("public "); + } + } + if (methodDecl.mods.getFlags().contains(Modifier.PRIVATE)) { + if (!constructor) { + if (!getScope().innerClass) { + if (!getScope().interfaceScope) { + if (!(inOverload && overload.coreMethod.equals(methodDecl) || getScope().hasInnerClass)) { + print("private "); + } + } else { + if (!(inOverload && overload.coreMethod.equals(methodDecl))) { + report(methodDecl, methodDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, + methodDecl.name, parent == null ? "" : parent.name); + } + } + } + } + } + if (methodDecl.mods.getFlags().contains(Modifier.STATIC)) { + if (!getScope().interfaceScope) { + print("static "); + } + } + if (methodDecl.mods.getFlags().contains(Modifier.ABSTRACT)) { + if (!getScope().interfaceScope && !inOverload) { + print("abstract "); + } + } + } + + protected void printVariableInitialization(JCClassDecl clazz, JCVariableDecl var) { + String name = var.getName().toString(); + if (context.getFieldNameMapping(var.sym) != null) { + name = context.getFieldNameMapping(var.sym); + } else { + name = getIdentifier(var.sym); + } + if (getScope().innerClassNotStatic && !Util.isConstantOrNullField(var)) { + if (doesMemberNameRequireQuotes(name)) { + printIndent().print("this['").print(name).print("'] = "); + } else { + printIndent().print("this.").print(name).print(" = "); + } + substituteAndPrintAssignedExpression(var.type, var.init); + print(";").println(); + } else if (var.init == null) { + if (doesMemberNameRequireQuotes(name)) { + printIndent().print("this['").print(name).print("'] = ").print(Util.getTypeInitialValue(var.type)) + .print(";").println(); + } else { + printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(var.type)) + .print(";").println(); + } + } + } + + protected void printInstanceInitialization(JCClassDecl clazz, MethodSymbol method) { + if (getContext().options.isInterfaceTracking() && method == null || method.isConstructor()) { + getScope().hasDeclaredConstructor = true; + if (getScope().innerClassNotStatic) { + printIndent().print("this." + PARENT_CLASS_FIELD_NAME + " = " + PARENT_CLASS_FIELD_NAME + ";") + .println(); + } + for (JCTree member : clazz.defs) { + if (member instanceof JCVariableDecl) { + JCVariableDecl var = (JCVariableDecl) member; + if (!var.sym.isStatic() && !context.hasAnnotationType(var.sym, JSweetConfig.ANNOTATION_ERASED)) { + printVariableInitialization(clazz, var); + } + } else if (member instanceof JCBlock) { + JCBlock block = (JCBlock) member; + if (!block.isStatic()) { + printIndent().print("(() => {").startIndent().println(); + printBlockStatements(block.stats); + endIndent().printIndent().print("})();").println(); + } + } + } + } + } + + private String parseJSNI(String jsniCode) { + return jsniCode.replaceAll("@[^:]*::[\\n]?([a-zA-Z_$][a-zA-Z\\d_$]*)[\\n]?\\([^)]*\\)", "$1") + .replaceAll("@[^:]*::\\n?([a-zA-Z_$][a-zA-Z\\d_$]*)", "$1"); + } + + private void printInlinedMethod(Overload overload, JCMethodDecl method, List args) { + print("{").println().startIndent(); + printIndent().print(VAR_DECL_KEYWORD + " __args = Array.prototype.slice.call(arguments);").println(); + for (int j = 0; j < method.getParameters().size(); j++) { + if (args.get(j) instanceof JCVariableDecl) { + if (method.getParameters().get(j).name.equals(((JCVariableDecl) args.get(j)).name)) { + continue; + } else { + printIndent().print(VAR_DECL_KEYWORD + " ") + .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") + .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "").print(" = ") + .print("__args[" + j + "]").print(";").println(); + } + } else { + if (method.getParameters().get(j).name.toString().equals(args.get(j).toString())) { + continue; + } else { + getScope().inlinedConstructorArgs = method.getParameters().stream().map(p -> p.sym.name.toString()) + .collect(Collectors.toList()); + printIndent().print(VAR_DECL_KEYWORD + " ") + .print(avoidJSKeyword(method.getParameters().get(j).name.toString())).print(" : ") + .print("any").print(Util.isVarargs(method.getParameters().get(j)) ? "[]" : "").print(" = ") + .print(args.get(j)).print(";").println(); + getScope().inlinedConstructorArgs = null; + } + } + } + if (method.getBody() != null) { + boolean skipFirst = false; + boolean initialized = false; + if (!method.getBody().stats.isEmpty() && method.getBody().stats.get(0).toString().startsWith("this(")) { + skipFirst = true; + JCMethodInvocation inv = (JCMethodInvocation) ((JCExpressionStatement) method.getBody().stats + .get(0)).expr; + MethodSymbol ms = Util.findMethodDeclarationInType(context.types, + (TypeSymbol) overload.coreMethod.sym.getEnclosingElement(), inv); + for (JCMethodDecl md : overload.methods) { + if (md.sym.equals(ms)) { + printIndent(); + initialized = true; + printInlinedMethod(overload, md, inv.args); + println(); + } + } + + } + String replacedBody = null; + if (context.hasAnnotationType(method.sym, JSweetConfig.ANNOTATION_REPLACE)) { + replacedBody = (String) context.getAnnotationValue(method.sym, JSweetConfig.ANNOTATION_REPLACE, null); + } + int position = getCurrentPosition(); + if (replacedBody == null || replacedBody.contains(BODY_MARKER)) { + enter(method.getBody()); + com.sun.tools.javac.util.List stats = skipFirst ? method.getBody().stats.tail + : method.getBody().stats; + if (!stats.isEmpty() && stats.head.toString().startsWith("super(")) { + printBlockStatement(stats.head); + printFieldInitializations(); + if (!initialized) { + printInstanceInitialization(getParent(JCClassDecl.class), method.sym); + } + if (!stats.tail.isEmpty()) { + printIndent().print("((").print(") => {").startIndent().println(); + printBlockStatements(stats.tail); + endIndent().printIndent().print("})(").print(");").println(); + } + } else { + if (!initialized) { + printInstanceInitialization(getParent(JCClassDecl.class), method.sym); + } + if (!stats.isEmpty() || !method.sym.isConstructor()) { + printIndent(); + } + if (!method.sym.isConstructor()) { + print("return "); + } + if (!stats.isEmpty() || !method.sym.isConstructor()) { + print("((").print(") => {").startIndent().println(); + printBlockStatements(stats); + endIndent().printIndent().print("})(").print(");").println(); + } + } + exit(); + if (replacedBody != null) { + getIndent(); + printIndent(); + String orgBody = getOutput().substring(position); + removeLastChars(getCurrentPosition() - position); + replacedBody = replacedBody.replace(BODY_MARKER, orgBody) + .replace(BASE_INDENT_MARKER, getIndentString()).replace(INDENT_MARKER, INDENT) + .replace(METHOD_NAME_MARKER, method.getName().toString()) + .replace(CLASS_NAME_MARKER, method.sym.getEnclosingElement().getQualifiedName().toString()); + } + } + if (replacedBody != null) { + printIndent().print(replacedBody).println(); + } + } else { + String returnValue = Util.getTypeInitialValue(method.sym.getReturnType()); + if (returnValue != null) { + print(" return ").print(returnValue).print("; "); + } + } + endIndent().printIndent().print("}"); + } + + private void printFieldInitializations() { + JCClassDecl clazz = getParent(JCClassDecl.class); + for (JCTree t : clazz.getMembers()) { + if (t instanceof JCVariableDecl && !getScope().fieldsWithInitializers.contains(t)) { + JCVariableDecl field = (JCVariableDecl) t; + if (!field.sym.isStatic() && !context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { + String name = getIdentifier(field.sym); + if (context.getFieldNameMapping(field.sym) != null) { + name = context.getFieldNameMapping(field.sym); + } + printIndent().print("this.").print(name).print(" = ").print(Util.getTypeInitialValue(field.type)) + .print(";").println(); + } + } + } + for (JCVariableDecl field : getScope().fieldsWithInitializers) { + if (context.hasAnnotationType(field.sym, JSweetConfig.ANNOTATION_ERASED)) { + continue; + } + String name = getIdentifier(field.sym); + if (context.getFieldNameMapping(field.sym) != null) { + name = context.getFieldNameMapping(field.sym); + } + printIndent().print("this.").print(name).print(" = "); + if (!substituteAssignedExpression(field.type, field.init)) { + print(field.init); + } + print(";").println(); + } + } + + protected void printBlockStatements(List statements) { + for (JCStatement statement : statements) { + if (context.options.isDebugMode()) { + JCMethodDecl methodDecl = getParent(JCMethodDecl.class); + if (isDebugMode(methodDecl)) { + int s = statement.getStartPosition(); + int e = statement.getEndPosition(diagnosticSource.getEndPosTable()); + if (e == -1) { + e = s; + } + printIndent().print("yield { row: ").print("" + diagnosticSource.getLineNumber(s)) + .print(", column: " + diagnosticSource.getColumnNumber(s, false)).print(", statement: \""); + print(StringEscapeUtils.escapeJson(statement.toString())).print("\""); + + final Stack> locals = new Stack<>(); + try { + new TreeScanner() { + public void scan(JCTree tree) { + if (tree == statement) { + throw new RuntimeException(); + } + boolean contextChange = false; + if (tree instanceof JCBlock || tree instanceof JCEnhancedForLoop + || tree instanceof JCLambda || tree instanceof JCForLoop + || tree instanceof JCDoWhileLoop) { + locals.push(new ArrayList<>()); + contextChange = true; + } + if (tree instanceof JCVariableDecl) { + locals.peek().add(((JCVariableDecl) tree).name.toString()); + } + super.scan(tree); + if (contextChange) { + locals.pop(); + } + } + + }.scan(methodDecl.body); + } catch (Exception end) { + // swallow + } + List accessibleLocals = new ArrayList<>(); + for (List l : locals) { + accessibleLocals.addAll(l); + } + if (!accessibleLocals.isEmpty()) { + print(", locals: "); + print("{"); + for (String local : accessibleLocals) { + print("" + local + ": " + local + ", "); } removeLastChars(2); + print("}"); + } + print(" };").println(); } - return this; + } + printBlockStatement(statement); + } + } + + private void printBlockStatement(JCStatement statement) { + printIndent(); + int pos = getCurrentPosition(); + print(statement); + if (getCurrentPosition() == pos) { + removeLastIndent(); + return; + } + if (!statementsWithNoSemis.contains(statement.getClass())) { + if (statement instanceof JCLabeledStatement) { + if (!statementsWithNoSemis.contains(((JCLabeledStatement) statement).body.getClass())) { + print(";"); + } + } else { + print(";"); + } + } + println(); + } + + private String getOverloadMethodName(MethodSymbol method) { + if (method.isConstructor()) { + return "constructor"; + } + StringBuilder sb = new StringBuilder(method.getSimpleName().toString()); + sb.append("$"); + for (VarSymbol p : method.getParameters()) { + sb.append(context.types.erasure(p.type).toString().replace('.', '_').replace("[]", "_A")); + sb.append("$"); + } + if (!method.getParameters().isEmpty()) { + sb.deleteCharAt(sb.length() - 1); + } + return sb.toString(); + } + + private void printMethodParamsTest(Overload overload, JCMethodDecl m) { + int i = 0; + for (; i < m.getParameters().size(); i++) { + print("("); + printInstanceOf(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()), null, + m.getParameters().get(i).type); + print(" || ") + .print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString()) + " === null") + .print(")"); + print(" && "); + } + for (; i < overload.coreMethod.getParameters().size(); i++) { + print(avoidJSKeyword(overload.coreMethod.getParameters().get(i).name.toString())).print(" === undefined"); + print(" && "); + } + removeLastChars(4); + } + + @Override + public void visitBlock(JCBlock block) { + JCTree parent = getParent(); + boolean globals = (parent instanceof JCClassDecl) + && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); + boolean initializer = (parent instanceof JCClassDecl) && !globals; + int static_i = 0; + if (initializer) { + if (getScope().interfaceScope) { + report(block, JSweetProblem.INVALID_INITIALIZER_IN_INTERFACE, ((JCClassDecl) parent).name); + } + if (!block.isStatic()) { + // non-static blocks are initialized in the constructor + return; + } + for (JCTree m : ((JCClassDecl) parent).getMembers()) { + if (m instanceof JCBlock) { + if (((JCBlock) m).isStatic()) { + if (block == m) { + print("static __static_initializer_" + static_i + "() "); + break; + } + static_i++; + } + } + } + } + if (!globals) { + print("{").println().startIndent(); } - @Override - public void visitLiteral(JCLiteral literal) { - String s = literal.toString(); - switch (literal.typetag) { - case FLOAT: - if (s.endsWith("F")) { - s = s.substring(0, s.length() - 1); - } - break; - case LONG: - if (s.endsWith("L")) { - s = s.substring(0, s.length() - 1); - } - break; - default: - } - print(s); + printBlockStatements(block.stats); + + if (!globals) { + endIndent().printIndent().print("}"); + } + } + + private String avoidJSKeyword(String name) { + if (JSweetConfig.JS_KEYWORDS.contains(name)) { + name = JSweetConfig.JS_KEYWORD_PREFIX + name; + } + return name; + } + + @Override + public void visitVarDef(JCVariableDecl varDecl) { + if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_ERASED)) { + // erased elements are ignored + return; + } + if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_STRING_TYPE)) { + // string type fields are ignored + return; } - @Override - public void visitIndexed(JCArrayAccess arrayAccess) { - if (!getAdapter().substituteArrayAccess(new ArrayAccessElementSupport(arrayAccess))) { - print(arrayAccess.indexed).print("[").print(arrayAccess.index).print("]"); + if (getScope().enumScope) { + print(varDecl.name.toString()); + if (varDecl.init instanceof JCNewClass) { + JCNewClass newClass = (JCNewClass) varDecl.init; + if (newClass.def != null) { + initAnonymousClass(newClass); } - } + } + } else { + JCTree parent = getParent(); - @Override - public void visitForeachLoop(JCEnhancedForLoop foreachLoop) { - String indexVarName = "index" + Util.getId(); - boolean[] hasLength = { false }; - TypeSymbol targetType = foreachLoop.expr.type.tsym; - Util.scanMemberDeclarationsInType(targetType, getAdapter().getErasedTypes(), element -> { - if (element instanceof VarSymbol) { - if ("length".equals(element.getSimpleName().toString()) && Util.isNumber(((VarSymbol) element).type)) { - hasLength[0] = true; - return false; - } - } - return true; - }); - if (!getAdapter().substituteForEachLoop(new ForeachLoopElementSupport(foreachLoop), hasLength[0], - indexVarName)) { - boolean noVariable = foreachLoop.expr instanceof JCIdent || foreachLoop.expr instanceof JCFieldAccess; - if (noVariable) { - print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < ") - .print(foreachLoop.expr).print("." + "length" + "; " + indexVarName + "++) {").println() - .startIndent().printIndent(); - print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = ").print(foreachLoop.expr) - .print("[" + indexVarName + "];").println(); - } else { - String arrayVarName = "array" + Util.getId(); - print("{").println().startIndent().printIndent(); - print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.expr).print(";").println() - .printIndent(); - print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < " + arrayVarName - + ".length; " + indexVarName + "++) {").println().startIndent().printIndent(); - print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = " + arrayVarName + "[" - + indexVarName + "];").println(); - } - printIndent().print(foreachLoop.body); - endIndent().println().printIndent().print("}"); - if (!noVariable) { - endIndent().println().printIndent().print("}"); - } + if (getScope().enumWrapperClassScope && varDecl.type.equals(parent.type)) { + return; + } + + String name = getIdentifier(varDecl.sym); + if (context.getFieldNameMapping(varDecl.sym) != null) { + name = context.getFieldNameMapping(varDecl.sym); + } + + boolean confictInDefinitionScope = false; + + if (parent instanceof JCClassDecl) { + MethodSymbol m = Util.findMethodDeclarationInType(context.types, ((JCClassDecl) parent).sym, name, + null); + if (m != null) { + if (!isDefinitionScope) { + report(varDecl, varDecl.name, JSweetProblem.FIELD_CONFLICTS_METHOD, name, m.owner); + } else { + confictInDefinitionScope = true; + } } - } - - @Override - public void visitTypeIdent(JCPrimitiveTypeTree type) { - switch (type.typetag) { - case BYTE: - case DOUBLE: - case FLOAT: - case INT: - case LONG: - case SHORT: - print("number"); - break; - default: - print(type.toString()); + if (!getScope().interfaceScope && name.equals("constructor")) { + report(varDecl, varDecl.name, JSweetProblem.CONSTRUCTOR_MEMBER); } - } - - private boolean singlePrecisionFloats() { - return !context.options.isDisableSinglePrecisionFloats() - && context.options.getEcmaTargetVersion().higherThan(EcmaScriptComplianceLevel.ES3); - } - - @Override - public void visitBinary(JCBinary binary) { - if (!getAdapter().substituteBinaryOperator(new BinaryOperatorElementSupport(binary))) { - boolean closeParen = false; - boolean truncate = false; - if (Util.isIntegral(binary.type) && binary.getKind() == Kind.DIVIDE) { - if (binary.type.getKind() == TypeKind.LONG) { - print("Math.floor("); - closeParen = true; - } else { - print("("); - truncate = true; - } - } - if (singlePrecisionFloats() && binary.type.getKind() == TypeKind.FLOAT) { - print("(Math).fround("); - closeParen = true; - } - boolean charWrapping = Util.isArithmeticOrLogicalOperator(binary.getKind()) - || Util.isComparisonOperator(binary.getKind()); - boolean actualCharWrapping = false; - if (charWrapping && binary.lhs.type.isPrimitive() && context.symtab.charType.tsym == binary.lhs.type.tsym - && !(binary.rhs.type.tsym == context.symtab.stringType.tsym)) { - actualCharWrapping = true; - if (binary.lhs instanceof JCLiteral) { - print(binary.lhs).print(".charCodeAt(0)"); - } else { - print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(").print(binary.lhs).print(")"); - } - } else { - print(binary.lhs); - } - String op = binary.operator.name.toString(); - if (binary.lhs.type.getKind() == TypeKind.BOOLEAN) { - if ("|".equals(op)) { - op = "||"; - } else if ("&".equals(op)) { - op = "&&"; - } else if ("^".equals(op)) { - op = "!=="; - } - } - if ("==".equals(op) || "!=".equals(op)) { - if (charWrapping && binary.rhs.type.isPrimitive() - && context.symtab.charType.tsym == binary.rhs.type.tsym - && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { - actualCharWrapping = true; - } - } - - if (!actualCharWrapping && ("==".equals(op) || "!=".equals(op))) { - switch (getComparisonMode()) { - case FORCE_STRICT: - op += "="; - break; - case STRICT: - if (!(Util.isNullLiteral(binary.lhs) || Util.isNullLiteral(binary.rhs))) { - op += "="; - } - break; - default: - break; - } - } - - space().print(op).space(); - if (charWrapping && binary.rhs.type.isPrimitive() && context.symtab.charType.tsym == binary.rhs.type.tsym - && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { - if (binary.rhs instanceof JCLiteral) { - print(binary.rhs).print(".charCodeAt(0)"); - } else { - print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(").print(binary.rhs).print(")"); - } - } else { - print(binary.rhs); - } - if (closeParen) { - print(")"); - } - if (truncate) { - print("|0)"); - } + } else { + if (context.bundleMode) { + if (context.importedTopPackages.contains(name)) { + name = "__var_" + name; + } } - } + if (JSweetConfig.JS_KEYWORDS.contains(name)) { + report(varDecl, varDecl.name, JSweetProblem.JS_KEYWORD_CONFLICT, name, name); + name = JSweetConfig.JS_KEYWORD_PREFIX + name; + } + } - @Override - public void visitIf(JCIf ifStatement) { - print("if").print(ifStatement.cond).print(" "); - print(ifStatement.thenpart); - if (!(ifStatement.thenpart instanceof JCBlock)) { - if (!statementsWithNoSemis.contains(ifStatement.thenpart.getClass())) { - print(";"); - } - } - if (ifStatement.elsepart != null) { - print(" else "); - print(ifStatement.elsepart); - if (!(ifStatement.elsepart instanceof JCBlock)) { - if (!statementsWithNoSemis.contains(ifStatement.elsepart.getClass())) { - print(";"); - } - } - } - } + boolean globals = (parent instanceof JCClassDecl) + && JSweetConfig.GLOBALS_CLASS_NAME.equals(((JCClassDecl) parent).name.toString()); - @Override - public void visitReturn(JCReturn returnStatement) { - print("return"); - if (returnStatement.expr != null) { - JCTree parentFunction = getFirstParent(JCMethodDecl.class, JCLambda.class); - if (returnStatement.expr.type == null) { - report(returnStatement, JSweetProblem.CANNOT_ACCESS_THIS, - parentFunction == null ? returnStatement.toString() : parentFunction.toString()); - return; - } - print(" "); - Type returnType = null; - if (parentFunction != null) { - if (parentFunction instanceof JCMethodDecl) { - returnType = ((JCMethodDecl) parentFunction).restype.type; - } else { - // TODO: this cannot work! Calculate the return type of the - // lambda - // either from the functional type type arguments, of from - // the method defining the lambda's signature - // returnType = ((JCLambda) parentFunction).type; - } - } - if (!substituteAssignedExpression(returnType, returnStatement.expr)) { - print(returnStatement.expr); - } - } - } + if (globals && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { + report(varDecl, varDecl.name, JSweetProblem.GLOBALS_CAN_ONLY_HAVE_STATIC_MEMBERS); + return; + } - private boolean staticInitializedAssignment = false; + globals = globals || (parent instanceof JCClassDecl && (((JCClassDecl) parent).sym.isInterface() + || getScope().interfaceScope && varDecl.sym.isStatic())); - private VarSymbol getStaticInitializedField(JCTree expr) { - if (expr instanceof JCIdent) { - return context.lazyInitializedStatics.contains(((JCIdent) expr).sym) ? (VarSymbol) ((JCIdent) expr).sym - : null; - } else if (expr instanceof JCFieldAccess) { - return context.lazyInitializedStatics.contains(((JCFieldAccess) expr).sym) - ? (VarSymbol) ((JCFieldAccess) expr).sym : null; - } else { - return null; + if (parent instanceof JCClassDecl) { + printDocComment(varDecl, false); + } + + print(varDecl.mods); + + if (!globals && parent instanceof JCClassDecl) { + if (varDecl.mods.getFlags().contains(Modifier.PUBLIC)) { + if (!getScope().interfaceScope) { + print("public "); + } } - } - - @Override - public void visitAssignop(JCAssignOp assignOp) { - boolean expand = staticInitializedAssignment = (getStaticInitializedField(assignOp.lhs) != null); - boolean expandChar = assignOp.lhs.type.getKind() == TypeKind.CHAR; - print(assignOp.lhs); - staticInitializedAssignment = false; - String op = assignOp.operator.name.toString(); - if (assignOp.lhs.type.getKind() == TypeKind.BOOLEAN) { - if ("|".equals(op)) { - print(" = ").print(assignOp.lhs).print(" || ").print(assignOp.rhs); - return; - } else if ("&".equals(op)) { - print(" = ").print(assignOp.lhs).print(" && ").print(assignOp.rhs); - return; + if (varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { + if (!getScope().interfaceScope) { + if (!getScope().innerClass && !varDecl.mods.getFlags().contains(Modifier.STATIC)) { + // cannot keep private fields because they may be + // accessed in an inner class + print("/*private*/ "); } + } else { + report(varDecl, varDecl.name, JSweetProblem.INVALID_PRIVATE_IN_INTERFACE, varDecl.name, + ((JCClassDecl) parent).name); + } } - if (expandChar) { - print(" = String.fromCharCode(").substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.lhs) - .print(" " + op + " ").substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.rhs) - .print(")"); - return; - } - if (expand) { - print(" = ").print(assignOp.lhs).print(" " + op + " ").print(assignOp.rhs); - return; - } - print(" " + op + "= "); - print(assignOp.rhs); - } - @Override - public void visitConditional(JCConditional conditional) { - print(conditional.cond); + if (varDecl.mods.getFlags().contains(Modifier.STATIC)) { + if (!getScope().interfaceScope) { + print("static "); + } + } + } + if (!getScope().interfaceScope && parent instanceof JCClassDecl) { + if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)) { + report(varDecl, varDecl.name, JSweetProblem.USELESS_OPTIONAL_ANNOTATION, varDecl.name, + ((JCClassDecl) parent).name); + } + } + boolean ambient = context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_AMBIENT); + if (globals || !(parent instanceof JCClassDecl || parent instanceof JCMethodDecl + || parent instanceof JCLambda)) { + if (globals) { + if (context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_MODULE)) { + getContext().addExportedElement( + context.getAnnotationValue(varDecl.sym, JSweetConfig.ANNOTATION_MODULE, null), + varDecl.sym, getCompilationUnit()); + } + if (context.useModules) { + if (!varDecl.mods.getFlags().contains(Modifier.PRIVATE)) { + print("export "); + } + } else { + if (!isTopLevelScope()) { + print("export "); + } + } + if (ambient || (isTopLevelScope() && isDefinitionScope)) { + print("declare "); + } + } + if (!(inArgListTail && (parent instanceof JCForLoop))) { + if (isDefinitionScope) { + print("var "); + } else { + print(VAR_DECL_KEYWORD + " "); + } + } + } else { + if (ambient) { + report(varDecl, varDecl.name, JSweetProblem.WRONG_USE_OF_AMBIENT, varDecl.name); + } + } + + if (Util.isVarargs(varDecl)) { + print("..."); + } + + if (doesMemberNameRequireQuotes(name)) { + print("'" + name + "'"); + } else { + print(name); + } + + if (!Util.isVarargs(varDecl) && (getScope().eraseVariableTypes || (getScope().interfaceScope + && context.hasAnnotationType(varDecl.sym, JSweetConfig.ANNOTATION_OPTIONAL)))) { print("?"); - if (!substituteAssignedExpression( - rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), - conditional.truepart)) { - print(conditional.truepart); - } - print(":"); - if (!substituteAssignedExpression( - rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), - conditional.falsepart)) { - print(conditional.falsepart); - } - if (!rootConditionalAssignedTypes.isEmpty()) { - rootConditionalAssignedTypes.pop(); - } - } - - @Override - public void visitForLoop(JCForLoop forLoop) { - print("for(").printArgList(null, forLoop.init).print("; ").print(forLoop.cond).print("; ") - .printArgList(null, forLoop.step).print(") "); - print(forLoop.body); - } - - @Override - public void visitContinue(JCContinue continueStatement) { - print("continue"); - if (continueStatement.label != null) { - print(" ").print(continueStatement.label.toString()); - } - } - - @Override - public void visitBreak(JCBreak breakStatement) { - print("break"); - if (breakStatement.label != null) { - print(" ").print(breakStatement.label.toString()); - } - } - - @Override - public void visitLabelled(JCLabeledStatement labelledStatement) { - print(labelledStatement.label.toString()).print(": "); - print(labelledStatement.body); - } - - @Override - public void visitTypeArray(JCArrayTypeTree arrayType) { - print(arrayType.elemtype).print("[]"); - } - - @Override - public void visitNewArray(JCNewArray newArray) { - if (newArray.elemtype != null) { - typeChecker.checkType(newArray, null, newArray.elemtype); - } - if (newArray.dims != null && !newArray.dims.isEmpty()) { - if (newArray.dims.size() == 1) { - if (Util.isNumber(newArray.elemtype.type)) { - if (newArray.dims.head instanceof JCLiteral - && ((int) ((JCLiteral) newArray.dims.head).value) <= 10) { - boolean hasElements = false; - print("["); - for (int i = 0; i < (int) ((JCLiteral) newArray.dims.head).value; i++) { - print("0, "); - hasElements = true; - } - if (hasElements) { - removeLastChars(2); - } - print("]"); - } else { - print("(s => { let a=[]; while(s-->0) a.push(0); return a; })(").print(newArray.dims.head) - .print(")"); - } - } else { - print("new Array(").print(newArray.dims.head).print(")"); - } + } + if (!getScope().skipTypeAnnotations && !getScope().enumWrapperClassScope) { + if (typeChecker.checkType(varDecl, varDecl.name, varDecl.vartype)) { + print(" : "); + if (confictInDefinitionScope) { + print("any"); + } else { + if (getScope().eraseVariableTypes) { + print("any"); + if (Util.isVarargs(varDecl)) { + print("[]"); + } } else { - print(" (function(dims) { " + VAR_DECL_KEYWORD - + " allocate = function(dims) { if(dims.length==0) { return " - + (Util.isNumber(newArray.elemtype.type) ? "0" : "undefined") + "; } else { " + VAR_DECL_KEYWORD - + " array = []; for(" + VAR_DECL_KEYWORD - + " i = 0; i < dims[0]; i++) { array.push(allocate(dims.slice(1))); } return array; }}; return allocate(dims);})"); - print("(["); - printArgList(null, newArray.dims); - print("])"); + if (context.hasAnnotationType(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE)) { + print("\""); + print(context.getAnnotationValue(varDecl.vartype.type.tsym, ANNOTATION_STRING_TYPE, + varDecl.vartype.type.tsym.name.toString()).toString()); + print("\""); + } else { + substituteAndPrintType(varDecl.vartype); + } } + } + } + } + if (context.lazyInitializedStatics.contains(varDecl.sym) && !getScope().enumWrapperClassScope) { + JCClassDecl clazz = (JCClassDecl) parent; + String prefix = clazz.getSimpleName().toString(); + if (GLOBALS_CLASS_NAME.equals(prefix)) { + prefix = ""; } else { - print("["); - if (newArray.elems != null && !newArray.elems.isEmpty()) { - for (JCExpression e : newArray.elems) { - if (!rootArrayAssignedTypes.isEmpty()) { - if (!substituteAssignedExpression(rootArrayAssignedTypes.peek(), e)) { - print(e); - } + prefix += "."; + } + print("; "); + if (globals) { + if (!isTopLevelScope()) { + print("export "); + } + print("function "); + } else { + print("public static "); + } + print(name).print(STATIC_INITIALIZATION_SUFFIX + "() : "); + substituteAndPrintType(varDecl.vartype); + print(" { "); + int liCount = context.getStaticInitializerCount(clazz.sym); + if (liCount > 0) { + if (!globals) { + print(prefix + "__static_initialize(); "); + } + } + if (varDecl.init != null && !isDefinitionScope) { + print("if(" + prefix).print(name).print(" == null) ").print(prefix).print(name).print(" = "); + if (getScope().enumWrapperClassScope) { + JCNewClass newClass = (JCNewClass) varDecl.init; + print("new ").print(clazz.getSimpleName().toString()).print("(") + .printArgList(null, newClass.args).print(")"); + } else { + if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { + print(varDecl.init); + } + } + print("; "); + } + print("return ").print(prefix).print(name).print("; }"); + if (!globals) { + String qualifiedClassName = getQualifiedTypeName(clazz.sym, globals); + context.addTopFooterStatement((isBlank(qualifiedClassName) ? "" : qualifiedClassName + ".") + name + + STATIC_INITIALIZATION_SUFFIX + "();"); + } + } else { + if (varDecl.init != null && !isDefinitionScope) { + if (!(parent instanceof JCClassDecl && getScope().innerClassNotStatic && !varDecl.sym.isStatic() + && !Util.isConstantOrNullField(varDecl))) { + if (!globals && parent instanceof JCClassDecl && getScope().interfaceScope) { + report(varDecl, varDecl.name, JSweetProblem.INVALID_FIELD_INITIALIZER_IN_INTERFACE, + varDecl.name, ((JCClassDecl) parent).name); + } else { + if (!(getScope().hasConstructorOverloadWithSuperClass + && getScope().fieldsWithInitializers.contains(varDecl))) { + print(" = "); + if (!substituteAssignedExpression(varDecl.type, varDecl.init)) { + print(varDecl.init); + } + } + } + } + } + + // var initialization is not allowed in definition + if (!isDefinitionScope && !(ambient || (isTopLevelScope() && isDefinitionScope)) + && varDecl.sym.isStatic() && varDecl.init == null) { + print(" = ").print(Util.getTypeInitialValue(varDecl.sym.type)); + } + } + } + } + + private String getTSMemberAccess(String memberName, boolean hasSelector) { + if (doesMemberNameRequireQuotes(memberName)) { + // TODO : hasSelector should not be false by now for member with + // special chars for now but we should handle node case (window + // isn't something) => replace with global context + return (hasSelector ? "" : "window") + "['" + memberName + "']"; + } else { + return (hasSelector ? "." : "") + memberName; + } + } + + private boolean doesMemberNameRequireQuotes(String name) { + for (char c : name.toCharArray()) { + if (TS_IDENTIFIER_FORBIDDEN_CHARS.contains(c)) { + return true; + } + } + return false; + } + + @Override + public void visitParens(JCParens parens) { + print("("); + super.visitParens(parens); + print(")"); + } + + @Override + public void visitImport(JCImport importDecl) { + String qualId = importDecl.getQualifiedIdentifier().toString(); + if (qualId.endsWith("*") && !(qualId.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME + ".*") + || qualId.equals(JSweetConfig.UTIL_CLASSNAME + ".*"))) { + report(importDecl, JSweetProblem.WILDCARD_IMPORT); + return; + } + String adaptedQualId = getAdapter().needsImport(new ImportElementSupport(importDecl), qualId); + if (adaptedQualId != null && adaptedQualId.contains(".")) { + if (importDecl.isStatic() && !qualId.contains("." + JSweetConfig.GLOBALS_CLASS_NAME + ".") + && !qualId.contains("." + JSweetConfig.STRING_TYPES_INTERFACE_NAME + ".")) { + if (!context.bundleMode) { + print(VAR_DECL_KEYWORD + " ").print(qualId.substring(qualId.lastIndexOf('.') + 1)).print(": any = ") + .print(qualId).print(";"); + } + } else { + String[] namePath; + if (context.useModules && importDecl.isStatic()) { + namePath = qualId.split("\\."); + } else { + namePath = adaptedQualId.split("\\."); + } + String name = namePath[namePath.length - 1]; + if (context.useModules) { + if (!adaptedQualId.startsWith(GLOBALS_PACKAGE_NAME)) { + if (!context.getImportedNames(compilationUnit.getSourceFile().getName()).contains(name)) { + print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); + context.registerImportedName(compilationUnit.getSourceFile().getName(), null, name); + } + } + } else { + if (topLevelPackage == null) { + if (context.globalImports.contains(name)) { + // Tsc global package does allow multiple import + // with + // the same name in the global namespace (bug?) + return; + } + context.globalImports.add(name); + } + if (context.bundleMode) { + // in bundle mode, we do not use imports to minimize + // dependencies + // (imports create unavoidable dependencies!) + context.importedTopPackages.add(namePath[0]); + } else { + print("import ").print(name).print(" = ").print(adaptedQualId).print(";"); + } + } + } + } + + } + + @Override + public void visitSelect(JCFieldAccess fieldAccess) { + if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(fieldAccess))) { + if (fieldAccess.selected.type.tsym instanceof PackageSymbol) { + if (context.isRootPackage(fieldAccess.selected.type.tsym)) { + if (fieldAccess.type != null && fieldAccess.type.tsym != null) { + printIdentifier(fieldAccess.type.tsym); + } else { + // TODO: see if it breaks something + print(fieldAccess.name.toString()); + } + return; + } + } + + if ("class".equals(fieldAccess.name.toString())) { + if (fieldAccess.type instanceof Type.ClassType + && context.isInterface(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) { + print("\"").print(context + .getRootRelativeJavaName(((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)) + .print("\""); + } else { + if (context.isMappedType(fieldAccess.selected.type.tsym.toString())) { + String target = context.getTypeMappingTarget(fieldAccess.selected.type.tsym.toString()); + if (CONSTRUCTOR_TYPE_MAPPING.containsKey(target)) { + print(mapConstructorType( + context.getTypeMappingTarget(fieldAccess.selected.type.tsym.toString()))); + } else { + print("\"").print(context.getRootRelativeJavaName( + ((Type.ClassType) fieldAccess.type).typarams_field.head.tsym)).print("\""); + } + } else { + print(fieldAccess.selected); + } + } + } else if ("this".equals(fieldAccess.name.toString()) && getScope().innerClassNotStatic) { + print("this." + PARENT_CLASS_FIELD_NAME); + } else if ("this".equals(fieldAccess.name.toString())) { + print("this"); + } else { + String selected = fieldAccess.selected.toString(); + if (!selected.equals(GLOBALS_CLASS_NAME)) { + if (selected.equals("super") && (fieldAccess.sym instanceof VarSymbol)) { + print("this."); + } else { + boolean accessSubstituted = false; + if (fieldAccess.sym instanceof VarSymbol) { + VarSymbol varSym = (VarSymbol) fieldAccess.sym; + if (varSym.isStatic() && varSym.owner.isInterface() + && varSym.owner != Util.getSymbol(fieldAccess.selected)) { + accessSubstituted = true; + if (context.useModules) { + // TODO: we assume it has been imported, but + // it is clearly not always the case (to be + // tested) + print(varSym.owner.getSimpleName().toString()).print("."); + } else { + print(context.getRootRelativeName(null, varSym.owner)).print("."); + } + } + } + if (!accessSubstituted) { + print(fieldAccess.selected).print("."); + } + } + } + + String fieldName = null; + if (fieldAccess.sym instanceof VarSymbol && context.getFieldNameMapping(fieldAccess.sym) != null) { + fieldName = context.getFieldNameMapping(fieldAccess.sym); + } else { + fieldName = getIdentifier(fieldAccess.sym); + } + if (doesMemberNameRequireQuotes(fieldName)) { + if (getLastPrintedChar() == '.') { + removeLastChar(); + print("['").print(fieldName).print("']"); + } else { + print("this['").print(fieldName).print("']"); + } + } else { + print(fieldName); + } + if (fieldAccess.sym instanceof VarSymbol && !fieldAccess.sym.owner.isEnum() + && context.lazyInitializedStatics.contains(fieldAccess.sym)) { + if (!staticInitializedAssignment) { + print(STATIC_INITIALIZATION_SUFFIX + "()"); + } + } + } + } + } + + private JCImport getStaticGlobalImport(String methName) { + if (getCompilationUnit() == null) { + return null; + } + for (JCImport i : getCompilationUnit().getImports()) { + if (i.staticImport) { + if (i.qualid.toString().endsWith(JSweetConfig.GLOBALS_CLASS_NAME + "." + methName)) { + return i; + } + } + } + return null; + } + + private String getStaticContainerFullName(JCImport importDecl) { + if (importDecl.getQualifiedIdentifier() instanceof JCFieldAccess) { + JCFieldAccess fa = (JCFieldAccess) importDecl.getQualifiedIdentifier(); + String name = context.getRootRelativeJavaName(fa.selected.type.tsym); + // function is a top-level global function (no need to import) + if (JSweetConfig.GLOBALS_CLASS_NAME.equals(name)) { + return null; + } + boolean globals = name.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME); + if (globals) { + name = name.substring(0, name.length() - JSweetConfig.GLOBALS_CLASS_NAME.length() - 1); + } + // function belong to the current package (no need to import) + if (compilationUnit.packge.getQualifiedName().toString().startsWith(name)) { + return null; + } + return name; + } + + return null; + } + + private long applyTargetRefCounter = 0; + + @Override + public void visitApply(JCMethodInvocation inv) { + + boolean debugMode = false; + if (context.options.isDebugMode()) { + if (Util.getSymbol(inv.meth) instanceof MethodSymbol) { + MethodSymbol methodSymbol = (MethodSymbol) Util.getSymbol(inv.meth); + if (!methodSymbol.isConstructor() && Util.isSourceElement(methodSymbol)) { + debugMode = true; + } + } + } + if (debugMode) { + print("__debug_result(yield "); + } + + if (!getAdapter().substituteMethodInvocation(new MethodInvocationElementSupport(inv))) { + String meth = inv.meth.toString(); + String methName = meth.substring(meth.lastIndexOf('.') + 1); + if (methName.equals("super") && getScope().removedSuperclass) { + return; + } + + boolean applyVarargs = true; + if (JSweetConfig.NEW_FUNCTION_NAME.equals(methName)) { + print("new "); + applyVarargs = false; + } + + boolean anonymous = isAnonymousMethod(methName); + boolean targetIsThisOrStaticImported = meth.equals(methName) || meth.equals("this." + methName); + + MethodType type = inv.meth.type instanceof MethodType ? (MethodType) inv.meth.type : null; + MethodSymbol methSym = null; + String methodName = null; + boolean keywordHandled = false; + if (targetIsThisOrStaticImported) { + JCImport staticImport = getStaticGlobalImport(methName); + if (staticImport == null) { + JCClassDecl p = getParent(JCClassDecl.class); + methSym = p == null ? null : Util.findMethodDeclarationInType(context.types, p.sym, methName, type); + if (methSym != null) { + typeChecker.checkApply(inv, methSym); + if (!methSym.isStatic()) { + if (!meth.startsWith("this.")) { + print("this"); + if (!anonymous) { + print("."); + } + } + } else { + if (meth.startsWith("this.") && methSym.isStatic()) { + report(inv, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, methSym.getSimpleName()); + } + if (!JSweetConfig.GLOBALS_CLASS_NAME.equals(methSym.owner.getSimpleName().toString())) { + print("" + methSym.owner.getSimpleName()); + if (methSym.owner.isEnum()) { + print(ENUM_WRAPPER_CLASS_SUFFIX); + } + if (!anonymous) { + print("."); + } + } + } + } else { + if (getScope().defaultMethodScope) { + TypeSymbol target = Util.getStaticImportTarget( + getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), + methName); + if (target != null) { + print(getRootRelativeName(target) + "."); + } + } else { + TypeSymbol target = Util.getStaticImportTarget(compilationUnit, methName); + if (target != null) { + print(getRootRelativeName(target) + "."); + } + } + + if (getScope().innerClass) { + JCClassDecl parent = getParent(JCClassDecl.class); + int level = 0; + MethodSymbol method = null; + if (parent != null) { + while (getScope(level++).innerClass) { + parent = getParent(JCClassDecl.class, parent); + if ((method = Util.findMethodDeclarationInType(context.types, parent.sym, methName, + type)) != null) { + break; + } + } + } + if (method != null) { + if (method.isStatic()) { + print(method.getEnclosingElement().getSimpleName().toString() + "."); + } else { + print("this."); + for (int i = 0; i < level; i++) { + print(PARENT_CLASS_FIELD_NAME + "."); + } + if (anonymous) { + removeLastChar(); + } + } + } + } + + } + } else { + JCFieldAccess staticFieldAccess = (JCFieldAccess) staticImport.qualid; + methSym = Util.findMethodDeclarationInType(context.types, staticFieldAccess.selected.type.tsym, + methName, type); + if (methSym != null) { + Map vars = new HashMap<>(); + Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); + if (vars.containsKey(methSym.getSimpleName().toString())) { + report(inv, JSweetProblem.HIDDEN_INVOCATION, methSym.getSimpleName()); + } + if (context.bundleMode && methSym.owner.getSimpleName().toString().equals(GLOBALS_CLASS_NAME) + && methSym.owner.owner != null + && !methSym.owner.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { + String prefix = getRootRelativeName(methSym.owner.owner); + if (!StringUtils.isEmpty(prefix)) { + print(getRootRelativeName(methSym.owner.owner) + "."); + } + } + } + if (JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(context.getActualName(methSym))) { + String targetClass = getStaticContainerFullName(staticImport); + if (!isBlank(targetClass)) { + print(targetClass); + print("."); + keywordHandled = true; + } + if (JSweetConfig.isLibPath(methSym.getEnclosingElement().getQualifiedName().toString())) { + methodName = methName.toLowerCase(); + } + } + } + } else { + if (inv.meth instanceof JCFieldAccess) { + JCExpression selected = ((JCFieldAccess) inv.meth).selected; + if (context.isFunctionalType(selected.type.tsym)) { + anonymous = true; + } + methSym = Util.findMethodDeclarationInType(context.types, selected.type.tsym, methName, type); + if (methSym != null) { + typeChecker.checkApply(inv, methSym); + } + } + } + + boolean isStatic = methSym == null || methSym.isStatic(); + if (!Util.hasVarargs(methSym) // + || !inv.args.isEmpty() && (inv.args.last().type.getKind() != TypeKind.ARRAY + // we dont use apply if var args type differ + || !context.types.erasure(((ArrayType) inv.args.last().type).elemtype).equals(context.types + .erasure(((ArrayType) methSym.getParameters().last().type).elemtype)))) { + applyVarargs = false; + } + + String targetVarName = null; + if (anonymous) { + if (inv.meth instanceof JCFieldAccess) { + JCExpression selected = ((JCFieldAccess) inv.meth).selected; + print(selected); + } + } else { + // method with name + if (inv.meth instanceof JCFieldAccess && applyVarargs && !targetIsThisOrStaticImported && !isStatic) { + targetVarName = "this['__jswref_" + (applyTargetRefCounter++) + "']"; + print("("); + print(targetVarName + " = "); + print(((JCFieldAccess) inv.meth).selected); + print(")"); + + String accessedMemberName; + if (keywordHandled) { + accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); + } else { + if (methSym == null) { + methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; + } + if (methSym != null) { + accessedMemberName = context.getActualName(methSym); + } else { + accessedMemberName = ((JCFieldAccess) inv.meth).name.toString(); + } + } + print(getTSMemberAccess(accessedMemberName, true)); + } else if (methodName != null) { + print(getTSMemberAccess(methodName, removeLastChar('.'))); + } else { + if (keywordHandled) { + print(inv.meth); + } else { + if (methSym == null && inv.meth instanceof JCFieldAccess + && ((JCFieldAccess) inv.meth).sym instanceof MethodSymbol) { + methSym = (MethodSymbol) ((JCFieldAccess) inv.meth).sym; + } + if (methSym != null && inv.meth instanceof JCFieldAccess) { + JCExpression selected = ((JCFieldAccess) inv.meth).selected; + if (!GLOBALS_CLASS_NAME.equals(selected.type.tsym.getSimpleName().toString())) { + print(selected).print("."); + } else { + if (context.useModules) { + if (!((ClassSymbol) selected.type.tsym).sourcefile.getName() + .equals(getCompilationUnit().sourcefile.getName())) { + // TODO: when using several qualified + // Globals classes, we + // need to disambiguate (use qualified + // name with + // underscores) + print(GLOBALS_CLASS_NAME).print("."); + } + } + + Map vars = new HashMap<>(); + Util.fillAllVariablesInScope(vars, getStack(), inv, getParent(JCMethodDecl.class)); + if (vars.containsKey(methName)) { + report(inv, JSweetProblem.HIDDEN_INVOCATION, methName); + } + } + } + if (methSym != null) { + if (context.isInvalidOverload(methSym) && !methSym.getParameters().isEmpty() + && !Util.hasTypeParameters(methSym) && !Util.hasVarargs(methSym) + && getParent(JCMethodDecl.class) != null + && !getParent(JCMethodDecl.class).sym.isDefault()) { + if (context.isInterface((TypeSymbol) methSym.getEnclosingElement())) { + removeLastChar('.'); + print("['" + getOverloadMethodName(methSym) + "']"); + } else { + print(getOverloadMethodName(methSym)); + } + } else { + print(getTSMemberAccess(context.getActualName(methSym), removeLastChar('.'))); + } + } else { + print(inv.meth); + } + } + } + } + + if (applyVarargs) { + print(".apply"); + } else { + if (inv.typeargs != null && !inv.typeargs.isEmpty()) { + print("<"); + for (JCExpression argument : inv.typeargs) { + substituteAndPrintType(argument).print(","); + } + removeLastChar(); + print(">"); + } else { + // force type arguments to any because they are inferred to + // {} by default + if (methSym != null && !methSym.getTypeParameters().isEmpty()) { + ClassSymbol target = (ClassSymbol) methSym.getEnclosingElement(); + if (!target.getQualifiedName().toString().startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { + // invalid overload type parameters are erased + Overload overload = context.getOverload(target, methSym); + boolean inOverload = overload != null && overload.methods.size() > 1; + if (!(inOverload && !overload.isValid)) { + printAnyTypeArguments(methSym.getTypeParameters().size()); + } + } + } + } + } + + print("("); + + if (applyVarargs) { + String contextVar = "null"; + if (targetIsThisOrStaticImported) { + contextVar = "this"; + } else if (targetVarName != null) { + contextVar = targetVarName; + } + + print(contextVar + ", "); + if (inv.args.size() > 1) { + print("["); + } + } + + int argsLength = applyVarargs ? inv.args.size() - 1 : inv.args.size(); + + if (getScope().innerClassNotStatic && "super".equals(methName)) { + TypeSymbol s = getParent(JCClassDecl.class).extending.type.tsym; + if (s.getEnclosingElement() instanceof ClassSymbol && !s.isStatic()) { + print(PARENT_CLASS_FIELD_NAME); + if (argsLength > 0) { + print(", "); + } + } + } + + if (getScope().enumWrapperClassScope && isAnonymousClass() && "super".equals(methName)) { + print(ENUM_WRAPPER_CLASS_ORDINAL + ", " + ENUM_WRAPPER_CLASS_NAME); + if (argsLength > 0) { + print(", "); + } + } + + for (int i = 0; i < argsLength; i++) { + JCExpression arg = inv.args.get(i); + if (inv.meth.type != null) { + List argTypes = ((MethodType) inv.meth.type).argtypes; + Type paramType = i < argTypes.size() ? argTypes.get(i) : argTypes.get(argTypes.size() - 1); + if (!substituteAssignedExpression(paramType, arg)) { + print(arg); + } + } else { + // this should never happen but we fall back just in case + print(arg); + } + if (i < argsLength - 1) { + print(", "); + } + } + + if (applyVarargs) { + if (inv.args.size() > 1) { + // we cast array to any[] to avoid concat error on + // different + // types + print("].concat("); + } + + print(inv.args.last()); + + if (inv.args.size() > 1) { + print(")"); + } + } + + print(")"); + } + if (debugMode) { + print(")"); + } + + } + + private boolean isAnonymousMethod(String methName) { + boolean anonymous = JSweetConfig.ANONYMOUS_FUNCTION_NAME.equals(methName) + || JSweetConfig.ANONYMOUS_STATIC_FUNCTION_NAME.equals(methName) + || (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_FUNCTION_NAME.equals(methName)) + || (context.deprecatedApply && JSweetConfig.ANONYMOUS_DEPRECATED_STATIC_FUNCTION_NAME.equals(methName)) + || JSweetConfig.NEW_FUNCTION_NAME.equals(methName); + return anonymous; + } + + @Override + public void visitIdent(JCIdent ident) { + String name = ident.toString(); + + if (getScope().inlinedConstructorArgs != null) { + if (ident.sym instanceof VarSymbol && getScope().inlinedConstructorArgs.contains(name)) { + print("__args[" + getScope().inlinedConstructorArgs.indexOf(name) + "]"); + return; + } + } + + if (!getAdapter().substitute(ExtendedElementFactory.INSTANCE.create(ident))) { + boolean lazyInitializedStatic = false; + // add this of class name if ident is a field + if (ident.sym instanceof VarSymbol && !ident.sym.name.equals(context.names._this) + && !ident.sym.name.equals(context.names._super)) { + VarSymbol varSym = (VarSymbol) ident.sym; // findFieldDeclaration(currentClass, + // ident.name); + if (varSym != null) { + if (varSym.owner instanceof ClassSymbol) { + if (context.getFieldNameMapping(varSym) != null) { + name = context.getFieldNameMapping(varSym); + } else { + name = getIdentifier(varSym); + } + if (!varSym.getModifiers().contains(Modifier.STATIC)) { + print("this."); + JCClassDecl parent = getParent(JCClassDecl.class); + int level = 0; + boolean foundInParent = false; + while (getScope(level++).innerClassNotStatic) { + parent = getParent(JCClassDecl.class, parent); + if (parent != null && varSym.owner == parent.sym) { + foundInParent = true; + break; + } + } + if (foundInParent) { + for (int i = 0; i < level; i++) { + print(PARENT_CLASS_FIELD_NAME + "."); + } + } + } else { + if (context.lazyInitializedStatics.contains(varSym)) { + lazyInitializedStatic = true; + } + if (!varSym.owner.getQualifiedName().toString().endsWith("." + GLOBALS_CLASS_NAME)) { + if (context.bundleMode && !varSym.owner.equals(getParent(JCClassDecl.class).sym)) { + String prefix = context.getRootRelativeName(null, varSym.owner); + if (!StringUtils.isEmpty(prefix)) { + print(context.getRootRelativeName(null, varSym.owner) + "."); + } + } else { + if (!varSym.owner.getSimpleName().toString().equals(GLOBALS_PACKAGE_NAME)) { + print(varSym.owner.getSimpleName() + "."); + } + } + } else { + if (context.bundleMode) { + String prefix = context.getRootRelativeName(null, varSym.owner); + prefix = prefix.substring(0, prefix.length() - GLOBALS_CLASS_NAME.length()); + if (!prefix.equals(GLOBALS_PACKAGE_NAME + ".") + && !prefix.endsWith("." + GLOBALS_PACKAGE_NAME + ".")) { + print(prefix); + } + } + } + } + } else { + if (varSym.owner instanceof MethodSymbol && isAnonymousClass() + && getScope(1).finalVariables + .get(getScope(1).anonymousClasses.indexOf(getParent(JCClassDecl.class))) + .contains(varSym)) { + print("this."); + } else { + if (context.bundleMode && varSym.owner instanceof MethodSymbol) { + if (context.importedTopPackages.contains(name)) { + name = "__var_" + name; + } + } + if (JSweetConfig.JS_KEYWORDS.contains(name)) { + name = JSweetConfig.JS_KEYWORD_PREFIX + name; + } + } + } + } + } + if (ident.sym instanceof ClassSymbol) { + ClassSymbol clazz = (ClassSymbol) ident.sym; + boolean prefixAdded = false; + if (getScope().defaultMethodScope) { + if (Util.isImported(getContext().getDefaultMethodCompilationUnit(getParent(JCMethodDecl.class)), + clazz)) { + print(getRootRelativeName(clazz.getEnclosingElement()) + "."); + prefixAdded = true; + } + } + // add parent class name if ident is an inner class of the + // current class + if (!prefixAdded && clazz.getEnclosingElement() instanceof ClassSymbol) { + if (context.useModules) { + print(clazz.getEnclosingElement().getSimpleName() + "."); + prefixAdded = true; + } else { + // if the class has not been imported, we need to add + // the containing class prefix + if (!getCompilationUnit().getImports().stream() + .map(i -> i.qualid.type == null ? null : i.qualid.type.tsym) + .anyMatch(t -> t == clazz)) { + print(clazz.getEnclosingElement().getSimpleName() + "."); + prefixAdded = true; + } + } + } + if (!prefixAdded && context.bundleMode && !clazz.equals(getParent(JCClassDecl.class).sym)) { + print(getRootRelativeName(clazz)); + } else { + print(name); + } + } else { + if (doesMemberNameRequireQuotes(name)) { + if (getLastPrintedChar() == '.') { + removeLastChar(); + print("['").print(name).print("']"); + } else { + print("this['").print(name).print("']"); + } + } else { + print(name); + } + if (lazyInitializedStatic) { + if (!staticInitializedAssignment) { + print(STATIC_INITIALIZATION_SUFFIX + "()"); + } + } + } + } + } + + @Override + public void visitTypeApply(JCTypeApply typeApply) { + substituteAndPrintType(typeApply); + } + + private int initAnonymousClass(JCNewClass newClass) { + int anonymousClassIndex = getScope().anonymousClasses.indexOf(newClass.def); + if (anonymousClassIndex == -1) { + anonymousClassIndex = getScope().anonymousClasses.size(); + getScope().anonymousClasses.add(newClass.def); + getScope().anonymousClassesConstructors.add(newClass); + LinkedHashSet finalVars = new LinkedHashSet<>(); + getScope().finalVariables.add(finalVars); + new TreeScanner() { + public void visitIdent(JCIdent var) { + if (var.sym != null && (var.sym instanceof VarSymbol)) { + VarSymbol varSymbol = (VarSymbol) var.sym; + if (varSymbol.getEnclosingElement() instanceof MethodSymbol && varSymbol.getEnclosingElement() + .getEnclosingElement() == getParent(JCClassDecl.class).sym) { + finalVars.add((VarSymbol) var.sym); + } + } + } + }.visitClassDef(newClass.def); + + } + return anonymousClassIndex; + } + + @Override + public void visitNewClass(JCNewClass newClass) { + ClassSymbol clazz = ((ClassSymbol) newClass.clazz.type.tsym); + if (clazz.getSimpleName().toString().equals(JSweetConfig.GLOBALS_CLASS_NAME)) { + report(newClass, JSweetProblem.GLOBAL_CANNOT_BE_INSTANTIATED); + return; + } + if (getScope().localClasses.stream().map(c -> c.type).anyMatch(t -> t.equals(newClass.type))) { + print("new ").print(getScope().name + ".").print(newClass.clazz.toString()); + print("(").printConstructorArgList(newClass, true).print(")"); + return; + } + boolean isInterface = context.isInterface(clazz); + if (newClass.def != null || isInterface) { + if (context.isAnonymousClass(newClass)) { + int anonymousClassIndex = initAnonymousClass(newClass); + print("new ").print(getScope().name + "." + getScope().name + ANONYMOUS_PREFIX + anonymousClassIndex); + if (newClass.def.getModifiers().getFlags().contains(Modifier.STATIC)) { + printAnonymousClassTypeArgs(newClass); + } + print("(").printConstructorArgList(newClass, false).print(")"); + return; + } + + if (isInterface + || context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { + if (isInterface) { + print(""); + } + + Set interfaces = new HashSet<>(); + if (getContext().options.isInterfaceTracking()) { + context.grabSupportedInterfaceNames(interfaces, clazz); + if (!interfaces.isEmpty()) { + print("Object.defineProperty("); + } + } + print("{").println().startIndent(); + boolean statementPrinted = false; + boolean initializationBlockFound = false; + if (newClass.def != null) { + for (JCTree m : newClass.def.getMembers()) { + if (m instanceof JCBlock) { + initializationBlockFound = true; + List initializedVars = new ArrayList<>(); + for (JCTree s : ((JCBlock) m).stats) { + boolean currentStatementPrinted = false; + if (s instanceof JCExpressionStatement + && ((JCExpressionStatement) s).expr instanceof JCAssign) { + JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; + VarSymbol var = null; + if (assignment.lhs instanceof JCFieldAccess) { + var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); + printIndent().print(var.getSimpleName().toString()); + } else if (assignment.lhs instanceof JCIdent) { + var = Util.findFieldDeclaration(clazz, ((JCIdent) assignment.lhs).name); + printIndent().print(assignment.lhs.toString()); + } else { + continue; + } + initializedVars.add(var); + print(": ").print(assignment.rhs).print(",").println(); + currentStatementPrinted = true; + statementPrinted = true; + } else if (s instanceof JCExpressionStatement + && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { + JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; + MethodInvocationElement invocationElement = (MethodInvocationElement) ExtendedElementFactory.INSTANCE + .create(invocation); + if (invocationElement.getMethodName() + .equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) { + if (invocation.getArguments().size() == 3) { + if ("this".equals(invocation.getArguments().get(0).toString())) { + printIndent().print(invocation.args.tail.head).print(": ") + .print(invocation.args.tail.tail.head).print(",").println(); + } + currentStatementPrinted = true; + statementPrinted = true; } else { - print(e); + printIndent().print(invocation.args.head).print(": ") + .print(invocation.args.tail.head).print(",").println(); + currentStatementPrinted = true; + statementPrinted = true; } - print(", "); + } } - removeLastChars(2); - if (!rootArrayAssignedTypes.isEmpty()) { - rootArrayAssignedTypes.pop(); + if (!currentStatementPrinted) { + report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); } + } + for (Symbol s : clazz.getEnclosedElements()) { + if (s instanceof VarSymbol) { + if (!initializedVars.contains(s)) { + if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { + report(m, JSweetProblem.UNINITIALIZED_FIELD, s); + } + } + } + } + } + if (m instanceof JCMethodDecl) { + JCMethodDecl method = (JCMethodDecl) m; + if (!method.sym.isConstructor()) { + printIndent().print(method.getName() + ": ("); + for (JCVariableDecl param : method.getParameters()) { + print(param.getName() + ", "); + } + if (!method.getParameters().isEmpty()) { + removeLastChars(2); + } + print(") => "); + print(method.body); + print(",").println(); + statementPrinted = true; + } + } + } + if (statementPrinted) { + removeLastChars(2); + } + + } + if (!statementPrinted && !initializationBlockFound) { + for (Symbol s : clazz.getEnclosedElements()) { + if (s instanceof VarSymbol) { + if (!context.hasAnnotationType(s, JSweetConfig.ANNOTATION_OPTIONAL)) { + report(newClass, JSweetProblem.UNINITIALIZED_FIELD, s); + } + } + } + } + + println().endIndent().printIndent().print("}"); + if (getContext().options.isInterfaceTracking()) { + if (!interfaces.isEmpty()) { + print(", '" + INTERFACES_FIELD_NAME + "', { configurable: true, value: "); + print("["); + for (String i : interfaces) { + print("\"").print(i).print("\","); + } + removeLastChar(); + print("]"); + print(" })"); + } + } + } else { + + // ((target : DataStruct3) => { + // target['i'] = 1; + // target['s2'] = ""; + // return target + // })(new DataStruct3()); + + print("((target:").print(newClass.clazz).print(") => {").println().startIndent(); + for (JCTree m : newClass.def.getMembers()) { + if (m instanceof JCBlock) { + for (JCTree s : ((JCBlock) m).stats) { + boolean currentStatementPrinted = false; + if (s instanceof JCExpressionStatement + && ((JCExpressionStatement) s).expr instanceof JCAssign) { + JCAssign assignment = (JCAssign) ((JCExpressionStatement) s).expr; + VarSymbol var = null; + if (assignment.lhs instanceof JCFieldAccess) { + var = Util.findFieldDeclaration(clazz, ((JCFieldAccess) assignment.lhs).name); + printIndent().print("target['").print(var.getSimpleName().toString()).print("']"); + } else if (assignment.lhs instanceof JCIdent) { + printIndent().print("target['").print(assignment.lhs.toString()).print("']"); + } else { + continue; + } + print(" = ").print(assignment.rhs).print(";").println(); + currentStatementPrinted = true; + } else if (s instanceof JCExpressionStatement + && ((JCExpressionStatement) s).expr instanceof JCMethodInvocation) { + JCMethodInvocation invocation = (JCMethodInvocation) ((JCExpressionStatement) s).expr; + MethodInvocationElement invocationElement = (MethodInvocationElement) ExtendedElementFactory.INSTANCE + .create(invocation); + if (invocationElement.getMethodName().equals(JSweetConfig.INDEXED_SET_FUCTION_NAME)) { + if (invocation.getArguments().size() == 3) { + if ("this".equals(invocation.getArguments().get(0).toString())) { + printIndent().print("target[").print(invocation.args.tail.head).print("]") + .print(" = ").print(invocation.args.tail.tail.head).print(";") + .println(); + } + currentStatementPrinted = true; + } else { + printIndent().print("target[").print(invocation.args.head).print("]") + .print(" = ").print(invocation.args.tail.head).print(";").println(); + currentStatementPrinted = true; + } + } + } + if (!currentStatementPrinted) { + report(s, JSweetProblem.INVALID_INITIALIZER_STATEMENT); + } + } + } + } + printIndent().print("return target;").println(); + println().endIndent().printIndent().print("})("); + print("new ").print(newClass.clazz).print("(").printArgList(null, newClass.args).print("))"); + } + } else { + if (context.hasAnnotationType(newClass.clazz.type.tsym, JSweetConfig.ANNOTATION_OBJECT_TYPE)) { + print("{}"); + } else { + if (!getAdapter().substituteNewClass(new NewClassElementSupport(newClass))) { + String mappedType = context.getTypeMappingTarget(newClass.clazz.type.toString()); + if (typeChecker.checkType(newClass, null, newClass.clazz)) { + + boolean applyVarargs = true; + MethodSymbol methSym = (MethodSymbol) newClass.constructor; + if (newClass.args.size() == 0 || !Util.hasVarargs(methSym) // + || newClass.args.last().type.getKind() != TypeKind.ARRAY + // we dont use apply if var args type differ + || !context.types.erasure(((ArrayType) newClass.args.last().type).elemtype) + .equals(context.types + .erasure(((ArrayType) methSym.getParameters().last().type).elemtype))) { + applyVarargs = false; + } + if (applyVarargs) { + // this is necessary in case the user defines a + // Function class that hides the global Function + // class + context.addGlobalsMapping("Function", "__Function"); + print("new (__Function.prototype.bind.apply("); + if (mappedType != null) { + print(mapConstructorType(mappedType)); + } else { + print(newClass.clazz); + } + print(", [null"); + for (int i = 0; i < newClass.args.length() - 1; i++) { + print(", ").print(newClass.args.get(i)); + } + print("].concat(").print(newClass.args.last()).print(")))"); + } else { + if (newClass.clazz instanceof JCTypeApply) { + JCTypeApply typeApply = (JCTypeApply) newClass.clazz; + mappedType = context.getTypeMappingTarget(typeApply.clazz.type.toString()); + print("new "); + if (mappedType != null) { + print(mapConstructorType(mappedType)); + } else { + print(typeApply.clazz); + } + if (!typeApply.arguments.isEmpty()) { + print("<").printTypeArgList(typeApply.arguments).print(">"); + } else { + // erase types since the diamond (<>) + // operator + // does not exists in TypeScript + printAnyTypeArguments( + ((ClassSymbol) newClass.clazz.type.tsym).getTypeParameters().length()); + } + print("(").printConstructorArgList(newClass, false).print(")"); + } else { + print("new "); + if (mappedType != null) { + print(mapConstructorType(mappedType)); + } else { + print(newClass.clazz); + } + print("(").printConstructorArgList(newClass, false).print(")"); + } + } + } + } + } + } + + } + + public void printAnyTypeArguments(int count) { + print("<"); + for (int i = 0; i < count; i++) { + print("any, "); + } + if (count > 0) { + removeLastChars(2); + } + print(">"); + + } + + @Override + public AbstractTreePrinter printConstructorArgList(JCNewClass newClass, boolean localClass) { + boolean printed = false; + if (localClass || (getScope().anonymousClasses.contains(newClass.def) + && !newClass.def.getModifiers().getFlags().contains(Modifier.STATIC))) { + print("this"); + if (!newClass.args.isEmpty()) { + print(", "); + } + printed = true; + } else if ((newClass.clazz.type.tsym.getEnclosingElement() instanceof ClassSymbol + && !newClass.clazz.type.tsym.getModifiers().contains(Modifier.STATIC))) { + print("this"); + JCClassDecl parent = getParent(JCClassDecl.class); + ClassSymbol parentSymbol = parent == null ? null : parent.sym; + if (newClass.clazz.type.tsym.getEnclosingElement() != parentSymbol) { + print("." + PARENT_CLASS_FIELD_NAME); + } + if (!newClass.args.isEmpty()) { + print(", "); + } + printed = true; + } + + MethodType t = (MethodType) newClass.constructorType; + + printArgList(t == null ? null : t.argtypes, newClass.args); + int index = getScope().anonymousClasses.indexOf(newClass.def); + if (index >= 0 && !getScope().finalVariables.get(index).isEmpty()) { + if (printed || !newClass.args.isEmpty()) { + print(", "); + } + for (VarSymbol v : getScope().finalVariables.get(index)) { + print(v.getSimpleName().toString()); + print(", "); + } + removeLastChars(2); + } + return this; + } + + @Override + public void visitLiteral(JCLiteral literal) { + String s = literal.toString(); + switch (literal.typetag) { + case FLOAT: + if (s.endsWith("F")) { + s = s.substring(0, s.length() - 1); + } + break; + case LONG: + if (s.endsWith("L")) { + s = s.substring(0, s.length() - 1); + } + break; + default: + } + print(s); + } + + @Override + public void visitIndexed(JCArrayAccess arrayAccess) { + if (!getAdapter().substituteArrayAccess(new ArrayAccessElementSupport(arrayAccess))) { + print(arrayAccess.indexed).print("[").print(arrayAccess.index).print("]"); + } + } + + @Override + public void visitForeachLoop(JCEnhancedForLoop foreachLoop) { + String indexVarName = "index" + Util.getId(); + boolean[] hasLength = { false }; + TypeSymbol targetType = foreachLoop.expr.type.tsym; + Util.scanMemberDeclarationsInType(targetType, getAdapter().getErasedTypes(), element -> { + if (element instanceof VarSymbol) { + if ("length".equals(element.getSimpleName().toString()) && Util.isNumber(((VarSymbol) element).type)) { + hasLength[0] = true; + return false; + } + } + return true; + }); + if (!getAdapter().substituteForEachLoop(new ForeachLoopElementSupport(foreachLoop), hasLength[0], + indexVarName)) { + boolean noVariable = foreachLoop.expr instanceof JCIdent || foreachLoop.expr instanceof JCFieldAccess; + if (noVariable) { + print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < ") + .print(foreachLoop.expr).print("." + "length" + "; " + indexVarName + "++) {").println() + .startIndent().printIndent(); + print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = ").print(foreachLoop.expr) + .print("[" + indexVarName + "];").println(); + } else { + String arrayVarName = "array" + Util.getId(); + print("{").println().startIndent().printIndent(); + print(VAR_DECL_KEYWORD + " " + arrayVarName + " = ").print(foreachLoop.expr).print(";").println() + .printIndent(); + print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=0; " + indexVarName + " < " + arrayVarName + + ".length; " + indexVarName + "++) {").println().startIndent().printIndent(); + print(VAR_DECL_KEYWORD + " " + foreachLoop.var.name.toString() + " = " + arrayVarName + "[" + + indexVarName + "];").println(); + } + printIndent().print(foreachLoop.body); + endIndent().println().printIndent().print("}"); + if (!noVariable) { + endIndent().println().printIndent().print("}"); + } + } + } + + @Override + public void visitTypeIdent(JCPrimitiveTypeTree type) { + switch (type.typetag) { + case BYTE: + case DOUBLE: + case FLOAT: + case INT: + case LONG: + case SHORT: + print("number"); + break; + default: + print(type.toString()); + } + } + + private boolean singlePrecisionFloats() { + return !context.options.isDisableSinglePrecisionFloats() + && context.options.getEcmaTargetVersion().higherThan(EcmaScriptComplianceLevel.ES3); + } + + @Override + public void visitBinary(JCBinary binary) { + if (!getAdapter().substituteBinaryOperator(new BinaryOperatorElementSupport(binary))) { + boolean closeParen = false; + boolean truncate = false; + if (Util.isIntegral(binary.type) && binary.getKind() == Kind.DIVIDE) { + if (binary.type.getKind() == TypeKind.LONG) { + print("Math.floor("); + closeParen = true; + } else { + print("("); + truncate = true; + } + } + if (singlePrecisionFloats() && binary.type.getKind() == TypeKind.FLOAT) { + print("(Math).fround("); + closeParen = true; + } + boolean charWrapping = Util.isArithmeticOrLogicalOperator(binary.getKind()) + || Util.isComparisonOperator(binary.getKind()); + boolean actualCharWrapping = false; + if (charWrapping && binary.lhs.type.isPrimitive() && context.symtab.charType.tsym == binary.lhs.type.tsym + && !(binary.rhs.type.tsym == context.symtab.stringType.tsym)) { + actualCharWrapping = true; + if (binary.lhs instanceof JCLiteral) { + print(binary.lhs).print(".charCodeAt(0)"); + } else { + print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(").print(binary.lhs).print(")"); + } + } else { + print(binary.lhs); + } + String op = binary.operator.name.toString(); + if (binary.lhs.type.getKind() == TypeKind.BOOLEAN) { + if ("|".equals(op)) { + op = "||"; + } else if ("&".equals(op)) { + op = "&&"; + } else if ("^".equals(op)) { + op = "!=="; + } + } + if ("==".equals(op) || "!=".equals(op)) { + if (charWrapping && binary.rhs.type.isPrimitive() + && context.symtab.charType.tsym == binary.rhs.type.tsym + && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { + actualCharWrapping = true; + } + } + + if (!actualCharWrapping && ("==".equals(op) || "!=".equals(op))) { + switch (getComparisonMode()) { + case FORCE_STRICT: + op += "="; + break; + case STRICT: + if (!(Util.isNullLiteral(binary.lhs) || Util.isNullLiteral(binary.rhs))) { + op += "="; + } + break; + default: + break; + } + } + + space().print(op).space(); + if (charWrapping && binary.rhs.type.isPrimitive() && context.symtab.charType.tsym == binary.rhs.type.tsym + && !(binary.lhs.type.tsym == context.symtab.stringType.tsym)) { + if (binary.rhs instanceof JCLiteral) { + print(binary.rhs).print(".charCodeAt(0)"); + } else { + print("(c => c.charCodeAt==null?c:c.charCodeAt(0))(").print(binary.rhs).print(")"); + } + } else { + print(binary.rhs); + } + if (closeParen) { + print(")"); + } + if (truncate) { + print("|0)"); + } + } + } + + @Override + public void visitIf(JCIf ifStatement) { + print("if").print(ifStatement.cond).print(" "); + print(ifStatement.thenpart); + if (!(ifStatement.thenpart instanceof JCBlock)) { + if (!statementsWithNoSemis.contains(ifStatement.thenpart.getClass())) { + print(";"); + } + } + if (ifStatement.elsepart != null) { + print(" else "); + print(ifStatement.elsepart); + if (!(ifStatement.elsepart instanceof JCBlock)) { + if (!statementsWithNoSemis.contains(ifStatement.elsepart.getClass())) { + print(";"); + } + } + } + } + + @Override + public void visitReturn(JCReturn returnStatement) { + print("return"); + if (returnStatement.expr != null) { + JCTree parentFunction = getFirstParent(JCMethodDecl.class, JCLambda.class); + if (returnStatement.expr.type == null) { + report(returnStatement, JSweetProblem.CANNOT_ACCESS_THIS, + parentFunction == null ? returnStatement.toString() : parentFunction.toString()); + return; + } + print(" "); + Type returnType = null; + if (parentFunction != null) { + if (parentFunction instanceof JCMethodDecl) { + returnType = ((JCMethodDecl) parentFunction).restype.type; + } else { + // TODO: this cannot work! Calculate the return type of the + // lambda + // either from the functional type type arguments, of from + // the method defining the lambda's signature + // returnType = ((JCLambda) parentFunction).type; + } + } + if (!substituteAssignedExpression(returnType, returnStatement.expr)) { + print(returnStatement.expr); + } + } + } + + private boolean staticInitializedAssignment = false; + + private VarSymbol getStaticInitializedField(JCTree expr) { + if (expr instanceof JCIdent) { + return context.lazyInitializedStatics.contains(((JCIdent) expr).sym) ? (VarSymbol) ((JCIdent) expr).sym + : null; + } else if (expr instanceof JCFieldAccess) { + return context.lazyInitializedStatics.contains(((JCFieldAccess) expr).sym) + ? (VarSymbol) ((JCFieldAccess) expr).sym : null; + } else { + return null; + } + } + + @Override + public void visitAssignop(JCAssignOp assignOp) { + boolean expand = staticInitializedAssignment = (getStaticInitializedField(assignOp.lhs) != null); + boolean expandChar = assignOp.lhs.type.getKind() == TypeKind.CHAR; + print(assignOp.lhs); + staticInitializedAssignment = false; + String op = assignOp.operator.name.toString(); + if (assignOp.lhs.type.getKind() == TypeKind.BOOLEAN) { + if ("|".equals(op)) { + print(" = ").print(assignOp.lhs).print(" || ").print(assignOp.rhs); + return; + } else if ("&".equals(op)) { + print(" = ").print(assignOp.lhs).print(" && ").print(assignOp.rhs); + return; + } + } + if (expandChar) { + print(" = String.fromCharCode(").substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.lhs) + .print(" " + op + " ").substituteAndPrintAssignedExpression(context.symtab.intType, assignOp.rhs) + .print(")"); + return; + } + if (expand) { + print(" = ").print(assignOp.lhs).print(" " + op + " ").print(assignOp.rhs); + return; + } + print(" " + op + "= "); + print(assignOp.rhs); + } + + @Override + public void visitConditional(JCConditional conditional) { + print(conditional.cond); + print("?"); + if (!substituteAssignedExpression( + rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), + conditional.truepart)) { + print(conditional.truepart); + } + print(":"); + if (!substituteAssignedExpression( + rootConditionalAssignedTypes.isEmpty() ? null : rootConditionalAssignedTypes.peek(), + conditional.falsepart)) { + print(conditional.falsepart); + } + if (!rootConditionalAssignedTypes.isEmpty()) { + rootConditionalAssignedTypes.pop(); + } + } + + @Override + public void visitForLoop(JCForLoop forLoop) { + print("for(").printArgList(null, forLoop.init).print("; ").print(forLoop.cond).print("; ") + .printArgList(null, forLoop.step).print(") "); + print(forLoop.body); + } + + @Override + public void visitContinue(JCContinue continueStatement) { + print("continue"); + if (continueStatement.label != null) { + print(" ").print(continueStatement.label.toString()); + } + } + + @Override + public void visitBreak(JCBreak breakStatement) { + print("break"); + if (breakStatement.label != null) { + print(" ").print(breakStatement.label.toString()); + } + } + + @Override + public void visitLabelled(JCLabeledStatement labelledStatement) { + print(labelledStatement.label.toString()).print(": "); + print(labelledStatement.body); + } + + @Override + public void visitTypeArray(JCArrayTypeTree arrayType) { + print(arrayType.elemtype).print("[]"); + } + + @Override + public void visitNewArray(JCNewArray newArray) { + if (newArray.elemtype != null) { + typeChecker.checkType(newArray, null, newArray.elemtype); + } + if (newArray.dims != null && !newArray.dims.isEmpty()) { + if (newArray.dims.size() == 1) { + if (Util.isNumber(newArray.elemtype.type)) { + if (newArray.dims.head instanceof JCLiteral + && ((int) ((JCLiteral) newArray.dims.head).value) <= 10) { + boolean hasElements = false; + print("["); + for (int i = 0; i < (int) ((JCLiteral) newArray.dims.head).value; i++) { + print("0, "); + hasElements = true; + } + if (hasElements) { + removeLastChars(2); } print("]"); - } - } - - boolean inRollback = false; - - @Override - public void visitUnary(JCUnary unary) { - if (!getAdapter().substituteUnaryOperator(new UnaryOperatorElementSupport(unary))) { - if (getContext().options.isSupportSaticLazyInitialization()) { - if (!inRollback) { - JCStatement statement = null; - VarSymbol[] staticInitializedField = { null }; - switch (unary.getTag()) { - case POSTDEC: - case POSTINC: - case PREDEC: - case PREINC: - staticInitializedAssignment = (staticInitializedField[0] = getStaticInitializedField( - unary.arg)) != null; - if (staticInitializedAssignment) { - statement = getParent(JCStatement.class); - } - default: - } - if (statement != null) { - rollback(statement, tree -> { - print(context.getRootRelativeName(null, staticInitializedField[0].getEnclosingElement())) - .print(".").print(staticInitializedField[0].getSimpleName().toString() - + STATIC_INITIALIZATION_SUFFIX + "();") - .println().printIndent(); - inRollback = true; - scan(tree); - }); - } - } else { - inRollback = false; - } - } - switch (unary.getTag()) { - case POS: - print("+").print(unary.arg); - break; - case NEG: - print("-").print(unary.arg); - break; - case POSTDEC: - case POSTINC: - print(unary.arg); - print(unary.operator.name.toString()); - break; - default: - print(unary.operator.name.toString()); - print(unary.arg); - break; - } - } - } - - @Override - public void visitSwitch(JCSwitch switchStatement) { - print("switch("); - print(switchStatement.selector); - if (context.types.isSameType(context.symtab.charType, switchStatement.selector.type)) { - print(".charCodeAt(0)"); - } - print(") {").println(); - for (JCCase caseStatement : switchStatement.cases) { - printIndent(); - print(caseStatement); - } - printIndent().print("}"); - } - - protected void printCaseStatementPattern(JCExpression pattern) { - } - - @Override - public void visitCase(JCCase caseStatement) { - if (caseStatement.pat != null) { - print("case "); - if (!getAdapter().substituteCaseStatementPattern(new CaseElementSupport(caseStatement), - ExtendedElementFactory.INSTANCE.create(caseStatement.pat))) { - if (caseStatement.pat.type.isPrimitive() - || context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { - if (caseStatement.pat instanceof JCIdent) { - Object value = ((VarSymbol) ((JCIdent) caseStatement.pat).sym).getConstValue(); - if (context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { - print("\"" + value + "\" /* " + caseStatement.pat + " */"); - } else { - print("" + value + " /* " + caseStatement.pat + " */"); - } - } else { - if (context.types.isSameType(context.symtab.charType, caseStatement.pat.type)) { - print("" + ((JCLiteral) caseStatement.pat).value + " /* " + caseStatement.pat + " */"); - } else { - print(caseStatement.pat); - } - } - } else { - if (context.useModules) { - print(caseStatement.pat.type.tsym.getSimpleName() + "." + caseStatement.pat); - } else { - print(getRootRelativeName(caseStatement.pat.type.tsym) + "." + caseStatement.pat); - } - } - } + } else { + print("(s => { let a=[]; while(s-->0) a.push(0); return a; })(").print(newArray.dims.head) + .print(")"); + } } else { - print("default"); + print("new Array(").print(newArray.dims.head).print(")"); } - print(":"); - println().startIndent(); - for (JCStatement statement : caseStatement.stats) { - printIndent(); - print(statement); - if (!statementsWithNoSemis.contains(statement.getClass())) { - print(";"); + } else { + print(" (function(dims) { " + VAR_DECL_KEYWORD + + " allocate = function(dims) { if(dims.length==0) { return " + + (Util.isNumber(newArray.elemtype.type) ? "0" : "undefined") + "; } else { " + VAR_DECL_KEYWORD + + " array = []; for(" + VAR_DECL_KEYWORD + + " i = 0; i < dims[0]; i++) { array.push(allocate(dims.slice(1))); } return array; }}; return allocate(dims);})"); + print("(["); + printArgList(null, newArray.dims); + print("])"); + } + } else { + print("["); + if (newArray.elems != null && !newArray.elems.isEmpty()) { + for (JCExpression e : newArray.elems) { + if (!rootArrayAssignedTypes.isEmpty()) { + if (!substituteAssignedExpression(rootArrayAssignedTypes.peek(), e)) { + print(e); } - println(); + } else { + print(e); + } + print(", "); } - endIndent(); + removeLastChars(2); + if (!rootArrayAssignedTypes.isEmpty()) { + rootArrayAssignedTypes.pop(); + } + } + print("]"); + } + } + + boolean inRollback = false; + + @Override + public void visitUnary(JCUnary unary) { + if (!getAdapter().substituteUnaryOperator(new UnaryOperatorElementSupport(unary))) { + if (getContext().options.isSupportSaticLazyInitialization()) { + if (!inRollback) { + JCStatement statement = null; + VarSymbol[] staticInitializedField = { null }; + switch (unary.getTag()) { + case POSTDEC: + case POSTINC: + case PREDEC: + case PREINC: + staticInitializedAssignment = (staticInitializedField[0] = getStaticInitializedField( + unary.arg)) != null; + if (staticInitializedAssignment) { + statement = getParent(JCStatement.class); + } + default: + } + if (statement != null) { + rollback(statement, tree -> { + print(context.getRootRelativeName(null, staticInitializedField[0].getEnclosingElement())) + .print(".").print(staticInitializedField[0].getSimpleName().toString() + + STATIC_INITIALIZATION_SUFFIX + "();") + .println().printIndent(); + inRollback = true; + scan(tree); + }); + } + } else { + inRollback = false; + } + } + switch (unary.getTag()) { + case POS: + print("+").print(unary.arg); + break; + case NEG: + print("-").print(unary.arg); + break; + case POSTDEC: + case POSTINC: + print(unary.arg); + print(unary.operator.name.toString()); + break; + default: + print(unary.operator.name.toString()); + print(unary.arg); + break; + } + } + } + + @Override + public void visitSwitch(JCSwitch switchStatement) { + print("switch("); + print(switchStatement.selector); + if (context.types.isSameType(context.symtab.charType, switchStatement.selector.type)) { + print(".charCodeAt(0)"); + } + print(") {").println(); + for (JCCase caseStatement : switchStatement.cases) { + printIndent(); + print(caseStatement); + } + printIndent().print("}"); + } + + protected void printCaseStatementPattern(JCExpression pattern) { + } + + @Override + public void visitCase(JCCase caseStatement) { + if (caseStatement.pat != null) { + print("case "); + if (!getAdapter().substituteCaseStatementPattern(new CaseElementSupport(caseStatement), + ExtendedElementFactory.INSTANCE.create(caseStatement.pat))) { + if (caseStatement.pat.type.isPrimitive() + || context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { + if (caseStatement.pat instanceof JCIdent) { + Object value = ((VarSymbol) ((JCIdent) caseStatement.pat).sym).getConstValue(); + if (context.types.isSameType(context.symtab.stringType, caseStatement.pat.type)) { + print("\"" + value + "\" /* " + caseStatement.pat + " */"); + } else { + print("" + value + " /* " + caseStatement.pat + " */"); + } + } else { + if (context.types.isSameType(context.symtab.charType, caseStatement.pat.type)) { + print("" + ((JCLiteral) caseStatement.pat).value + " /* " + caseStatement.pat + " */"); + } else { + print(caseStatement.pat); + } + } + } else { + if (context.useModules) { + print(caseStatement.pat.type.tsym.getSimpleName() + "." + caseStatement.pat); + } else { + print(getRootRelativeName(caseStatement.pat.type.tsym) + "." + caseStatement.pat); + } + } + } + } else { + print("default"); + } + print(":"); + println().startIndent(); + for (JCStatement statement : caseStatement.stats) { + printIndent(); + print(statement); + if (!statementsWithNoSemis.contains(statement.getClass())) { + print(";"); + } + println(); + } + endIndent(); + } + + @Override + public void visitTypeCast(JCTypeCast cast) { + if (substituteAssignedExpression(cast.type, cast.expr)) { + return; + } + if (Util.isIntegral(cast.type)) { + if (cast.type.getKind() == TypeKind.LONG) { + print("Math.floor("); + } else { + print("("); + } + } + if (!context.hasAnnotationType(cast.clazz.type.tsym, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE, + ANNOTATION_FUNCTIONAL_INTERFACE)) { + // Java is more permissive than TypeScript when casting type + // variables + if (cast.expr.type.getKind() == TypeKind.TYPEVAR) { + print(""); + } else { + print("<"); + substituteAndPrintType(cast.clazz).print(">"); + } + } + print(cast.expr); + if (Util.isIntegral(cast.type)) { + if (cast.type.getKind() == TypeKind.LONG) { + print(")"); + } else { + print("|0)"); + } + } + } + + @Override + public void visitDoLoop(JCDoWhileLoop doWhileLoop) { + print("do "); + if (doWhileLoop.body instanceof JCBlock) { + print(doWhileLoop.body); + } else { + print(doWhileLoop.body).print(";"); + } + print(" while(").print(doWhileLoop.cond).print(")"); + } + + @Override + public void visitWhileLoop(JCWhileLoop whileLoop) { + print("while(").print(whileLoop.cond).print(") "); + print(whileLoop.body); + } + + @Override + public void visitAssign(JCAssign assign) { + if (!getAdapter().substituteAssignment(new AssignmentElementSupport(assign))) { + staticInitializedAssignment = getStaticInitializedField(assign.lhs) != null; + print(assign.lhs).print(isAnnotationScope ? ": " : " = "); + if (!substituteAssignedExpression(assign.lhs.type, assign.rhs)) { + print(assign.rhs); + } + staticInitializedAssignment = false; + } + } + + @Override + public void visitTry(JCTry tryStatement) { + if (tryStatement.resources != null && !tryStatement.resources.isEmpty()) { + report(tryStatement, JSweetProblem.UNSUPPORTED_TRY_WITH_RESOURCE); + } + if (tryStatement.catchers.isEmpty() && tryStatement.finalizer == null) { + report(tryStatement, JSweetProblem.TRY_WITHOUT_CATCH_OR_FINALLY); + } + print("try ").print(tryStatement.body); + if (tryStatement.catchers.size() > 1) { + print(" catch(__e) {").startIndent(); + for (JCCatch catcher : tryStatement.catchers) { + println().printIndent().print("if"); + printInstanceOf("__e", null, catcher.param.type); + print(" {").startIndent().println().printIndent(); + // if (!context.options.isUseJavaApis() && + // catcher.param.type.toString().startsWith("java.")) { + // print(catcher.param).print(" = ").print("__e;").println(); + // } else { + print(catcher.param).print(" = <"); + substituteAndPrintType(catcher.param.getType()); + print(">__e;").println(); + // } + printBlockStatements(catcher.body.getStatements()); + endIndent().println().printIndent().print("}"); + } + endIndent().println().printIndent().print("}"); + } else if (tryStatement.catchers.size() == 1) { + print(tryStatement.catchers.head); + } + if (tryStatement.finalizer != null) { + print(" finally ").print(tryStatement.finalizer); + } + } + + @Override + public void visitCatch(JCCatch catcher) { + print(" catch(").print(catcher.param.name.toString()).print(") "); + print(catcher.body); + } + + @Override + public void visitLambda(JCLambda lamba) { + boolean regularFunction = false; + if (getParent() instanceof JCMethodInvocation + && ((JCMethodInvocation) getParent()).meth.toString().endsWith("function") + && getParentOfParent() instanceof JCMethodInvocation + && ((JCMethodInvocation) getParentOfParent()).meth.toString().endsWith("$noarrow")) { + MethodInvocationElement invocation = (MethodInvocationElement) ExtendedElementFactory.INSTANCE + .create(getParent()); + if (JSweetConfig.UTIL_CLASSNAME.equals(invocation.getMethod().getEnclosingElement().toString())) { + regularFunction = true; + } + } + Map varAccesses = new HashMap<>(); + Util.fillAllVariableAccesses(varAccesses, lamba); + Collection finalVars = new ArrayList<>(varAccesses.values()); + if (!varAccesses.isEmpty()) { + Map varDefs = new HashMap<>(); + int parentIndex = getStack().size() - 2; + int i = parentIndex; + JCStatement statement = null; + while (i > 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION + && getStack().get(i).getKind() != Kind.METHOD) { + if (statement == null && getStack().get(i) instanceof JCStatement) { + statement = (JCStatement) getStack().get(i); + } + i--; + } + if (i >= 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION && statement != null) { + Util.fillAllVariablesInScope(varDefs, getStack(), lamba, getStack().get(i)); + } + finalVars.retainAll(varDefs.values()); + } + if (!finalVars.isEmpty()) { + print("(("); + for (VarSymbol var : finalVars) { + print(var.name.toString()).print(","); + } + removeLastChar(); + print(") => {").println().startIndent().printIndent().print("return "); + } + getScope().skipTypeAnnotations = true; + if (regularFunction) { + print("function(").printArgList(null, lamba.params).print(") "); + } else { + print("(").printArgList(null, lamba.params).print(") => "); + } + getScope().skipTypeAnnotations = false; + print(lamba.body); + + if (!finalVars.isEmpty()) { + endIndent().println().printIndent().print("})("); + for (VarSymbol var : finalVars) { + print(var.name.toString()).print(","); + } + removeLastChar(); + print(")"); + } + } + + @Override + public void visitReference(JCMemberReference memberReference) { + if (memberReference.sym instanceof MethodSymbol) { + MethodSymbol method = (MethodSymbol) memberReference.sym; + if (getParent() instanceof JCTypeCast) { + print("("); + } + print("("); + if (method.getParameters() != null) { + for (VarSymbol var : method.getParameters()) { + print(var.name.toString()); + print(","); + } + if (!method.getParameters().isEmpty()) { + removeLastChar(); + } + } + print(")"); + print(" => { return "); } - @Override - public void visitTypeCast(JCTypeCast cast) { - if (substituteAssignedExpression(cast.type, cast.expr)) { - return; + if (JSweetConfig.GLOBALS_CLASS_NAME.equals(memberReference.expr.type.tsym.getSimpleName().toString())) { + print(memberReference.name.toString()); + } else { + if ("".equals(memberReference.name.toString())) { + if (context.types.isArray(memberReference.expr.type)) { + print("new Array<"); + substituteAndPrintType(((JCArrayTypeTree) memberReference.expr).elemtype); + print(">"); + } else { + print("new ").print(memberReference.expr); } - if (Util.isIntegral(cast.type)) { - if (cast.type.getKind() == TypeKind.LONG) { - print("Math.floor("); - } else { - print("("); + } else { + print(memberReference.expr).print(".").print(memberReference.name.toString()); + } + } + + if (memberReference.sym instanceof MethodSymbol) { + MethodSymbol method = (MethodSymbol) memberReference.sym; + + print("("); + if (method.getParameters() != null) { + for (VarSymbol var : method.getParameters()) { + print(var.name.toString()); + print(","); + } + if (!method.getParameters().isEmpty()) { + removeLastChar(); + } + } + print(")"); + print(" }"); + if (getParent() instanceof JCTypeCast) { + print(")"); + } + } + + } + + @Override + public void visitTypeParameter(JCTypeParameter typeParameter) { + print(typeParameter.name.toString()); + if (typeParameter.bounds != null && !typeParameter.bounds.isEmpty()) { + print(" extends "); + for (JCExpression e : typeParameter.bounds) { + substituteAndPrintType(e).print(" & "); + } + removeLastChars(3); + } + } + + @Override + public void visitSynchronized(JCSynchronized sync) { + report(sync, JSweetProblem.SYNCHRONIZATION); + if (sync.body != null) { + print(sync.body); + } + } + + public void print(String exprStr, JCTree expr) { + if (exprStr == null) { + print(expr); + } else { + print(exprStr); + } + } + + private void printInstanceOf(String exprStr, JCTree expr, Type type) { + printInstanceOf(exprStr, expr, type, false); + } + + private void printInstanceOf(String exprStr, JCTree expr, Type type, boolean checkFirstArrayElement) { + if (!(getParent() instanceof JCParens)) { + print("("); + } + if (checkFirstArrayElement + || !getAdapter().substituteInstanceof(exprStr, ExtendedElementFactory.INSTANCE.create(expr), type)) { + if (TYPE_MAPPING.containsKey(type.toString())) { + print("typeof "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(" === ").print("'" + TYPE_MAPPING.get(type.toString()).toLowerCase() + "'"); + } else if (type.tsym.isEnum()) { + print("typeof "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(" === 'number'"); + } else if (type.toString().startsWith(JSweetConfig.FUNCTION_CLASSES_PACKAGE + ".") + || type.toString().startsWith("java.util.function.") + || Runnable.class.getName().equals(type.toString()) + || context.hasAnnotationType(type.tsym, JSweetConfig.ANNOTATION_FUNCTIONAL_INTERFACE)) { + print("typeof "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(" === 'function'"); + int parameterCount = context.getFunctionalTypeParameterCount(type); + if (parameterCount != -1) { + print(" && ("); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(").length == " + context.getFunctionalTypeParameterCount(type)); + } + } else { + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + if (context.isInterface(type.tsym)) { + print(" != null && "); + print("("); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print("[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print("[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") + .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); + print(" || "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(".constructor != null && "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") + .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); + if (CharSequence.class.getName().equals(type.tsym.getQualifiedName().toString())) { + print(" || typeof "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(" === \"string\""); + } + print(")"); + } else { + if (type.tsym instanceof TypeVariableSymbol + || Object.class.getName().equals(type.tsym.getQualifiedName().toString())) { + print(" != null"); + } else { + String qualifiedName = getQualifiedTypeName(type.tsym, false); + if (qualifiedName.startsWith("{")) { + qualifiedName = "Object"; } - } - if (!context.hasAnnotationType(cast.clazz.type.tsym, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE, - ANNOTATION_FUNCTIONAL_INTERFACE)) { - // Java is more permissive than TypeScript when casting type - // variables - if (cast.expr.type.getKind() == TypeKind.TYPEVAR) { - print(""); - } else { - print("<"); - substituteAndPrintType(cast.clazz).print(">"); - } - } - print(cast.expr); - if (Util.isIntegral(cast.type)) { - if (cast.type.getKind() == TypeKind.LONG) { + print(" != null"); + if (!"any".equals(qualifiedName)) { + print(" && "); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + if (qualifiedName.startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { + print(" instanceof ").print(qualifiedName); + } else { + print(" instanceof ").print(qualifiedName); + } + if (type instanceof ArrayType) { + ArrayType t = (ArrayType) type; + print(" && ("); + print(exprStr, expr); + if (checkFirstArrayElement) + print("[0]"); + print(".length==0 || "); + print(exprStr, expr); + print("[0] == null ||"); + if (t.elemtype instanceof ArrayType) { + print(exprStr, expr); + print("[0] instanceof Array"); + } else { + printInstanceOf(exprStr, expr, t.elemtype, true); + } print(")"); - } else { - print("|0)"); + } } + } } + } } + if (!(getParent() instanceof JCParens)) { + print(")"); + } + } - @Override - public void visitDoLoop(JCDoWhileLoop doWhileLoop) { - print("do "); - if (doWhileLoop.body instanceof JCBlock) { - print(doWhileLoop.body); + @Override + public void visitTypeTest(JCInstanceOf instanceOf) { + printInstanceOf(null, instanceOf.expr, instanceOf.clazz.type); + } + + @Override + public void visitThrow(JCThrow throwStatement) { + print("throw ").print(throwStatement.expr); + } + + @Override + public void visitAssert(JCAssert assertion) { + if (!context.options.isIgnoreAssertions()) { + String assertCode = assertion.toString().replace("\"", "'"); + print("if(!(").print(assertion.cond).print( + ")) throw new Error(\"Assertion error line " + getCurrentLine() + ": " + assertCode + "\");"); + } + } + + @Override + public void visitAnnotation(JCAnnotation annotation) { + if (!context.hasAnnotationType(annotation.type.tsym, JSweetConfig.ANNOTATION_DECORATOR)) { + return; + } + print("@").print(annotation.getAnnotationType()); + if (annotation.getArguments() != null && !annotation.getArguments().isEmpty()) { + print("("); + isAnnotationScope = true; + print(" { "); + for (JCExpression e : annotation.getArguments()) { + print(e); + print(", "); + } + removeLastChars(2); + print(" } "); + isAnnotationScope = false; + print(")"); + } + println().printIndent(); + } + + Stack rootConditionalAssignedTypes = new Stack<>(); + Stack rootArrayAssignedTypes = new Stack<>(); + + @Override + protected boolean substituteAssignedExpression(Type assignedType, JCExpression expression) { + if (assignedType == null) { + return false; + } + if (expression instanceof JCConditional) { + rootConditionalAssignedTypes.push(assignedType); + return false; + } + if (expression instanceof JCNewArray && assignedType instanceof ArrayType) { + rootArrayAssignedTypes.push(((ArrayType) assignedType).elemtype); + return false; + } + if (assignedType.getTag() == TypeTag.CHAR && expression.type.getTag() != TypeTag.CHAR) { + print("String.fromCharCode(").print(expression).print(")"); + return true; + } else if (Util.isNumber(assignedType) && expression.type.getTag() == TypeTag.CHAR) { + print("(").print(expression).print(").charCodeAt(0)"); + return true; + } else if (singlePrecisionFloats() && assignedType.getTag() == TypeTag.FLOAT + && expression.type.getTag() == TypeTag.DOUBLE) { + print("(Math).fround(").print(expression).print(")"); + return true; + } else { + if (expression instanceof JCLambda) { + if (assignedType.tsym.isInterface() && !context.isFunctionalType(assignedType.tsym)) { + JCLambda lambda = (JCLambda) expression; + MethodSymbol method = (MethodSymbol) assignedType.tsym.getEnclosedElements().get(0); + print("{ " + method.getSimpleName() + " : ").print(lambda).print(" }"); + return true; + } + } else if (expression instanceof JCNewClass) { + JCNewClass newClass = (JCNewClass) expression; + if (newClass.def != null && context.isFunctionalType(assignedType.tsym)) { + List defs = newClass.def.defs; + boolean printed = false; + for (JCTree def : defs) { + if (def instanceof JCMethodDecl) { + if (printed) { + // should never happen... report error? + } + JCMethodDecl method = (JCMethodDecl) def; + if (method.sym.isConstructor()) { + continue; + } + getStack().push(method); + print("(").printArgList(null, method.getParameters()).print(") => ").print(method.body); + getStack().pop(); + printed = true; + } + } + if (printed) { + return true; + } } else { - print(doWhileLoop.body).print(";"); - } - print(" while(").print(doWhileLoop.cond).print(")"); - } - - @Override - public void visitWhileLoop(JCWhileLoop whileLoop) { - print("while(").print(whileLoop.cond).print(") "); - print(whileLoop.body); - } - - @Override - public void visitAssign(JCAssign assign) { - if (!getAdapter().substituteAssignment(new AssignmentElementSupport(assign))) { - staticInitializedAssignment = getStaticInitializedField(assign.lhs) != null; - print(assign.lhs).print(isAnnotationScope ? ": " : " = "); - if (!substituteAssignedExpression(assign.lhs.type, assign.rhs)) { - print(assign.rhs); - } - staticInitializedAssignment = false; - } - } - - @Override - public void visitTry(JCTry tryStatement) { - if (tryStatement.resources != null && !tryStatement.resources.isEmpty()) { - report(tryStatement, JSweetProblem.UNSUPPORTED_TRY_WITH_RESOURCE); - } - if (tryStatement.catchers.isEmpty() && tryStatement.finalizer == null) { - report(tryStatement, JSweetProblem.TRY_WITHOUT_CATCH_OR_FINALLY); - } - print("try ").print(tryStatement.body); - if (tryStatement.catchers.size() > 1) { - print(" catch(__e) {").startIndent(); - for (JCCatch catcher : tryStatement.catchers) { - println().printIndent().print("if"); - printInstanceOf("__e", null, catcher.param.type); - print(" {").startIndent().println().printIndent(); - // if (!context.options.isUseJavaApis() && - // catcher.param.type.toString().startsWith("java.")) { - // print(catcher.param).print(" = ").print("__e;").println(); - // } else { - print(catcher.param).print(" = <"); - substituteAndPrintType(catcher.param.getType()); - print(">__e;").println(); - // } - printBlockStatements(catcher.body.getStatements()); - endIndent().println().printIndent().print("}"); - } - endIndent().println().printIndent().print("}"); - } else if (tryStatement.catchers.size() == 1) { - print(tryStatement.catchers.head); - } - if (tryStatement.finalizer != null) { - print(" finally ").print(tryStatement.finalizer); - } - } - - @Override - public void visitCatch(JCCatch catcher) { - print(" catch(").print(catcher.param.name.toString()).print(") "); - print(catcher.body); - } - - @Override - public void visitLambda(JCLambda lamba) { - boolean regularFunction = false; - if (getParent() instanceof JCMethodInvocation - && ((JCMethodInvocation) getParent()).meth.toString().endsWith("function") && getParentOfParent() instanceof JCMethodInvocation - && ((JCMethodInvocation) getParentOfParent()).meth.toString().endsWith("$noarrow")) { - MethodInvocationElement invocation = (MethodInvocationElement) ExtendedElementFactory.INSTANCE - .create(getParent()); - if (JSweetConfig.UTIL_CLASSNAME.equals(invocation.getMethod().getEnclosingElement().toString())) { - regularFunction = true; - } - } - Map varAccesses = new HashMap<>(); - Util.fillAllVariableAccesses(varAccesses, lamba); - Collection finalVars = new ArrayList<>(varAccesses.values()); - if (!varAccesses.isEmpty()) { - Map varDefs = new HashMap<>(); - int parentIndex = getStack().size() - 2; - int i = parentIndex; - JCStatement statement = null; - while (i > 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION - && getStack().get(i).getKind() != Kind.METHOD) { - if (statement == null && getStack().get(i) instanceof JCStatement) { - statement = (JCStatement) getStack().get(i); - } - i--; - } - if (i >= 0 && getStack().get(i).getKind() != Kind.LAMBDA_EXPRESSION && statement != null) { - Util.fillAllVariablesInScope(varDefs, getStack(), lamba, getStack().get(i)); - } - finalVars.retainAll(varDefs.values()); - } - if (!finalVars.isEmpty()) { - print("(("); - for (VarSymbol var : finalVars) { - print(var.name.toString()).print(","); - } - removeLastChar(); - print(") => {").println().startIndent().printIndent().print("return "); - } - getScope().skipTypeAnnotations = true; - if (regularFunction) { - print("function(").printArgList(null, lamba.params).print(") "); - } else { - print("(").printArgList(null, lamba.params).print(") => "); - } - getScope().skipTypeAnnotations = false; - print(lamba.body); - - if (!finalVars.isEmpty()) { - endIndent().println().printIndent().print("})("); - for (VarSymbol var : finalVars) { - print(var.name.toString()).print(","); - } - removeLastChar(); - print(")"); - } - } - - @Override - public void visitReference(JCMemberReference memberReference) { - if (memberReference.sym instanceof MethodSymbol) { - MethodSymbol method = (MethodSymbol) memberReference.sym; - if (getParent() instanceof JCTypeCast) { + // object assignment to functional type + if ((newClass.def == null && context.isFunctionalType(assignedType.tsym))) { + MethodSymbol method; + for (Symbol s : assignedType.tsym.getEnclosedElements()) { + if (s instanceof MethodSymbol) { + // TODO also check that the method is compatible + // (here we just apply to the first found + // method) + method = (MethodSymbol) s; print("("); - } - print("("); - if (method.getParameters() != null) { - for (VarSymbol var : method.getParameters()) { - print(var.name.toString()); - print(","); + for (VarSymbol p : method.getParameters()) { + print(p.getSimpleName().toString()).print(", "); } if (!method.getParameters().isEmpty()) { - removeLastChar(); + removeLastChars(2); } - } - print(")"); - print(" => { return "); - } - - if (JSweetConfig.GLOBALS_CLASS_NAME.equals(memberReference.expr.type.tsym.getSimpleName().toString())) { - print(memberReference.name.toString()); - } else { - if ("".equals(memberReference.name.toString())) { - if (context.types.isArray(memberReference.expr.type)) { - print("new Array<"); - substituteAndPrintType(((JCArrayTypeTree) memberReference.expr).elemtype); - print(">"); - } else { - print("new ").print(memberReference.expr); - } - } else { - print(memberReference.expr).print(".").print(memberReference.name.toString()); - } - } - - if (memberReference.sym instanceof MethodSymbol) { - MethodSymbol method = (MethodSymbol) memberReference.sym; - - print("("); - if (method.getParameters() != null) { - for (VarSymbol var : method.getParameters()) { - print(var.name.toString()); - print(","); + print(") => { return new ").print(newClass.clazz).print("(").printArgList(null, + newClass.args); + print(").").print(method.getSimpleName().toString()).print("("); + for (VarSymbol p : method.getParameters()) { + print(p.getSimpleName().toString()).print(", "); } if (!method.getParameters().isEmpty()) { - removeLastChar(); + removeLastChars(2); } - } - print(")"); - print(" }"); - if (getParent() instanceof JCTypeCast) { - print(")"); - } - } - - } - - @Override - public void visitTypeParameter(JCTypeParameter typeParameter) { - print(typeParameter.name.toString()); - if (typeParameter.bounds != null && !typeParameter.bounds.isEmpty()) { - print(" extends "); - for (JCExpression e : typeParameter.bounds) { - substituteAndPrintType(e).print(" & "); - } - removeLastChars(3); - } - } - - @Override - public void visitSynchronized(JCSynchronized sync) { - report(sync, JSweetProblem.SYNCHRONIZATION); - if (sync.body != null) { - print(sync.body); - } - } - - public void print(String exprStr, JCTree expr) { - if (exprStr == null) { - print(expr); - } else { - print(exprStr); - } - } - - private void printInstanceOf(String exprStr, JCTree expr, Type type) { - printInstanceOf(exprStr, expr, type, false); - } - - private void printInstanceOf(String exprStr, JCTree expr, Type type, boolean checkFirstArrayElement) { - if (!(getParent() instanceof JCParens)) { - print("("); - } - if (checkFirstArrayElement - || !getAdapter().substituteInstanceof(exprStr, ExtendedElementFactory.INSTANCE.create(expr), type)) { - if (TYPE_MAPPING.containsKey(type.toString())) { - print("typeof "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(" === ").print("'" + TYPE_MAPPING.get(type.toString()).toLowerCase() + "'"); - } else if (type.tsym.isEnum()) { - print("typeof "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(" === 'number'"); - } else if (type.toString().startsWith(JSweetConfig.FUNCTION_CLASSES_PACKAGE + ".") - || type.toString().startsWith("java.util.function.") - || Runnable.class.getName().equals(type.toString()) - || context.hasAnnotationType(type.tsym, JSweetConfig.ANNOTATION_FUNCTIONAL_INTERFACE)) { - print("typeof "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(" === 'function'"); - int parameterCount = context.getFunctionalTypeParameterCount(type); - if (parameterCount != -1) { - print(" && ("); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(").length == " + context.getFunctionalTypeParameterCount(type)); - } - } else { - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - if (context.isInterface(type.tsym)) { - print(" != null && "); - print("("); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print("[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print("[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") - .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); - print(" || "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(".constructor != null && "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"]").print(" != null && "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(".constructor[\"" + INTERFACES_FIELD_NAME + "\"].indexOf(\"") - .print(type.tsym.getQualifiedName().toString()).print("\") >= 0"); - if (CharSequence.class.getName().equals(type.tsym.getQualifiedName().toString())) { - print(" || typeof "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(" === \"string\""); - } - print(")"); - } else { - if (type.tsym instanceof TypeVariableSymbol - || Object.class.getName().equals(type.tsym.getQualifiedName().toString())) { - print(" != null"); - } else { - String qualifiedName = getQualifiedTypeName(type.tsym, false); - if (qualifiedName.startsWith("{")) { - qualifiedName = "Object"; - } - print(" != null"); - if (!"any".equals(qualifiedName)) { - print(" && "); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - if (qualifiedName.startsWith(JSweetConfig.LIBS_PACKAGE + ".")) { - print(" instanceof ").print(qualifiedName); - } else { - print(" instanceof ").print(qualifiedName); - } - if (type instanceof ArrayType) { - ArrayType t = (ArrayType) type; - print(" && ("); - print(exprStr, expr); - if (checkFirstArrayElement) - print("[0]"); - print(".length==0 || "); - print(exprStr, expr); - print("[0] == null ||"); - if (t.elemtype instanceof ArrayType) { - print(exprStr, expr); - print("[0] instanceof Array"); - } else { - printInstanceOf(exprStr, expr, t.elemtype, true); - } - print(")"); - } - } - } - } - } - } - if (!(getParent() instanceof JCParens)) { - print(")"); - } - } - - @Override - public void visitTypeTest(JCInstanceOf instanceOf) { - printInstanceOf(null, instanceOf.expr, instanceOf.clazz.type); - } - - @Override - public void visitThrow(JCThrow throwStatement) { - print("throw ").print(throwStatement.expr); - } - - @Override - public void visitAssert(JCAssert assertion) { - if (!context.options.isIgnoreAssertions()) { - String assertCode = assertion.toString().replace("\"", "'"); - print("if(!(").print(assertion.cond).print( - ")) throw new Error(\"Assertion error line " + getCurrentLine() + ": " + assertCode + "\");"); - } - } - - @Override - public void visitAnnotation(JCAnnotation annotation) { - if (!context.hasAnnotationType(annotation.type.tsym, JSweetConfig.ANNOTATION_DECORATOR)) { - return; - } - print("@").print(annotation.getAnnotationType()); - if (annotation.getArguments() != null && !annotation.getArguments().isEmpty()) { - print("("); - isAnnotationScope = true; - print(" { "); - for (JCExpression e : annotation.getArguments()) { - print(e); - print(", "); - } - removeLastChars(2); - print(" } "); - isAnnotationScope = false; - print(")"); - } - println().printIndent(); - } - - Stack rootConditionalAssignedTypes = new Stack<>(); - Stack rootArrayAssignedTypes = new Stack<>(); - - @Override - protected boolean substituteAssignedExpression(Type assignedType, JCExpression expression) { - if (assignedType == null) { - return false; - } - if (expression instanceof JCConditional) { - rootConditionalAssignedTypes.push(assignedType); - return false; - } - if (expression instanceof JCNewArray && assignedType instanceof ArrayType) { - rootArrayAssignedTypes.push(((ArrayType) assignedType).elemtype); - return false; - } - if (assignedType.getTag() == TypeTag.CHAR && expression.type.getTag() != TypeTag.CHAR) { - print("String.fromCharCode(").print(expression).print(")"); - return true; - } else if (Util.isNumber(assignedType) && expression.type.getTag() == TypeTag.CHAR) { - print("(").print(expression).print(").charCodeAt(0)"); - return true; - } else if (singlePrecisionFloats() && assignedType.getTag() == TypeTag.FLOAT - && expression.type.getTag() == TypeTag.DOUBLE) { - print("(Math).fround(").print(expression).print(")"); - return true; - } else { - if (expression instanceof JCLambda) { - if (assignedType.tsym.isInterface() && !context.isFunctionalType(assignedType.tsym)) { - JCLambda lambda = (JCLambda) expression; - MethodSymbol method = (MethodSymbol) assignedType.tsym.getEnclosedElements().get(0); - print("{ " + method.getSimpleName() + " : ").print(lambda).print(" }"); - return true; - } - } else if (expression instanceof JCNewClass) { - JCNewClass newClass = (JCNewClass) expression; - if (newClass.def != null && context.isFunctionalType(assignedType.tsym)) { - List defs = newClass.def.defs; - boolean printed = false; - for (JCTree def : defs) { - if (def instanceof JCMethodDecl) { - if (printed) { - // should never happen... report error? - } - JCMethodDecl method = (JCMethodDecl) def; - if (method.sym.isConstructor()) { - continue; - } - getStack().push(method); - print("(").printArgList(null, method.getParameters()).print(") => ").print(method.body); - getStack().pop(); - printed = true; - } - } - if (printed) { - return true; - } - } else { - // object assignment to functional type - if ((newClass.def == null && context.isFunctionalType(assignedType.tsym))) { - MethodSymbol method; - for (Symbol s : assignedType.tsym.getEnclosedElements()) { - if (s instanceof MethodSymbol) { - // TODO also check that the method is compatible - // (here we just apply to the first found - // method) - method = (MethodSymbol) s; - print("("); - for (VarSymbol p : method.getParameters()) { - print(p.getSimpleName().toString()).print(", "); - } - if (!method.getParameters().isEmpty()) { - removeLastChars(2); - } - print(") => { return new ").print(newClass.clazz).print("(").printArgList(null, - newClass.args); - print(").").print(method.getSimpleName().toString()).print("("); - for (VarSymbol p : method.getParameters()) { - print(p.getSimpleName().toString()).print(", "); - } - if (!method.getParameters().isEmpty()) { - removeLastChars(2); - } - print("); }"); - return true; - } - } - - } - // raw generic type - if (!newClass.type.tsym.getTypeParameters().isEmpty() && newClass.typeargs.isEmpty()) { - print("(").print(expression).print(")"); - return true; - } - } - } else if (!(expression instanceof JCLambda || expression instanceof JCMemberReference) - && context.isFunctionalType(assignedType.tsym)) { - // disallow typing to force objects to be passed as function - // (may require runtime checks later on) - print("(").print(expression).print(")"); + print("); }"); return true; - } else if (expression instanceof JCMethodInvocation) { - // disable type checking when the method returns a type variable - // because it may to be correctly set in the invocation - MethodSymbol m = (MethodSymbol) Util.getSymbol(((JCMethodInvocation) expression).meth); - if (m != null && m.getReturnType() instanceof TypeVar - && m.getReturnType().tsym.getEnclosingElement() == m) { - print("(").print(expression).print(")"); - return true; - } + } } - return false; - } - } - @Override - public String getQualifiedTypeName(TypeSymbol type, boolean globals) { - String qualifiedName = super.getQualifiedTypeName(type, globals); - String typeName = type.getQualifiedName().toString(); - if (context.getLangTypeMappings().containsKey(typeName)) { - qualifiedName = context.getLangTypeMappings().get(typeName); - } else if (context.isMappedType(typeName)) { - qualifiedName = context.getTypeMappingTarget(typeName); - if (qualifiedName.endsWith("<>")) { - qualifiedName = qualifiedName.substring(0, qualifiedName.length() - 2); - } - } else { - if (context.useModules) { - String[] namePath = qualifiedName.split("\\."); - int i = namePath.length - 1; - Symbol s = type; - qualifiedName = ""; - while (i >= 0 && !(s instanceof PackageSymbol)) { - qualifiedName = namePath[i--] + ("".equals(qualifiedName) ? "" : "." + qualifiedName); - s = s.getEnclosingElement(); - } - } - if (globals) { - int dotIndex = qualifiedName.lastIndexOf("."); - if (dotIndex == -1) { - qualifiedName = ""; - } else { - qualifiedName = qualifiedName.substring(0, dotIndex); - } - } + } + // raw generic type + if (!newClass.type.tsym.getTypeParameters().isEmpty() && newClass.typeargs.isEmpty()) { + print("(").print(expression).print(")"); + return true; + } } - return qualifiedName; + } else if (!(expression instanceof JCLambda || expression instanceof JCMemberReference) + && context.isFunctionalType(assignedType.tsym)) { + // disallow typing to force objects to be passed as function + // (may require runtime checks later on) + print("(").print(expression).print(")"); + return true; + } else if (expression instanceof JCMethodInvocation) { + // disable type checking when the method returns a type variable + // because it may to be correctly set in the invocation + MethodSymbol m = (MethodSymbol) Util.getSymbol(((JCMethodInvocation) expression).meth); + if (m != null && m.getReturnType() instanceof TypeVar + && m.getReturnType().tsym.getEnclosingElement() == m) { + print("(").print(expression).print(")"); + return true; + } + } + return false; } + } + + @Override + public String getQualifiedTypeName(TypeSymbol type, boolean globals) { + String qualifiedName = super.getQualifiedTypeName(type, globals); + String typeName = type.getQualifiedName().toString(); + if (context.getLangTypeMappings().containsKey(typeName)) { + qualifiedName = context.getLangTypeMappings().get(typeName); + } else if (context.isMappedType(typeName)) { + qualifiedName = context.getTypeMappingTarget(typeName); + if (qualifiedName.endsWith("<>")) { + qualifiedName = qualifiedName.substring(0, qualifiedName.length() - 2); + } + } else { + if (context.useModules) { + String[] namePath = qualifiedName.split("\\."); + int i = namePath.length - 1; + Symbol s = type; + qualifiedName = ""; + while (i >= 0 && !(s instanceof PackageSymbol)) { + qualifiedName = namePath[i--] + ("".equals(qualifiedName) ? "" : "." + qualifiedName); + s = s.getEnclosingElement(); + } + } + if (globals) { + int dotIndex = qualifiedName.lastIndexOf("."); + if (dotIndex == -1) { + qualifiedName = ""; + } else { + qualifiedName = qualifiedName.substring(0, dotIndex); + } + } + } + return qualifiedName; + } } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java b/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java index a52d0d8a..65f5a569 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java @@ -119,1318 +119,1322 @@ import com.sun.tools.javac.tree.JCTree.Tag; */ public class Java2TypeScriptAdapter extends PrinterAdapter { - private final static String VAR_DECL_KEYWORD = Java2TypeScriptTranslator.VAR_DECL_KEYWORD; + private final static String VAR_DECL_KEYWORD = Java2TypeScriptTranslator.VAR_DECL_KEYWORD; - public Java2TypeScriptTranslator getPrinter() { - return (Java2TypeScriptTranslator) super.getPrinter(); + public Java2TypeScriptTranslator getPrinter() { + return (Java2TypeScriptTranslator) super.getPrinter(); + } + + /** + * Creates a root adapter (with no parent). + * + * @param context + * the transpilation context + */ + public Java2TypeScriptAdapter(JSweetContext context) { + super(context); + init(); + } + + /** + * Creates a new adapter that will try delegate to the given parent adapter + * when not implementing its own behavior. + * + * @param parentAdapter + * cannot be null: if no parent you must use the + * {@link #AbstractPrinterAdapter(JSweetContext)} constructor + */ + public Java2TypeScriptAdapter(PrinterAdapter parent) { + super(parent); + init(); + } + + private void init() { + addTypeMapping(Object.class.getName(), "any"); + addTypeMapping(Runnable.class.getName(), "() => void"); + + addTypeMapping(DoubleConsumer.class.getName(), "(number) => void"); + addTypeMapping(DoublePredicate.class.getName(), "(number) => boolean"); + addTypeMapping(DoubleSupplier.class.getName(), "() => number"); + addTypeMapping(DoubleBinaryOperator.class.getName(), "(number, number) => number"); + addTypeMapping(DoubleUnaryOperator.class.getName(), "(number) => number"); + addTypeMapping(DoubleToIntFunction.class.getName(), "(number) => number"); + addTypeMapping(DoubleToLongFunction.class.getName(), "(number) => number"); + + addTypeMapping(IntConsumer.class.getName(), "(number) => void"); + addTypeMapping(IntPredicate.class.getName(), "(number) => boolean"); + addTypeMapping(IntSupplier.class.getName(), "() => number"); + addTypeMapping(IntBinaryOperator.class.getName(), "(number, number) => number"); + addTypeMapping(IntUnaryOperator.class.getName(), "(number) => number"); + addTypeMapping(IntToDoubleFunction.class.getName(), "(number) => number"); + addTypeMapping(IntToLongFunction.class.getName(), "(number) => number"); + + addTypeMapping(LongConsumer.class.getName(), "(number) => void"); + addTypeMapping(LongPredicate.class.getName(), "(number) => boolean"); + addTypeMapping(LongSupplier.class.getName(), "() => number"); + addTypeMapping(LongBinaryOperator.class.getName(), "(number, number) => number"); + addTypeMapping(LongUnaryOperator.class.getName(), "(number) => number"); + addTypeMapping(LongToDoubleFunction.class.getName(), "(number) => number"); + addTypeMapping(LongToIntFunction.class.getName(), "(number) => number"); + + addTypeMapping(BooleanSupplier.class.getName(), "() => boolean"); + + addTypeMapping(String.class.getName(), "string"); + addTypeMapping(Number.class.getName(), "number"); + addTypeMapping(Integer.class.getName(), "number"); + addTypeMapping(Short.class.getName(), "number"); + addTypeMapping(Float.class.getName(), "number"); + addTypeMapping(Long.class.getName(), "number"); + addTypeMapping(Byte.class.getName(), "number"); + addTypeMapping(Double.class.getName(), "number"); + addTypeMapping(Boolean.class.getName(), "boolean"); + addTypeMapping(Character.class.getName(), "string"); + addTypeMapping(CharSequence.class.getName(), "any"); + addTypeMapping(Void.class.getName(), "void"); + addTypeMapping("double", "number"); + addTypeMapping("int", "number"); + addTypeMapping("float", "number"); + addTypeMapping("long", "number"); + addTypeMapping("byte", "number"); + addTypeMapping("short", "number"); + addTypeMapping("char", "string"); + addTypeMapping("Class", "Function"); + addTypeMapping(LANG_PACKAGE + ".Object", "Object"); + addTypeMapping(LANG_PACKAGE + ".Boolean", "boolean"); + addTypeMapping(LANG_PACKAGE + ".String", "string"); + addTypeMapping(LANG_PACKAGE + ".Number", "number"); + addTypeMapping(LANG_PACKAGE_ALT + ".Object", "Object"); + addTypeMapping(LANG_PACKAGE_ALT + ".Boolean", "boolean"); + addTypeMapping(LANG_PACKAGE_ALT + ".String", "string"); + addTypeMapping(LANG_PACKAGE_ALT + ".Number", "number"); + + context.getLangTypeMappings().put(Object.class.getName(), "Object"); + context.getLangTypeMappings().put(String.class.getName(), "String"); + context.getLangTypeMappings().put(Boolean.class.getName(), "Boolean"); + context.getLangTypeMappings().put(Number.class.getName(), "Number"); + context.getLangTypeMappings().put(Integer.class.getName(), "Number"); + context.getLangTypeMappings().put(Long.class.getName(), "Number"); + context.getLangTypeMappings().put(Short.class.getName(), "Number"); + context.getLangTypeMappings().put(Float.class.getName(), "Number"); + context.getLangTypeMappings().put(Double.class.getName(), "Number"); + context.getLangTypeMappings().put(Byte.class.getName(), "Number"); + context.getLangTypeMappings().put(Character.class.getName(), "String"); + context.getLangTypeMappings().put(Math.class.getName(), "Math"); + context.getLangTypeMappings().put(Exception.class.getName(), "Error"); + context.getLangTypeMappings().put(Throwable.class.getName(), "Error"); + context.getLangTypeMappings().put(Error.class.getName(), "Error"); + + for (String s : context.getLangTypeMappings().keySet()) { + context.getLangTypesSimpleNames().add(s.substring(s.lastIndexOf('.') + 1)); } - /** - * Creates a root adapter (with no parent). - * - * @param context - * the transpilation context - */ - public Java2TypeScriptAdapter(JSweetContext context) { - super(context); - init(); + context.getBaseThrowables().add(Throwable.class.getName()); + context.getBaseThrowables().add(Error.class.getName()); + context.getBaseThrowables().add(Exception.class.getName()); + + } + + @Override + public String needsImport(ImportElement importElement, String qualifiedName) { + JCImport importDecl = ((ImportElementSupport) importElement).getTree(); + if (isJSweetPath(qualifiedName) || isMappedType(qualifiedName) + || context.getLangTypeMappings().containsKey(qualifiedName) + || qualifiedName.startsWith("java.util.function.") + || qualifiedName.endsWith(GLOBALS_PACKAGE_NAME + "." + GLOBALS_CLASS_NAME)) { + return null; } - - /** - * Creates a new adapter that will try delegate to the given parent adapter - * when not implementing its own behavior. - * - * @param parentAdapter - * cannot be null: if no parent you must use the - * {@link #AbstractPrinterAdapter(JSweetContext)} constructor - */ - public Java2TypeScriptAdapter(PrinterAdapter parent) { - super(parent); - init(); + if (importDecl.qualid.type != null) { + if (context.hasAnnotationType(importDecl.qualid.type.tsym, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE)) { + return null; + } + if (importDecl.qualid.type.tsym.getKind() == ElementKind.ANNOTATION_TYPE + && !context.hasAnnotationType(importDecl.qualid.type.tsym, JSweetConfig.ANNOTATION_DECORATOR)) { + return null; + } } + if (importDecl.isStatic()) { + if (importDecl.getQualifiedIdentifier() instanceof JCFieldAccess) { + JCFieldAccess fa = (JCFieldAccess) importDecl.getQualifiedIdentifier(); + switch (fa.selected.toString()) { + case "java.lang.Math": + return null; + } + String name = getPrinter().getRootRelativeName(fa.selected.type.tsym, context.useModules); + String methodName = fa.name.toString(); - private void init() { - addTypeMapping(Object.class.getName(), "any"); - addTypeMapping(Runnable.class.getName(), "() => void"); - - addTypeMapping(DoubleConsumer.class.getName(), "(number) => void"); - addTypeMapping(DoublePredicate.class.getName(), "(number) => boolean"); - addTypeMapping(DoubleSupplier.class.getName(), "() => number"); - addTypeMapping(DoubleBinaryOperator.class.getName(), "(number, number) => number"); - addTypeMapping(DoubleUnaryOperator.class.getName(), "(number) => number"); - addTypeMapping(DoubleToIntFunction.class.getName(), "(number) => number"); - addTypeMapping(DoubleToLongFunction.class.getName(), "(number) => number"); - - addTypeMapping(IntConsumer.class.getName(), "(number) => void"); - addTypeMapping(IntPredicate.class.getName(), "(number) => boolean"); - addTypeMapping(IntSupplier.class.getName(), "() => number"); - addTypeMapping(IntBinaryOperator.class.getName(), "(number, number) => number"); - addTypeMapping(IntUnaryOperator.class.getName(), "(number) => number"); - addTypeMapping(IntToDoubleFunction.class.getName(), "(number) => number"); - addTypeMapping(IntToLongFunction.class.getName(), "(number) => number"); - - addTypeMapping(LongConsumer.class.getName(), "(number) => void"); - addTypeMapping(LongPredicate.class.getName(), "(number) => boolean"); - addTypeMapping(LongSupplier.class.getName(), "() => number"); - addTypeMapping(LongBinaryOperator.class.getName(), "(number, number) => number"); - addTypeMapping(LongUnaryOperator.class.getName(), "(number) => number"); - addTypeMapping(LongToDoubleFunction.class.getName(), "(number) => number"); - addTypeMapping(LongToIntFunction.class.getName(), "(number) => number"); - - addTypeMapping(BooleanSupplier.class.getName(), "() => boolean"); - - addTypeMapping(String.class.getName(), "string"); - addTypeMapping(Number.class.getName(), "number"); - addTypeMapping(Integer.class.getName(), "number"); - addTypeMapping(Short.class.getName(), "number"); - addTypeMapping(Float.class.getName(), "number"); - addTypeMapping(Long.class.getName(), "number"); - addTypeMapping(Byte.class.getName(), "number"); - addTypeMapping(Double.class.getName(), "number"); - addTypeMapping(Boolean.class.getName(), "boolean"); - addTypeMapping(Character.class.getName(), "string"); - addTypeMapping(CharSequence.class.getName(), "any"); - addTypeMapping(Void.class.getName(), "void"); - addTypeMapping("double", "number"); - addTypeMapping("int", "number"); - addTypeMapping("float", "number"); - addTypeMapping("long", "number"); - addTypeMapping("byte", "number"); - addTypeMapping("short", "number"); - addTypeMapping("char", "string"); - addTypeMapping("Class", "Function"); - addTypeMapping(LANG_PACKAGE + ".Object", "Object"); - addTypeMapping(LANG_PACKAGE + ".Boolean", "boolean"); - addTypeMapping(LANG_PACKAGE + ".String", "string"); - addTypeMapping(LANG_PACKAGE + ".Number", "number"); - addTypeMapping(LANG_PACKAGE_ALT + ".Object", "Object"); - addTypeMapping(LANG_PACKAGE_ALT + ".Boolean", "boolean"); - addTypeMapping(LANG_PACKAGE_ALT + ".String", "string"); - addTypeMapping(LANG_PACKAGE_ALT + ".Number", "number"); - - context.getLangTypeMappings().put(Object.class.getName(), "Object"); - context.getLangTypeMappings().put(String.class.getName(), "String"); - context.getLangTypeMappings().put(Boolean.class.getName(), "Boolean"); - context.getLangTypeMappings().put(Number.class.getName(), "Number"); - context.getLangTypeMappings().put(Integer.class.getName(), "Number"); - context.getLangTypeMappings().put(Long.class.getName(), "Number"); - context.getLangTypeMappings().put(Short.class.getName(), "Number"); - context.getLangTypeMappings().put(Float.class.getName(), "Number"); - context.getLangTypeMappings().put(Double.class.getName(), "Number"); - context.getLangTypeMappings().put(Byte.class.getName(), "Number"); - context.getLangTypeMappings().put(Character.class.getName(), "String"); - context.getLangTypeMappings().put(Math.class.getName(), "Math"); - context.getLangTypeMappings().put(Exception.class.getName(), "Error"); - context.getLangTypeMappings().put(Throwable.class.getName(), "Error"); - context.getLangTypeMappings().put(Error.class.getName(), "Error"); - - for (String s : context.getLangTypeMappings().keySet()) { - context.getLangTypesSimpleNames().add(s.substring(s.lastIndexOf('.') + 1)); + // function is a top-level global function (no need to import) + if (GLOBALS_CLASS_NAME.equals(name)) { + return null; + } + if (!context.useModules && name.endsWith(GLOBALS_PACKAGE_NAME + "." + GLOBALS_CLASS_NAME)) { + return null; } - context.getBaseThrowables().add(Throwable.class.getName()); - context.getBaseThrowables().add(Error.class.getName()); - context.getBaseThrowables().add(Exception.class.getName()); - - } - - @Override - public String needsImport(ImportElement importElement, String qualifiedName) { - JCImport importDecl = ((ImportElementSupport) importElement).getTree(); - if (isJSweetPath(qualifiedName) || isMappedType(qualifiedName) - || context.getLangTypeMappings().containsKey(qualifiedName) - || qualifiedName.startsWith("java.util.function.") - || qualifiedName.endsWith(GLOBALS_PACKAGE_NAME + "." + GLOBALS_CLASS_NAME)) { + if (JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(methodName.toLowerCase())) { + // if method name is a reserved ts keyword, we have to fully + // qualify calls to it (hence discarding any import) + return null; + } + boolean globals = name.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME); + if (globals) { + name = name.substring(0, name.length() - JSweetConfig.GLOBALS_CLASS_NAME.length() - 1); + } + // function belong to the current package (no need to + // import) + String current = getPrinter().getRootRelativeName(getPrinter().getCompilationUnit().packge, + context.useModules); + if (context.useModules) { + if (current.equals(name)) { return null; - } - if (importDecl.qualid.type != null) { - if (context.hasAnnotationType(importDecl.qualid.type.tsym, ANNOTATION_ERASED, ANNOTATION_OBJECT_TYPE)) { - return null; - } - if (importDecl.qualid.type.tsym.getKind() == ElementKind.ANNOTATION_TYPE - && !context.hasAnnotationType(importDecl.qualid.type.tsym, JSweetConfig.ANNOTATION_DECORATOR)) { - return null; - } - } - if (importDecl.isStatic()) { - if (importDecl.getQualifiedIdentifier() instanceof JCFieldAccess) { - JCFieldAccess fa = (JCFieldAccess) importDecl.getQualifiedIdentifier(); - switch (fa.selected.toString()) { - case "java.lang.Math": - return null; - } - String name = getPrinter().getRootRelativeName(fa.selected.type.tsym, context.useModules); - String methodName = fa.name.toString(); - - // function is a top-level global function (no need to import) - if (GLOBALS_CLASS_NAME.equals(name)) { - return null; - } - if (!context.useModules && name.endsWith(GLOBALS_PACKAGE_NAME + "." + GLOBALS_CLASS_NAME)) { - return null; - } - - if (JSweetConfig.TS_STRICT_MODE_KEYWORDS.contains(methodName.toLowerCase())) { - // if method name is a reserved ts keyword, we have to fully - // qualify calls to it (hence discarding any import) - return null; - } - boolean globals = name.endsWith("." + JSweetConfig.GLOBALS_CLASS_NAME); - if (globals) { - name = name.substring(0, name.length() - JSweetConfig.GLOBALS_CLASS_NAME.length() - 1); - } - // function belong to the current package (no need to - // import) - String current = getPrinter().getRootRelativeName(getPrinter().getCompilationUnit().packge, - context.useModules); - if (context.useModules) { - if (current.equals(name)) { - return null; - } - } else { - if (current.startsWith(name)) { - return null; - } - } - Symbol nameSymbol = fa.sym; - if (nameSymbol == null) { - TypeSymbol t = fa.selected.type.tsym; - nameSymbol = Util.findFirstDeclarationInType(t, methodName); - } - - return StringUtils.isBlank(name) ? null - : name + "." + (nameSymbol == null ? methodName : getPrinter().getIdentifier(nameSymbol)); - } else { - return null; - } + } } else { - if (context.useModules) { - // check if inner class and do not import - if (importDecl.qualid instanceof JCFieldAccess) { - JCFieldAccess qualified = (JCFieldAccess) importDecl.qualid; - if (qualified.sym instanceof ClassSymbol - && qualified.sym.getEnclosingElement() instanceof ClassSymbol) { - return null; - } - } - } - } - if (importElement.isStatic()) { + if (current.startsWith(name)) { return null; - } else { - return super.needsImport(importElement, qualifiedName); + } } + Symbol nameSymbol = fa.sym; + if (nameSymbol == null) { + TypeSymbol t = fa.selected.type.tsym; + nameSymbol = Util.findFirstDeclarationInType(t, methodName); + } + + return StringUtils.isBlank(name) ? null + : name + "." + (nameSymbol == null ? methodName : getPrinter().getIdentifier(nameSymbol)); + } else { + return null; + } + } else { + if (context.useModules) { + // check if inner class and do not import + if (importDecl.qualid instanceof JCFieldAccess) { + JCFieldAccess qualified = (JCFieldAccess) importDecl.qualid; + if (qualified.sym instanceof ClassSymbol + && qualified.sym.getEnclosingElement() instanceof ClassSymbol) { + return null; + } + } + } + } + if (importElement.isStatic()) { + return null; + } else { + return super.needsImport(importElement, qualifiedName); + } + } + + private boolean isWithinGlobals(String targetClassName) { + if (targetClassName == null + || (targetClassName.equals(UTIL_CLASSNAME) || targetClassName.equals(DEPRECATED_UTIL_CLASSNAME))) { + JCClassDecl c = getPrinter().getParent(JCClassDecl.class); + return c != null && c.sym.getQualifiedName().toString().endsWith("." + GLOBALS_CLASS_NAME); + } else { + return false; + } + } + + @Override + public boolean substituteMethodInvocation(MethodInvocationElement invocationElement) { + + Element targetType = invocationElement.getMethod().getEnclosingElement(); + // This is some sort of hack to avoid invoking erased methods. + // If the containing class is erased, we still invoke it because we + // don't know if the class may be provided externally. + // Pitfalls: (1) may erase invocations that are provided externally, (2) + // if the invocation is the target of an enclosing invocation or field + // access, TS compilation will fail. + // So, we should probably find a better way to erase invocations (or at + // least do it conditionally). + if (hasAnnotationType(invocationElement.getMethod(), ANNOTATION_ERASED) + && !isAmbientDeclaration(invocationElement.getMethod())) { + print("null"); + return true; + } + if (invocationElement.getTargetExpression() != null) { + targetType = invocationElement.getTargetExpression().getTypeAsElement(); + } + String targetMethodName = invocationElement.getMethodName(); + String targetClassName = targetType.toString(); + + if ("println".equals(targetMethodName)) { + if (invocationElement.getTargetExpression() != null) { + if ("System.out".equals(invocationElement.getTargetExpression().toString())) { + PrinterAdapter print = print("console.info("); + if (invocationElement.getArgumentCount() > 0) + print.print(invocationElement.getArgument(0)); + print.print(")"); + return true; + } + if ("System.err".equals(invocationElement.getTargetExpression().toString())) { + PrinterAdapter print = print("console.error("); + if (invocationElement.getArgumentCount() > 0) + print.print(invocationElement.getArgument(0)); + print.print(")"); + return true; + } + } } - private boolean isWithinGlobals(String targetClassName) { - if (targetClassName == null - || (targetClassName.equals(UTIL_CLASSNAME) || targetClassName.equals(DEPRECATED_UTIL_CLASSNAME))) { - JCClassDecl c = getPrinter().getParent(JCClassDecl.class); - return c != null && c.sym.getQualifiedName().toString().endsWith("." + GLOBALS_CLASS_NAME); - } else { - return false; + if ("super".equals(invocationElement.getMethodName())) { + // we omit call to super if class extends nothing or if parent is an + // interface + if (getPrinter().getParent(JCClassDecl.class).extending == null // + || context.isInterface(getPrinter().getParent(JCClassDecl.class).extending.type.tsym)) { + return true; + } + // special case when subclassing a Java exception type + if (((MethodInvocationElementSupport) invocationElement).getTree().meth instanceof JCIdent) { + String superClassName = ((JCIdent) ((MethodInvocationElementSupport) invocationElement) + .getTree().meth).sym.getEnclosingElement().getQualifiedName().toString(); + if (context.getBaseThrowables().contains(superClassName)) { + // ES6 would take the cause, but we ignore it so far for + // backward compatibility + // PATCH: + // https://github.com/Microsoft/TypeScript/issues/5069 + if (invocationElement.getArgumentCount() > 0) { + print("super(").print(invocationElement.getArgument(0)).print(")"); + print("; this.message=").print(invocationElement.getArgument(0)); + } else { + print("super()"); + } + return true; } + } } - @Override - public boolean substituteMethodInvocation(MethodInvocationElement invocationElement) { - - Element targetType = invocationElement.getMethod().getEnclosingElement(); - // This is some sort of hack to avoid invoking erased methods. - // If the containing class is erased, we still invoke it because we - // don't know if the class may be provided externally. - // Pitfalls: (1) may erase invocations that are provided externally, (2) - // if the invocation is the target of an enclosing invocation or field - // access, TS compilation will fail. - // So, we should probably find a better way to erase invocations (or at - // least do it conditionally). - if (hasAnnotationType(invocationElement.getMethod(), ANNOTATION_ERASED) - && !isAmbientDeclaration(invocationElement.getMethod())) { - print("null"); - return true; + if (targetType != null && targetType.getKind() == ElementKind.ENUM + && (invocationElement.getTargetExpression() != null + && !"this".equals(invocationElement.getTargetExpression().toString()))) { + // TODO: enum type simple name will not be valid when uses as fully + // qualified name (not imported) + String relTarget = context.useModules ? targetType.getSimpleName().toString() + : getRootRelativeName((Symbol) targetType); + switch (targetMethodName) { + case "name": + print(relTarget).print("[").print(invocationElement.getTargetExpression()).print("]"); + return true; + case "ordinal": + print(relTarget).print("[").print(relTarget).print("[").print(invocationElement.getTargetExpression()) + .print("]").print("]"); + return true; + case "valueOf": + if (invocationElement.getArgumentCount() == 1) { + print("").print(invocationElement.getTargetExpression()).print("[") + .print(invocationElement.getArgument(0)).print("]"); + return true; } - if (invocationElement.getTargetExpression() != null) { - targetType = invocationElement.getTargetExpression().getTypeAsElement(); - } - String targetMethodName = invocationElement.getMethodName(); - String targetClassName = targetType.toString(); - - if ("println".equals(targetMethodName)) { - if (invocationElement.getTargetExpression() != null) { - if ("System.out".equals(invocationElement.getTargetExpression().toString())) { - PrinterAdapter print = print("console.info("); - if (invocationElement.getArgumentCount() > 0) - print.print(invocationElement.getArgument(0)); - print.print(")"); - return true; - } - if ("System.err".equals(invocationElement.getTargetExpression().toString())) { - PrinterAdapter print = print("console.error("); - if (invocationElement.getArgumentCount() > 0) - print.print(invocationElement.getArgument(0)); - print.print(")"); - return true; - } - } - } - - if ("super".equals(invocationElement.getMethodName())) { - // we omit call to super if class extends nothing or if parent is an - // interface - if (getPrinter().getParent(JCClassDecl.class).extending == null // - || context.isInterface(getPrinter().getParent(JCClassDecl.class).extending.type.tsym)) { - return true; - } - // special case when subclassing a Java exception type - if (((MethodInvocationElementSupport) invocationElement).getTree().meth instanceof JCIdent) { - String superClassName = ((JCIdent) ((MethodInvocationElementSupport) invocationElement) - .getTree().meth).sym.getEnclosingElement().getQualifiedName().toString(); - if (context.getBaseThrowables().contains(superClassName)) { - // ES6 would take the cause, but we ignore it so far for - // backward compatibility - // PATCH: - // https://github.com/Microsoft/TypeScript/issues/5069 - if (invocationElement.getArgumentCount() > 0) { - print("super(").print(invocationElement.getArgument(0)).print(")"); - print("; this.message=").print(invocationElement.getArgument(0)); - } else { - print("super()"); - } - return true; - } - } - } - - if (targetType != null && targetType.getKind() == ElementKind.ENUM - && (invocationElement.getTargetExpression() != null - && !"this".equals(invocationElement.getTargetExpression().toString()))) { - // TODO: enum type simple name will not be valid when uses as fully - // qualified name (not imported) - String relTarget = context.useModules ? targetType.getSimpleName().toString() - : getRootRelativeName((Symbol) targetType); - switch (targetMethodName) { - case "name": - print(relTarget).print("[").print(invocationElement.getTargetExpression()).print("]"); - return true; - case "ordinal": - print(relTarget).print("[").print(relTarget).print("[").print(invocationElement.getTargetExpression()) - .print("]").print("]"); - return true; - case "valueOf": - if (invocationElement.getArgumentCount() == 1) { - print("").print(invocationElement.getTargetExpression()).print("[") - .print(invocationElement.getArgument(0)).print("]"); - return true; - } - break; - case "values": - print("function() { " + VAR_DECL_KEYWORD + " result: number[] = []; for(" + VAR_DECL_KEYWORD - + " val in ").print(relTarget).print( - ") { if(!isNaN(val)) { result.push(parseInt(val,10)); } } return result; }()"); - return true; - } - // enum objets wrapping - if (invocationElement.getTargetExpression() != null) { - if (invocationElement.getMethod().getModifiers().contains(Modifier.STATIC)) { - print(invocationElement.getTargetExpression()) - .print(Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_SUFFIX + ".") - .print(invocationElement.getMethodName()).print("(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - } - } - print(relTarget).print("[\"" + Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_WRAPPERS + "\"][") - .print(invocationElement.getTargetExpression()).print("].").print(invocationElement.getMethodName()) - .print("(").printArgList(invocationElement.getArguments()).print(")"); - return true; - } - - if (targetClassName != null && targetMethodName != null) { - switch (targetClassName) { - case UTIL_CLASSNAME: - case DEPRECATED_UTIL_CLASSNAME: - switch (targetMethodName) { - case "$export": - if (!invocationElement.getArgument(0).isStringLiteral()) { - report(invocationElement.getArgument(0), JSweetProblem.STRING_LITERAL_EXPECTED); - } - String varName = "_exportedVar_" - + StringUtils.strip(invocationElement.getArgument(0).toString(), "\""); - getPrinter().footer.append(VAR_DECL_KEYWORD + " " + varName + ";\n"); - if (invocationElement.getArgumentCount() == 1) { - print(varName); - } else { - print("{ " + varName + " = ").print(invocationElement.getArgument(1)).print("; "); - print("console.log('" + JSweetTranspiler.EXPORTED_VAR_BEGIN - + StringUtils.strip(invocationElement.getArgument(0).toString(), "\"") + "='+") - .print(varName).print("+'" + JSweetTranspiler.EXPORTED_VAR_END + "') }"); - } - return true; - - case "array": - case "function": - case "string": - case "bool": - case "number": - case "integer": - case "object": - printCastMethodInvocation(invocationElement); - return true; - - case "any": - print("("); - printCastMethodInvocation(invocationElement); - print(")"); - return true; - - case "union": - getPrinter().typeChecker.checkUnionTypeAssignment(context.types, getPrinter().getParent(), - ((MethodInvocationElementSupport) invocationElement).getTree()); - print("("); - printCastMethodInvocation(invocationElement); - print(")"); - return true; - - case "typeof": - print("typeof ").print(invocationElement.getArgument(0)); - return true; - - case "$noarrow": - print(invocationElement.getArgument(0)); - return true; - - case "equalsStrict": - print("(").print(invocationElement.getArgument(0)).print(" === ") - .print(invocationElement.getArgument(1)).print(")"); - return true; - - case "notEqualsStrict": - print("(").print(invocationElement.getArgument(0)).print(" !== ") - .print(invocationElement.getArgument(1)).print(")"); - return true; - - case "equalsLoose": - print("(").print(invocationElement.getArgument(0)).print(" == ") - .print(invocationElement.getArgument(1)).print(")"); - return true; - - case "notEqualsLoose": - print("(").print(invocationElement.getArgument(0)).print(" != ") - .print(invocationElement.getArgument(1)).print(")"); - return true; - - case "$strict": - getPrinter().enterComparisonMode(ComparisonMode.STRICT); - print(invocationElement.getArgument(0)); - getPrinter().exitComparisonMode(); - return true; - - case "$loose": - getPrinter().enterComparisonMode(ComparisonMode.LOOSE); - print(invocationElement.getArgument(0)); - getPrinter().exitComparisonMode(); - return true; - - case "$insert": - if (invocationElement.getArgument(0) instanceof LiteralElement) { - print(((LiteralElement) invocationElement.getArgument(0)).getValue().toString()); - return true; - } else { - report(invocationElement, JSweetProblem.MISUSED_INSERT_MACRO, - invocationElement.getMethodName()); - } - - case "$template": - if (invocationElement.getArgumentCount() == 1) { - if (invocationElement.getArgument(0) instanceof LiteralElement) { - print("`" + ((LiteralElement) invocationElement.getArgument(0)).getValue().toString() - + "`"); - return true; - } else { - if (invocationElement.getArgument(1) instanceof LiteralElement) { - print(invocationElement.getArgument(0)).print( - "`" + ((LiteralElement) invocationElement.getArgument(1)).getValue().toString() - + "`"); - return true; - } - } - } - report(invocationElement, JSweetProblem.MISUSED_INSERT_MACRO, invocationElement.getMethodName()); - - case "$map": - if (invocationElement.getArgumentCount() % 2 != 0) { - report(invocationElement, JSweetProblem.UNTYPED_OBJECT_ODD_PARAMETER_COUNT); - } - print("{"); - com.sun.tools.javac.util.List args = ((MethodInvocationElementSupport) invocationElement) - .getTree().args; - while (args != null && args.head != null) { - String key = args.head.toString(); - if (args.head.getTag() == Tag.LITERAL && key.startsWith("\"")) { - key = key.substring(1, key.length() - 1); - if (JJavaName.isJavaIdentifier(key)) { - print(key); - } else { - print("\"" + key + "\""); - } - } else { - report(invocationElement.getArgument(0), JSweetProblem.UNTYPED_OBJECT_WRONG_KEY, - args.head.toString()); - } - print(": "); - getPrinter().print(args.tail.head); - ; - args = args.tail.tail; - if (args != null && args.head != null) { - print(","); - } - } - print("}"); - return true; - - case "$array": - print("[").printArgList(invocationElement.getArguments()).print("]"); - return true; - - case "$apply": - print("(").print(invocationElement.getArgument(0)).print(")(") - .printArgList(invocationElement.getArgumentTail()).print(")"); - return true; - case "$new": - print("new (").print(invocationElement.getArgument(0)).print(")(") - .printArgList(invocationElement.getArgumentTail()).print(")"); - return true; - } - } - } - - if (targetMethodName != null) { - switch (targetMethodName) { - case INDEXED_GET_FUCTION_NAME: - if (isWithinGlobals(targetClassName)) { - if (invocationElement.getArgumentCount() == 1) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); - return true; - } else { - if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") - || invocationElement.getArgument(0).toString() - .endsWith("." + GLOBALS_CLASS_NAME + ".class")) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); - return true; - } - } - } - - if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) - || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { - print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) - .print("]"); - } else { - if (invocationElement.getArgumentCount() == 1) { - print("this[").print(invocationElement.getArguments().get(0)).print("]"); - } else { - print(invocationElement.getArguments().get(0)).print("[") - .print(invocationElement.getArguments().get(1)).print("]"); - } - } - return true; - case INDEXED_GET_STATIC_FUCTION_NAME: - if (invocationElement.getArgumentCount() == 1 && isWithinGlobals(targetClassName)) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); - return true; - } - - print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) - .print("]"); - return true; - - case INDEXED_SET_FUCTION_NAME: - if (isWithinGlobals(targetClassName)) { - if (invocationElement.getArgumentCount() == 2) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); - return true; - } else { - if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") - || invocationElement.getArgument(0).toString() - .endsWith(GLOBALS_CLASS_NAME + ".class")) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); - return true; - } - } - } - - if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) - || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { - // check the type through the getter - for (Element e : invocationElement.getTargetExpression().getTypeAsElement().getEnclosedElements()) { - if (e instanceof ExecutableElement - && INDEXED_GET_FUCTION_NAME.equals(e.getSimpleName().toString())) { - ExecutableElement getter = (ExecutableElement) e; - TypeMirror getterType = getter.getReturnType(); - TypeMirror getterIndexType = getter.getParameters().get(0).asType(); - - TypeMirror invokedIndexType = invocationElement.getArgument(0).getType(); - TypeMirror invokedValueType = invocationElement.getArgument(1).getType(); - - boolean sameIndexType = types().isSameType(getterIndexType, invokedIndexType); - - if (sameIndexType && !types().isAssignable(invokedValueType, types().erasure(getterType))) { - report(invocationElement.getArgument(1), JSweetProblem.INDEXED_SET_TYPE_MISMATCH, - getterType); - } - } - } - - print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) - .print("] = ").print(invocationElement.getArgument(1)); - } else { - if (invocationElement.getArgumentCount() == 2) { - print("this[").print(invocationElement.getArgument(0)).print("] = ") - .print(invocationElement.getArgument(1)); - } else { - print(invocationElement.getArgument(0)).print("[").print(invocationElement.getArgument(1)) - .print("] = ").print(invocationElement.getArgument(2)); - } - } - return true; - - case INDEXED_SET_STATIC_FUCTION_NAME: - - if (invocationElement.getArgumentCount() == 2 && isWithinGlobals(targetClassName)) { - report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); - return true; - } - - print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArguments().get(0)) - .print("] = ").print(invocationElement.getArguments().get(1)); - return true; - - case INDEXED_DELETE_FUCTION_NAME: - if (isWithinGlobals(targetClassName)) { - if (invocationElement.getArgumentCount() == 1) { - report(invocationElement, JSweetProblem.GLOBAL_DELETE); - return true; - } else { - if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") - || invocationElement.getArguments().get(0).toString() - .endsWith(GLOBALS_CLASS_NAME + ".class")) { - report(invocationElement, JSweetProblem.GLOBAL_DELETE); - return true; - } - } - } - - if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) - || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { - print("delete ").print(invocationElement.getTargetExpression()).print("[") - .print(invocationElement.getArguments().get(0)).print("]"); - } else { - if (invocationElement.getArgumentCount() == 1) { - print("delete this[").print(invocationElement.getArgument(0)).print("]"); - } else { - print("delete ").print(invocationElement.getArgument(0)).print("[") - .print(invocationElement.getArgument(1)).print("]"); - } - } - return true; - - case INDEXED_DELETE_STATIC_FUCTION_NAME: - if (invocationElement.getArgumentCount() == 1 && isWithinGlobals(targetClassName)) { - report(invocationElement, JSweetProblem.GLOBAL_DELETE); - return true; - } - - if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) - || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { - print("delete ").print(invocationElement.getTargetExpression()).print("[") - .print(invocationElement.getArgument(0)).print("]"); - } else { - if (invocationElement.getArgumentCount() == 1) { - print("delete ").print("this[").print(invocationElement.getArgument(0)).print("]"); - } else { - print("delete ").print(invocationElement.getArgument(0)).print("[") - .print(invocationElement.getArgument(1)).print("]"); - } - } - return true; - } - - } - - if (invocationElement.getTargetExpression() == null && "$super".equals(targetMethodName)) { - print("super(").printArgList(invocationElement.getArguments()).print(")"); - return true; - } - if (invocationElement.getTargetExpression() != null && targetClassName != null - && (targetClassName.startsWith(UTIL_PACKAGE + ".function.") - || targetClassName.startsWith(Function.class.getPackage().getName()))) { - if (!TypeChecker.jdkAllowed && targetClassName.startsWith(Function.class.getPackage().getName()) - && TypeChecker.FORBIDDEN_JDK_FUNCTIONAL_METHODS.contains(targetMethodName)) { - report(invocationElement, JSweetProblem.JDK_METHOD, targetMethodName); - } - printFunctionalInvocation(invocationElement.getTargetExpression(), targetMethodName, - invocationElement.getArguments()); - return true; - } - if (invocationElement.getTargetExpression() != null && targetClassName != null - && targetClassName.equals(java.lang.Runnable.class.getName())) { - printFunctionalInvocation(invocationElement.getTargetExpression(), targetMethodName, - invocationElement.getArguments()); - return true; - } - - // built-in Java support - - if (targetClassName != null) { - - // expand macros - switch (targetMethodName) { - case "getMessage": - if (targetType instanceof TypeElement) { - if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { - print(invocationElement.getTargetExpression()).print(".message"); - return true; - } - } - break; - case "getCause": - if (targetType instanceof TypeElement) { - if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { - print("(null)"); - return true; - } - } - break; - case "printStackTrace": - if (targetType instanceof TypeElement) { - if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { - print("console.error(").print(invocationElement.getTargetExpression()).print(".message, ") - .print(invocationElement.getTargetExpression()).print(")"); - return true; - } - } - break; - } - - switch (targetClassName) { - case "java.lang.String": - case "java.lang.CharSequence": - switch (targetMethodName) { - case "valueOf": - printMacroName(targetMethodName); - if (invocationElement.getArgumentCount() == 3) { - print("((str, index, len) => str.join('').substring(index, index + len))(") - .printArgList(invocationElement.getArguments()).print(")"); - } else { - print("new String(").printArgList(invocationElement.getArguments()).print(").toString()"); - } - return true; - case "subSequence": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".substring(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - // this macro should use 'includes' in ES6 - case "contains": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".indexOf(") - .printArgList(invocationElement.getArguments()).print(") != -1"); - return true; - case "length": - print(invocationElement.getTargetExpression()).print(".length"); - return true; - // this macro is not needed in ES6 - case "startsWith": - printMacroName(targetMethodName); - print("((str, searchString, position = 0) => str.substr(position, searchString.length) === searchString)(") - .print(invocationElement.getTargetExpression()).print(", ") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "endsWith": - printMacroName(targetMethodName); - print("((str, searchString) => { " + VAR_DECL_KEYWORD + " pos = str.length - searchString.length; " - + VAR_DECL_KEYWORD - + " lastIndex = str.indexOf(searchString, pos); return lastIndex !== -1 && lastIndex === pos; })(") - .print(invocationElement.getTargetExpression()).print(", ") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - // this macro is not needed in ES6 - case "codePointAt": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".charCodeAt(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "isEmpty": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()).print(".length === 0)"); - return true; - case "compareToIgnoreCase": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".toUpperCase().localeCompare(") - .printArgList(invocationElement.getArguments()).print(".toUpperCase())"); - return true; - case "compareTo": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".localeCompare(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "equalsIgnoreCase": - printMacroName(targetMethodName); - print("((o1, o2) => o1.toUpperCase() === (o2===null?o2:o2.toUpperCase()))(") - .print(invocationElement.getTargetExpression()).print(", ") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "toChars": - printMacroName(targetMethodName); - print("String.fromCharCode(").printArgList(invocationElement.getArguments()).print(")"); - return true; - // In ES6, we can use the Array.from method - case "getBytes": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()) - .print(").split('').map(s => s.charCodeAt(0))"); - return true; - // In ES6, we can use the Array.from method - case "toCharArray": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()).print(").split('')"); - return true; - case "getChars": - printMacroName(targetMethodName); - print("((a, s, e, d, l) => { d.splice.apply(d, [l, e-s].concat(a.substring(s, e).split(''))); })(") - .print(invocationElement.getTargetExpression()).print(", ") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "replaceAll": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".replace(new RegExp(") - .print(invocationElement.getArguments().get(0)).print(", 'g'),") - .print(invocationElement.getArguments().get(1)).print(")"); - return true; - case "replace": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".split(") - .print(invocationElement.getArguments().get(0)).print(").join(") - .print(invocationElement.getArguments().get(1)).print(")"); - return true; - case "lastIndexOf": - print(invocationElement.getTargetExpression()).print(".lastIndexOf(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "indexOf": - if (invocationElement.getArgumentCount() == 1 - && util().isNumber(invocationElement.getArgument(0).getType())) { - print(invocationElement.getTargetExpression()).print(".indexOf(String.fromCharCode(") - .print(invocationElement.getArgument(0)).print("))"); - } else { - print(invocationElement.getTargetExpression()).print(".indexOf(") - .printArgList(invocationElement.getArguments()).print(")"); - } - return true; - case "toLowerCase": - if (invocationElement.getArgumentCount() > 0) { - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".toLowerCase()"); - return true; - } - break; - case "toUpperCase": - if (invocationElement.getArgumentCount() > 0) { - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()).print(".toUpperCase()"); - return true; - } - break; - } - break; - case "java.lang.Character": - switch (targetMethodName) { - case "toChars": - printMacroName(targetMethodName); - print("String.fromCharCode(").printArgList(invocationElement.getArguments()).print(")"); - return true; - } - break; - case "java.lang.Float": - case "java.lang.Double": - case "java.lang.Integer": - case "java.lang.Byte": - case "java.lang.Long": - case "java.lang.Short": - switch (targetMethodName) { - case "isNaN": - printMacroName(targetMethodName); - if (invocationElement.getArgumentCount() > 0) { - print("isNaN(").printArgList(invocationElement.getArguments()).print(")"); - return true; - } else { - print("isNaN(").print(invocationElement.getTargetExpression()).print(")"); - return true; - } - case "isInfinite": - printMacroName(targetMethodName); - if (invocationElement.getArgumentCount() > 0) { - print("((value) => Number.NEGATIVE_INFINITY === value || Number.POSITIVE_INFINITY === value)(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - } else { - print("((value) => Number.NEGATIVE_INFINITY === value || Number.POSITIVE_INFINITY === value)(") - .print(invocationElement.getTargetExpression()).print(")"); - return true; - } - case "intValue": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); - return true; - case "shortValue": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); - return true; - case "byteValue": - printMacroName(targetMethodName); - print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); - return true; - case "floatValue": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()); - return true; - case "doubleValue": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()); - return true; - case "longValue": - printMacroName(targetMethodName); - print(invocationElement.getTargetExpression()); - return true; - case "compare": - if (invocationElement.getArgumentCount() == 2) { - printMacroName(targetMethodName); - print("(").print(invocationElement.getArgument(0)).print(" - ") - .print(invocationElement.getArgument(1)).print(")"); - return true; - } - break; - case "toString": - if (invocationElement.getArgumentCount() > 0) { - printMacroName(targetMethodName); - print("(''+(").print(invocationElement.getArgument(0)).print("))"); - return true; - } - } - break; - case "java.lang.Math": - switch (targetMethodName) { - case "cbrt": - printMacroName(targetMethodName); - print("Math.pow(").printArgList(invocationElement.getArguments()).print(", 1/3)"); - return true; - case "copySign": - printMacroName(targetMethodName); - print("((magnitude, sign) => { if (sign < 0) { return (magnitude < 0) ? magnitude : -magnitude; } else { return (magnitude > 0) ? magnitude : -magnitude; } })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "cosh": - printMacroName(targetMethodName); - print("(x => (Math.exp(x) + Math.exp(-x)) / 2)(").printArgList(invocationElement.getArguments()) - .print(")"); - return true; - case "expm1": - printMacroName(targetMethodName); - print("(d => { if (d == 0.0 || d === Number.NaN) { return d; } else if (!Number.POSITIVE_INFINITY === d && !Number.NEGATIVE_INFINITY === d) { if (d < 0) { return -1; } else { return Number.POSITIVE_INFINITY; } } })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "hypot": - printMacroName(targetMethodName); - print("(x => Math.sqrt(x * x + y * y))(").printArgList(invocationElement.getArguments()).print(")"); - return true; - case "log10": - printMacroName(targetMethodName); - print("(x => Math.log(x) * Math.LOG10E)(").printArgList(invocationElement.getArguments()) - .print(")"); - return true; - case "log1p": - printMacroName(targetMethodName); - print("(x => Math.log(x + 1))(").printArgList(invocationElement.getArguments()).print(")"); - return true; - case "rint": - printMacroName(targetMethodName); - print("(d => { if (d === Number.NaN) { return d; } else if (Number.POSITIVE_INFINITY === d || Number.NEGATIVE_INFINITY === d) { return d; } else if(d == 0) { return d; } else { return Math.round(d); } })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "scalb": - printMacroName(targetMethodName); - print("((d, scaleFactor) => { if (scaleFactor >= 31 || scaleFactor <= -31) { return d * Math.pow(2, scaleFactor); } else if (scaleFactor > 0) { return d * (1 << scaleFactor); } else if (scaleFactor == 0) { return d; } else { return d * 1 / (1 << -scaleFactor); } })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "signum": - printMacroName(targetMethodName); - print("(f => { if (f > 0) { return 1; } else if (f < 0) { return -1; } else { return 0; } })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "sinh": - printMacroName(targetMethodName); - print("(x => (Math.exp(x) - Math.exp(-x)) / 2)(").printArgList(invocationElement.getArguments()) - .print(")"); - return true; - case "tanh": - printMacroName(targetMethodName); - print("(x => { if (x == Number.POSITIVE_INFINITY) { return 1; } else if (x == Number.NEGATIVE_INFINITY) { return -1; } double e2x = Math.exp(2 * x); return (e2x - 1) / (e2x + 1); })(") - .printArgList(invocationElement.getArguments()).print(")"); - return true; - case "toDegrees": - printMacroName(targetMethodName); - print("(x => x * 180 / Math.PI)(").printArgList(invocationElement.getArguments()).print(")"); - return true; - case "toRadians": - printMacroName(targetMethodName); - print("(x => x * Math.PI / 180)(").printArgList(invocationElement.getArguments()).print(")"); - return true; - case "nextUp": - delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); - return true; - case "nextDown": - delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); - return true; - case "ulp": - delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); - return true; - case "IEEEremainder": - delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); - return true; - default: - print("Math." + targetMethodName + "(").printArgList(invocationElement.getArguments()).print(")"); - return true; - } - - case "java.lang.Class": - switch (targetMethodName) { - case "getName": - if (context.options.isSupportGetClass()) { - printMacroName(targetMethodName); - getPrinter() - .print("(c => c[\"" + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]?c[\"" - + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]:c[\"name\"])("); - printTarget(invocationElement.getTargetExpression()); - print(")"); - return true; - } else { - if (invocationElement.getTargetExpression() != null - && invocationElement.getTargetExpression().toString().endsWith(".class")) { - printMacroName(targetMethodName); - print("\"").print(util() - .getQualifiedName(((DeclaredType) invocationElement.getTargetExpression().getType()) - .getTypeArguments().get(0))) - .print("\""); - return true; - } - } - break; - case "getSimpleName": - if (context.options.isSupportGetClass()) { - printMacroName(targetMethodName); - print("(c => c[\"" + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]?c[\"" - + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"].substring(c[\"" - + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR - + "\"].lastIndexOf('.')+1):c[\"name\"].substring(c[\"name\"].lastIndexOf('.')+1))("); - printTarget(invocationElement.getTargetExpression()); - print(")"); - return true; - } else { - if (invocationElement.getTargetExpression() != null - && invocationElement.getTargetExpression().toString().endsWith(".class")) { - printMacroName(targetMethodName); - print("\"").print(util() - .getQualifiedName(((DeclaredType) invocationElement.getTargetExpression().getType()) - .getTypeArguments().get(0))) - .print("\""); - return true; - } - } - break; - } - break; - - // case "java.util.Date": - // switch (targetMethodName) { - // case "setFullYear": - // printMacroName(targetMethodName); - // print(fieldAccess.getExpression()).print(".setYear(").printArgList(invocation.args).print(")"); - // - // } - // break; - } - - if (invocationElement.getTargetExpression() != null && isMappedType(targetClassName) - && targetClassName.startsWith("java.lang.")) { - if (invocationElement.getMethod().getModifiers().contains(Modifier.STATIC)) { - // delegation to javaemul - delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); - return true; - } else { - switch (targetMethodName) { - case "equals": - printMacroName(targetMethodName); - print("(((o1: any, o2: any) => { if(o1 && o1.equals) { return o1.equals(o2); } else { return o1 === o2; } })("); - printTarget(invocationElement.getTargetExpression()).print(",") - .print(invocationElement.getArgument(0)); - print("))"); - return true; - } - } - } - + break; + case "values": + print("function() { " + VAR_DECL_KEYWORD + " result: number[] = []; for(" + VAR_DECL_KEYWORD + + " val in ").print(relTarget).print( + ") { if(!isNaN(val)) { result.push(parseInt(val,10)); } } return result; }()"); + return true; + } + // enum objets wrapping + if (invocationElement.getTargetExpression() != null) { + if (invocationElement.getMethod().getModifiers().contains(Modifier.STATIC)) { + print(invocationElement.getTargetExpression()) + .print(Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_SUFFIX + ".") + .print(invocationElement.getMethodName()).print("(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; } + } + print(relTarget).print("[\"" + Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_WRAPPERS + "\"][") + .print(invocationElement.getTargetExpression()).print("].").print(invocationElement.getMethodName()) + .print("(").printArgList(invocationElement.getArguments()).print(")"); + return true; + } + if (targetClassName != null && targetMethodName != null) { + switch (targetClassName) { + case UTIL_CLASSNAME: + case DEPRECATED_UTIL_CLASSNAME: switch (targetMethodName) { - case "getClass": - print("("); - printTarget(invocationElement.getTargetExpression()); - print(".constructor)"); + case "$export": + if (!invocationElement.getArgument(0).isStringLiteral()) { + report(invocationElement.getArgument(0), JSweetProblem.STRING_LITERAL_EXPECTED); + } + String varName = "_exportedVar_" + + StringUtils.strip(invocationElement.getArgument(0).toString(), "\""); + getPrinter().footer.append(VAR_DECL_KEYWORD + " " + varName + ";\n"); + if (invocationElement.getArgumentCount() == 1) { + print(varName); + } else { + print("{ " + varName + " = ").print(invocationElement.getArgument(1)).print("; "); + print("console.log('" + JSweetTranspiler.EXPORTED_VAR_BEGIN + + StringUtils.strip(invocationElement.getArgument(0).toString(), "\"") + "='+") + .print(varName).print("+'" + JSweetTranspiler.EXPORTED_VAR_END + "') }"); + } + return true; + + case "array": + case "function": + case "string": + case "bool": + case "number": + case "integer": + case "object": + printCastMethodInvocation(invocationElement); + return true; + + case "any": + print("("); + printCastMethodInvocation(invocationElement); + print(")"); + return true; + + case "union": + getPrinter().typeChecker.checkUnionTypeAssignment(context.types, getPrinter().getParent(), + ((MethodInvocationElementSupport) invocationElement).getTree()); + print("("); + printCastMethodInvocation(invocationElement); + print(")"); + return true; + + case "typeof": + print("typeof ").print(invocationElement.getArgument(0)); + return true; + + case "$noarrow": + print(invocationElement.getArgument(0)); + return true; + + case "equalsStrict": + print("(").print(invocationElement.getArgument(0)).print(" === ") + .print(invocationElement.getArgument(1)).print(")"); + return true; + + case "notEqualsStrict": + print("(").print(invocationElement.getArgument(0)).print(" !== ") + .print(invocationElement.getArgument(1)).print(")"); + return true; + + case "equalsLoose": + print("(").print(invocationElement.getArgument(0)).print(" == ") + .print(invocationElement.getArgument(1)).print(")"); + return true; + + case "notEqualsLoose": + print("(").print(invocationElement.getArgument(0)).print(" != ") + .print(invocationElement.getArgument(1)).print(")"); + return true; + + case "$strict": + getPrinter().enterComparisonMode(ComparisonMode.STRICT); + print(invocationElement.getArgument(0)); + getPrinter().exitComparisonMode(); + return true; + + case "$loose": + getPrinter().enterComparisonMode(ComparisonMode.LOOSE); + print(invocationElement.getArgument(0)); + getPrinter().exitComparisonMode(); + return true; + + case "$insert": + if (invocationElement.getArgument(0) instanceof LiteralElement) { + print(((LiteralElement) invocationElement.getArgument(0)).getValue().toString()); return true; - case "hashCode": + } else { + report(invocationElement, JSweetProblem.MISUSED_INSERT_MACRO, + invocationElement.getMethodName()); + } + + case "$template": + if (invocationElement.getArgumentCount() == 1) { + if (invocationElement.getArgument(0) instanceof LiteralElement) { + print("`" + ((LiteralElement) invocationElement.getArgument(0)).getValue().toString() + + "`"); + return true; + } else { + if (invocationElement.getArgument(1) instanceof LiteralElement) { + print(invocationElement.getArgument(0)).print( + "`" + ((LiteralElement) invocationElement.getArgument(1)).getValue().toString() + + "`"); + return true; + } + } + } + report(invocationElement, JSweetProblem.MISUSED_INSERT_MACRO, invocationElement.getMethodName()); + + case "$map": + if (invocationElement.getArgumentCount() % 2 != 0) { + report(invocationElement, JSweetProblem.UNTYPED_OBJECT_ODD_PARAMETER_COUNT); + } + print("{"); + com.sun.tools.javac.util.List args = ((MethodInvocationElementSupport) invocationElement) + .getTree().args; + while (args != null && args.head != null) { + String key = args.head.toString(); + if (args.head.getTag() == Tag.LITERAL && key.startsWith("\"")) { + key = key.substring(1, key.length() - 1); + if (JJavaName.isJavaIdentifier(key)) { + print(key); + } else { + print("\"" + key + "\""); + } + } else { + report(invocationElement.getArgument(0), JSweetProblem.UNTYPED_OBJECT_WRONG_KEY, + args.head.toString()); + } + print(": "); + getPrinter().print(args.tail.head); + ; + args = args.tail.tail; + if (args != null && args.head != null) { + print(","); + } + } + print("}"); + return true; + + case "$array": + print("[").printArgList(invocationElement.getArguments()).print("]"); + return true; + + case "$apply": + print("(").print(invocationElement.getArgument(0)).print(")(") + .printArgList(invocationElement.getArgumentTail()).print(")"); + return true; + case "$new": + print("new (").print(invocationElement.getArgument(0)).print(")(") + .printArgList(invocationElement.getArgumentTail()).print(")"); + return true; + } + } + } + + if (targetMethodName != null) { + switch (targetMethodName) { + case INDEXED_GET_FUCTION_NAME: + if (isWithinGlobals(targetClassName)) { + if (invocationElement.getArgumentCount() == 1) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); + return true; + } else { + if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") + || invocationElement.getArgument(0).toString() + .endsWith("." + GLOBALS_CLASS_NAME + ".class")) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); + return true; + } + } + } + + if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) + || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { + print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) + .print("]"); + } else { + if (invocationElement.getArgumentCount() == 1) { + print("this[").print(invocationElement.getArguments().get(0)).print("]"); + } else { + print(invocationElement.getArguments().get(0)).print("[") + .print(invocationElement.getArguments().get(1)).print("]"); + } + } + return true; + case INDEXED_GET_STATIC_FUCTION_NAME: + if (invocationElement.getArgumentCount() == 1 && isWithinGlobals(targetClassName)) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_GET); + return true; + } + + print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) + .print("]"); + return true; + + case INDEXED_SET_FUCTION_NAME: + if (isWithinGlobals(targetClassName)) { + if (invocationElement.getArgumentCount() == 2) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); + return true; + } else { + if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") + || invocationElement.getArgument(0).toString() + .endsWith(GLOBALS_CLASS_NAME + ".class")) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); + return true; + } + } + } + + if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) + || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { + // check the type through the getter + for (Element e : invocationElement.getTargetExpression().getTypeAsElement().getEnclosedElements()) { + if (e instanceof ExecutableElement + && INDEXED_GET_FUCTION_NAME.equals(e.getSimpleName().toString())) { + ExecutableElement getter = (ExecutableElement) e; + TypeMirror getterType = getter.getReturnType(); + TypeMirror getterIndexType = getter.getParameters().get(0).asType(); + + TypeMirror invokedIndexType = invocationElement.getArgument(0).getType(); + TypeMirror invokedValueType = invocationElement.getArgument(1).getType(); + + boolean sameIndexType = types().isSameType(getterIndexType, invokedIndexType); + + if (sameIndexType && !types().isAssignable(invokedValueType, types().erasure(getterType))) { + report(invocationElement.getArgument(1), JSweetProblem.INDEXED_SET_TYPE_MISMATCH, + getterType); + } + } + } + + print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArgument(0)) + .print("] = ").print(invocationElement.getArgument(1)); + } else { + if (invocationElement.getArgumentCount() == 2) { + print("this[").print(invocationElement.getArgument(0)).print("] = ") + .print(invocationElement.getArgument(1)); + } else { + print(invocationElement.getArgument(0)).print("[").print(invocationElement.getArgument(1)) + .print("] = ").print(invocationElement.getArgument(2)); + } + } + return true; + + case INDEXED_SET_STATIC_FUCTION_NAME: + + if (invocationElement.getArgumentCount() == 2 && isWithinGlobals(targetClassName)) { + report(invocationElement, JSweetProblem.GLOBAL_INDEXER_SET); + return true; + } + + print(invocationElement.getTargetExpression()).print("[").print(invocationElement.getArguments().get(0)) + .print("] = ").print(invocationElement.getArguments().get(1)); + return true; + + case INDEXED_DELETE_FUCTION_NAME: + if (isWithinGlobals(targetClassName)) { + if (invocationElement.getArgumentCount() == 1) { + report(invocationElement, JSweetProblem.GLOBAL_DELETE); + return true; + } else { + if (invocationElement.getArgument(0).toString().equals(GLOBALS_CLASS_NAME + ".class") + || invocationElement.getArguments().get(0).toString() + .endsWith(GLOBALS_CLASS_NAME + ".class")) { + report(invocationElement, JSweetProblem.GLOBAL_DELETE); + return true; + } + } + } + + if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) + || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { + print("delete ").print(invocationElement.getTargetExpression()).print("[") + .print(invocationElement.getArguments().get(0)).print("]"); + } else { + if (invocationElement.getArgumentCount() == 1) { + print("delete this[").print(invocationElement.getArgument(0)).print("]"); + } else { + print("delete ").print(invocationElement.getArgument(0)).print("[") + .print(invocationElement.getArgument(1)).print("]"); + } + } + return true; + + case INDEXED_DELETE_STATIC_FUCTION_NAME: + if (invocationElement.getArgumentCount() == 1 && isWithinGlobals(targetClassName)) { + report(invocationElement, JSweetProblem.GLOBAL_DELETE); + return true; + } + + if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) + || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { + print("delete ").print(invocationElement.getTargetExpression()).print("[") + .print(invocationElement.getArgument(0)).print("]"); + } else { + if (invocationElement.getArgumentCount() == 1) { + print("delete ").print("this[").print(invocationElement.getArgument(0)).print("]"); + } else { + print("delete ").print(invocationElement.getArgument(0)).print("[") + .print(invocationElement.getArgument(1)).print("]"); + } + } + return true; + } + + } + + if (invocationElement.getTargetExpression() == null && "$super".equals(targetMethodName)) { + print("super(").printArgList(invocationElement.getArguments()).print(")"); + return true; + } + if (invocationElement.getTargetExpression() != null && targetClassName != null + && (targetClassName.startsWith(UTIL_PACKAGE + ".function.") + || targetClassName.startsWith(Function.class.getPackage().getName()))) { + if (!TypeChecker.jdkAllowed && targetClassName.startsWith(Function.class.getPackage().getName()) + && TypeChecker.FORBIDDEN_JDK_FUNCTIONAL_METHODS.contains(targetMethodName)) { + report(invocationElement, JSweetProblem.JDK_METHOD, targetMethodName); + } + printFunctionalInvocation(invocationElement.getTargetExpression(), targetMethodName, + invocationElement.getArguments()); + return true; + } + if (invocationElement.getTargetExpression() != null && targetClassName != null + && targetClassName.equals(java.lang.Runnable.class.getName())) { + printFunctionalInvocation(invocationElement.getTargetExpression(), targetMethodName, + invocationElement.getArguments()); + return true; + } + + // built-in Java support + + if (targetClassName != null) { + + // expand macros + switch (targetMethodName) { + case "getMessage": + if (targetType instanceof TypeElement) { + if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { + print(invocationElement.getTargetExpression()).print(".message"); + return true; + } + } + break; + case "getCause": + if (targetType instanceof TypeElement) { + if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { + print("(null)"); + return true; + } + } + break; + case "printStackTrace": + if (targetType instanceof TypeElement) { + if (types().isAssignable(targetType.asType(), util().getType(Throwable.class))) { + print("console.error(").print(invocationElement.getTargetExpression()).print(".message, ") + .print(invocationElement.getTargetExpression()).print(")"); + return true; + } + } + break; + } + + switch (targetClassName) { + case "java.lang.String": + case "java.lang.CharSequence": + switch (targetMethodName) { + case "valueOf": + printMacroName(targetMethodName); + if (invocationElement.getArgumentCount() == 3) { + print("((str, index, len) => str.join('').substring(index, index + len))(") + .printArgList(invocationElement.getArguments()).print(")"); + } else { + print("new String(").printArgList(invocationElement.getArguments()).print(").toString()"); + } + return true; + case "subSequence": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".substring(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + // this macro should use 'includes' in ES6 + case "contains": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".indexOf(") + .printArgList(invocationElement.getArguments()).print(") != -1"); + return true; + case "length": + print(invocationElement.getTargetExpression()).print(".length"); + return true; + // this macro is not needed in ES6 + case "startsWith": + printMacroName(targetMethodName); + print("((str, searchString, position = 0) => str.substr(position, searchString.length) === searchString)(") + .print(invocationElement.getTargetExpression()).print(", ") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "endsWith": + printMacroName(targetMethodName); + print("((str, searchString) => { " + VAR_DECL_KEYWORD + " pos = str.length - searchString.length; " + + VAR_DECL_KEYWORD + + " lastIndex = str.indexOf(searchString, pos); return lastIndex !== -1 && lastIndex === pos; })(") + .print(invocationElement.getTargetExpression()).print(", ") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + // this macro is not needed in ES6 + case "codePointAt": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".charCodeAt(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "isEmpty": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()).print(".length === 0)"); + return true; + case "compareToIgnoreCase": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".toUpperCase().localeCompare(") + .printArgList(invocationElement.getArguments()).print(".toUpperCase())"); + return true; + case "compareTo": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".localeCompare(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "equalsIgnoreCase": + printMacroName(targetMethodName); + print("((o1, o2) => o1.toUpperCase() === (o2===null?o2:o2.toUpperCase()))(") + .print(invocationElement.getTargetExpression()).print(", ") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "toChars": + printMacroName(targetMethodName); + print("String.fromCharCode(").printArgList(invocationElement.getArguments()).print(")"); + return true; + // In ES6, we can use the Array.from method + case "getBytes": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()) + .print(").split('').map(s => s.charCodeAt(0))"); + return true; + // In ES6, we can use the Array.from method + case "toCharArray": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()).print(").split('')"); + return true; + case "getChars": + printMacroName(targetMethodName); + print("((a, s, e, d, l) => { d.splice.apply(d, [l, e-s].concat(a.substring(s, e).split(''))); })(") + .print(invocationElement.getTargetExpression()).print(", ") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "replaceAll": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".replace(new RegExp(") + .print(invocationElement.getArguments().get(0)).print(", 'g'),") + .print(invocationElement.getArguments().get(1)).print(")"); + return true; + case "replace": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".split(") + .print(invocationElement.getArguments().get(0)).print(").join(") + .print(invocationElement.getArguments().get(1)).print(")"); + return true; + case "lastIndexOf": + print(invocationElement.getTargetExpression()).print(".lastIndexOf(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "indexOf": + if (invocationElement.getArgumentCount() == 1 + && util().isNumber(invocationElement.getArgument(0).getType())) { + print(invocationElement.getTargetExpression()).print(".indexOf(String.fromCharCode(") + .print(invocationElement.getArgument(0)).print("))"); + } else { + print(invocationElement.getTargetExpression()).print(".indexOf(") + .printArgList(invocationElement.getArguments()).print(")"); + } + return true; + case "toLowerCase": + if (invocationElement.getArgumentCount() > 0) { printMacroName(targetMethodName); - print("(((o: any) => { if(o.hashCode) { return o.hashCode(); } else { return o.toString(); } })("); + print(invocationElement.getTargetExpression()).print(".toLowerCase()"); + return true; + } + break; + case "toUpperCase": + if (invocationElement.getArgumentCount() > 0) { + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()).print(".toUpperCase()"); + return true; + } + break; + } + break; + case "java.lang.Character": + switch (targetMethodName) { + case "toChars": + printMacroName(targetMethodName); + print("String.fromCharCode(").printArgList(invocationElement.getArguments()).print(")"); + return true; + } + break; + case "java.lang.Float": + case "java.lang.Double": + case "java.lang.Integer": + case "java.lang.Byte": + case "java.lang.Long": + case "java.lang.Short": + switch (targetMethodName) { + case "isNaN": + printMacroName(targetMethodName); + if (invocationElement.getArgumentCount() > 0) { + print("isNaN(").printArgList(invocationElement.getArguments()).print(")"); + return true; + } else { + print("isNaN(").print(invocationElement.getTargetExpression()).print(")"); + return true; + } + case "isInfinite": + printMacroName(targetMethodName); + if (invocationElement.getArgumentCount() > 0) { + print("((value) => Number.NEGATIVE_INFINITY === value || Number.POSITIVE_INFINITY === value)(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + } else { + print("((value) => Number.NEGATIVE_INFINITY === value || Number.POSITIVE_INFINITY === value)(") + .print(invocationElement.getTargetExpression()).print(")"); + return true; + } + case "intValue": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); + return true; + case "shortValue": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); + return true; + case "byteValue": + printMacroName(targetMethodName); + print("(").print(invocationElement.getTargetExpression()).print("|0").print(")"); + return true; + case "floatValue": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()); + return true; + case "doubleValue": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()); + return true; + case "longValue": + printMacroName(targetMethodName); + print(invocationElement.getTargetExpression()); + return true; + case "compare": + if (invocationElement.getArgumentCount() == 2) { + printMacroName(targetMethodName); + print("(").print(invocationElement.getArgument(0)).print(" - ") + .print(invocationElement.getArgument(1)).print(")"); + return true; + } + break; + case "toString": + if (invocationElement.getArgumentCount() > 0) { + printMacroName(targetMethodName); + print("(''+(").print(invocationElement.getArgument(0)).print("))"); + return true; + } + } + break; + case "java.lang.Math": + switch (targetMethodName) { + case "cbrt": + printMacroName(targetMethodName); + print("Math.pow(").printArgList(invocationElement.getArguments()).print(", 1/3)"); + return true; + case "copySign": + printMacroName(targetMethodName); + print("((magnitude, sign) => { if (sign < 0) { return (magnitude < 0) ? magnitude : -magnitude; } else { return (magnitude > 0) ? magnitude : -magnitude; } })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "cosh": + printMacroName(targetMethodName); + print("(x => (Math.exp(x) + Math.exp(-x)) / 2)(").printArgList(invocationElement.getArguments()) + .print(")"); + return true; + case "expm1": + printMacroName(targetMethodName); + print("(d => { if (d == 0.0 || d === Number.NaN) { return d; } else if (!Number.POSITIVE_INFINITY === d && !Number.NEGATIVE_INFINITY === d) { if (d < 0) { return -1; } else { return Number.POSITIVE_INFINITY; } } })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "hypot": + printMacroName(targetMethodName); + print("(x => Math.sqrt(x * x + y * y))(").printArgList(invocationElement.getArguments()).print(")"); + return true; + case "log10": + printMacroName(targetMethodName); + print("(x => Math.log(x) * Math.LOG10E)(").printArgList(invocationElement.getArguments()) + .print(")"); + return true; + case "log1p": + printMacroName(targetMethodName); + print("(x => Math.log(x + 1))(").printArgList(invocationElement.getArguments()).print(")"); + return true; + case "rint": + printMacroName(targetMethodName); + print("(d => { if (d === Number.NaN) { return d; } else if (Number.POSITIVE_INFINITY === d || Number.NEGATIVE_INFINITY === d) { return d; } else if(d == 0) { return d; } else { return Math.round(d); } })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "scalb": + printMacroName(targetMethodName); + print("((d, scaleFactor) => { if (scaleFactor >= 31 || scaleFactor <= -31) { return d * Math.pow(2, scaleFactor); } else if (scaleFactor > 0) { return d * (1 << scaleFactor); } else if (scaleFactor == 0) { return d; } else { return d * 1 / (1 << -scaleFactor); } })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "signum": + printMacroName(targetMethodName); + print("(f => { if (f > 0) { return 1; } else if (f < 0) { return -1; } else { return 0; } })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "sinh": + printMacroName(targetMethodName); + print("(x => (Math.exp(x) - Math.exp(-x)) / 2)(").printArgList(invocationElement.getArguments()) + .print(")"); + return true; + case "tanh": + printMacroName(targetMethodName); + print("(x => { if (x == Number.POSITIVE_INFINITY) { return 1; } else if (x == Number.NEGATIVE_INFINITY) { return -1; } double e2x = Math.exp(2 * x); return (e2x - 1) / (e2x + 1); })(") + .printArgList(invocationElement.getArguments()).print(")"); + return true; + case "toDegrees": + printMacroName(targetMethodName); + print("(x => x * 180 / Math.PI)(").printArgList(invocationElement.getArguments()).print(")"); + return true; + case "toRadians": + printMacroName(targetMethodName); + print("(x => x * Math.PI / 180)(").printArgList(invocationElement.getArguments()).print(")"); + return true; + case "nextUp": + delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); + return true; + case "nextDown": + delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); + return true; + case "ulp": + delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); + return true; + case "IEEEremainder": + delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); + return true; + default: + print("Math." + targetMethodName + "(").printArgList(invocationElement.getArguments()).print(")"); + return true; + } + + case "java.lang.Class": + switch (targetMethodName) { + case "getName": + if (context.options.isSupportGetClass()) { + printMacroName(targetMethodName); + getPrinter() + .print("(c => c[\"" + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]?c[\"" + + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]:c[\"name\"])("); printTarget(invocationElement.getTargetExpression()); + print(")"); + return true; + } else { + if (invocationElement.getTargetExpression() != null + && invocationElement.getTargetExpression().toString().endsWith(".class")) { + printMacroName(targetMethodName); + print("\"").print(util() + .getQualifiedName(((DeclaredType) invocationElement.getTargetExpression().getType()) + .getTypeArguments().get(0))) + .print("\""); + return true; + } + } + break; + case "getSimpleName": + if (context.options.isSupportGetClass()) { + printMacroName(targetMethodName); + print("(c => c[\"" + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"]?c[\"" + + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + "\"].substring(c[\"" + + Java2TypeScriptTranslator.CLASS_NAME_IN_CONSTRUCTOR + + "\"].lastIndexOf('.')+1):c[\"name\"].substring(c[\"name\"].lastIndexOf('.')+1))("); + printTarget(invocationElement.getTargetExpression()); + print(")"); + return true; + } else { + if (invocationElement.getTargetExpression() != null + && invocationElement.getTargetExpression().toString().endsWith(".class")) { + printMacroName(targetMethodName); + print("\"").print(util() + .getQualifiedName(((DeclaredType) invocationElement.getTargetExpression().getType()) + .getTypeArguments().get(0))) + .print("\""); + return true; + } + } + break; + } + break; + + // case "java.util.Date": + // switch (targetMethodName) { + // case "setFullYear": + // printMacroName(targetMethodName); + // print(fieldAccess.getExpression()).print(".setYear(").printArgList(invocation.args).print(")"); + // + // } + // break; + } + + if (invocationElement.getTargetExpression() != null && isMappedType(targetClassName) + && targetClassName.startsWith("java.lang.")) { + if (invocationElement.getMethod().getModifiers().contains(Modifier.STATIC)) { + // delegation to javaemul + delegateToEmulLayer(targetClassName, targetMethodName, invocationElement); + return true; + } else { + switch (targetMethodName) { + case "equals": + printMacroName(targetMethodName); + print("(((o1: any, o2: any) => { if(o1 && o1.equals) { return o1.equals(o2); } else { return o1 === o2; } })("); + printTarget(invocationElement.getTargetExpression()).print(",") + .print(invocationElement.getArgument(0)); print("))"); return true; - case "equals": - if (invocationElement.getTargetExpression() != null) { - MethodSymbol methSym = Util.findMethodDeclarationInType(context.types, - (TypeSymbol) invocationElement.getTargetExpression().getTypeAsElement(), targetMethodName, - (MethodType) invocationElement.getMethod().asType()); - if (methSym != null && (Object.class.getName().equals(methSym.getEnclosingElement().toString()) - || methSym.getEnclosingElement().isInterface())) { - printMacroName(targetMethodName); - print("(((o1: any, o2: any) => { if(o1 && o1.equals) { return o1.equals(o2); } else { return o1 === o2; } })("); - printTarget(invocationElement.getTargetExpression()).print(",") - .print(invocationElement.getArgument(0)); - print("))"); - return true; - } - } - break; - case "clone": - if (!invocationElement.getMethod().getModifiers().contains(Modifier.STATIC) - && invocationElement.getArgumentCount() == 0) { - printMacroName(targetMethodName); - if (invocationElement.getTargetExpression() != null - && "super".equals(invocationElement.getTargetExpression().toString())) { - JCClassDecl parent = getPrinter().getParent(JCClassDecl.class); - if (parent.sym.getSuperclass() != null - && !context.symtab.objectType.equals(parent.sym.getSuperclass())) { - print("((o:any) => { if(super.clone!=undefined) { return super.clone(); } else { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; } })(this)"); - } else { - print("((o:any) => { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; })(this)"); - } - } else { - print("((o:any) => { if(o.clone!=undefined) { return (o).clone(); } else { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; } })("); - printTarget(invocationElement.getTargetExpression()); - print(")"); - } - return true; - } + } } - - return super.substituteMethodInvocation(invocationElement); + } } - protected void printFunctionalInvocation(ExtendedElement target, String functionName, - List arguments) { - if (target instanceof IdentifierElement) { - print("(typeof ").print(target).print(" === 'function'?target").print("(").printArgList(arguments) - .print("):(target).").print(functionName).print("(").printArgList(arguments).print("))"); + switch (targetMethodName) { + case "getClass": + print("("); + printTarget(invocationElement.getTargetExpression()); + print(".constructor)"); + return true; + case "hashCode": + printMacroName(targetMethodName); + print("(((o: any) => { if(o.hashCode) { return o.hashCode(); } else { return o.toString(); } })("); + printTarget(invocationElement.getTargetExpression()); + print("))"); + return true; + case "equals": + if (invocationElement.getTargetExpression() != null) { + MethodSymbol methSym = Util.findMethodDeclarationInType(context.types, + (TypeSymbol) invocationElement.getTargetExpression().getTypeAsElement(), targetMethodName, + (MethodType) invocationElement.getMethod().asType()); + if (methSym != null + && (Object.class.getName().equals(methSym.getEnclosingElement().toString()) + || methSym.getEnclosingElement().isInterface()) + || invocationElement.getTargetExpression().getTypeAsElement() + .getKind() == ElementKind.INTERFACE) { + printMacroName(targetMethodName); + print("(((o1: any, o2: any) => { if(o1 && o1.equals) { return o1.equals(o2); } else { return o1 === o2; } })("); + printTarget(invocationElement.getTargetExpression()).print(",") + .print(invocationElement.getArgument(0)); + print("))"); + return true; + } + } + break; + case "clone": + if (!invocationElement.getMethod().getModifiers().contains(Modifier.STATIC) + && invocationElement.getArgumentCount() == 0) { + printMacroName(targetMethodName); + if (invocationElement.getTargetExpression() != null + && "super".equals(invocationElement.getTargetExpression().toString())) { + JCClassDecl parent = getPrinter().getParent(JCClassDecl.class); + if (parent.sym.getSuperclass() != null + && !context.symtab.objectType.equals(parent.sym.getSuperclass())) { + print("((o:any) => { if(super.clone!=undefined) { return super.clone(); } else { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; } })(this)"); + } else { + print("((o:any) => { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; })(this)"); + } } else { - print("(target => (typeof target === 'function')?target").print("(").printArgList(arguments) - .print("):(target).").print(functionName).print("(").printArgList(arguments).print("))(") - .print(target).print(")"); + print("((o:any) => { if(o.clone!=undefined) { return (o).clone(); } else { let clone = Object.create(o); for(let p in o) { if (o.hasOwnProperty(p)) clone[p] = o[p]; } return clone; } })("); + printTarget(invocationElement.getTargetExpression()); + print(")"); } + return true; + } } - protected final PrinterAdapter printTarget(ExtendedElement target) { - if (target == null) { - return print("this"); - } else if ("super".equals(target.toString())) { - return print("this"); - } else { - return print(target); + return super.substituteMethodInvocation(invocationElement); + + } + + protected void printFunctionalInvocation(ExtendedElement target, String functionName, + List arguments) { + if (target instanceof IdentifierElement) { + print("(typeof ").print(target).print(" === 'function'?target").print("(").printArgList(arguments) + .print("):(target).").print(functionName).print("(").printArgList(arguments).print("))"); + } else { + print("(target => (typeof target === 'function')?target").print("(").printArgList(arguments) + .print("):(target).").print(functionName).print("(").printArgList(arguments).print("))(") + .print(target).print(")"); + } + } + + protected final PrinterAdapter printTarget(ExtendedElement target) { + if (target == null) { + return print("this"); + } else if ("super".equals(target.toString())) { + return print("this"); + } else { + return print(target); + } + } + + protected final void delegateToEmulLayer(String targetClassName, String targetMethodName, + InvocationElement invocation) { + print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(targetMethodName).print("(") + .printArgList(invocation.getArguments()).print(")"); + } + + protected final void delegateToEmulLayerStatic(String targetClassName, String targetMethodName, + ExtendedElement target) { + print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(targetMethodName).print("("); + printTarget(target).print(")"); + } + + protected final void printCastMethodInvocation(InvocationElement invocation) { + if (getPrinter().getParent() instanceof JCMethodInvocation) { + print("("); + } + print(invocation.getArgument(0)); + if (getPrinter().getParent() instanceof JCMethodInvocation) { + print(")"); + } + } + + @Override + public boolean substituteVariableAccess(VariableAccessElement variableAccess) { + if (variableAccess.getTargetExpression() != null) { + JCFieldAccess fieldAccess = (JCFieldAccess) ((VariableAccessElementSupport) variableAccess).getTree(); + String targetFieldName = variableAccess.getVariableName(); + Element targetType = variableAccess.getTargetElement(); + + // automatic static field access target redirection + if (!"class".equals(variableAccess.getVariableName()) + && variableAccess.getVariable().getModifiers().contains(Modifier.STATIC)) { + if (isMappedType(targetType.toString())) { + print(getTypeMappingTarget(targetType.toString()) + ".").print(variableAccess.getVariableName()); + return true; } - } + } - protected final void delegateToEmulLayer(String targetClassName, String targetMethodName, - InvocationElement invocation) { - print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(targetMethodName).print("(") - .printArgList(invocation.getArguments()).print(")"); - } - - protected final void delegateToEmulLayerStatic(String targetClassName, String targetMethodName, - ExtendedElement target) { - print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(targetMethodName).print("("); - printTarget(target).print(")"); - } - - protected final void printCastMethodInvocation(InvocationElement invocation) { - if (getPrinter().getParent() instanceof JCMethodInvocation) { - print("("); + // translate tuple accesses + if (targetFieldName.startsWith("$") && targetFieldName.length() > 1 + && Character.isDigit(targetFieldName.charAt(1))) { + try { + int i = Integer.parseInt(targetFieldName.substring(1)); + print(variableAccess.getTargetExpression()); + print("[" + i + "]"); + return true; + } catch (NumberFormatException e) { + // swallow } - print(invocation.getArgument(0)); - if (getPrinter().getParent() instanceof JCMethodInvocation) { - print(")"); + } + + if (hasAnnotationType(variableAccess.getVariable(), ANNOTATION_STRING_TYPE)) { + print("\""); + print(getAnnotationValue(variableAccess.getVariable(), ANNOTATION_STRING_TYPE, + variableAccess.getVariableName())); + print("\""); + return true; + } + + if (fieldAccess.selected.toString().equals("this")) { + if (fieldAccess.sym.isStatic()) { + report(variableAccess, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, fieldAccess.name); } + } + + // enum objects wrapping + if (targetType != null && targetType.getKind() == ElementKind.ENUM && !fieldAccess.sym.isEnum() + && !"this".equals(fieldAccess.selected.toString()) && !"class".equals(targetFieldName)) { + String relTarget = context.useModules ? targetType.getSimpleName().toString() + : getRootRelativeName((Symbol) targetType); + getPrinter().print(relTarget) + .print("[\"" + Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_WRAPPERS + "\"][") + .print(fieldAccess.selected).print("].").print(fieldAccess.name.toString()); + return true; + } + + // built-in Java support + String accessedType = ((Symbol) targetType).getQualifiedName().toString(); + if (fieldAccess.sym.isStatic() && isMappedType(accessedType) && accessedType.startsWith("java.lang.") + && !"class".equals(fieldAccess.name.toString())) { + delegateToEmulLayer(accessedType, variableAccess); + return true; + } + } else { + if (JSweetConfig.UTIL_CLASSNAME.equals(variableAccess.getTargetElement().toString())) { + if ("$this".equals(variableAccess.getVariableName())) { + print("this"); + return true; + } + } + JCIdent identifier = (JCIdent) ((VariableAccessElementSupport) variableAccess).getTree(); + if (context.hasAnnotationType(identifier.sym, ANNOTATION_STRING_TYPE)) { + print("\""); + getPrinter().print( + context.getAnnotationValue(identifier.sym, ANNOTATION_STRING_TYPE, identifier.toString())); + print("\""); + return true; + } + } + return super.substituteVariableAccess(variableAccess); + } + + protected final void delegateToEmulLayer(String targetClassName, VariableAccessElement fieldAccess) { + print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(fieldAccess.getVariableName()); + } + + @Override + public boolean substituteNewClass(NewClassElement newClassElement) { + JCNewClass newClass = ((NewClassElementSupport) newClassElement).getTree(); + String className = newClassElement.getTypeAsElement().toString(); + if (className.startsWith(JSweetConfig.TUPLE_CLASSES_PACKAGE + ".")) { + getPrinter().print("[").printArgList(null, newClass.args).print("]"); + return true; } - @Override - public boolean substituteVariableAccess(VariableAccessElement variableAccess) { - if (variableAccess.getTargetExpression() != null) { - JCFieldAccess fieldAccess = (JCFieldAccess) ((VariableAccessElementSupport) variableAccess).getTree(); - String targetFieldName = variableAccess.getVariableName(); - Element targetType = variableAccess.getTargetElement(); - - // automatic static field access target redirection - if (!"class".equals(variableAccess.getVariableName()) && variableAccess.getVariable().getModifiers().contains(Modifier.STATIC)) { - if (isMappedType(targetType.toString())) { - print(getTypeMappingTarget(targetType.toString()) + ".").print(variableAccess.getVariableName()); - return true; - } - } - - // translate tuple accesses - if (targetFieldName.startsWith("$") && targetFieldName.length() > 1 - && Character.isDigit(targetFieldName.charAt(1))) { - try { - int i = Integer.parseInt(targetFieldName.substring(1)); - print(variableAccess.getTargetExpression()); - print("[" + i + "]"); - return true; - } catch (NumberFormatException e) { - // swallow - } - } - - if (hasAnnotationType(variableAccess.getVariable(), ANNOTATION_STRING_TYPE)) { - print("\""); - print(getAnnotationValue(variableAccess.getVariable(), ANNOTATION_STRING_TYPE, - variableAccess.getVariableName())); - print("\""); - return true; - } - - if (fieldAccess.selected.toString().equals("this")) { - if (fieldAccess.sym.isStatic()) { - report(variableAccess, JSweetProblem.CANNOT_ACCESS_STATIC_MEMBER_ON_THIS, fieldAccess.name); - } - } - - // enum objects wrapping - if (targetType != null && targetType.getKind() == ElementKind.ENUM && !fieldAccess.sym.isEnum() - && !"this".equals(fieldAccess.selected.toString()) && !"class".equals(targetFieldName)) { - String relTarget = context.useModules ? targetType.getSimpleName().toString() - : getRootRelativeName((Symbol) targetType); - getPrinter().print(relTarget) - .print("[\"" + Java2TypeScriptTranslator.ENUM_WRAPPER_CLASS_WRAPPERS + "\"][") - .print(fieldAccess.selected).print("].").print(fieldAccess.name.toString()); - return true; - } - - // built-in Java support - String accessedType = ((Symbol) targetType).getQualifiedName().toString(); - if (fieldAccess.sym.isStatic() && isMappedType(accessedType) && accessedType.startsWith("java.lang.") - && !"class".equals(fieldAccess.name.toString())) { - delegateToEmulLayer(accessedType, variableAccess); - return true; - } - } else { - if (JSweetConfig.UTIL_CLASSNAME.equals(variableAccess.getTargetElement().toString())) { - if ("$this".equals(variableAccess.getVariableName())) { - print("this"); - return true; - } - } - JCIdent identifier = (JCIdent) ((VariableAccessElementSupport) variableAccess).getTree(); - if (context.hasAnnotationType(identifier.sym, ANNOTATION_STRING_TYPE)) { - print("\""); - getPrinter().print( - context.getAnnotationValue(identifier.sym, ANNOTATION_STRING_TYPE, identifier.toString())); - print("\""); - return true; - } + if (isMappedType(className)) { + print("<").print(getTypeMappingTarget(className)).print(">"); + } + // macros + if (newClass.clazz.type.equals(context.symtab.stringType)) { + if (newClass.args.length() >= 3) { + getPrinter().print("((str, index, len) => ").print("str.substring(index, index + len))((") + .print(newClass.args.head).print(")"); + if ("byte[]".equals(newClass.args.get(0).type.toString())) { + print(".map(s => String.fromCharCode(s))"); } - return super.substituteVariableAccess(variableAccess); + print(".join(''), "); + getPrinter().print(newClass.args.tail.head).print(", ").print(newClass.args.tail.tail.head).print(")"); + return true; + } } - protected final void delegateToEmulLayer(String targetClassName, VariableAccessElement fieldAccess) { - print("javaemul.internal." + targetClassName.substring(10) + "Helper.").print(fieldAccess.getVariableName()); - } + return super.substituteNewClass(newClassElement); - @Override - public boolean substituteNewClass(NewClassElement newClassElement) { - JCNewClass newClass = ((NewClassElementSupport) newClassElement).getTree(); - String className = newClassElement.getTypeAsElement().toString(); - if (className.startsWith(JSweetConfig.TUPLE_CLASSES_PACKAGE + ".")) { - getPrinter().print("[").printArgList(null, newClass.args).print("]"); - return true; + } + + @Override + public boolean substituteIdentifier(IdentifierElement identifierElement) { + JCIdent identifier = ((IdentifierElementSupport) identifierElement).getTree(); + if (identifier.type != null) { + if (context.getLangTypesSimpleNames().contains(identifier.toString()) + && context.getLangTypeMappings().containsKey(identifier.type.toString())) { + print(context.getLangTypeMappings().get(identifier.type.toString())); + return true; + } + if (identifier.type.toString().startsWith("java.lang.")) { + if (("java.lang." + identifier.toString()).equals(identifier.type.toString())) { + // it is a java.lang class being referenced, so we expand + // its name + print(identifier.type.toString()); + return true; } - - if (isMappedType(className)) { - print("<").print(getTypeMappingTarget(className)).print(">"); - } - // macros - if (newClass.clazz.type.equals(context.symtab.stringType)) { - if (newClass.args.length() >= 3) { - getPrinter().print("((str, index, len) => ").print("str.substring(index, index + len))((") - .print(newClass.args.head).print(")"); - if ("byte[]".equals(newClass.args.get(0).type.toString())) { - print(".map(s => String.fromCharCode(s))"); - } - print(".join(''), "); - getPrinter().print(newClass.args.tail.head).print(", ").print(newClass.args.tail.tail.head).print(")"); - return true; - } - } - - return super.substituteNewClass(newClassElement); - + } } + return super.substituteIdentifier(identifierElement); + } - @Override - public boolean substituteIdentifier(IdentifierElement identifierElement) { - JCIdent identifier = ((IdentifierElementSupport) identifierElement).getTree(); - if (identifier.type != null) { - if (context.getLangTypesSimpleNames().contains(identifier.toString()) - && context.getLangTypeMappings().containsKey(identifier.type.toString())) { - print(context.getLangTypeMappings().get(identifier.type.toString())); - return true; - } - if (identifier.type.toString().startsWith("java.lang.")) { - if (("java.lang." + identifier.toString()).equals(identifier.type.toString())) { - // it is a java.lang class being referenced, so we expand - // its name - print(identifier.type.toString()); - return true; - } - } - } - return super.substituteIdentifier(identifierElement); - } + @Override + public Set getErasedTypes() { + return context.getLangTypeMappings().keySet(); + } - @Override - public Set getErasedTypes() { - return context.getLangTypeMappings().keySet(); - } - - @Override - public boolean substituteForEachLoop(ForeachLoopElement foreachLoop, boolean targetHasLength, String indexVarName) { - if (!targetHasLength) { - JCEnhancedForLoop loop = ((ForeachLoopElementSupport) foreachLoop).getTree(); - getPrinter().print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=").print(loop.expr) - .print(".iterator();" + indexVarName + ".hasNext();) {").println().startIndent().printIndent(); - getPrinter().print(VAR_DECL_KEYWORD + " " + loop.var.name.toString() + " = ") - .print(indexVarName + ".next();").println(); - getPrinter().printIndent().print(loop.body); - endIndent().println().printIndent().print("}"); - return true; - } - return super.substituteForEachLoop(foreachLoop, targetHasLength, indexVarName); + @Override + public boolean substituteForEachLoop(ForeachLoopElement foreachLoop, boolean targetHasLength, String indexVarName) { + if (!targetHasLength) { + JCEnhancedForLoop loop = ((ForeachLoopElementSupport) foreachLoop).getTree(); + getPrinter().print("for(" + VAR_DECL_KEYWORD + " " + indexVarName + "=").print(loop.expr) + .print(".iterator();" + indexVarName + ".hasNext();) {").println().startIndent().printIndent(); + getPrinter().print(VAR_DECL_KEYWORD + " " + loop.var.name.toString() + " = ") + .print(indexVarName + ".next();").println(); + getPrinter().printIndent().print(loop.body); + endIndent().println().printIndent().print("}"); + return true; } + return super.substituteForEachLoop(foreachLoop, targetHasLength, indexVarName); + } } diff --git a/transpiler/src/main/java/org/jsweet/transpiler/extension/RemoveJavaDependenciesAdapter.java b/transpiler/src/main/java/org/jsweet/transpiler/extension/RemoveJavaDependenciesAdapter.java index 5400f19c..78d2816f 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/extension/RemoveJavaDependenciesAdapter.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/extension/RemoveJavaDependenciesAdapter.java @@ -58,10 +58,10 @@ import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import org.jsweet.transpiler.JSweetContext; +import org.jsweet.transpiler.Java2TypeScriptTranslator; import org.jsweet.transpiler.ModuleKind; import org.jsweet.transpiler.model.BinaryOperatorElement; import org.jsweet.transpiler.model.ExtendedElement; @@ -218,12 +218,30 @@ public class RemoveJavaDependenciesAdapter extends Java2TypeScriptAdapter { case "parseLong": case "parseShort": case "parseByte": + printMacroName(targetMethodName); print("parseInt").print("(").printArgList(invocation.getArguments()).print(")"); return true; case "parseFloat": case "parseDouble": + printMacroName(targetMethodName); print("parseFloat").print("(").printArgList(invocation.getArguments()).print(")"); return true; + case "floatToIntBits": + case "floatToRawIntBits": + printMacroName(targetMethodName); + print("((f) => { let buf = new ArrayBuffer(4); (new Float32Array(buf))[0]=f; return (new Uint32Array(buf))[0]; })(").printArgList(invocation.getArguments()).print(")"); + return true; + case "intBitsToFloat": + print("((v) => { let buf = new ArrayBuffer(4); (new Uint32Array(buf))[0]=v; return (new Float32Array(buf))[0]; })(").printArgList(invocation.getArguments()).print(")"); + return true; + case "doubleToLongBits": + case "doubleToRawLongBits": + printMacroName(targetMethodName); + print("((f) => { let buf = new ArrayBuffer(4); (new Float32Array(buf))[0]=f; return (new Uint32Array(buf))[0]; })((Math).fround(").printArgList(invocation.getArguments()).print("))"); + return true; + case "longBitsToDouble": + print("((v) => { let buf = new ArrayBuffer(4); (new Uint32Array(buf))[0]=v; return (new Float32Array(buf))[0]; })(").printArgList(invocation.getArguments()).print(")"); + return true; case "valueOf": if (util().isNumber(invocation.getArgument(0).getType())) { print(invocation.getArgument(0)); @@ -935,8 +953,29 @@ public class RemoveJavaDependenciesAdapter extends Java2TypeScriptAdapter { print(invocation.getTargetExpression(), delegate).print("(").printArgList(invocation.getArguments()) .print(")"); return true; + case "isInstance": + printMacroName(targetMethodName); + print("((c:any,o:any) => { if(typeof c === 'string') return (o.constructor && o.constructor") + .print("[\"" + Java2TypeScriptTranslator.INTERFACES_FIELD_NAME + "\"] && o.constructor") + .print("[\"" + Java2TypeScriptTranslator.INTERFACES_FIELD_NAME + + "\"].indexOf(c) >= 0) || (o") + .print("[\"" + Java2TypeScriptTranslator.INTERFACES_FIELD_NAME + "\"] && o") + .print("[\"" + Java2TypeScriptTranslator.INTERFACES_FIELD_NAME + + "\"].indexOf(c) >= 0); else if(typeof c === 'function') return (o instanceof c) || (o.constructor && o.constructor === c); })("); + print(invocation.getTargetExpression(), delegate).print(", ") + .printArgList(invocation.getArguments()).print(")"); + return true; } + case "java.lang.reflect.Array": + switch (targetMethodName) { + case "newInstance": + printMacroName(targetMethodName); + if (invocation.getArgumentCount() == 2) { + print("new Array(").print(invocation.getArgument(1)).print(")"); + return true; + } + } } switch (targetMethodName) { diff --git a/transpiler/src/test/java/source/api/Equals.java b/transpiler/src/test/java/source/api/Equals.java index 797e8201..40f0d053 100644 --- a/transpiler/src/test/java/source/api/Equals.java +++ b/transpiler/src/test/java/source/api/Equals.java @@ -55,3 +55,28 @@ class MyObject2 { } } + +class MyObject3 { + + MyInterface data; + + public MyObject3(MyInterface data) { + this.data = data; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MyObject3)) { + return false; + } + return data.equals(((MyObject3) obj).data); + } + +} + + +interface MyInterface { + + void m(); + +} diff --git a/transpiler/src/test/java/source/api/Numbers.java b/transpiler/src/test/java/source/api/Numbers.java index 5dc88999..cf0fca3a 100644 --- a/transpiler/src/test/java/source/api/Numbers.java +++ b/transpiler/src/test/java/source/api/Numbers.java @@ -1,6 +1,8 @@ package source.api; import static jsweet.util.Lang.$export; +import static jsweet.util.Lang.number; +import static jsweet.util.Lang.string; import def.js.Array; @@ -13,6 +15,14 @@ public class Numbers { Integer i = parseDuration("1:20"); assert i == 80; + float f = Float.intBitsToFloat(Float.floatToIntBits(3.14f)); + assert f!=3.14f; + assert number(f).toFixed(2) == string("3.14"); + + double d = Double.longBitsToDouble(Double.doubleToLongBits(3.14)); + assert d!=3.14; + assert ((double)Math.round(d*100))/100 == 3.14; + $export("trace", trace.join(",")); } diff --git a/transpiler/src/test/java/source/nativestructures/Reflect.java b/transpiler/src/test/java/source/nativestructures/Reflect.java index 621aeed2..e883e950 100644 --- a/transpiler/src/test/java/source/nativestructures/Reflect.java +++ b/transpiler/src/test/java/source/nativestructures/Reflect.java @@ -1,29 +1,51 @@ package source.nativestructures; +import static jsweet.util.Lang.any; + import def.js.Array; import jsweet.util.Lang; public class Reflect { - static Array trace = new Array<>(); - - public static void main(String[] args) { - String className = "source.nativestructures.MyAccessedClass"; - - try { - Class c = Class.forName(className); - c.newInstance(); - } catch (Exception e) { - } - Lang.$export("trace", trace.join(",")); + static Array trace = new Array<>(); + + public static void main(String[] args) { + String className = "source.nativestructures.MyAccessedClass"; + Class c = null; + Object o = null; + try { + c = Class.forName(className); + o = c.newInstance(); + } catch (Exception e) { } + MyAccessedClass[] array = (MyAccessedClass[]) java.lang.reflect.Array.newInstance(c, 3); + assert array.length == 3; + assert c.isInstance(o); + assert MyAccessedClass.class.isInstance(o); + assert MyAccessedInterface.class.isInstance(o); + assert char.class.isInstance('c'); + assert "c".getClass() == String.class; + // this works with JSweet (because chars are mapped to strings) + assert "c".getClass() == any(char.class); + assert "c".getClass() == any(Character.class); + Object oo = 'c'; + assert oo.getClass().isInstance(oo); + assert !StringBuffer.class.isInstance(o); + //assert MyAccessedInterface.class.isAssignableFrom(MyAccessedClass.class); + //assert c.isAssignableFrom(MyAccessedClass.class); + Lang.$export("trace", trace.join(",")); + } } -class MyAccessedClass { +class MyAccessedClass implements MyAccessedInterface { - public MyAccessedClass() { - Reflect.trace.push("constructor"); - } + public MyAccessedClass() { + Reflect.trace.push("constructor"); + } } + +interface MyAccessedInterface { + +} \ No newline at end of file