Git Product home page Git Product logo

ksp's Introduction

Kotlin Symbol Processing API

Welcome to KSP!

Kotlin Symbol Processing (KSP) is an API that you can use to develop lightweight compiler plugins. KSP provides a simplified compiler plugin API that leverages the power of Kotlin while keeping the learning curve at a minimum. Compared to KAPT, annotation processors that use KSP can run up to 2x faster.

Most of the documentation of KSP can be found on kotlinlang.org. Here are some handy links:

For debugging and testing processors, as well as KSP itself, please check DEVELOPMENT.md

Feedback and Bug Reporting

Please let us know what you think about KSP by filing a Github issue or connecting with our team in the #ksp channel in the Kotlin Slack workspace!

If you are interested in sending PRs, please also check out the Contributor guide.

Ongoing and Future Works

Here are some planned features that have not yet been completely implemented:

  • Support new Kotlin compiler
  • Improve support to multiplatform. E.g., running KSP on a subset of targets / sharing computations between targets
  • Improve performance. There are a bunch of optimizations to be done!
  • Make the IDE aware of the generated code
  • Keep fixing bugs!

ksp's People

Contributors

3flex avatar agolubevgo avatar alexbeggs avatar ansman avatar asemy avatar bencodes avatar bennyhuo avatar cdsap avatar danysantiago avatar davidjwiner avatar dsteve595 avatar edrd-f avatar elect86 avatar evant avatar gavra0 avatar jameskleeh avatar jeffset avatar jsjeon avatar k163377 avatar larryxiao625 avatar lukellmann avatar mars885 avatar natario1 avatar neetopia avatar ribafish avatar runningcode avatar ting-yuan avatar virelion avatar yigit avatar zacsweers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ksp's Issues

Model init blocks

Although they do not carry symbol information, local declarations inside them do.

trying to resolve an annotation's type from a Java class throws NPE

// full exception at the bottom

When I have a class like this:

@Entity
public class JavaEntity {
    @PrimaryKey
    public Long id;

    public String bookId;
}

When JavaEntity is obtained as a KsAnnotated, calling it.annotations.first().resolve() throws NPE.

To reproduce:

I think this is a KI but couldn't find the bug hence filing. I can try to create an isolated repro if it helps.

e: java.lang.NullPointerException
        at org.jetbrains.kotlin.ksp.symbol.impl.java.KSClassDeclarationJavaImpl.asType(KSClassDeclarationJavaImpl.kt:115)
        at org.jetbrains.kotlin.ksp.symbol.impl.java.KSAnnotationJavaImpl$annotationType$2.invoke(KSAnnotationJavaImpl.kt:23)
        at org.jetbrains.kotlin.ksp.symbol.impl.java.KSAnnotationJavaImpl$annotationType$2.invoke(KSAnnotationJavaImpl.kt:13)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at org.jetbrains.kotlin.ksp.symbol.impl.java.KSAnnotationJavaImpl.getAnnotationType(KSAnnotationJavaImpl.kt)
        at androidx.room.compiler.ksp.KspUtilKt.hasAnnotation(KspUtil.kt:16)
        at androidx.room.compiler.ksp.KspElement.hasAnnotation(KspElement.kt:33)
        at androidx.room.compiler.usp.UElement$DefaultImpls.hasAnyOf(UElement.kt:17)
        at androidx.room.compiler.ksp.KspElement.hasAnyOf(KspElement.kt:10)
        at androidx.room.processor.EntityProcessorKt.EntityProcessor(EntityProcessor.kt:102)
        at androidx.room.processor.EntityProcessorKt.EntityProcessor$default(EntityProcessor.kt:99)
        at androidx.room.processor.DatabaseProcessor.processEntities(DatabaseProcessor.kt:282)
        at androidx.room.processor.DatabaseProcessor.doProcess(DatabaseProcessor.kt:62)
        at androidx.room.processor.DatabaseProcessor.process(DatabaseProcessor.kt:53)
        at androidx.room.RoomKspProcessor.process(RoomKspProcessor.kt:50)
        at org.jetbrains.kotlin.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:63)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:114)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:91)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:560)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:83)
        at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:551)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:178)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:164)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:86)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:105)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:83)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execAndOutputXml(CLICompiler.kt:50)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileInProcessImpl(GradleKotlinCompilerWork.kt:350)
        at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.access$compileInProcessImpl(GradleKotlinCompilerWork.kt:66)
        at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork$compileInProcess$future$1.call(GradleKotlinCompilerWork.kt:318)
        at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork$compileInProcess$future$1.call(GradleKotlinCompilerWork.kt:66)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
        at java.base/java.lang.Thread.run(Thread.java:834)

