Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.js.translate.general.Translation Maven / Gradle / Ivy
/*
* Copyright 2010-2017 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.kotlin.js.translate.general;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
import org.jetbrains.kotlin.idea.MainFunctionDetector;
import org.jetbrains.kotlin.js.backend.ast.*;
import org.jetbrains.kotlin.js.config.JsConfig;
import org.jetbrains.kotlin.js.facade.MainCallParameters;
import org.jetbrains.kotlin.js.facade.TranslationUnit;
import org.jetbrains.kotlin.js.facade.exceptions.TranslationException;
import org.jetbrains.kotlin.js.facade.exceptions.TranslationRuntimeException;
import org.jetbrains.kotlin.js.facade.exceptions.UnsupportedFeatureException;
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator;
import org.jetbrains.kotlin.js.translate.context.Namer;
import org.jetbrains.kotlin.js.translate.context.StaticContext;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.declaration.FileDeclarationVisitor;
import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor;
import org.jetbrains.kotlin.js.translate.expression.PatternTranslator;
import org.jetbrains.kotlin.js.translate.test.JSTestGenerator;
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.mutator.AssignToExpressionMutator;
import org.jetbrains.kotlin.psi.KtDeclaration;
import org.jetbrains.kotlin.psi.KtExpression;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtUnaryExpression;
import org.jetbrains.kotlin.resolve.BindingTrace;
import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilsKt;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.NullValue;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.serialization.js.ast.JsAstDeserializer;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.utils.ExceptionUtilsKt;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
import static org.jetbrains.kotlin.js.translate.general.ModuleWrapperTranslation.wrapIfNecessary;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.convertToStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.toStringLiteralList;
import static org.jetbrains.kotlin.js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
/**
* This class provides a interface which all translators use to interact with each other.
* Goal is to simplify interaction between translators.
*/
public final class Translation {
private static final String ENUM_SIGNATURE = "kotlin$Enum";
private Translation() {
}
@NotNull
public static PatternTranslator patternTranslator(@NotNull TranslationContext context) {
return PatternTranslator.newInstance(context);
}
@NotNull
public static JsNode translateExpression(@NotNull KtExpression expression, @NotNull TranslationContext context) {
return translateExpression(expression, context, context.dynamicContext().jsBlock());
}
@NotNull
public static JsNode translateExpression(@NotNull KtExpression expression, @NotNull TranslationContext context, @NotNull JsBlock block) {
JsExpression aliasForExpression = context.aliasingContext().getAliasForExpression(expression);
if (aliasForExpression != null) {
return aliasForExpression;
}
CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext());
if (compileTimeValue != null) {
KotlinType type = context.bindingContext().getType(expression);
if (type != null) {
if (KotlinBuiltIns.isLong(type) || (KotlinBuiltIns.isInt(type) && expression instanceof KtUnaryExpression)) {
JsExpression constantResult = translateConstant(compileTimeValue, expression, context);
if (constantResult != null) return constantResult.source(expression);
}
}
}
TranslationContext innerContext = context.innerBlock();
JsNode result = doTranslateExpression(expression, innerContext);
context.moveVarsFrom(innerContext);
block.getStatements().addAll(innerContext.dynamicContext().jsBlock().getStatements());
return result;
}
@Nullable
public static JsExpression translateConstant(
@NotNull CompileTimeConstant compileTimeValue,
@NotNull KtExpression expression,
@NotNull TranslationContext context
) {
KotlinType expectedType = context.bindingContext().getType(expression);
ConstantValue constant = compileTimeValue.toConstantValue(expectedType != null ? expectedType : TypeUtils.NO_EXPECTED_TYPE);
if (constant instanceof NullValue) {
return new JsNullLiteral();
}
Object value = constant.getValue();
if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
return new JsIntLiteral(((Number) value).intValue());
}
else if (value instanceof Long) {
return JsAstUtils.newLong((Long) value);
}
else if (value instanceof Float) {
float floatValue = (Float) value;
double doubleValue;
if (Float.isInfinite(floatValue) || Float.isNaN(floatValue)) {
doubleValue = floatValue;
}
else {
doubleValue = Double.parseDouble(Float.toString(floatValue));
}
return new JsDoubleLiteral(doubleValue);
}
else if (value instanceof Number) {
return new JsDoubleLiteral(((Number) value).doubleValue());
}
else if (value instanceof Boolean) {
return new JsBooleanLiteral((Boolean) value);
}
//TODO: test
if (value instanceof String) {
return new JsStringLiteral((String) value);
}
if (value instanceof Character) {
return new JsIntLiteral(((Character) value).charValue());
}
return null;
}
@NotNull
private static JsNode doTranslateExpression(KtExpression expression, TranslationContext context) {
try {
return expression.accept(new ExpressionVisitor(), context);
}
catch (TranslationRuntimeException e) {
throw e;
}
catch (RuntimeException | AssertionError e) {
throw new TranslationRuntimeException(expression, e);
}
}
@NotNull
public static JsExpression translateAsExpression(@NotNull KtExpression expression, @NotNull TranslationContext context) {
return translateAsExpression(expression, context, context.dynamicContext().jsBlock());
}
@NotNull
public static JsExpression translateAsExpression(
@NotNull KtExpression expression,
@NotNull TranslationContext context,
@NotNull JsBlock block
) {
JsNode jsNode = translateExpression(expression, context, block);
if (jsNode instanceof JsExpression) {
KotlinType expressionType = context.bindingContext().getType(expression);
return unboxIfNeeded((JsExpression) jsNode, expressionType != null && KotlinBuiltIns.isCharOrNullableChar(expressionType));
}
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
if (BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext())) {
TemporaryVariable result = context.declareTemporary(null, expression);
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
return result.reference();
}
block.getStatements().add(convertToStatement(jsNode));
return new JsNullLiteral().source(expression);
}
@NotNull
public static JsExpression unboxIfNeeded(@NotNull JsExpression expression, boolean charOrNullableChar) {
if (charOrNullableChar &&
(expression instanceof JsInvocation || expression instanceof JsNameRef || expression instanceof JsArrayAccess)
) {
expression = JsAstUtils.boxedCharToChar(expression);
}
return expression;
}
@NotNull
public static JsStatement translateAsStatement(@NotNull KtExpression expression, @NotNull TranslationContext context) {
return translateAsStatement(expression, context, context.dynamicContext().jsBlock());
}
@NotNull
public static JsStatement translateAsStatement(
@NotNull KtExpression expression,
@NotNull TranslationContext context,
@NotNull JsBlock block) {
return convertToStatement(translateExpression(expression, context, block));
}
@NotNull
public static JsStatement translateAsStatementAndMergeInBlockIfNeeded(
@NotNull KtExpression expression,
@NotNull TranslationContext context
) {
JsBlock block = new JsBlock();
JsNode node = translateExpression(expression, context, block);
return JsAstUtils.mergeStatementInBlockIfNeeded(convertToStatement(node), block);
}
@NotNull
public static AstGenerationResult generateAst(
@NotNull BindingTrace bindingTrace,
@NotNull Collection units,
@NotNull MainCallParameters mainCallParameters,
@NotNull ModuleDescriptor moduleDescriptor,
@NotNull JsConfig config
) throws TranslationException {
try {
return doGenerateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config);
}
catch (UnsupportedOperationException e) {
throw new UnsupportedFeatureException("Unsupported feature used.", e);
}
catch (Throwable e) {
throw ExceptionUtilsKt.rethrow(e);
}
}
@NotNull
private static AstGenerationResult doGenerateAst(
@NotNull BindingTrace bindingTrace,
@NotNull Collection units,
@NotNull MainCallParameters mainCallParameters,
@NotNull ModuleDescriptor moduleDescriptor,
@NotNull JsConfig config
) {
JsProgram program = new JsProgram();
JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function");
JsName internalModuleName = program.getScope().declareName("_");
Merger merger = new Merger(rootFunction, internalModuleName, moduleDescriptor);
Map fragmentMap = new HashMap<>();
List fragments = new ArrayList<>();
List newFragments = new ArrayList<>();
Map> fileMemberScopes = new HashMap<>();
List sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList());
JsAstDeserializer deserializer = new JsAstDeserializer(program, sourceRoots);
for (TranslationUnit unit : units) {
if (unit instanceof TranslationUnit.SourceFile) {
KtFile file = ((TranslationUnit.SourceFile) unit).getFile();
StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor);
TranslationContext context = TranslationContext.rootContext(staticContext);
List fileMemberScope = new ArrayList<>();
translateFile(context, file, fileMemberScope);
fragments.add(staticContext.getFragment());
newFragments.add(staticContext.getFragment());
fragmentMap.put(file, staticContext.getFragment());
fileMemberScopes.put(file, fileMemberScope);
merger.addFragment(staticContext.getFragment());
}
else if (unit instanceof TranslationUnit.BinaryAst) {
byte[] astData = ((TranslationUnit.BinaryAst) unit).getData();
JsProgramFragment fragment = deserializer.deserialize(new ByteArrayInputStream(astData));
merger.addFragment(fragment);
fragments.add(fragment);
}
}
JsProgramFragment testFragment = mayBeGenerateTests(config, bindingTrace, moduleDescriptor);
fragments.add(testFragment);
newFragments.add(testFragment);
merger.addFragment(testFragment);
rootFunction.getParameters().add(new JsParameter(internalModuleName));
if (mainCallParameters.shouldBeGenerated()) {
JsProgramFragment mainCallFragment = generateCallToMain(
bindingTrace, config, moduleDescriptor, mainCallParameters.arguments());
if (mainCallFragment != null) {
fragments.add(mainCallFragment);
newFragments.add(mainCallFragment);
merger.addFragment(mainCallFragment);
}
}
merger.merge();
JsBlock rootBlock = rootFunction.getBody();
List statements = rootBlock.getStatements();
statements.add(0, new JsStringLiteral("use strict").makeStmt());
if (!isBuiltinModule(fragments)) {
defineModule(program, statements, config.getModuleId());
}
// Invoke function passing modules as arguments
// This should help minifier tool to recognize references to these modules as local variables and make them shorter.
List importedModuleList = merger.getImportedModules();
for (JsImportedModule importedModule : importedModuleList) {
rootFunction.getParameters().add(new JsParameter(importedModule.getInternalName()));
}
statements.add(new JsReturn(internalModuleName.makeRef()));
JsBlock block = program.getGlobalBlock();
block.getStatements().addAll(wrapIfNecessary(config.getModuleId(), rootFunction, importedModuleList, program,
config.getModuleKind()));
return new AstGenerationResult(program, internalModuleName, fragments, fragmentMap, newFragments,
fileMemberScopes, importedModuleList);
}
private static boolean isBuiltinModule(@NotNull List fragments) {
for (JsProgramFragment fragment : fragments) {
for (JsNameBinding nameBinding : fragment.getNameBindings()) {
if (nameBinding.getKey().equals(ENUM_SIGNATURE) && !fragment.getImports().containsKey(ENUM_SIGNATURE)) {
return true;
}
}
}
return false;
}
private static void translateFile(
@NotNull TranslationContext context,
@NotNull KtFile file,
@NotNull List fileMemberScope
) {
FileDeclarationVisitor fileVisitor = new FileDeclarationVisitor(context);
try {
for (KtDeclaration declaration : file.getDeclarations()) {
DeclarationDescriptor descriptor = BindingUtils.getDescriptorForElement(context.bindingContext(), declaration);
fileMemberScope.add(descriptor);
if (!AnnotationsUtils.isPredefinedObject(descriptor)) {
declaration.accept(fileVisitor, context);
}
}
}
catch (TranslationRuntimeException e) {
throw e;
}
catch (RuntimeException | AssertionError e) {
throw new TranslationRuntimeException(file, e);
}
}
private static void defineModule(@NotNull JsProgram program, @NotNull List statements, @NotNull String moduleId) {
JsName rootPackageName = program.getScope().findName(Namer.getRootPackageName());
if (rootPackageName != null) {
Namer namer = Namer.newInstance(program.getScope());
statements.add(new JsInvocation(namer.kotlin("defineModule"), new JsStringLiteral(moduleId),
rootPackageName.makeRef()).makeStmt());
}
}
@NotNull
private static JsProgramFragment mayBeGenerateTests(
@NotNull JsConfig config, @NotNull BindingTrace trace,
@NotNull ModuleDescriptor moduleDescriptor
) {
StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor);
TranslationContext context = TranslationContext.rootContext(staticContext);
new JSTestGenerator(context).generateTestCalls(moduleDescriptor);
return staticContext.getFragment();
}
//TODO: determine whether should throw exception
@Nullable
private static JsProgramFragment generateCallToMain(
@NotNull BindingTrace trace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor,
@NotNull List arguments
) {
StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor);
TranslationContext context = TranslationContext.rootContext(staticContext);
MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext());
FunctionDescriptor functionDescriptor = mainFunctionDetector.getMainFunction(moduleDescriptor);
if (functionDescriptor == null) {
return null;
}
JsArrayLiteral argument = new JsArrayLiteral(toStringLiteralList(arguments));
JsExpression call = CallTranslator.INSTANCE.buildCall(context, functionDescriptor, Collections.singletonList(argument), null);
context.addTopLevelStatement(call.makeStmt());
return staticContext.getFragment();
}
}