From 17a78dd5220ccef1c6048411208d8e6308ab2c75 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Thu, 2 Oct 2014 15:38:30 +0400 Subject: [PATCH] JS inline refactor: moved invocation and function utils to separate files --- .../jetbrains/k2js/inline/InvocationUtil.java | 113 ---------------- .../k2js/inline/util/functionUtils.kt | 44 ++++++ .../k2js/inline/util/invocationUtils.kt | 125 ++++++++++++++++++ .../expression/LiteralFunctionTranslator.kt | 6 +- 4 files changed, 170 insertions(+), 118 deletions(-) delete mode 100644 js/js.inliner/src/org/jetbrains/k2js/inline/InvocationUtil.java create mode 100644 js/js.inliner/src/org/jetbrains/k2js/inline/util/functionUtils.kt create mode 100644 js/js.inliner/src/org/jetbrains/k2js/inline/util/invocationUtils.kt diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/InvocationUtil.java b/js/js.inliner/src/org/jetbrains/k2js/inline/InvocationUtil.java deleted file mode 100644 index a943f97bd17..00000000000 --- a/js/js.inliner/src/org/jetbrains/k2js/inline/InvocationUtil.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2010-2014 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.k2js.inline; - -import com.google.dart.compiler.backend.js.ast.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -class InvocationUtil { - - /** - * Tests if result of call is being used in containing statement - */ - public static boolean isResultUsed(JsStatement containingStatement, JsInvocation call) { - return !(containingStatement instanceof JsExpressionStatement) - || call != ((JsExpressionStatement) containingStatement).getExpression(); - } - - @Nullable - public static JsName getName(@NotNull JsInvocation call) { - JsExpression qualifier = call.getQualifier(); - if (qualifier instanceof JsNameRef) { - return ((JsNameRef) qualifier).getName(); - } - - return null; - } - - /** - * Gets inner function from function, that creates closure - * - * For example: - * function(a) { - * return function() { return a; } - * } - * - * Inner functions can only be generated when lambda - * with closure is created - */ - @Nullable - public static JsFunction getInnerFunction(@NotNull JsFunction outer) { - List statements = outer.getBody().getStatements(); - if (statements.size() != 1) { - return null; - } - - JsStatement statement = statements.get(0); - if (!(statement instanceof JsReturn)) { - return null; - } - - JsExpression returnExpr = ((JsReturn) statement).getExpression(); - if (!(returnExpr instanceof JsFunction)) { - return null; - } - - return (JsFunction) returnExpr; - } - - /** - * Tests if invocation is JavaScript call function - * - * @return true if invocation is something like `x.call(thisReplacement)` - * false otherwise - */ - public static boolean isCallInvocation(JsInvocation invocation) { - JsExpression qualifier = invocation.getQualifier(); - if (!(qualifier instanceof JsNameRef)) { - return false; - } - - JsNameRef qualifierNameRef = (JsNameRef) qualifier; - return qualifierNameRef.getIdent().equals("call") - && invocation.getArguments().size() == 1; - } - - public static boolean hasReceiver(@NotNull JsInvocation invocation) { - return getReceiverImpl(invocation) != null; - } - - @NotNull - public static JsExpression getReceiver(@NotNull JsInvocation invocation) { - JsExpression receiver = getReceiverImpl(invocation); - assert receiver != null; - return receiver; - } - - @Nullable - private static JsExpression getReceiverImpl(@NotNull JsInvocation invocation) { - JsExpression qualifier = invocation.getQualifier(); - if (!(qualifier instanceof JsNameRef)) { - return null; - } - - return ((JsNameRef) qualifier).getQualifier(); - } -} diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/functionUtils.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/functionUtils.kt new file mode 100644 index 00000000000..4ce0410119d --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/functionUtils.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.inline.util + +import com.google.dart.compiler.backend.js.ast.JsFunction +import com.google.dart.compiler.backend.js.ast.JsReturn + +public fun isFunctionCreator(outer: JsFunction): Boolean = + outer.getInnerFunction() != null + +/** + * Gets inner function from function, that creates closure + * + * For example: + * function(a) { + * return function() { return a; } + * } + * + * Inner functions can only be generated when lambda + * with closure is created + */ +public fun JsFunction.getInnerFunction(): JsFunction? { + val statements = getBody().getStatements() + if (statements.size() != 1) return null + + val statement = statements.get(0) + val returnExpr = (statement as? JsReturn)?.getExpression() + + return returnExpr as? JsFunction +} \ No newline at end of file diff --git a/js/js.inliner/src/org/jetbrains/k2js/inline/util/invocationUtils.kt b/js/js.inliner/src/org/jetbrains/k2js/inline/util/invocationUtils.kt new file mode 100644 index 00000000000..da2e6942409 --- /dev/null +++ b/js/js.inliner/src/org/jetbrains/k2js/inline/util/invocationUtils.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.k2js.inline.util + +import com.google.dart.compiler.backend.js.ast.JsStatement +import com.google.dart.compiler.backend.js.ast.JsInvocation +import com.google.dart.compiler.backend.js.ast.JsExpressionStatement +import com.google.dart.compiler.backend.js.ast.JsName +import com.google.dart.compiler.backend.js.ast.JsExpression +import com.google.dart.compiler.backend.js.ast.JsNameRef +import com.google.dart.compiler.backend.js.ast.JsFunction +import com.google.dart.compiler.backend.js.ast.JsReturn +import org.jetbrains.k2js.translate.context.Namer +import com.google.dart.compiler.backend.js.ast.HasName +import com.google.dart.compiler.backend.js.ast.JsNode + +/** + * Gets invocation qualifier name. + * + * @returns `f` for `_.foo.f()` call + */ +public fun getSimpleName(call: JsInvocation): JsName? { + val qualifier = call.getQualifier() + return (qualifier as? JsNameRef)?.getName() +} + +/** + * Tries to get ident for call. + * + * @returns first name ident (iterating through qualifier chain) + */ +public fun getSimpleIdent(call: JsInvocation): String? { + var qualifier = call.getQualifier() + + while (qualifier != null) { + when (qualifier) { + is JsInvocation -> { + val callableQualifier = qualifier as JsInvocation + qualifier = callableQualifier.getQualifier() + + if (isCallInvocation(callableQualifier)) { + qualifier = (qualifier as? JsNameRef)?.getQualifier() + } + } + is HasName -> return (qualifier as HasName).getName()?.getIdent() + else -> break + } + } + + return null +} + +/** + * Checks if JsInvocation is function creator call. + * + * Function creator is a function, that creates closure. + */ +public fun isFunctionCreatorInvocation(invocation: JsInvocation): Boolean { + val staticRef = getStaticRef(invocation) + return when (staticRef) { + is JsFunction -> isFunctionCreator(staticRef) + else -> false + } +} + +/** + * Tests if invocation is JavaScript call function + * + * @return true if invocation is something like `x.call(thisReplacement)` + * false otherwise + */ +public fun isCallInvocation(invocation: JsInvocation): Boolean { + val qualifier = invocation.getQualifier() as? JsNameRef + val arguments = invocation.getArguments() + + return qualifier?.getIdent() == Namer.CALL_FUNCTION && arguments.notEmpty +} + +/** + * Checks if invocation has qualifier before call. + * + * @return true, if invocation is similar to `something.f()` + * false, if invocation is similar to `f()` + */ +public fun hasCallerQualifier(invocation: JsInvocation): Boolean { + return getCallerQualifierImpl(invocation) != null +} + +/** + * Gets qualifier preceding call. + * + * @return caller for invocation of type `caller.f()`, + * where caller is any JsNameRef (for example a.b.c. etc.) + * + * @throws AssertionError, if invocation does not have caller qualifier. + */ +public fun getCallerQualifier(invocation: JsInvocation): JsExpression { + return getCallerQualifierImpl(invocation) ?: + throw AssertionError("must check hasQualifier() before calling getQualifier") + +} + +private fun getCallerQualifierImpl(invocation: JsInvocation): JsExpression? { + return (invocation.getQualifier() as? JsNameRef)?.getQualifier() +} + +private fun getStaticRef(invocation: JsInvocation): JsNode? { + val qualifier = invocation.getQualifier() + val qualifierName = (qualifier as? HasName)?.getName() + return qualifierName?.getStaticRef() +} \ No newline at end of file diff --git a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt index 88d5a6a7b15..c5c748de0a2 100644 --- a/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt +++ b/js/js.translator/src/org/jetbrains/k2js/translate/expression/LiteralFunctionTranslator.kt @@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.descriptors.Visibilities import com.google.dart.compiler.backend.js.ast.JsVars.JsVar import org.jetbrains.k2js.translate.utils.JsAstUtils import com.intellij.util.SmartList +import org.jetbrains.k2js.inline.util.getInnerFunction import org.jetbrains.jet.lang.types.lang.InlineUtil import org.jetbrains.jet.lang.descriptors.FunctionDescriptor @@ -177,12 +178,7 @@ private fun getFreshNamesInScope(scope: JsScope, suggested: List): List return freshNames.toList() } -private fun JsFunction.getInnerFunction(): JsFunction? { - val outerStatements = this.getBody().getStatements() - val outerReturn = outerStatements.get(0) as? JsReturn - val innerFunction = outerReturn?.getExpression() as? JsFunction - return innerFunction } private fun JsFunction.addDeclaration(name: JsName, value: JsExpression?) {