Skip to content

Wondering if may be used for mocking library #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
oleksiyp opened this issue Mar 7, 2019 · 16 comments
Open

Wondering if may be used for mocking library #1

oleksiyp opened this issue Mar 7, 2019 · 16 comments

Comments

@oleksiyp
Copy link

oleksiyp commented Mar 7, 2019

Last time I was trying to port MockK to Kotlin JS, the problem was in Kotlin JS reflection. Just've trapped on a post in discuss about your plugin and thinking if I can give a second try to this direction.

Just a few questions (I didn't want to explore it myself, because hope will have correct and up-to-date answers through communication rather than code):

  • how mature is a plugin? production ready enough?
  • who supports it?
  • are there any known issues?
  • does it play well with multiplatform projects?
  • does it need to be applied to all modules separately or just finally at the bundling stage?
  • how big is additional space needed?
  • is it using Kotlin reflection API or separate API?
  • changing Kotlin metadata?
  • does it support Kotlin standard library classes?
  • fallback for native Javascript?
@decembrist-revolt
Copy link
Owner

Last time I was trying to port MockK to Kotlin JS, the problem was in Kotlin JS reflection. Just've trapped on a post in discuss about your plugin and thinking if I can give a second try to this direction.

Just a few questions (I didn't want to explore it myself, because hope will have correct and up-to-date answers through communication rather than code):

Hello!

  • how mature is a plugin? production ready enough?
  • It was just a first try to reach an annotation availability, I was not sure that somebody except me needed this feature that was reached this way
  • who supports it?
  • Only me, you can find my contact info in github profile
  • are there any known issues?
  • You gave the first feedback that someone interested in this, thus answer is No
  • does it play well with multiplatform projects?
  • Answer can be yes, I'm working on checking that possibility and readme to explain how
  • does it need to be applied to all modules separately or just finally at the bundling stage?
  • You can't use annotations from one library inside others right now, as I mentioned in readme (Cross-project annotations will be added in upcoming patches)
  • how big is additional space needed?
  • This plugin creates one addition .kt file for each package that contains annotations
  • is it using Kotlin reflection API or separate API?
  • This plugin parses raw kotlin code through antlr
  • changing Kotlin metadata?
  • Maybe I misunderstood somthing but I think No
  • does it support Kotlin standard library classes?
  • I'm not sure what you mean but right now plugin supports only datatypes that were mentioned in readme
  • fallback for native Javascript?
  • Plugin overrides few kotlin.js functions look at KotlinPatch.kt
    After antlr recognotion. plugin creates .kt file per package with annotation data, that should be compiled with sources

@oleksiyp
Copy link
Author

oleksiyp commented Mar 8, 2019

Okay, I see... such approach then is limited to your own classes then, without the possibility to get metadata for classes that you linked as libraries, is it?

@decembrist-revolt
Copy link
Owner

Right now it's true. But it is not so difficult to make it possible. I'm going to add this feature.

@oleksiyp
Copy link
Author

oleksiyp commented Mar 8, 2019

And then I need function parameter types, list of functions in a class.

If that is possible I can go back to JS implementation and tell what is needed else.

@decembrist-revolt
Copy link
Owner

You already can get all class methods. Look at JsClassReflect interface in the readme. Or maybe you mean something else?

@oleksiyp
Copy link
Author

oleksiyp commented Mar 9, 2019

And the method parameter types?

@decembrist-revolt
Copy link
Owner

I'll add this also

@oleksiyp
Copy link
Author

oleksiyp commented Mar 9, 2019

Ok, if all that possible I think we can give it a try. I will recover JS branch. It will not happen very fast, one or two weeks. And then I'll come back with more detailed requirements on what is needed

@oleksiyp
Copy link
Author

oleksiyp commented Mar 9, 2019

Actually recovered branch 😄 Just didn't know how outdated is it.

Here are two main points where meta information is needed:

In JS everything is potentially callable and you can get function reference at one place and call it a totally different place. That's why both things get intercepted: getting function and applying that function.

Internal metadata format is following:
image

Hopefully all this is easy to retrive with your anotation processor.

Besides this information about method call, is there an ability to retrieve mangled name somewhere?
image

@decembrist-revolt
Copy link
Owner

If I understand correctly, do you need to fill in all the MethodDescription fields to achieve your goal?

  • Besides this information about method call, is there an ability to retrieve mangled name somewhere?
    Do you want to do it inside the kotlin without my plugin?
    It is possible, you should use the method reference and regular expression in this way.
    image

On the right is the result js file
You should override the function 1) kotlin.js (getCallableRef) to get the function 2) as an object
after you should do toString for the function object 2) and regexp the name of the function 3)

I did something like this for JsFunctionReflect.jsName

@oleksiyp
Copy link
Author