Add helper for equivlent of Types.asMemeberOf

When writing an annotation processor you can use Types.asMemeberOf() to resolve the concrete type of a method based on the enclosing class. Ex if you had:

interface GenericInterface<T> {
  bar(): T
}

abstract class Foo : GenericInterface<String> {
}

you could get that bar() in the context of Foo returns String.

Helpers around implicit elements

Here are examples by Evan Tatarka:

class Foo --> primaryConstructor is null
class Bar() -> primaryConstructor is not null

Another example is that properties of interfaces are implicitly abstract.

Currently, KSP models program after its grammar structure and does not expose those implicit elements. We need helpers to unify the behaviors of explicit and implicit elements.

Resolution for local declarations

Currently, the implementation doesn't support resolving references to local declarations.

Got some directions from JB:
https://jetbrains.slack.com/archives/CHLKDT4Q6/p1582675225005300

Approach 1. Resolve the body of the inner most, non-local function and look up the elements. This is sub-optimal as it resolves everything in the body.

Approach 2. On demand build the scope hierarchy should be the optimal way, although it doesn't help in the worst case described below.

Performance note:
Because all local variables are modeled as local properties, the total number of accessible elements are much more than kapt. Resolving them in TestProcessor would be slowed down considerably (~2x slower). If only local function and local classes are resolved, the slow down is ~10% in tachiyomi.

Simple string member is always null in `20200716`

This appears to be a regression from 20200626 to 20200716

Repro case in this PR: ZacSweers/MoshiX#25. Run ./gradlew :sample:compileKotlin

In KspUtil.kt, there is a helper getMember() function that gets an annotation member's value by name. That PR uses this from MoshiSealedSymbolProcessor to look up the value of a generator member. It does find the member, but now its string value is always null.

internal inline fun <reified T> KSAnnotation.getMember(name: String): T {
  val matchingArg = arguments.find { it.name?.asString() == name }
      ?: error("No member name found for '$name'. All arguments: ${arguments.map { it.name?.asString() }}")

  // NOTE fails here
  return matchingArg.value as? T ?: error("No value found for $name. Was ${matchingArg.value}")
}
e: java.lang.IllegalStateException: No value found for generator. Was null
	at dev.zacsweers.moshisealed.codegen.MoshiSealedSymbolProcessor.process(MoshiSealedSymbolProcessor.kt:179)
	at org.jetbrains.kotlin.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:76)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:114)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:91)

Annotation values that are default are not visible in KSP

When reading a Java annotation with default value, its value is not visible in the arguments list.

public @interface JavaAnnotation {
    String debug();
    String withDefaultValue()  default "";
}

If you annotate an element with JavaAnnotation and don't specify a withDefaultValue property, KSP processor threats it as not existing such that arguments map in KSAnnotation will only include debug field.

Adjustments to ClassKind

Needs entries for annotation, enum entry, etc.

KSEnumEntryDeclaration seems unnecessary then.

Files are always generated to `src/main` even if they are not source files

I'm testing KSP partially with an AutoService implementation of it, which involves generating service files under META-INF/services/.... Due to the hardcoded src/main path, I have to work around it by manually including it via sourceSets

sourceSets {
  main {
    resources {
      srcDir(file("build/generated/ksp/src/main"))
    }
  }
}

compileOnly dependencies are not visible to ksp

In annotation processing, a common pattern is to use compileOnly to put annotations artifacts on the classpath for the processor. With ksp, I've noticed I always have to put these as implementation or api dependencies.

Repro PR: ZacSweers/MoshiX#25

Change the autoServiceAnnotations dep in /moshi-sealed-codegen/build.gradle to compileOnly and run a simple build, the processor will fail because it cannot find the AutoService class in its processor (AutoServiceSymbolProcessor.kt).

$ gw clean :moshi-sealed-codegen:build

