github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/bind/java/Seq.java (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package go;
     6  
     7  import java.util.Arrays;
     8  import java.util.concurrent.ExecutorService;
     9  import java.util.concurrent.Executors;
    10  import java.util.logging.Logger;
    11  
    12  // Seq is a sequence of machine-dependent encoded values.
    13  // Used by automatically generated language bindings to talk to Go.
    14  public class Seq {
    15  	private static Logger log = Logger.getLogger("GoSeq");
    16  
    17  	static {
    18  		// Look for the shim class auto-generated by gomobile bind.
    19  		// Its only purpose is to call System.loadLibrary.
    20  		try {
    21  			Class loadJNI = Class.forName("go.LoadJNI");
    22  			setContext(loadJNI.getDeclaredField("ctx").get(null));
    23  		} catch (ClassNotFoundException e) {
    24  			// Ignore, assume the user will load JNI for it.
    25  			log.warning("LoadJNI class not found");
    26  		} catch (NoSuchFieldException e) {
    27  			log.severe("LoadJNI class missing field: " + e);
    28  		} catch (IllegalAccessException e) {
    29  			log.severe("LoadJNI class bad field: " + e);
    30  		}
    31  
    32  		initSeq();
    33  		new Thread("GoSeq") {
    34  			public void run() { Seq.receive(); }
    35  		}.start();
    36  	}
    37  
    38  	@SuppressWarnings("UnusedDeclaration")
    39  	private long memptr; // holds C-allocated pointer
    40  
    41  	public Seq() {
    42  		ensure(64);
    43  	}
    44  
    45  	// ctx is an android.context.Context.
    46  	static native void setContext(java.lang.Object ctx);
    47  
    48  	// Ensure that at least size bytes can be written to the Seq.
    49  	// Any existing data in the buffer is preserved.
    50  	public native void ensure(int size);
    51  
    52  	// Moves the internal buffer offset back to zero.
    53  	// Length and contents are maintained. Data can be read after a reset.
    54  	public native void resetOffset();
    55  
    56  	public native void log(String label);
    57  
    58  	public native boolean readBool();
    59  	public native byte readInt8();
    60  	public native short readInt16();
    61  	public native int readInt32();
    62  	public native long readInt64();
    63  	public long readInt() { return readInt64(); }
    64  
    65  	public native float readFloat32();
    66  	public native double readFloat64();
    67  	public native String readUTF16();
    68  	public String readString() { return readUTF16(); }
    69  	public native byte[] readByteArray();
    70  
    71  	public native void writeBool(boolean v);
    72  	public native void writeInt8(byte v);
    73  	public native void writeInt16(short v);
    74  	public native void writeInt32(int v);
    75  	public native void writeInt64(long v);
    76  	public void writeInt(long v) { writeInt64(v); }
    77  
    78  	public native void writeFloat32(float v);
    79  	public native void writeFloat64(double v);
    80  	public native void writeUTF16(String v);
    81  	public void writeString(String v) { writeUTF16(v); }
    82  	public native void writeByteArray(byte[] v);
    83  
    84  	public void writeRef(Ref ref) {
    85  		tracker.inc(ref);
    86  		writeInt32(ref.refnum);
    87  	}
    88  
    89  	public Ref readRef() {
    90  		int refnum = readInt32();
    91  		return tracker.get(refnum);
    92  	}
    93  
    94  	static native void initSeq();
    95  
    96  	// Informs the Go ref tracker that Java is done with this ref.
    97  	static native void destroyRef(int refnum);
    98  
    99  	// createRef creates a Ref to a Java object.
   100  	public static Ref createRef(Seq.Object o) {
   101  		return tracker.createRef(o);
   102  	}
   103  
   104  	// sends a function invocation request to Go.
   105  	//
   106  	// Blocks until the function completes.
   107  	// If the request is for a method, the first element in src is
   108  	// a Ref to the receiver.
   109  	public static native void send(String descriptor, int code, Seq src, Seq dst);
   110  
   111  	// recv returns the next request from Go for a Java call.
   112  	static native void recv(Seq in, Receive params);
   113  
   114  	// recvRes sends the result of a Java call back to Go.
   115  	static native void recvRes(int handle, Seq out);
   116  
   117  	static final class Receive {
   118  		int refnum;
   119  		int code;
   120  		int handle;
   121  	}
   122  
   123  	protected void finalize() throws Throwable {
   124  		super.finalize();
   125  		free();
   126  	}
   127  	private native void free();
   128  
   129  	private static final ExecutorService receivePool = Executors.newCachedThreadPool();
   130  
   131  	// receive listens for callback requests from Go, invokes them on a thread
   132  	// pool and sends the responses.
   133  	public static void receive() {
   134  		Seq.Receive params = new Seq.Receive();
   135  		while (true) {
   136  			final Seq in = new Seq();
   137  			Seq.recv(in, params);
   138  
   139  			final int code = params.code;
   140  			final int handle = params.handle;
   141  			final int refnum = params.refnum;
   142  
   143  			if (code == -1) {
   144  				// Special signal from seq.FinalizeRef.
   145  				tracker.dec(refnum);
   146  				Seq out = new Seq();
   147  				Seq.recvRes(handle, out);
   148  				continue;
   149  			}
   150  
   151  			receivePool.execute(new Runnable() {
   152  				public void run() {
   153  					Ref r = tracker.get(refnum);
   154  					Seq out = new Seq();
   155  					r.obj.call(code, in, out);
   156  					Seq.recvRes(handle, out);
   157  				}
   158  			});
   159  		}
   160  	}
   161  
   162  	// An Object is a Java object that matches a Go object.
   163  	// The implementation of the object may be in either Java or Go,
   164  	// with a proxy instance in the other language passing calls
   165  	// through to the other language.
   166  	//
   167  	// Don't implement an Object directly. Instead, look for the
   168  	// generated abstract Stub.
   169  	public interface Object {
   170  		public Ref ref();
   171  		public void call(int code, Seq in, Seq out);
   172  	}
   173  
   174  	// A Ref is an object tagged with an integer for passing back and
   175  	// forth across the language boundary.
   176  	//
   177  	// A Ref may represent either an instance of a Java Object subclass,
   178  	// or an instance of a Go object. The explicit allocation of a Ref
   179  	// is used to pin Go object instances when they are passed to Java.
   180  	// The Go Seq library maintains a reference to the instance in a map
   181  	// keyed by the Ref number. When the JVM calls finalize, we ask Go
   182  	// to clear the entry in the map.
   183  	public static final class Ref {
   184  		// refnum < 0: Go object tracked by Java
   185  		// refnum > 0: Java object tracked by Go
   186  		int refnum;
   187  
   188  		int refcnt;  // for Java obj: track how many times sent to Go.
   189  
   190  		public Seq.Object obj;  // for Java obj: pointers to the Java obj.
   191  
   192  		private Ref(int refnum, Seq.Object o) {
   193  			this.refnum = refnum;
   194  			this.refcnt = 0;
   195  			this.obj = o;
   196  		}
   197  
   198  		@Override
   199  		protected void finalize() throws Throwable {
   200  			if (refnum < 0) {
   201  				// Go object: signal Go to decrement the reference count.
   202  				Seq.destroyRef(refnum);
   203  			}
   204  			super.finalize();
   205  		}
   206  	}
   207  
   208  	static final RefTracker tracker = new RefTracker();
   209  
   210  	static final class RefTracker {
   211  		private static final int REF_OFFSET = 42;
   212  
   213  		// use single Ref for null Seq.Object
   214  		private static final Ref nullRef = new Ref(REF_OFFSET - 1, null);
   215  
   216  		// Next Java object reference number.
   217  		//
   218  		// Reference numbers are positive for Java objects,
   219  		// and start, arbitrarily at a different offset to Go
   220  		// to make debugging by reading Seq hex a little easier.
   221  		private int next = REF_OFFSET; // next Java object ref
   222  
   223  		// Java objects that have been passed to Go. refnum -> Ref
   224  		// The Ref obj field is non-null.
   225  		// This map pins Java objects so they don't get GCed while the
   226  		// only reference to them is held by Go code.
   227  		private RefMap javaObjs = new RefMap();
   228  
   229  		// inc increments the reference count of a Java object when it
   230  		// is sent to Go.
   231  		synchronized void inc(Ref ref) {
   232  			int refnum = ref.refnum;
   233  			if (refnum <= 0) {
   234  				// We don't keep track of the Go object.
   235  				return;
   236  			}
   237  			if (refnum == nullRef.refnum) {
   238  				return;
   239  			}
   240  			// Count how many times this ref's Java object is passed to Go.
   241  			if (ref.refcnt == Integer.MAX_VALUE) {
   242  				throw new RuntimeException("refnum " + refnum + " overflow");
   243  			}
   244  			ref.refcnt++;
   245  			Ref obj = javaObjs.get(refnum);
   246  			if (obj == null) {
   247  				javaObjs.put(refnum, ref);
   248  			}
   249  		}
   250  
   251  		// dec decrements the reference count of a Java object when
   252  		// Go signals a corresponding proxy object is finalized.
   253  		// If the count reaches zero, the Java object is removed
   254  		// from the javaObjs map.
   255  		synchronized void dec(int refnum) {
   256  			if (refnum <= 0) {
   257  				// We don't keep track of the Go object.
   258  				// This must not happen.
   259  				log.severe("dec request for Go object "+ refnum);
   260  				return;
   261  			}
   262  			if (refnum == nullRef.refnum) {
   263  				return;
   264  			}
   265  			// Java objects are removed on request of Go.
   266  			Ref obj = javaObjs.get(refnum);
   267  			if (obj == null) {
   268  				throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
   269  			}
   270  			obj.refcnt--;
   271  			if (obj.refcnt <= 0) {
   272  				javaObjs.remove(refnum);
   273  			}
   274  		}
   275  
   276  		synchronized Ref createRef(Seq.Object o) {
   277  			if (o == null) {
   278  				return nullRef;
   279  			}
   280  			if (next == Integer.MAX_VALUE) {
   281  				throw new RuntimeException("createRef overflow for " + o);
   282  			}
   283  			int refnum = next++;
   284  			Ref ref = new Ref(refnum, o);
   285  			javaObjs.put(refnum, ref);
   286  			return ref;
   287  		}
   288  
   289  		// get returns an existing Ref to either a Java or Go object.
   290  		// It may be the first time we have seen the Go object.
   291  		//
   292  		// TODO(crawshaw): We could cut down allocations for frequently
   293  		// sent Go objects by maintaining a map to weak references. This
   294  		// however, would require allocating two objects per reference
   295  		// instead of one. It also introduces weak references, the bane
   296  		// of any Java debugging session.
   297  		//
   298  		// When we have real code, examine the tradeoffs.
   299  		synchronized Ref get(int refnum) {
   300  			if (refnum > 0) {
   301  				if (refnum == nullRef.refnum) {
   302  					return nullRef;
   303  				}
   304  				Ref ref = javaObjs.get(refnum);
   305  				if (ref == null) {
   306  					throw new RuntimeException("unknown java Ref: "+refnum);
   307  				}
   308  				return ref;
   309  			} else {
   310  				// Go object.
   311  				return new Ref(refnum, null);
   312  			}
   313  		}
   314  	}
   315  
   316  	// RefMap is a mapping of integers to Ref objects.
   317  	//
   318  	// The integers can be sparse. In Go this would be a map[int]*Ref.
   319  	private static final class RefMap {
   320  		private int next = 0;
   321  		private int live = 0;
   322  		private int[] keys = new int[16];
   323  		private Ref[] objs = new Ref[16];
   324  
   325  		RefMap() {}
   326  
   327  		Ref get(int key) {
   328  			int i = Arrays.binarySearch(keys, 0, next, key);
   329  			if (i >= 0) {
   330  				return objs[i];
   331  			}
   332  			return null;
   333  		}
   334  
   335  		void remove(int key) {
   336  			int i = Arrays.binarySearch(keys, 0, next, key);
   337  			if (i >= 0) {
   338  				if (objs[i] != null) {
   339  					objs[i] = null;
   340  					live--;
   341  				}
   342  			}
   343  		}
   344  
   345  		void put(int key, Ref obj) {
   346  			if (obj == null) {
   347  				throw new RuntimeException("put a null ref (with key "+key+")");
   348  			}
   349  			int i = Arrays.binarySearch(keys, 0, next, key);
   350  			if (i >= 0) {
   351  				if (objs[i] == null) {
   352  					objs[i] = obj;
   353  				}
   354  				if (objs[i] != obj) {
   355  					throw new RuntimeException("replacing an existing ref (with key "+key+")");
   356  				}
   357  				return;
   358  			}
   359  			if (next >= keys.length) {
   360  				grow();
   361  				i = Arrays.binarySearch(keys, 0, next, key);
   362  			}
   363  			i = ~i;
   364  			if (i < next) {
   365  				// Insert, shift everything afterwards down.
   366  				System.arraycopy(keys, i, keys, i+1, next-i);
   367  				System.arraycopy(objs, i, objs, i+1, next-i);
   368  			}
   369  			keys[i] = key;
   370  			objs[i] = obj;
   371  			live++;
   372  			next++;
   373  		}
   374  
   375                  private void grow() {
   376  			// Compact and (if necessary) grow backing store.
   377  			int[] newKeys;
   378  			Ref[] newObjs;
   379  			int len = 2*roundPow2(live);
   380  			if (len > keys.length) {
   381  				newKeys = new int[keys.length*2];
   382  				newObjs = new Ref[objs.length*2];
   383  			} else {
   384  				newKeys = keys;
   385  				newObjs = objs;
   386  			}
   387  
   388  			int j = 0;
   389  			for (int i = 0; i < keys.length; i++) {
   390  				if (objs[i] != null) {
   391  					newKeys[j] = keys[i];
   392  					newObjs[j] = objs[i];
   393  					j++;
   394  				}
   395  			}
   396  			for (int i = j; i < newKeys.length; i++) {
   397  				newKeys[i] = 0;
   398  				newObjs[i] = null;
   399  			}
   400  
   401  			keys = newKeys;
   402  			objs = newObjs;
   403  			next = j;
   404  
   405  			if (live != next) {
   406  				throw new RuntimeException("bad state: live="+live+", next="+next);
   407  			}
   408                  }
   409  
   410  		private static int roundPow2(int x) {
   411  			int p = 1;
   412  			while (p < x) {
   413  				p *= 2;
   414  			}
   415  			return p;
   416  		}
   417  	}
   418  }