oleksiyp commented Mar 9, 2019

Yes, all these fields are from Java/Kotlin Reflection in JVM version.

I see. Unmangling sounds like a hack... as all mocking library itself 😃
But the question was if it is possible to do on the level of annotation processing. Is it?

@decembrist-revolt
Copy link
Owner

I need to figure it out, but I think yes

@oleksiyp
Copy link
Author

oleksiyp commented Mar 9, 2019

Tried to run it as Gradle plugin and have following error:

Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.decembrist.parser.KotlinLexer
	at org.decembrist.parsers.KtFileParser.parse(KtFileParser.kt:17)
	at org.decembrist.parsers.SourceParser.parse(SourceParser.kt:27)
	at org.decembrist.tasks.ProcessReflectionTask.run(ProcessReflectionTask.kt:27)
	at org.decembrist.Kotlin2jsReflectionPlugin.executeReflectionProcessing(Kotlin2jsReflectionPlugin.kt:50)
	at org.decembrist.Kotlin2jsReflectionPlugin.access$executeReflectionProcessing(Kotlin2jsReflectionPlugin.kt:16)
	at org.decembrist.Kotlin2jsReflectionPlugin$setUpReflectionTask$1.invoke(Kotlin2jsReflectionPlugin.kt:57)
	at org.decembrist.Kotlin2jsReflectionPlugin$setUpReflectionTask$1.invoke(Kotlin2jsReflectionPlugin.kt:16)
	at org.decembrist.Kotlin2jsReflectionPlugin$sam$org_gradle_api_Action$0.execute(Kotlin2jsReflectionPlugin.kt)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:801)
	at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:768)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:131)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:300)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:292)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:174)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:90)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:120)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:99)

@decembrist-revolt
Copy link
Owner

I think I found the source of the problem. I will try to fix it

@oleksiyp
Copy link
Author

I tried to build it on my own. See #2

The current problem(after resolving the issue with ANTLR4 clash and shadowing it) is I believe the syntax level of Kotlin parser. It is not able to understand the actual keyword.

1.2.71 does not equal recommended Kotlin2JsPlugin version
recommended version is 1.2.51
line 11:0 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 14:4 no viable alternative at input 'JsCounter()\n\nactual'
line 14:4 extraneous input 'actual' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 16:4 no viable alternative at input 'timeCounter.next()\n\nactual'
line 16:4 extraneous input 'actual' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 18:4 no viable alternative at input 'CommonRef(obj)\n\nactual'
line 18:4 extraneous input 'actual' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 20:4 no viable alternative at input 'JsHexLongHelper.toHexString(InternalPlatformDsl.identityHashCode(obj).toLong())\n\nactual'
line 20:4 extraneous input 'actual' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 35:4 extraneous input 'actual' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 35:32 mismatched input '<' expecting {NL, '('}
line 37:8 no viable alternative at input 'get(key)\nreturn'
line 37:8 extraneous input 'return' expecting {NL, '}', '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'constructor', 'companion', 'init', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 38:12 extraneous input 'val' expecting {NL, '}'}
line 39:12 no viable alternative at input 'valueFunc(key)\nput'
line 39:12 extraneous input 'put' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 53:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 55:4 no viable alternative at input 'ex\n\nactual'
line 55:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 57:4 no viable alternative at input 'CommonIdentityHashMapOf()\n\nactual'
line 57:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 59:4 no viable alternative at input 'CommonIdentityHashMapOf()\n\nactual'
line 59:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 61:4 no viable alternative at input 'mutableListOf()\n\nactual'
line 61:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 64:4 no viable alternative at input 'hashMapOf()\n\n@Suppress("NAME_SHADOWING","UNUSED_VARIABLE")\nactual'
line 64:4 no viable alternative at input '@Suppress("NAME_SHADOWING","UNUSED_VARIABLE")\nactual'
line 71:4 extraneous input 'actual' expecting {<EOF>, '@', '@file', 'class', 'interface', 'fun', 'object', 'val', 'var', 'typealias', 'in', 'out', '@field', '@property', '@get', '@set', '@receiver', '@param', '@setparam', '@delegate', 'public', 'private', 'protected', 'internal', 'enum', 'sealed', 'annotation', 'data', 'inner', 'tailrec', 'operator', 'inline', 'infix', 'external', 'suspend', 'override', 'abstract', 'final', 'open', 'const', 'lateinit', 'vararg', 'noinline', 'crossinline', 'reified', LabelReference}
line 73:4 no viable alternative at input '{listOf<StackElement>()}\n\nactual'

@oleksiyp
Copy link
Author

oleksiyp commented Mar 10, 2019

Then I upgraded to newest Kotlin grammar and was finally able to generate some reflection metadata(as Kotlin code) 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants