Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split jsontext out as a separate package (#300)
This has long been discussed as an action item. The "jsontext" package deals with JSON text at the syntactic layer. Use of it avoids any dependencies on the "reflect" package and in general carries a much shallower dependency tree. However, it still must depend on "unicode", which carries a non-trivial amount of bloat. There is no change to behavior, just massive shuffling of code. High-level migration approach: 1. Move all standalone functions in encode.go and decode.go to an internal "jsonwire" package, which performs stateless operations dealing with JSON text. For example, this includes all consumeXXX, parseXXX, appendXXX, and trimXXX functions, which then exported for use by other packages in this module. 2. Move escape.go to "jsonwire" since string encoding depends on that data structure, and export EscapeRunes. 3. Move remainder of encode.go and decode.go, and the entirety of state.go, token.go, value.go, pools.go, quote.go, and coder_options.go over to "jsontext". 4. Split errors.go apart such that SemanticError stays in "json" and SyntacticError goes over to "jsontext". 5. Split doc.go apart; half in "json", other half in "jsontext". 6. The "jsonwire" package should return SyntacticError types, but cannot since that type is declared in "jsontext", and we need "jsontext" to depend on "jsonwire". We could move SyntacticError to "jsonwire" and use a type alias, but that messes up GoDoc representation of the error type. For the time being, rely on reverse dependency injection (see jsonwire.NewError) to have it produce SyntacticErrors. It will be a future change to clean this up. 7. Move jsontext/text.go over to the "json" package, and reverse the alias directions so that the declarations in "jsontext" continue to exist in the "json" package. We will delete these in a follow-up commit. 8. At this point, "jsontext" builds and the tests pass. However, "json" is completely broken since it no longer has access to internal functionality in the Encoder/Decoder types. In theory, "json" could be implemented purely in terms of the public API of "jsontext", but at a performance cost. Since we know we already generate valid JSON, we can circumvent a number of correctness checks in "jsontext". To overcome this problem, we move the struct representation of Encoder/Decoder to unexported encoderState/decoderState types, and nest an encoderState/decoderState within Encoder/Decoder. Most fields and methods used by "json" are exported. This transitively includes exporting methods on the unexported state machine types declared in state.go. Note that these are not directly callable by external users because the type itself is still unexported. 9. Add export.go to "jsontext" where we expose a public Internal variable with an Export method that returns an unexported type with exported methods that converts a *jsontext.Encoder into an *jsontext.encoderState (and similar for Decoder). This gives "json" the power to now interact with the internal implementation of "jsontext" as it can freely call the exported methods of *jsontext.encoderState. While the jsontext.Internal variable is publicly visible, it cannot be used by external users since it requires you to call it with a variable that can only be referenced if you also have access to the internal package. Note that the "correct" way to do all this is to implement the entirety of "jsontext" in an internal package, and only expose the public parts in a public "jsontext" package through the use of type aliases. However, this approach results in a terrible GoDoc experience since all of the methods of a aliased types get hidden. Perhaps future improvements to GoDoc will make this better, but until that day, this is a working approach that balances both having clean documentation in GoDoc and also preventing external users from touching internal implementation details. We can always switch to the "correct" way in the future as we clearly document that the jsontext.Internal variable is exempt from the Go compatibility agreement. There is a 5% performance slow down due to this change. It is future work to investigate and regain the lost performance. In theory, the compiler should be able to produce the exact same code (setting aside different reflect data structures).
- Loading branch information