From 27e319a2795a45db61f38e56220f7736c308a498 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 29 Sep 2017 13:09:42 +0300 Subject: [PATCH] JS: fix parsing of object literals in js() function. Also, fix generation of source maps of content of js() function. See KT-19794 --- .../js/resolve/diagnostics/JsCallChecker.kt | 5 +- .../com/google/gwt/dev/js/JsAstMapper.java | 2 +- .../com/google/gwt/dev/js/rhino/Parser.java | 2 +- .../jetbrains/kotlin/js/parser/parserUtils.kt | 54 ++++++++++++++++++- .../js/test/JsLineNumberTestGenerated.java | 6 +++ .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../kotlin/js/facade/K2JSTranslator.java | 9 ++-- .../js/facade/SourceMapBuilderConsumer.java | 16 ++---- .../js/translate/context/StaticContext.java | 13 ++++- .../translate/context/TranslationContext.java | 6 +++ .../js/translate/general/Translation.java | 22 ++++---- .../reference/CallExpressionTranslator.java | 17 +++++- .../kotlin/js/translate/utils/PsiUtils.java | 22 ++++++++ .../testData/box/jsCode/objectExpression.kt | 26 +++++++++ .../testData/lineNumbers/jsCode.kt | 8 +++ 15 files changed, 183 insertions(+), 31 deletions(-) create mode 100644 js/js.translator/testData/box/jsCode/objectExpression.kt create mode 100644 js/js.translator/testData/lineNumbers/jsCode.kt diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt index 07525f0df05..1f8b9bdf1b6 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsCallChecker.kt @@ -29,7 +29,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1 import org.jetbrains.kotlin.js.backend.ast.JsFunctionScope import org.jetbrains.kotlin.js.backend.ast.JsProgram import org.jetbrains.kotlin.js.backend.ast.JsRootScope -import org.jetbrains.kotlin.js.parser.parse +import org.jetbrains.kotlin.js.parser.parseExpressionOrStatement import org.jetbrains.kotlin.js.patterns.DescriptorPredicate import org.jetbrains.kotlin.js.patterns.PatternBuilder import org.jetbrains.kotlin.js.resolve.LEXICAL_SCOPE_FOR_JS @@ -90,7 +90,8 @@ class JsCallChecker( try { val parserScope = JsFunctionScope(JsRootScope(JsProgram()), "") - val statements = parse(code, errorReporter, parserScope, reportOn.containingFile?.name ?: "") + val statements = parseExpressionOrStatement( + code, errorReporter, parserScope, CodePosition(0, 0), reportOn.containingFile?.name ?: "") if (statements == null || statements.isEmpty()) { context.trace.report(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED.on(argument)) diff --git a/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java b/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java index 9f8a10069c0..b75d37f61e8 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java +++ b/js/js.parser/src/com/google/gwt/dev/js/JsAstMapper.java @@ -462,7 +462,7 @@ public class JsAstMapper { } } - private JsExpression mapExpression(Node exprNode) throws JsParserException { + public JsExpression mapExpression(Node exprNode) throws JsParserException { JsNode unknown = map(exprNode); if (unknown instanceof JsExpression) { diff --git a/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java b/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java index b8b7562f7c4..466a15391b8 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java +++ b/js/js.parser/src/com/google/gwt/dev/js/rhino/Parser.java @@ -727,7 +727,7 @@ public class Parser { return pn; } - private Node expr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { + public Node expr(TokenStream ts, boolean inForInit) throws IOException, JavaScriptException { Node pn = assignExpr(ts, inForInit); while (ts.matchToken(TokenStream.COMMA)) { CodePosition position = ts.tokenPosition; diff --git a/js/js.parser/src/org/jetbrains/kotlin/js/parser/parserUtils.kt b/js/js.parser/src/org/jetbrains/kotlin/js/parser/parserUtils.kt index 58ab8a27c22..f76615ceb10 100644 --- a/js/js.parser/src/org/jetbrains/kotlin/js/parser/parserUtils.kt +++ b/js/js.parser/src/org/jetbrains/kotlin/js/parser/parserUtils.kt @@ -30,6 +30,42 @@ fun parse(code: String, reporter: ErrorReporter, scope: JsScope, fileName: Strin } } +fun parseExpressionOrStatement( + code: String, + reporter: ErrorReporter, scope: JsScope, + startPosition: CodePosition, fileName: String +): List? { + val accumulatingReporter = AccumulatingReporter() + val exprNode = try { + parse(code, startPosition, 0, accumulatingReporter, true) { + val result = expr(it, false) + if (it.token != TokenStream.EOF) { + accumulatingReporter.hasErrors = true + } + result + } + } + catch (e: JavaScriptException) { + null + } + + return if (!accumulatingReporter.hasErrors) { + for (warning in accumulatingReporter.warnings) { + reporter.warning(warning.message, warning.startPosition, warning.endPosition) + } + val expr = exprNode?.toJsAst(scope, fileName) { + mapExpression(it) + } + expr?.let { listOf(JsExpressionStatement(it)) } + } + else { + val node = parse(code, startPosition, 0, reporter, true, Parser::parse) + node?.toJsAst(scope, fileName) { + mapStatements(it) + } + } +} + fun parseFunction(code: String, fileName: String, position: CodePosition, offset: Int, reporter: ErrorReporter, scope: JsScope): JsFunction? { val rootNode = parse(code, position, offset, reporter, insideFunction = false) { addListener(FunctionParsingObserver()) @@ -80,4 +116,20 @@ private fun StringReader(string: String, offset: Int): Reader { val reader = StringReader(string) reader.skip(offset.toLong()) return reader -} \ No newline at end of file +} + +private class AccumulatingReporter : ErrorReporter { + var hasErrors = false + val warnings = mutableListOf() + + override fun warning(message: String, startPosition: CodePosition, endPosition: CodePosition) { + warnings += Warning(message, startPosition, endPosition) + } + + override fun error(message: String, startPosition: CodePosition, endPosition: CodePosition) { + hasErrors = true + } + + class Warning(val message: String, val startPosition: CodePosition, val endPosition: CodePosition) +} + diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java index 73c1f42009b..56233d66562 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/JsLineNumberTestGenerated.java @@ -216,6 +216,12 @@ public class JsLineNumberTestGenerated extends AbstractJsLineNumberTest { doTest(fileName); } + @TestMetadata("jsCode.kt") + public void testJsCode() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/jsCode.kt"); + doTest(fileName); + } + @TestMetadata("lambdaWithClosure.kt") public void testLambdaWithClosure() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/lineNumbers/lambdaWithClosure.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 132db01c201..9e92b830d3d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -5516,6 +5516,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("objectExpression.kt") + public void testObjectExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsCode/objectExpression.kt"); + doTest(fileName); + } + @TestMetadata("objectScopes.kt") public void testObjectScopes() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/jsCode/objectScopes.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java index a615cca820f..7f6edf31e10 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/K2JSTranslator.java @@ -128,7 +128,11 @@ public final class K2JSTranslator { ModuleDescriptor moduleDescriptor = analysisResult.getModuleDescriptor(); Diagnostics diagnostics = bindingTrace.getBindingContext().getDiagnostics(); - AstGenerationResult translationResult = Translation.generateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config); + List sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList()); + SourceFilePathResolver pathResolver = new SourceFilePathResolver(sourceRoots); + + AstGenerationResult translationResult = Translation.generateAst( + bindingTrace, units, mainCallParameters, moduleDescriptor, config, pathResolver); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); if (hasError(diagnostics)) return new TranslationResult.Fail(diagnostics); @@ -152,9 +156,6 @@ public final class K2JSTranslator { ExpandIsCallsKt.expandIsCalls(newFragments); ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); - List sourceRoots = config.getSourceMapRoots().stream().map(File::new).collect(Collectors.toList()); - SourceFilePathResolver pathResolver = new SourceFilePathResolver(sourceRoots); - JsAstSerializer serializer = new JsAstSerializer(file -> { try { return pathResolver.getPathRelativeToSourceRoots(file); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/facade/SourceMapBuilderConsumer.java b/js/js.translator/src/org/jetbrains/kotlin/js/facade/SourceMapBuilderConsumer.java index 4f3f1a1e458..74aaee2553c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/facade/SourceMapBuilderConsumer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/facade/SourceMapBuilderConsumer.java @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.js.facade; -import com.intellij.openapi.editor.Document; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; @@ -26,6 +25,7 @@ import org.jetbrains.kotlin.js.backend.ast.JsLocation; import org.jetbrains.kotlin.js.backend.ast.JsLocationWithSource; import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.js.sourceMap.SourceMapMappingConsumer; +import org.jetbrains.kotlin.js.translate.utils.PsiUtils; import java.io.*; import java.nio.charset.Charset; @@ -82,15 +82,10 @@ public class SourceMapBuilderConsumer implements SourceLocationConsumer { } if (sourceInfo instanceof PsiElement) { PsiElement element = (PsiElement) sourceInfo; - PsiFile psiFile = element.getContainingFile(); - int offset = element.getNode().getStartOffset(); - Document document = psiFile.getViewProvider().getDocument(); - assert document != null; - int sourceLine = document.getLineNumber(offset); - int sourceColumn = offset - document.getLineStartOffset(sourceLine); - - File file = new File(psiFile.getViewProvider().getVirtualFile().getPath()); try { + JsLocation location = PsiUtils.extractLocationFromPsi(element, pathResolver); + PsiFile psiFile = element.getContainingFile(); + File file = new File(psiFile.getViewProvider().getVirtualFile().getPath()); Supplier contentSupplier; if (provideCurrentModuleContent) { contentSupplier = () -> { @@ -105,8 +100,7 @@ public class SourceMapBuilderConsumer implements SourceLocationConsumer { else { contentSupplier = () -> null; } - mappingConsumer.addMapping(pathResolver.getPathRelativeToSourceRoots(file), null, contentSupplier, - sourceLine, sourceColumn); + mappingConsumer.addMapping(location.getFile(), null, contentSupplier, location.getStartLine(), location.getStartChar()); } catch (IOException e) { throw new RuntimeException("IO error occurred generating source maps", e); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index 570e89791cc..1071efb092c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.NameSuggestionKt; import org.jetbrains.kotlin.js.naming.SuggestedName; +import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.js.translate.context.generator.Generator; import org.jetbrains.kotlin.js.translate.context.generator.Rule; import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator; @@ -137,10 +138,14 @@ public final class StaticContext { private final Map intrinsicNames = new HashMap<>(); + @NotNull + private final SourceFilePathResolver sourceFilePathResolver; + public StaticContext( @NotNull BindingTrace bindingTrace, @NotNull JsConfig config, - @NotNull ModuleDescriptor moduleDescriptor + @NotNull ModuleDescriptor moduleDescriptor, + @NotNull SourceFilePathResolver sourceFilePathResolver ) { program = new JsProgram(); JsFunction rootFunction = JsAstUtils.createFunctionWithEmptyBody(program.getScope()); @@ -157,6 +162,7 @@ public final class StaticContext { createImportedModule(new JsImportedModuleKey(Namer.KOTLIN_LOWER_NAME, null), Namer.KOTLIN_LOWER_NAME, kotlinName, null); classModelGenerator = new ClassModelGenerator(TranslationContext.rootContext(this)); + this.sourceFilePathResolver = sourceFilePathResolver; } @NotNull @@ -189,6 +195,11 @@ public final class StaticContext { return namer; } + @NotNull + public SourceFilePathResolver getSourceFilePathResolver() { + return sourceFilePathResolver; + } + @NotNull public JsScope getScopeForDescriptor(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof ModuleDescriptor) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java index 7e79141058c..791e6d7a7eb 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/TranslationContext.java @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.js.backend.ast.metadata.SpecialFunction; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.SuggestedName; +import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.js.translate.declaration.ClassModelGenerator; import org.jetbrains.kotlin.js.translate.intrinsic.Intrinsics; import org.jetbrains.kotlin.js.translate.reference.CallExpressionTranslator; @@ -896,4 +897,9 @@ public class TranslationContext { }); } } + + @NotNull + public SourceFilePathResolver getSourceFilePathResolver() { + return staticContext.getSourceFilePathResolver(); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java index 9122698d1ec..e1f3cc9b7a0 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/general/Translation.java @@ -31,6 +31,7 @@ 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.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.Namer; import org.jetbrains.kotlin.js.translate.context.StaticContext; @@ -253,10 +254,11 @@ public final class Translation { @NotNull Collection units, @NotNull MainCallParameters mainCallParameters, @NotNull ModuleDescriptor moduleDescriptor, - @NotNull JsConfig config + @NotNull JsConfig config, + @NotNull SourceFilePathResolver sourceFilePathResolver ) throws TranslationException { try { - return doGenerateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config); + return doGenerateAst(bindingTrace, units, mainCallParameters, moduleDescriptor, config, sourceFilePathResolver); } catch (UnsupportedOperationException e) { throw new UnsupportedFeatureException("Unsupported feature used.", e); @@ -272,7 +274,8 @@ public final class Translation { @NotNull Collection units, @NotNull MainCallParameters mainCallParameters, @NotNull ModuleDescriptor moduleDescriptor, - @NotNull JsConfig config + @NotNull JsConfig config, + @NotNull SourceFilePathResolver sourceFilePathResolver ) { JsProgram program = new JsProgram(); JsFunction rootFunction = new JsFunction(program.getRootScope(), new JsBlock(), "root function"); @@ -290,7 +293,7 @@ public final class Translation { for (TranslationUnit unit : units) { if (unit instanceof TranslationUnit.SourceFile) { KtFile file = ((TranslationUnit.SourceFile) unit).getFile(); - StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor); + StaticContext staticContext = new StaticContext(bindingTrace, config, moduleDescriptor, sourceFilePathResolver); TranslationContext context = TranslationContext.rootContext(staticContext); List fileMemberScope = new ArrayList<>(); translateFile(context, file, fileMemberScope); @@ -308,7 +311,7 @@ public final class Translation { } } - JsProgramFragment testFragment = mayBeGenerateTests(config, bindingTrace, moduleDescriptor); + JsProgramFragment testFragment = mayBeGenerateTests(config, bindingTrace, moduleDescriptor, sourceFilePathResolver); fragments.add(testFragment); newFragments.add(testFragment); merger.addFragment(testFragment); @@ -316,7 +319,7 @@ public final class Translation { if (mainCallParameters.shouldBeGenerated()) { JsProgramFragment mainCallFragment = generateCallToMain( - bindingTrace, config, moduleDescriptor, mainCallParameters.arguments()); + bindingTrace, config, moduleDescriptor, sourceFilePathResolver, mainCallParameters.arguments()); if (mainCallFragment != null) { fragments.add(mainCallFragment); newFragments.add(mainCallFragment); @@ -400,9 +403,9 @@ public final class Translation { @NotNull private static JsProgramFragment mayBeGenerateTests( @NotNull JsConfig config, @NotNull BindingTrace trace, - @NotNull ModuleDescriptor moduleDescriptor + @NotNull ModuleDescriptor moduleDescriptor, @NotNull SourceFilePathResolver sourceFilePathResolver ) { - StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor); + StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor, sourceFilePathResolver); TranslationContext context = TranslationContext.rootContext(staticContext); new JSTestGenerator(context).generateTestCalls(moduleDescriptor); @@ -414,9 +417,10 @@ public final class Translation { @Nullable private static JsProgramFragment generateCallToMain( @NotNull BindingTrace trace, @NotNull JsConfig config, @NotNull ModuleDescriptor moduleDescriptor, + @NotNull SourceFilePathResolver sourceFilePathResolver, @NotNull List arguments ) { - StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor); + StaticContext staticContext = new StaticContext(trace, config, moduleDescriptor, sourceFilePathResolver); TranslationContext context = TranslationContext.rootContext(staticContext); MainFunctionDetector mainFunctionDetector = new MainFunctionDetector(context.bindingContext()); FunctionDescriptor functionDescriptor = mainFunctionDetector.getMainFunction(moduleDescriptor); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java index 13dbbd524cc..cd4e7cd5780 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallExpressionTranslator.java @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.js.translate.reference; import com.google.gwt.dev.js.ThrowExceptionOnErrorReporter; +import com.google.gwt.dev.js.rhino.CodePosition; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.config.CommonConfigurationKeys; @@ -30,6 +31,7 @@ import org.jetbrains.kotlin.js.resolve.BindingContextSlicesJsKt; import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker; import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator; import org.jetbrains.kotlin.js.translate.context.TranslationContext; +import org.jetbrains.kotlin.js.translate.utils.PsiUtils; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.KtCallExpression; import org.jetbrains.kotlin.psi.KtExpression; @@ -40,6 +42,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.scopes.LexicalScope; +import java.io.IOException; import java.util.*; import static org.jetbrains.kotlin.js.resolve.diagnostics.JsCallChecker.isJsCall; @@ -175,6 +178,18 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl assert currentScope instanceof JsFunctionScope : "Usage of js outside of function is unexpected"; JsScope temporaryRootScope = new JsRootScope(new JsProgram()); JsScope scope = new DelegatingJsFunctionScopeWithTemporaryParent((JsFunctionScope) currentScope, temporaryRootScope); - return ParserUtilsKt.parse(jsCode, ThrowExceptionOnErrorReporter.INSTANCE, scope, jsCodeExpression.getContainingKtFile().getName()); + + JsLocation location; + try { + location = PsiUtils.extractLocationFromPsi(jsCodeExpression, context().getSourceFilePathResolver()); + } + catch (IOException e) { + location = new JsLocation(jsCodeExpression.getContainingKtFile().getName(), 0, 0); + } + + List statements = ParserUtilsKt.parseExpressionOrStatement( + jsCode, ThrowExceptionOnErrorReporter.INSTANCE, scope, + new CodePosition(location.getStartLine(), location.getStartChar()), location.getFile()); + return statements != null ? statements : Collections.emptyList(); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java index 440f371c462..567e1632586 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java @@ -16,18 +16,26 @@ package org.jetbrains.kotlin.js.translate.utils; +import com.intellij.openapi.editor.Document; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableDescriptor; +import org.jetbrains.kotlin.js.backend.ast.JsLocation; +import org.jetbrains.kotlin.js.sourceMap.SourceFilePathResolver; import org.jetbrains.kotlin.lexer.KtToken; import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall; +import java.io.*; +import java.nio.charset.Charset; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; public final class PsiUtils { @@ -120,4 +128,18 @@ public final class PsiUtils { return resolvedCall.getCandidateDescriptor(); } + + @NotNull + public static JsLocation extractLocationFromPsi(@NotNull PsiElement element, @NotNull SourceFilePathResolver pathResolver) + throws IOException { + PsiFile psiFile = element.getContainingFile(); + int offset = element.getNode().getStartOffset(); + Document document = psiFile.getViewProvider().getDocument(); + assert document != null; + int sourceLine = document.getLineNumber(offset); + int sourceColumn = offset - document.getLineStartOffset(sourceLine); + + File file = new File(psiFile.getViewProvider().getVirtualFile().getPath()); + return new JsLocation(pathResolver.getPathRelativeToSourceRoots(file), sourceLine, sourceColumn); + } } diff --git a/js/js.translator/testData/box/jsCode/objectExpression.kt b/js/js.translator/testData/box/jsCode/objectExpression.kt new file mode 100644 index 00000000000..55710dbabdd --- /dev/null +++ b/js/js.translator/testData/box/jsCode/objectExpression.kt @@ -0,0 +1,26 @@ +// EXPECTED_REACHABLE_NODES: 1026 +import kotlin.js.* + +private fun isOrdinaryObject(o: Any?): Boolean = jsTypeOf(o) == "object" && Object.getPrototypeOf(o).`constructor` === Any::class.js + +external class Object { + companion object { + fun getPrototypeOf(x: Any?): dynamic + + fun keys(x: Any): Array + } +} + +fun box(): String { + val a = js("{}") + if (!isOrdinaryObject(a)) return "fail: a is not an object" + if (Object.keys(a).size != 0) return "fail: a should not have any properties" + + val b = js("{ foo: 23, bar: 42 }") + if (!isOrdinaryObject(b)) return "fail: b is not an object" + if (Object.keys(b).size != 2) return "fail: b should have two properties" + if (b.foo != 23) return "fail: b.foo == ${b.foo}" + if (b.bar != 42) return "fail: b.bar == ${b.bar}" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/lineNumbers/jsCode.kt b/js/js.translator/testData/lineNumbers/jsCode.kt new file mode 100644 index 00000000000..735dbaa1f69 --- /dev/null +++ b/js/js.translator/testData/lineNumbers/jsCode.kt @@ -0,0 +1,8 @@ +fun foo() { + var x = "foo" + println("before: $x") + js("console.log('js' + x);") + println("after: $x") +} + +// LINES: 6 2 2 3 3 4 4 5 5 \ No newline at end of file