> Task :moshi-sealed-codegen:compileTestKotlin FAILED
e: java.lang.IllegalStateException: @AutoService type not found on the classpath.
        at dev.zacsweers.auto.service.ksp.AutoServiceSymbolProcessor.process(AutoServiceSymbolProcessor.kt:65)
        at org.jetbrains.kotlin.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:76)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:114)
        at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:91)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:560)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:83)
        at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:551)
        at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:178)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:164)
        at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:86)
        at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
        at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:105)

Model Java modifiers

Java modifiers have different semantics to Kotlin modifiers. It's better to have a separate set of modifiers for Java. android/kotlin-for-ksp#9 depends on this.

overrides check with a protected java methods throws IllegalStateException

override-bug.zip

See attached sample for repro (simply run its tests)

If I have a java class like this:

public class TestClass {
  protected void test() {}
}

And try to check if test overrides Any.equals, it throws IllegalStateException.

// processor
        override fun process(resolver: Resolver) {
            val testClass = resolver.getClassDeclarationByName(
                resolver.getKSNameFromString("TestClass")
            )!!
            val any = resolver.getClassDeclarationByName(
                resolver.getKSNameFromString("kotlin.Any")
            )!!
            val equalsMethod = any.getAllFunctions().first {
                it.simpleName.asString() == "equals"
            }
            val testMethod = testClass.getDeclaredFunctions().first {
                it.simpleName.asString() == "test"
            }
            check(testMethod.overrides(equalsMethod) == false) { // << throws
                "should not override"
            }
        }
e: java.lang.IllegalStateException: unhandled visibility: protected/*protected and package*/
	at org.jetbrains.kotlin.ksp.symbol.impl.UtilsKt.toKSModifiers(utils.kt:122)
	at org.jetbrains.kotlin.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl$modifiers$2.invoke(KSFunctionDeclarationDescriptorImpl.kt:101)
	at org.jetbrains.kotlin.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl$modifiers$2.invoke(KSFunctionDeclarationDescriptorImpl.kt:22)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at org.jetbrains.kotlin.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl.getModifiers(KSFunctionDeclarationDescriptorImpl.kt)
	at org.jetbrains.kotlin.ksp.symbol.impl.binary.KSFunctionDeclarationDescriptorImpl.overrides(KSFunctionDeclarationDescriptorImpl.kt:47)

API to check if a function has default implementation

As far as I can see, there is no way to check if a KSFunctionDeclaration has a default implementation.

As Room generates Java code, when creating the implementation for a Dao interface, we have to generate the implementation that delegates to the default implementation so this is necessary for room.

Even if Room was generating kotlin code, we would still need this functionality as we have to print an error if a function in dao is not annotated properly. For that case, interface methods with default implementations are OK.

e.g.

@Dao
interface MyDao {
    @Query("select * from foo")
    fun goodQuery():List<Foo>

   fun okMethod() {
       
   }

   @Transaction
   fun transactionMethod() {
        // room will override this to be called inside a database transaction
   }

   // method without implementation nor any room annotations so we need to throw an error
   fun badMethod()
    
}

Debugging support

This is less of a feature request and more just sharing this in case anyone else is interested.

In general, I'd recommend writing up an example of debugging/testing story, perhaps a demo or contributing support to @tschuchortdev's https://github.com/tschuchortdev/kotlin-compile-testing project.

In the meantime, I've found success attaching the debugger manually from the Gradle invocation.

Example: ./gradlew :sample:build --no-daemon -Dorg.gradle.debug=true -Pkotlin.compiler.execution.strategy=in-process. Then connect via remote debugger in the IDE and you can hit your processor's breakpoints.

image

Flaky test suite

testJavaModifiers is failing when running the whole test suite, but passing for running single test case. Possibly due to compiled Java classes from other tests not cleared after test case.

KSP sample project (20200716) failed to build on Windows

Relavent log:

PS C:\Users\jiaxiang\Downloads\playground-1.4-M1-dev-experimental-20200716> .\gradlew.bat build --stacktrace

