From 7f2000e03beee56ab4454b2bdb2d886048f6112e Mon Sep 17 00:00:00 2001 From: Louis Grignon Date: Sat, 27 Apr 2019 18:47:13 +0200 Subject: [PATCH] add $invoke capability Brings dynamic invoke capability to def.js.Object. It allows to dynamically call a method on an object for instance if definition is missing or simply if it is not possible to do it otherwise. --- core-lib/es5/src/main/java/def/js/Object.java | 5 ++ core-lib/es6/src/main/java/def/js/Object.java | 5 ++ .../es6/src/main/java/jsweet/util/Lang.java | 2 + transpiler/pom.xml | 2 +- .../main/java/org/jsweet/JSweetConfig.java | 2 + .../org/jsweet/transpiler/JSweetContext.java | 4 ++ .../extension/Java2TypeScriptAdapter.java | 40 +++++++++-- .../jsweet/test/transpiler/SyntaxTests.java | 48 +++++++++---- .../java/source/syntax/DynamicInvoke.java | 70 +++++++++++++++++++ .../syntax/IndexedAccessInStaticScope.java | 51 -------------- 10 files changed, 155 insertions(+), 74 deletions(-) create mode 100644 transpiler/src/test/java/source/syntax/DynamicInvoke.java delete mode 100644 transpiler/src/test/java/source/syntax/IndexedAccessInStaticScope.java diff --git a/core-lib/es5/src/main/java/def/js/Object.java b/core-lib/es5/src/main/java/def/js/Object.java index 9d575687..941377de 100644 --- a/core-lib/es5/src/main/java/def/js/Object.java +++ b/core-lib/es5/src/main/java/def/js/Object.java @@ -234,6 +234,11 @@ public class Object { */ native public static Array keys(java.lang.Object o); + /** + * Invoke method of given name on this object with given parameters. + */ + native public T $invoke(java.lang.String methodName, java.lang.Object... args); + /** * Gets the value for the given key. Generates this[key]. */ diff --git a/core-lib/es6/src/main/java/def/js/Object.java b/core-lib/es6/src/main/java/def/js/Object.java index 5b3280b5..f5942263 100644 --- a/core-lib/es6/src/main/java/def/js/Object.java +++ b/core-lib/es6/src/main/java/def/js/Object.java @@ -255,6 +255,11 @@ public class Object { */ native public java.lang.Boolean propertyIsEnumerable(double v); + /** + * Invoke method of given name on this object with given parameters. + */ + native public T $invoke(java.lang.String methodName, java.lang.Object... args); + /** * Gets the value for the given key. Generates this[key]. */ 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 9139c9bb..9b16bc63 100644 --- a/core-lib/es6/src/main/java/jsweet/util/Lang.java +++ b/core-lib/es6/src/main/java/jsweet/util/Lang.java @@ -707,6 +707,8 @@ public final class Lang { public static native R await(Promise promise); + public static native R await(R promise); + public static native def.js.Function async(def.js.Function function); public static native Promise asyncReturn(T result); diff --git a/transpiler/pom.xml b/transpiler/pom.xml index 0ec363d1..5b4731d7 100644 --- a/transpiler/pom.xml +++ b/transpiler/pom.xml @@ -234,7 +234,7 @@ org.jsweet jsweet-core - 6 + 6.0.1-SNAPSHOT test true diff --git a/transpiler/src/main/java/org/jsweet/JSweetConfig.java b/transpiler/src/main/java/org/jsweet/JSweetConfig.java index d5a71f56..498d1cba 100644 --- a/transpiler/src/main/java/org/jsweet/JSweetConfig.java +++ b/transpiler/src/main/java/org/jsweet/JSweetConfig.java @@ -204,6 +204,8 @@ public abstract class JSweetConfig { public static final String UNION_PACKAGE = UTIL_PACKAGE + ".union"; /** The constant for the Union core class full name. */ public static final String UNION_CLASS_NAME = UNION_PACKAGE + ".Union"; + /** The constant for dynamic invoke function. */ + public static final String INVOKE_FUCTION_NAME = "$invoke"; /** The constant for indexed access function. */ public static final String INDEXED_GET_FUCTION_NAME = "$get"; /** The constant for indexed assignment function. */ diff --git a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java index 527bdb3d..642f59c6 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/JSweetContext.java @@ -1620,6 +1620,10 @@ public class JSweetContext extends Context { return false; } + /** + * Returns true if given statement can be used in a Java object instantation block + * to be transformed to a JS Object Literal + */ private static boolean isAllowedStatementInMap(JCStatement statement) { if (statement instanceof JCExpressionStatement) { JCExpressionStatement exprStat = (JCExpressionStatement) statement; 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 039b35ba..57a2f6f6 100644 --- a/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java +++ b/transpiler/src/main/java/org/jsweet/transpiler/extension/Java2TypeScriptAdapter.java @@ -30,6 +30,7 @@ import static org.jsweet.JSweetConfig.INDEXED_GET_FUCTION_NAME; import static org.jsweet.JSweetConfig.INDEXED_GET_STATIC_FUCTION_NAME; import static org.jsweet.JSweetConfig.INDEXED_SET_FUCTION_NAME; import static org.jsweet.JSweetConfig.INDEXED_SET_STATIC_FUCTION_NAME; +import static org.jsweet.JSweetConfig.INVOKE_FUCTION_NAME; import static org.jsweet.JSweetConfig.LANG_PACKAGE; import static org.jsweet.JSweetConfig.LANG_PACKAGE_ALT; import static org.jsweet.JSweetConfig.UTIL_CLASSNAME; @@ -134,8 +135,7 @@ public class Java2TypeScriptAdapter extends PrinterAdapter { /** * Creates a root adapter (with no parent). * - * @param context - * the transpilation context + * @param context the transpilation context */ public Java2TypeScriptAdapter(JSweetContext context) { super(context); @@ -146,9 +146,9 @@ public class Java2TypeScriptAdapter extends PrinterAdapter { * 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 + * @param parentAdapter cannot be null: if no parent you must use the + * {@link #AbstractPrinterAdapter(JSweetContext)} + * constructor */ public Java2TypeScriptAdapter(PrinterAdapter parent) { super(parent); @@ -639,6 +639,33 @@ public class Java2TypeScriptAdapter extends PrinterAdapter { if (targetMethodName != null) { switch (targetMethodName) { + case INVOKE_FUCTION_NAME: + if (invocationElement.getTargetExpression() != null && !(UTIL_CLASSNAME.equals(targetClassName) + || DEPRECATED_UTIL_CLASSNAME.equals(targetClassName))) { + + } 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("]"); + } + } + print(invocationElement.getTargetExpression()); + print("["); + print(invocationElement.getArgument(0)); + print("]"); + print("("); + List arguments = invocationElement.getArguments(); + int argCount = arguments.size(); + for (int i = 1; i < argCount; i++) { + print(arguments.get(i)); + if (i < argCount - 1) { + print(","); + } + } + print(")"); + return true; case INDEXED_GET_FUCTION_NAME: if (isWithinGlobals(targetClassName)) { if (invocationElement.getArgumentCount() == 1) { @@ -1327,8 +1354,7 @@ public class Java2TypeScriptAdapter extends PrinterAdapter { } /** - * @param enclosingElement - * is required for functional (ie dynamic) type mappings + * @param enclosingElement is required for functional (ie dynamic) type mappings */ public boolean substituteAndPrintType(ExtendedElement enclosingElement, TypeElement type) { String typeFullName = type.toString(); diff --git a/transpiler/src/test/java/org/jsweet/test/transpiler/SyntaxTests.java b/transpiler/src/test/java/org/jsweet/test/transpiler/SyntaxTests.java index 7cc0fa0a..1f24e0ac 100644 --- a/transpiler/src/test/java/org/jsweet/test/transpiler/SyntaxTests.java +++ b/transpiler/src/test/java/org/jsweet/test/transpiler/SyntaxTests.java @@ -22,22 +22,37 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.util.List; import java.util.regex.Pattern; -import java.util.stream.Stream; import org.apache.commons.io.FileUtils; -import org.jsweet.test.transpiler.util.TranspilerTestRunner; -import org.jsweet.transpiler.*; -import org.jsweet.transpiler.extension.PrinterAdapter; -import org.jsweet.transpiler.model.ExtendedElement; -import org.jsweet.transpiler.model.MethodInvocationElement; +import org.jsweet.transpiler.JSweetProblem; +import org.jsweet.transpiler.ModuleKind; +import org.jsweet.transpiler.SourceFile; import org.jsweet.transpiler.util.EvaluationResult; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; -import source.syntax.*; +import source.syntax.AnnotationQualifiedNames; +import source.syntax.Casts; +import source.syntax.DocComments; +import source.syntax.DynamicInvoke; +import source.syntax.FinalVariables; +import source.syntax.FinalVariablesRuntime; +import source.syntax.GlobalsCastMethod; +import source.syntax.GlobalsInvocation; +import source.syntax.Keywords; +import source.syntax.Labels; +import source.syntax.LambdaExpression; +import source.syntax.LambdasWithInterfaces; +import source.syntax.Literals; +import source.syntax.Looping; +import source.syntax.MemberReferences; +import source.syntax.QualifiedNames; +import source.syntax.References; +import source.syntax.SpecialFunctions; +import source.syntax.StatementsWithNoBlocks; +import source.syntax.SuperInvocation; +import source.syntax.ValidIndexedAccesses; public class SyntaxTests extends AbstractTest { @@ -140,14 +155,17 @@ public class SyntaxTests extends AbstractTest { } - @Ignore @Test - public void testIndexedAccessInStaticScope() { + public void testDynamicInvoke() { eval((logHandler, r) -> { - Assert.assertEquals("Wrong output value", "value", r.get("out_a")); - Assert.assertNull("Wrong output value", r.get("out_b")); - Assert.assertNull("var wasn't deleted", r.get("out_c")); - }, getSourceFile(IndexedAccessInStaticScope.class)); + assertEquals(true, r.get("a_1")); + assertEquals(true, r.get("a")); + assertEquals(true, r.get("b")); + assertEquals(true, r.get("c")); + assertEquals(true, r.get("d")); + assertEquals(true, r.get("e")); + assertEquals("5;true;foo", r.get("f")); + }, getSourceFile(DynamicInvoke.class)); } @Test diff --git a/transpiler/src/test/java/source/syntax/DynamicInvoke.java b/transpiler/src/test/java/source/syntax/DynamicInvoke.java new file mode 100644 index 00000000..d1129f5a --- /dev/null +++ b/transpiler/src/test/java/source/syntax/DynamicInvoke.java @@ -0,0 +1,70 @@ +/* + * JSweet - http://www.jsweet.org + * Copyright (C) 2015 CINCHEO SAS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package source.syntax; + +import static jsweet.util.Lang.$export; +import static jsweet.util.Lang.object; + +public class DynamicInvoke { + + { + object(this).$invoke("a"); + jsweet.util.Lang.object(this).$invoke("b"); + } + + public DynamicInvoke() { + object(this).$invoke("c"); + jsweet.util.Lang.object(this).$invoke("d"); + } + + public void a_1() { + $export("a_1", "true"); + } + + public void a() { + object(this).$invoke("a_1"); + $export("a", "true"); + } + + public void b() { + $export("b", "true"); + } + + public void c() { + $export("c", "true"); + } + + public void d() { + $export("d", "true"); + } + + public void e() { + $export("e", "true"); + } + + public void f(Integer i, Boolean b, String s) { + $export("f", i + ";" + b + ";" + s); + } + + public static void main(String[] args) { + + DynamicInvoke test = new DynamicInvoke(); + object(test).$invoke("e"); + object(test).$invoke("f", 5, new Boolean(true), "foo"); + + } +} diff --git a/transpiler/src/test/java/source/syntax/IndexedAccessInStaticScope.java b/transpiler/src/test/java/source/syntax/IndexedAccessInStaticScope.java deleted file mode 100644 index 84fb396e..00000000 --- a/transpiler/src/test/java/source/syntax/IndexedAccessInStaticScope.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * JSweet - http://www.jsweet.org - * Copyright (C) 2015 CINCHEO SAS - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package source.syntax; - -public class IndexedAccessInStaticScope { - - static { -// $get("a"); -// $set("a", "value"); -// jsweet.util.Globals.$get("a"); -// jsweet.util.Globals.$set("a", "value"); - } - - public static void m() { -// $get("a"); -// $set("a", "value"); -// jsweet.util.Globals.$get("a"); -// jsweet.util.Globals.$set("a", "value"); -// -// $set("c", "i want to be deleted"); -// $delete("c"); - } - - public static void main(String[] args) { -// m(); -// $export("out_a", $get("a")); -// $export("out_b", $get("b")); -// $export("out_c", $get("c")); - } - -} - -class C { - public static void m() { -// $set("b", "value"); - } -} \ No newline at end of file