How do you serialize a fragment of an Xtext parse tree/AST? - serialization

I have some xtend code that gets a parse tree/AST from a file in my DSL.
I want a method that will serialize any node in the tree. This means I want text in the language of my DSL for an EObject somewhere in the parse tree.
So, if a program in my language is
class C {
int foo = 3;
}
the parse tree might look like
MyProgram
|_ MyClass
|_MyFieldDecl
|_Type
| |_IntType
|_Identifier
|_Expression
|_LiteralInteger
If I assigned a variable x the MyFieldDecl object in the tree, then I might want to call
var s = serialize(x)
to get the String, "int foo = 3;"
So, how do I implement that serialize() function?
The code I find when searching around will serialize the entire tree but not a node/fragment.
I found things in the xtext core code that appear to be doing this with either some instance of ISerializer or GrammarAccessExtensions. I tried to inject
#Inject #Extension GrammarAccessExtensions _grammarAccessExtensions
and get the serialization using
val s = _grammarAccessExtensions.grammarFragmentToString(e, prefix);
where e is an EObject, some node in the parse tree.
That failed on the statement
val main = injector.getInstance(MyClass)
with this error:
Exception in thread "main" com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for org.eclipse.xtext.xtext.generator.model.project.IXtextProjectConfig was bound.
while locating org.eclipse.xtext.xtext.generator.model.project.IXtextProjectConfig
for field at org.eclipse.xtext.xtext.generator.XtextGeneratorNaming.projectConfig(Unknown Source)
while locating org.eclipse.xtext.xtext.generator.XtextGeneratorNaming
for field at org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions._xtextGeneratorNaming(Unknown Source)
while locating org.eclipse.xtext.xtext.generator.grammarAccess.GrammarAccessExtensions
for field at myorg.MyClass._grammarAccessExtensions(Unknown Source)
while locating myorg.MyClass
1 error
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1004)
at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:961)
at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)
at myorg.MyClass.main(MyClass.java:435)
I think this means I need to bind or inject things, but I just started working with this code base a couple of days ago, and all of this is new to me.
I also tried to copy code out of the xtext core to get an ISerializer and call
s = GrammarAccessExtensions.grammarFragmentToString(serializer, e, prefix)
That failed with this:
java.lang.NullPointerException: Invalid context: Grammar returns ScenarioModel

Related

Moshi - issue serializing library class after minification

Recently ran into an issue with library, which uses moshi to create runtime TypeAdapter for serialization/deserialization. The library seems to work fine with R8 disabled, but after enabling it I ran into an error message:
Fatal Exception: java.lang.IllegalArgumentException: Cannot serialize Kotlin type org.walletconnect.impls.WCSessionStore$State. Reflective serialization of Kotlin classes without using kotlin-reflect has undefined and unexpected behavior. Please use KotlinJsonAdapterFactory from the moshi-kotlin artifact or use code gen from the moshi-kotlin-codegen artifact.
for class org.walletconnect.impls.WCSessionStore$State
for java.util.Map<java.lang.String, org.walletconnect.impls.WCSessionStore$State>
The solution seems to be defining keepclass R8 rule and it works.
But, prior to adding R8 rule, what bothers me is that if I decompile the minified binary I can see that the class in question (WCSessionStore$State) is not minified at all and I can clearly see that it's referenced when constructing the adapter:
public final class C12848a implements WCSessionStore {
public C12848a(File file, C10727r rVar) {
C11124p.m43616g(file, "storageFile");
C11124p.m43616g(rVar, "moshi");
this.f32438c = file;
this.f32436a = rVar.mo25906d(C10747u.m42144j(Map.class, String.class, WCSessionStore.State.class));
.
.
.
public interface WCSessionStore {
public State(Session$Config session$Config, Session$PeerData session$PeerData, Session$PeerData session$PeerData2, Long l, String str, List list, Long l2) {}
.
.
.
Also worth noting would be, that I'm running android.enableR8.fullMode=true
Is this expected behavior? Is the R8 rule the only way to get around this?

Intellij IDEA returns different results with vs. without enabled REPL for Kotlin

I create a Kotlin scratch file in Intellij IDEA and use my current project's module classpath in order to access all libraries of the project (i'm using Jackson in this example)
In both scenarios I have declared the following class:
class Test(var first: String = "a", var second: String = "b")
Without REPL enabled
val jsonAsString = "{\"first\": \"a\", \"second\":\"b\"}"
println(ObjectMapper().readValue(jsonAsString, Test::class.java).first) // prints out "a"
"a" is printed out as expected
With REPL enabled the ObjectMapper.readValue() throws the following exception
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `Line_2$Test` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"first": "a", "second":"b"}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1904)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:400)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1349)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1415)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:351)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:184)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3597)
I reproduced the bug and created an issue - https://youtrack.jetbrains.com/issue/KTIJ-21598/Scratch-REPL:-%22InvalidDefinitionException:-Cannot-construct-inst. Feel free to follow it.