> Task :workload:compileKotlin FAILED
e: C:\Users\jiaxiang\Downloads\playground-1.4-M1-dev-experimental-20200716\workload\src\main\java\com\example\A.kt: (3, 8): Unresolved reference: HELLO
e: C:\Users\jiaxiang\Downloads\playground-1.4-M1-dev-experimental-20200716\workload\src\main\java\com\example\A.kt: (6, 17): Unresolved reference: HELLO
e: C:\Users\jiaxiang\Downloads\playground-1.4-M1-dev-experimental-20200716\workload\src\main\java\com\example\A.kt: (9, 19): Unresolved reference: AClassBuilder

FAILURE: Build failed with an exception.

It looks that generated source is not included in normal build.

exception in ksp compilation

I've hit an exception in compiler but cannot reproduce in a sample.

org.jetbrains.kotlin.utils.KotlinExceptionWithAttachments: Exception while analyzing expression at (339,28) in /home/yboyar/src/androidx-master-dev/frameworks/support/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt

full stacktrace

It happens when I try to resolve the return type of a function actual code:

@Transaction
fun getDefaultBook() = getBook("DEFAULT_ID")

@Query("SELECT * FROM book WHERE bookId = :bookId")
fun getBook(bookId: String): Book

It happens when I call funDeclaration.returnType?.resolve(). Here, funDeclaration is the KSFunctionDeclaration of the getDefaultBook method.

I can help reproduce within androidx but unfortunately my efforts to isolate it didn't work :/.

Unable to get the typealias after resolving a reference

When resolving a reference to a type alias, the underlying type is returned instead of the alias. This makes type checking much easier. However, the type alias is skipped and cannot be obtained. Something like KSType.alias: KSTypeAlias? is needed.

KSName doesn't offer a deterministic naming API

KSName isn't ideal for handling non-trivial class names, such as heavily nested types. KotlinPoet's ClassName.bestGuess() API is used in the Glide example project, but that API should really only be used as a last resort.

In Kotlin's internals and metadata, it has a pattern for its names that is deterministic (e.g. "org/foo/bar/Baz.Nested"). It would be ideal if this name was exposed or otherwise surfaced a more concrete API.

Here's an implementation we have in KotlinPoet for piecing together a name from it: https://github.com/square/kotlinpoet/blob/06aad8e024aaa453477d420ad65cfe4af9e84f3a/kotlinpoet-metadata-specs/src/main/kotlin/com/squareup/kotlinpoet/metadata/specs/internal/ClassInspectorUtil.kt#L171-L209

Example of where KSName currently falls short:

  • Given this name: dev.zacsweers.moshisealed.sample.test.MessageTest.MessageWithNullDefault
  • The result of getQualifier is dev.zacsweers.moshisealed.sample.test.MessageTest ๐Ÿค”

API to check if property has a default implmenntation

This is similar to android/kotlin-for-ksp#49, the same thing applies to properties as well. Right know you can't distinguish between:

interface Foo {
  val bar: Int get() = 1
}

and

interface Foo {
   val bar: Int
}

Adding isAbstract to KSPropertyDeclaration as well should work.

Add overrides function to KSPropertyDeclaration

KSFunctionDeclaration has an overrides function to check if the function overrides another function. Properties can override too so it makes sense to have the same function. ex:

interface Foo {
    val foo: String
}

class Bar : Foo {
    override val foo: String = ""
}

IDE not indexing generated source

Looks like that kapt has an IDE plugin to achieve this: KaptProjectResolverExtension.kt

We either need to copy it over, or simply tell users to mark generated source root themselves in the preview.

Changes in symbol processor does not invalidate compilation

Seems like dependencies in the ksp classpath do not affect the task invalidation for the project compilation.

e.g. if you have
:app, :processor modules in a project where processor is a ksp configuration dependency

// app's gradle file

ksp(project(":processor"))

Changes in the processor project do not invalidate the compilation for app unless i invoke clean.

To reproduce,

  • compile the whole project
./gradlew :app:build --info

make changes in the processor

./gradlew :app:build --info

processor will be recompiled but app is not recompiled.

Feature request: Logging APIs

Similar to how annotation processing has Messager APIs, this would make communicating information to the user easy.

Caching for KSTypeReferenceJavaImpl is not working correctly

Reproduce:

class A {
public List<? extends Set> extendsSetFun() {}

public List<? extends List> extendsListFun() {}

}

Two functions' return type will be same KSTypeReferenceJavaImpl, despite difference in type argument. My guess is PsiType didn't calculate hash based on type argument.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.