JS: fix parsing of object literals in js() function.
Also, fix generation of source maps of content of js() function. See KT-19794
This commit is contained in:
@@ -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()), "<js fun>")
|
||||
val statements = parse(code, errorReporter, parserScope, reportOn.containingFile?.name ?: "<unknown file>")
|
||||
val statements = parseExpressionOrStatement(
|
||||
code, errorReporter, parserScope, CodePosition(0, 0), reportOn.containingFile?.name ?: "<unknown file>")
|
||||
|
||||
if (statements == null || statements.isEmpty()) {
|
||||
context.trace.report(ErrorsJs.JSCODE_NO_JAVASCRIPT_PRODUCED.on(argument))
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<JsStatement>? {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
private class AccumulatingReporter : ErrorReporter {
|
||||
var hasErrors = false
|
||||
val warnings = mutableListOf<Warning>()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<File> 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<File> 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);
|
||||
|
||||
@@ -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<Reader> 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);
|
||||
|
||||
@@ -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<String, JsName> 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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<TranslationUnit> 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<TranslationUnit> 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<DeclarationDescriptor> 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<String> 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);
|
||||
|
||||
+16
-1
@@ -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<JsStatement> statements = ParserUtilsKt.parseExpressionOrStatement(
|
||||
jsCode, ThrowExceptionOnErrorReporter.INSTANCE, scope,
|
||||
new CodePosition(location.getStartLine(), location.getStartChar()), location.getFile());
|
||||
return statements != null ? statements : Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String>
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user