Kotlin scripting support fails with "wrong number of arguments" whenever I try to run any script

I'm trying to run a very basic script with org.jetbrains.kotlin:kotlin-scripting-jvm, but I get two errors, when I should get none. This is my script:
1
I expect to get back a ResultWithDiagnostics.Success with a resultValue of 1 but instead I get a Failure, with these reports:
The expression is unused
wrong number of arguments
Even if I fix the warning by modifying my script to
class Foo(val foo: String = "foo")
Foo()
I still get the wrong number of arguments error. I checked the source and it seems that in
BasicJvmScriptEvaluator:95
return try {
ctor.newInstance(*args.toArray()) <-- here
} finally {
Thread.currentThread().contextClassLoader = saveClassLoader
}
args is empty. What am I doing wrong? This is how I try to run the script:
private fun evalFile(scriptFile: File): ResultWithDiagnostics<EvaluationResult> {
val compilationConfiguration = createJvmCompilationConfigurationFromTemplate<TestScript> {
jvm {
dependenciesFromCurrentContext(wholeClasspath = true)
}
}
return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConfiguration, null)
}
and this is the stack trace for this wrong number of arguments error I get:
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.evalWithConfigAndOtherScriptsResults(BasicJvmScriptEvaluator.kt:95)
at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke$suspendImpl(BasicJvmScriptEvaluator.kt:40)
at kotlin.script.experimental.jvm.BasicJvmScriptEvaluator.invoke(BasicJvmScriptEvaluator.kt)
at kotlin.script.experimental.host.BasicScriptingHost$eval$1.invokeSuspend(BasicScriptingHost.kt:47)
at kotlin.script.experimental.host.BasicScriptingHost$eval$1.invoke(BasicScriptingHost.kt)
at kotlin.script.experimental.host.BasicScriptingHost$runInCoroutineContext$1.invokeSuspend(BasicScriptingHost.kt:35)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:80)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at kotlin.script.experimental.host.BasicScriptingHost.runInCoroutineContext(BasicScriptingHost.kt:35)
at kotlin.script.experimental.host.BasicScriptingHost.eval(BasicScriptingHost.kt:45)
This isn't fix, just workaround.
You can pass the source code to Kotlin Compiler by the different ways:
From FileScriptSource - when you pass list of files in the configuration
From the list of source code content in memory - e.g. each file should be read and content should be placed inside StringScriptSource
From the single memory script, which is created just with all input source files concatenation.
As I found in my experiments:
If you have mockk+kotest jars in the same classpath, option 1 doesn't work. In that case I'd like to assume for you to make one change:
// this doesn't work - scriptFile.toScriptSource()
scriptFile.readText().toScriptSource() // ok - we read source from memory, not from file
If you have huge service with a lot of Spring jars, all options above work. It means that you couldn't test your compilation in the unit tests, however your service will work!
If you want to do compilation from Gradle Plugin, you can catch another kind of issue - class conflict with coroutines library, so all options above don't work.
Finally, I changed the following in my code:
On input I always have a lot of kt/kts files.
I have three compilation options (described above). So my code executes createJvmCompilationConfigurationFromTemplate with the different logic, according on my compilation mode (it is just enum).
For unit tests I have to use option 3 only.
For service I use the first option as it is the fastest one
For gradle plugin classpath I start separate instance of java (with fresh classpath) which executes the input kts files.

Use antlr v4 for syntax check

Can I use antlr v4 for syntax check before I actually run the code?
Example :
I defined syntax: select * from table, I want to know the statement is correct or not before actually executing it.
Following is my code :
val listener = new SQLListener()
val loadLexer = new SQLLexer(new ANTLRInputStream(input))
val tokens = new CommonTokenStream(loadLexer)
val parser = new SQLParser(tokens)
val stat = parser.statement()
I tried but DefaultErrorStrategy won't throw an Exception
I tried this:
parser.addErrorListener(new BaseErrorListener {
override def syntaxError(recognizer: Recognizer[_, _ <: ATNSimulator],
offendingSymbol: scala.Any,
line: Int,
charPositionInLine: Int,
msg: String, e: RecognitionException ): Unit = {
println("==========2============"+msg)
throw new AssertionError("line: " + line + ", offset: " + charPositionInLine +
", symbol:" + offendingSymbol + " " + msg)
}
})
but get this:
Error: Note: the super classes of contain the following, non final members named syntaxError:
If the input contains any syntax errors, this will call the visitErrorNode method on the listener. So if you define that method in your listener, you'll see any errors that occur.
If your listener is directly executing the code (rather than first building an AST or other form of IR), you probably won't want your listener to even start executing when there's a syntax error. One way to achieve that would be to set the BailErrorStrategy instead of the DefaultErrorStrategy as the error handling strategy of your parser (using setErrorHandler on the parser). This will throw an exception as soon as a syntax error occurs.
If you don't want to abort on the first error and/or you want some additional checks beyond just syntax errors (like checking for certain types of semantic errors), an alternative is to have a listener just to perform those checks. Then you'd run your code-executing listener only if the error-checking listener does not find any errors.
You are on the right track here. Use your error listener to store the errors in a list while parsing. Afterwards you can then check that list.
That requires however not to do any action during the parsing process (e.g. in a parse listener) other than stuff related to the parsing process itself. Any follow up action (e.g. error markup in an editor) should be done after the parse run.
If you like to see an example of an application using this approach take a look at the parser module implementation of MySQL Workbench. It also demonstrates the 2-stage parsing strategy for quicker parsing.

Xtext: Customizing Error msg by unordered groups

I've defined an unordered group and it works like I expected. The only thing I would like to change is the error msg, which appears when an element of an unordered group isn't modelled yet. Is there an easy way to solve this? I tried already custom checks, but there I got an unexpected behaviour.
Following my rule for the unordered group and the error msg:
Element:
(name=ConfigurationName) &
(description=Description)? &
(tool=Tool) &
(model=Model) &
(interfaces=Interfaces)? &
(paramaters=Parameters)? &
(paramfile=ParamFile)?
;
rule ruleElement failed predicate: {getUnorderedGroupHelper().canLeave(grammarAccess.getElementAccess().getUnorderedGroup())}?
I want to change this error msg to something like: "The following elements are required in the configuration:...."
Xtext has a service called SyntaxErrorMessageProvider that is used to reword parser error messages. You have to define your messages on the parser level (so there will be no EMF model to use), but it is possible to get the original error message and the context, traverse it and provide your own error message.
To register this, open the «YourLanguage»RuntimeModule class, and add the following method:
public Class<? extends ISyntaxErrorMessageProvider> bindISyntaxErrorMessageProvider() {
return «YourLanguage»SyntaxErrorMessageProvider.class;
}
where «YourLanguage«SyntaxErrorMessageProvider is a class introduced by you, extending the class SyntaxErrorMessageProvider, where you can implement your custom function.
I works Automatic Validation customize,I create
public class MyDslLanguageSyntaxErrorMessageProvider extends SyntaxErrorMessageProvider {
}
And I Register it in the MyDslRuntimeModule:
public Class bindISyntaxErrorMessageProvider() {
return MyDslLanguageSyntaxErrorMessageProvider.class;}
But my problem is which package is used for this customization.I used org.xtext.example.mydsl.validation package for create java class .Also I do this customization with xtend class.I do not find enough source in the internet :(
You can use Java to write this Custom SyntaxErrorMessageProvider class, but to bind this you can bind in Runtime Module class. Also u can use any package to declare this class but declaring this class in same package where u have Runtime class makes sense