Java Puzzle 10: No Dot - Solution

Sep 13, 2019

A couple days ago, I sent out this challenge:

Here's some more details on the solution.

But first: Why does the puzzle disallow those characters exactly?

  • The dot because that's the point of this challenge. We use them so much, it's refreshing to imagine life without them.
  • The backslash because otherwise you'd just sneak in some dots as \u unicode escapes, like System\u002Eout\u002Eprintln("Hello world")
  • The semicolon because it looks weird. Where did you get the crazy idea to put a dot on top of a comma anyways?

Syntactic tricks

How do you even invoke any method without using a dot?
For starters, you can invoke constructors, like new Thread(). Inside of a class, you also don't need to write this. explicitly to invoke a method on the same class or instance, so you can write for example class X extends Thread { X() {/* call methods on Thread here, without using a dot */} }. A shorter way to that is with an anonymous inner class, and using a naked {} block to define an initializer: new Thread(){{/* code goes here */}}. Since this requires making a subclass, you can't use this e.g. on final classes. And without using a dot, we can only refer to classes in the current package (the default package here) and java.lang. This is rather restrictive, but it's a start.

How do you write a statement without ending with a semicolon?
Block constructs like if and while do not include a semicolon, e.g. if (new Thread()==null){} is a complete statement that does not include a semicolon. This only works for expressions, so you cannot invoke a void method like this.

One more trick
Method references can be used to refer to methods, without using a dot. Note that method references (just like lambdas) can get converted to the type of an interface that has exactly one non-abstract method, if that method references matches the signature of that method. So we can e.g. do Runnable run = new Thread()::start. But that doesn't actually run that method yet. For that, we abuse try-with-resources. A try-with-resources block takes an instance of AutoClosable, and executes its close() method at the end of the block. This way we can invoke any method without arguments, without needing to make a subclass.

For example, this starts a new thread: try(AutoCloseable o = new Thread()::start){}. The Thread.start method becomes the implementation of the close method of the AutoCloseable interface. Even though in the AutoCloseable interface the close method is void, method references with a non-void return type can be converted to it too, but unfortunately there is no way to capture their return value.

Solution 1: printStackTrace

The simplest way to print something out under these restrictions, is by using the fact that throwables are printed to stderr. You can't write a throw statement without using a semicolon, but you can directly call the Throwable.printStackTrace method.
To avoid outputting more than one line, we can use the 4-argument constructor of Throwable that takes a boolean writableStackTrace, that suppresses the generation of the stacktrace.
The output of printStackTrace starts with the name of the throwable (like java.lang.Throwable: your message) and we only want to visibly print "Hello World", so we also need to hide that classname. We can start our message with \r (ascii 13) to move back to the beginning of the line and overwrite the name of the class in the output. Putting that together gives us:

class NoDot {
  public static void main(String[] args) throws Exception {
   try (AutoCloseable o = new Throwable(((char)13)+"Hello world", null, true, false){}::printStackTrace) {}
  }
}

Tada! Hello world without dot, backslash or semicolon.

This works and the output looks right, but it doesn't feel entirely satisfying. It's printing garbage that it then hides, and it uses stderr instead of stdout. Hello world should act like System.out.println("Hello world"), not like System.err.println("Garbage\rHello World"). We can do better.

A curious dead end

Here's another technique to invoke some method references. We can turn an interface that has multiple methods into a functional interface (one where a method reference can be converted into) by providing default implementations for all but one of its methods. Then, we can invoke that method from that default implementation. This avoids the need for a dot because in those default interface implementations this refers to the method reference. Here we'll do that trick with the Appendable interface, and also extend Closeable because we know how invoke that. This is not a complete solution, because this doesn't include a way to access System.out or System.err without needing an import of java.io.PrintStream.

import java.io.PrintStream; // <- dots, this is not a real solution.

class NoDot {
  interface A extends Appendable, AutoCloseable {
    @Override default Appendable append(CharSequence csq, int start, int end) {
      // avoid the need for a return statement that requires a semicolon by never returning
      while(true){}
    }

    @Override default Appendable append(char c) {while(true){}}

    // not overriding this one, leaving it abstract:
    // @Override Appendable append(CharSequence csq) throws IOException

    @Override default void close() throws Exception {
      if (append("Hello World") == null) {}
    }
  }

  public static void main(String[] args) throws Exception {
    try (AutoCloseable a = new Throwable(){
      // steal System err
      @Override public void printStackTrace(PrintStream s) {
        try (A aaa = s::append) {} catch (Exception e) {}
      }
    }::printStackTrace) {}
  }
}

Solution 2: defineClass

The final solution uses the Classload.defineClass method, that takes bytecode as an argument, and it returns that as a java.lang.Class instance. We can pass in a byte[] that contains the contents of a .class file that calls System.out.println("Hello world"). The defineClass call doesn't execute any of the code of the class yet, but a call to Class.newInstance() after it takes care of that:

@SuppressWarnings("deprecation") // newInstance is deprecated
class NoDot {
  public static void main(String[] args) throws Exception {
    if(new ClassLoader(){{
      try(AutoCloseable o = defineClass("A", new byte[]{
-54,-2,-70,-66,0,3,0,45,0,25,12,0,20,0,24,1,0,16,106,97,118,97,47,108,97,110,103,47,79,98,
106,101,99,116,1,0,6,60,105,110,105,116,62,7,0,2,12,0,3,0,8,7,0,13,1,0,5,112,114,105,110,
116,1,0,3,40,41,86,7,0,18,1,0,4,67,111,100,101,9,0,9,0,1,1,0,10,83,111,117,114,99,101,70,
105,108,101,1,0,19,106,97,118,97,47,105,111,47,80,114,105,110,116,83,116,114,101,97,109,
10,0,4,0,5,10,0,6,0,16,12,0,7,0,22,1,0,1,65,1,0,16,106,97,118,97,47,108,97,110,103,47,83,
121,115,116,101,109,1,0,12,72,101,108,108,111,32,119,111,114,108,100,10,1,0,3,111,117,116,
8,0,19,1,0,21,40,76,106,97,118,97,47,108,97,110,103,47,83,116,114,105,110,103,59,41,86,7,
0,17,1,0,21,76,106,97,118,97,47,105,111,47,80,114,105,110,116,83,116,114,101,97,109,59,0,
33,0,23,0,4,0,0,0,0,0,1,0,1,0,3,0,8,0,1,0,10,0,0,0,25,0,2,0,1,0,0,0,13,42,-73,0,14,-78,0,
11,18,21,-74,0,15,-79,0,0,0,0,0,1,0,12,0,0,0,2,0,17
        }, 0, 292)::newInstance){}
    }}==null){}
  }
}

I received two solutions to this puzzle, first in private from Garrett, and this tweet from tackline, both using defineClass. Of all my java puzzles so far, this might have been the hardest. Thanks for playing everyone, I hope you enjoyed it!