Revision: 6215 Author: j...@google.com Date: Fri Sep 25 11:00:02 2009 Log: Fix GC handling for HtmlUnit in development mode.
Patch by: jat Review by: amitmanjhi http://code.google.com/p/google-web-toolkit/source/detail?r=6215 Added: /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/RemoteObjectTable.java /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ServerObjectsTable.java /branches/farewellSwt/dev/oophm/test/com/google/gwt/dev/shell/RemoteObjectTableTest.java Deleted: /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ObjectsTable.java Modified: /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelClient.java /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java /branches/farewellSwt/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java /branches/farewellSwt/user/src/com/google/gwt/junit/RunStyle.java ======================================= --- /dev/null +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/RemoteObjectTable.java Fri Sep 25 11:00:02 2009 @@ -0,0 +1,142 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.shell; + +import com.google.gwt.dev.shell.BrowserChannel.RemoteObjectRef; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Keeps track of references to remote objects. When the objects are no longer + * needed, their ids are returned in {...@link #getRefIdsForCleanup()}. + * + * @param <T> subtype of RemoteObjectRef contained in this table + */ +public class RemoteObjectTable<T extends RemoteObjectRef> { + + /** + * This maps References to RemoteObjectRefs back to the original refId. + * Because we need the refId of the RemoteObjectRef after it's been + * garbage-collected, this state must be stored externally. + */ + private final Map<Reference<T>, Integer> idFromRemoteObject; + + /** + * This accumulates remote objects that are no longer referenced on this side + * of the channel. + */ + private final ReferenceQueue<T> refQueue; + + /** + * This map associates a remote object ID with a Reference to the + * RemoteObjectRef that currently represents that id. + */ + private final Map<Integer, Reference<T>> remoteObjectFromId; + + /** + * Create a new RemoteObjectTable. + */ + public RemoteObjectTable() { + refQueue = new ReferenceQueue<T>(); + remoteObjectFromId = new HashMap<Integer, Reference<T>>(); + idFromRemoteObject = new IdentityHashMap<Reference<T>, Integer>(); + } + + /** + * @return the set of remote object reference IDs that should be freed. + */ + public synchronized Set<Integer> getRefIdsForCleanup() { + // Access to these objects is inherently synchronous + Map<Integer, Reference<T>> objectMap = remoteObjectFromId; + Map<Reference<T>, Integer> refIdMap = idFromRemoteObject; + Set<Integer> toReturn = new HashSet<Integer>(); + + // Find all refIds associated with previous garbage collection cycles + Reference<? extends RemoteObjectRef> ref; + while ((ref = refQueue.poll()) != null) { + Integer i = refIdMap.remove(ref); + assert i != null; + toReturn.add(i); + } + + /* + * Check for liveness. This is necessary because the last reference to a + * RemoteObjectRef could have been cleared and a new reference to that refId + * created before this method has been called. + */ + for (Iterator<Integer> i = toReturn.iterator(); i.hasNext();) { + Integer refId = i.next(); + if (objectMap.containsKey(refId)) { + if (objectMap.get(refId).get() != null) { + i.remove(); + } else { + objectMap.remove(refId); + } + } + } + + return toReturn; + } + + /** + * Obtain the RemoteObjectRef that is currently in use to act as a proxy for + * the given remote object ID. + * + * @return the RemoteObjectRef or null if the ID is not currently in use + */ + public synchronized T getRemoteObjectRef(int refId) { + if (remoteObjectFromId.containsKey(refId)) { + Reference<T> ref = remoteObjectFromId.get(refId); + T toReturn = ref.get(); + if (toReturn != null) { + return toReturn; + } + } + return null; + } + + /** + * Check to see if this ID does not already exist. + * + * @param refId reference ID to check + * @return true if this ID is not currently in use + */ + public synchronized boolean isNewObjectId(int refId) { + return !remoteObjectFromId.containsKey(refId) + || (remoteObjectFromId.get(refId).get() == null); + } + + /** + * Store a remote object reference in the table. + * + * @param refId + * @param remoteObjectRef + */ + public synchronized void putRemoteObjectRef(int refId, + T remoteObjectRef) { + Reference<T> ref = new WeakReference<T>(remoteObjectRef, refQueue); + remoteObjectFromId.put(refId, ref); + idFromRemoteObject.put(ref, refId); + } +} ======================================= --- /dev/null +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ServerObjectsTable.java Fri Sep 25 11:00:02 2009 @@ -0,0 +1,80 @@ +/* + * Copyright 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.shell; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.TreeMap; + +/** + * A class that keeps track of Java objects which have been exposed to the other + * side and assigns unique ids. + */ +public class ServerObjectsTable { + + /** + * A type that records the next-available free id slot to use, without itself + * being a valid return type. + */ + private static class Tombstone { + private final int nextFree; + + public Tombstone(int nextFree) { + this.nextFree = nextFree; + } + } + + private int nextFree = -1; + private int nextId = 0; + private final Map<Integer, Object> objects = new TreeMap<Integer, Object>(); + private final Map<Object, Integer> refMap = new IdentityHashMap<Object, Integer>(); + + public int add(Object obj) { + int id = find(obj); + if (id >= 0) { + return id; + } + if (nextFree >= 0) { + id = nextFree; + nextFree = ((Tombstone) objects.get(id)).nextFree; + } else { + id = nextId++; + } + objects.put(id, obj); + refMap.put(obj, id); + return id; + } + + public int find(Object obj) { + Integer objId = refMap.get(obj); + return objId != null ? objId.intValue() : -1; + } + + public void free(int id) { + Object object = objects.get(id); + assert object != null : "Trying to free never-used id " + id; + assert !(object instanceof Tombstone) : "Duplicate free " + id; + refMap.remove(object); + objects.put(id, new Tombstone(nextFree)); + nextFree = id; + } + + public Object get(int id) { + Object toReturn = objects.get(id); + assert !(toReturn instanceof Tombstone) : id + " is not an active id"; + return toReturn; + } +} ======================================= --- /dev/null +++ /branches/farewellSwt/dev/oophm/test/com/google/gwt/dev/shell/RemoteObjectTableTest.java Fri Sep 25 11:00:02 2009 @@ -0,0 +1,71 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dev.shell; + +import com.google.gwt.dev.shell.BrowserChannel.RemoteObjectRef; + +import junit.framework.TestCase; + +import java.util.Set; + +/** + * Test RemoteObjectTable. + */ +public class RemoteObjectTableTest extends TestCase { + + /** + * Mock implementation of a RemoteObjectRef. + */ + public class MockRemoteObjectRef implements RemoteObjectRef { + + public final int refId; + + public MockRemoteObjectRef(int refId) { + this.refId = refId; + } + + public int getRefid() { + return refId; + } + } + + public void testFree() throws InterruptedException { + RemoteObjectTable<MockRemoteObjectRef> table = new RemoteObjectTable< + MockRemoteObjectRef>(); + MockRemoteObjectRef ref = new MockRemoteObjectRef(1); + table.putRemoteObjectRef(1, ref); + Set<Integer> freed = table.getRefIdsForCleanup(); + ensureGC(); + assertEquals(0, freed.size()); + ref = null; + ensureGC(); + freed = table.getRefIdsForCleanup(); + assertEquals(1, freed.size()); + } + + /** + * This method attempts to ensure that a GC has happened and any weak + * references have been cleaned up. + * + * @throws InterruptedException + */ + private void ensureGC() throws InterruptedException { + // TODO(jat): is this sufficient across all VMs? + System.gc(); + Thread.sleep(10); + System.gc(); + } +} ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/ObjectsTable.java Tue Nov 25 16:21:22 2008 +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2008 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.google.gwt.dev.shell; - -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.TreeMap; - -/** - * A class that keeps track of Java objects which have been exposed to the other - * side and assigns unique ids. - */ -public class ObjectsTable { - - /** - * A type that records the next-available free id slot to use, without itself - * being a valid return type. - */ - private static class Tombstone { - private final int nextFree; - - public Tombstone(int nextFree) { - this.nextFree = nextFree; - } - } - - private int nextFree = -1; - private int nextId = 0; - private final Map<Integer, Object> objects = new TreeMap<Integer, Object>(); - private final Map<Object, Integer> refMap = new IdentityHashMap<Object, Integer>(); - - public int add(Object obj) { - int id = find(obj); - if (id >= 0) { - return id; - } - if (nextFree >= 0) { - id = nextFree; - nextFree = ((Tombstone) objects.get(id)).nextFree; - } else { - id = nextId++; - } - objects.put(id, obj); - refMap.put(obj, id); - return id; - } - - public int find(Object obj) { - Integer objId = refMap.get(obj); - return objId != null ? objId.intValue() : -1; - } - - public void free(int id) { - assert objects.get(id) != null : "Trying to free never-used id " + id; - assert !(objects.get(id) instanceof Tombstone) : "Duplicate free " + id; - refMap.remove(objects.get(id)); - objects.put(id, new Tombstone(nextFree)); - nextFree = id; - } - - public Object get(int id) { - Object toReturn = objects.get(id); - assert !(toReturn instanceof Tombstone) : id + " is not an active id"; - return toReturn; - } -} ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java Thu Sep 10 16:26:01 2009 +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannel.java Fri Sep 25 11:00:02 2009 @@ -29,16 +29,9 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; import java.net.Socket; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.Collections; import java.util.Set; -import java.util.TreeMap; /** * @@ -48,7 +41,7 @@ /** * Class representing a reference to a Java object. */ - public static class JavaObjectRef { + public static class JavaObjectRef implements RemoteObjectRef { private int refId; public JavaObjectRef(int refId) { @@ -58,6 +51,15 @@ public int getRefid() { return Math.abs(refId); } + + @Override + public int hashCode() { + return refId; + } + + public boolean isException() { + return refId < 0; + } @Override public String toString() { @@ -68,14 +70,8 @@ /** * Class representing a reference to a JS object. */ - public static class JsObjectRef { - - // TODO: refactor and remove this method. - public static void checkIdMap(int refId) { - assert !JSOBJECT_ID_MAP.get().containsKey(refId) - || (JSOBJECT_ID_MAP.get().get(refId).get() == null); - } - + public static class JsObjectRef implements RemoteObjectRef { + private int refId; public JsObjectRef(int refId) { @@ -110,13 +106,77 @@ /** * Enumeration of message type ids. * - * NOTE: order is important as this defines the ordinals used in the wire - * protocol. + * <p>Ids are used instead of relying on the ordinal to avoid sychronization + * problems with the client. */ public enum MessageType { - INVOKE, RETURN, OLD_LOAD_MODULE, QUIT, LOAD_JSNI, INVOKE_SPECIAL, FREE_VALUE, - FATAL_ERROR, CHECK_VERSIONS, PROTOCOL_VERSION, CHOOSE_TRANSPORT, - SWITCH_TRANSPORT, LOAD_MODULE; + /** + * A message to invoke a method on the other side of the wire. Note that + * the messages are asymmetric -- see {...@link InvokeOnClientMessage} and + * {...@link InvokeOnServerMessage}. + */ + INVOKE(0), + + /** + * Returns the result of an INVOKE, INVOKE_SPECIAL, or LOAD_MODULE message. + */ + RETURN(1), + + /** + * v1 LOAD_MODULE message. + */ + OLD_LOAD_MODULE(2), + + /** + * Normal closure of the connection. + */ + QUIT(3), + + /** + * A request by the server to load JSNI source into the client's JS engine. + */ + LOAD_JSNI(4), + + INVOKE_SPECIAL(5), + + FREE_VALUE(6), + + /** + * Abnormal termination of the connection. + */ + FATAL_ERROR(7), + + CHECK_VERSIONS(8), + + PROTOCOL_VERSION(9), + + CHOOSE_TRANSPORT(10), + + SWITCH_TRANSPORT(11), + + LOAD_MODULE(12); + + private final int id; + + private MessageType(int id) { + this.id = id; + } + + public int getId() { + return id; + } + } + + /** + * Represents an object on the other side of the channel, known to this side + * by an reference ID. + */ + public interface RemoteObjectRef { + + /** + * Return the reference ID for this object. + */ + int getRefid(); } /** @@ -149,10 +209,23 @@ /** * Enumeration of dispatch IDs on object 0 (the ServerMethods object). * + * <p>Ids are set specifically rather than relying on the ordinal to avoid + * synchronization problems with the client. + * * TODO: hasMethod/hasProperty no longer used, remove them! */ public enum SpecialDispatchId { - HasMethod, HasProperty, GetProperty, SetProperty, + HasMethod(0), HasProperty(1), GetProperty(2), SetProperty(3); + + private final int id; + + private SpecialDispatchId(int id) { + this.id = id; + } + + public int getId() { + return id; + } } public abstract void freeValue(BrowserChannel channel, int[] ids); @@ -197,24 +270,28 @@ /** * Primitive values. */ - NULL, BOOLEAN, BYTE, CHAR, SHORT, INT, LONG, FLOAT, DOUBLE, STRING, + NULL(0), BOOLEAN(1), BYTE(2), CHAR(3), SHORT(4), INT(5), LONG(6), + FLOAT(7), DOUBLE(8), STRING(9), /** * Representations of Java or JS objects, sent as an index into a table * kept on the side holding the actual object. */ - JAVA_OBJECT, JS_OBJECT, + JAVA_OBJECT(10), JS_OBJECT(11), /** * A Javascript undef value, also used for void returns. */ - UNDEFINED; - - ValueType() { + UNDEFINED(12); + + private final int id; + + private ValueType(int id) { + this.id = id; } byte getTag() { - return (byte) this.ordinal(); + return (byte) id; } } @@ -556,11 +633,11 @@ hostedHtmlVersion); } - private final int minVersion; + private final String hostedHtmlVersion; private final int maxVersion; - private final String hostedHtmlVersion; + private final int minVersion; public CheckVersionsMessage(BrowserChannel channel, int minVersion, int maxVersion, String hostedHtmlVersion) { @@ -585,7 +662,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.CHECK_VERSIONS.ordinal()); + stream.writeByte(MessageType.CHECK_VERSIONS.getId()); stream.writeInt(minVersion); stream.writeInt(maxVersion); writeUtf8String(stream, hostedHtmlVersion); @@ -625,7 +702,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.CHOOSE_TRANSPORT.ordinal()); + stream.writeByte(MessageType.CHOOSE_TRANSPORT.getId()); stream.writeInt(transports.length); for (String transport : transports) { writeUtf8String(stream, transport); @@ -660,7 +737,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.FATAL_ERROR.ordinal()); + stream.writeByte(MessageType.FATAL_ERROR.getId()); writeUtf8String(stream, error); } } @@ -686,7 +763,7 @@ public static void send(BrowserChannel channel, int[] ids) throws IOException { DataOutputStream stream = channel.getStreamToOtherSide(); - stream.writeByte(MessageType.FREE_VALUE.ordinal()); + stream.writeByte(MessageType.FREE_VALUE.getId()); stream.writeInt(ids.length); for (int id : ids) { stream.writeInt(id); @@ -730,19 +807,18 @@ DataInputStream stream = channel.getStreamFromOtherSide(); // NOTE: Tag has already been read. String methodName = readUtf8String(stream); - Value thisRef = readValue(stream); + Value thisRef = channel.readValue(stream); int argLen = stream.readInt(); Value[] args = new Value[argLen]; for (int i = 0; i < argLen; i++) { - args[i] = readValue(stream); + args[i] = channel.readValue(stream); } return new InvokeOnClientMessage(channel, methodName, thisRef, args); } + private final Value[] args; private final String methodName; - private final Value thisRef; - private final Value[] args; public InvokeOnClientMessage(BrowserChannel channel, String methodName, Value thisRef, Value[] args) { @@ -768,12 +844,12 @@ public void send() throws IOException { final DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.INVOKE.ordinal()); + stream.writeByte(MessageType.INVOKE.getId()); writeUtf8String(stream, methodName); - writeValue(stream, thisRef); + getBrowserChannel().writeValue(stream, thisRef); stream.writeInt(args.length); for (int i = 0; i < args.length; i++) { - writeValue(stream, args[i]); + getBrowserChannel().writeValue(stream, args[i]); } stream.flush(); } @@ -793,19 +869,19 @@ DataInputStream stream = channel.getStreamFromOtherSide(); // NOTE: Tag has already been read. int methodDispatchId = stream.readInt(); - Value thisRef = readValue(stream); + Value thisRef = channel.readValue(stream); int argLen = stream.readInt(); Value[] args = new Value[argLen]; for (int i = 0; i < argLen; i++) { - args[i] = readValue(stream); + args[i] = channel.readValue(stream); } return new InvokeOnServerMessage(channel, methodDispatchId, thisRef, args); } + private final Value[] args; private final int methodDispatchId; private final Value thisRef; - private final Value[] args; public InvokeOnServerMessage(BrowserChannel channel, int methodDispatchId, Value thisRef, Value[] args) { @@ -831,12 +907,12 @@ public void send() throws IOException { final DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.INVOKE.ordinal()); + stream.writeByte(MessageType.INVOKE.getId()); stream.writeInt(methodDispatchId); - writeValue(stream, thisRef); + getBrowserChannel().writeValue(stream, thisRef); stream.writeInt(args.length); for (int i = 0; i < args.length; i++) { - writeValue(stream, args[i]); + getBrowserChannel().writeValue(stream, args[i]); } stream.flush(); } @@ -860,13 +936,13 @@ final int argLen = stream.readInt(); final Value[] args = new Value[argLen]; for (int i = 0; i < argLen; i++) { - args[i] = readValue(stream); + args[i] = channel.readValue(stream); } return new InvokeSpecialMessage(channel, dispatchId, args); } - private final SpecialDispatchId dispatchId; private final Value[] args; + private final SpecialDispatchId dispatchId; public InvokeSpecialMessage(BrowserChannel channel, SpecialDispatchId dispatchId, Value[] args) { @@ -887,11 +963,11 @@ public void send() throws IOException { final DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.INVOKE_SPECIAL.ordinal()); - stream.writeByte(dispatchId.ordinal()); + stream.writeByte(MessageType.INVOKE_SPECIAL.getId()); + stream.writeByte(dispatchId.getId()); stream.writeInt(args.length); for (int i = 0; i < args.length; i++) { - writeValue(stream, args[i]); + getBrowserChannel().writeValue(stream, args[i]); } stream.flush(); } @@ -913,7 +989,7 @@ public static void send(BrowserChannel channel, String js) throws IOException { DataOutputStream stream = channel.getStreamToOtherSide(); - stream.write(MessageType.LOAD_JSNI.ordinal()); + stream.write(MessageType.LOAD_JSNI.getId()); writeUtf8String(stream, js); stream.flush(); } @@ -959,13 +1035,13 @@ private final String moduleName; - private final String userAgent; - - private final String url; - private final String sessionKey; private final String tabKey; + + private final String url; + + private final String userAgent; /** * Creates a LoadModule message to be sent to the server. @@ -1017,7 +1093,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.LOAD_MODULE.ordinal()); + stream.writeByte(MessageType.LOAD_MODULE.getId()); writeUtf8String(stream, url); writeUtf8String(stream, tabKey); writeUtf8String(stream, sessionKey); @@ -1067,6 +1143,20 @@ + " is a message format that can only be received."); } } + + /** + * Provides a way of allocating JS and Java object ids without knowing + * which one is the remote type, so code can be shared between client and + * server. + */ + protected interface ObjectRefFactory { + + JavaObjectRef getJavaObjectRef(int refId); + + JsObjectRef getJsObjectRef(int refId); + + Set<Integer> getRefIdsForCleanup(); + } /** * A request from the client that the server load and initialize a given @@ -1085,9 +1175,9 @@ private final String moduleName; - private final String userAgent; - private final int protoVersion; + + private final String userAgent; public OldLoadModuleMessage(BrowserChannel channel, int protoVersion, String moduleName, String userAgent) { @@ -1112,7 +1202,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.OLD_LOAD_MODULE.ordinal()); + stream.writeByte(MessageType.OLD_LOAD_MODULE.getId()); stream.writeInt(protoVersion); writeUtf8String(stream, moduleName); writeUtf8String(stream, userAgent); @@ -1146,7 +1236,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.PROTOCOL_VERSION.ordinal()); + stream.writeByte(MessageType.PROTOCOL_VERSION.getId()); stream.writeInt(protocolVersion); stream.flush(); } @@ -1162,7 +1252,7 @@ public static void send(BrowserChannel channel) throws IOException { final DataOutputStream stream = channel.getStreamToOtherSide(); - stream.writeByte(MessageType.QUIT.ordinal()); + stream.writeByte(MessageType.QUIT.getId()); stream.flush(); } @@ -1184,16 +1274,16 @@ throws IOException { final DataInputStream stream = channel.getStreamFromOtherSide(); final boolean isException = stream.readBoolean(); - final Value returnValue = readValue(stream); + final Value returnValue = channel.readValue(stream); return new ReturnMessage(channel, isException, returnValue); } public static void send(BrowserChannel channel, boolean isException, Value returnValue) throws IOException { final DataOutputStream stream = channel.getStreamToOtherSide(); - stream.writeByte(MessageType.RETURN.ordinal()); + stream.writeByte(MessageType.RETURN.getId()); stream.writeBoolean(isException); - writeValue(stream, returnValue); + channel.writeValue(stream, returnValue); stream.flush(); } @@ -1203,8 +1293,8 @@ returnOrException.getReturnValue()); } - private final Value returnValue; private final boolean isException; + private final Value returnValue; public ReturnMessage(BrowserChannel channel, boolean isException, Value returnValue) { @@ -1270,7 +1360,7 @@ @Override public void send() throws IOException { DataOutputStream stream = getBrowserChannel().getStreamToOtherSide(); - stream.writeByte(MessageType.SWITCH_TRANSPORT.ordinal()); + stream.writeByte(MessageType.SWITCH_TRANSPORT.getId()); writeUtf8String(stream, transport); writeUtf8String(stream, transportArgs); } @@ -1282,61 +1372,8 @@ public static final int SPECIAL_SERVERMETHODS_OBJECT = 0; - /** - * This accumulates JsObjectRefs that are no longer referenced in the JVM. - */ - private static final ThreadLocal<ReferenceQueue<JsObjectRef>> JSOBJECT_REF_QUEUE = new ThreadLocal<ReferenceQueue<JsObjectRef>>() { - @Override - protected ReferenceQueue<JsObjectRef> initialValue() { - return new ReferenceQueue<JsObjectRef>(); - } - }; - - /** - * This map associates a JS reference id with a Reference to the JSObjectRef - * that currently represents that id. - */ - private static final ThreadLocal<Map<Integer, Reference<JsObjectRef>>> JSOBJECT_ID_MAP = new ThreadLocal<Map<Integer, Reference<JsObjectRef>>>() { - @Override - protected Map<Integer, Reference<JsObjectRef>> initialValue() { - return new TreeMap<Integer, Reference<JsObjectRef>>(); - } - }; - - /** - * This maps References to JsObjectRefs back to the original refId. Because we - * need the refId of the JsValueRef after it's been garbage-collected, this - * state must be stored externally. - */ - private static final ThreadLocal<Map<Reference<JsObjectRef>, Integer>> REFERENCE_ID_MAP = new ThreadLocal<Map<Reference<JsObjectRef>, Integer>>() { - @Override - protected Map<Reference<JsObjectRef>, Integer> initialValue() { - return new IdentityHashMap<Reference<JsObjectRef>, Integer>(); - } - }; - - /** - * Obtain the JsObjectRef that is currently in use to act as a proxy for the - * given JS object id. - */ - protected static JsObjectRef getJsObjectRef(int refId) { - // Access is implicitly synchronous due to ThreadLocal - Map<Integer, Reference<JsObjectRef>> map = JSOBJECT_ID_MAP.get(); - if (map.containsKey(refId)) { - Reference<JsObjectRef> ref = map.get(refId); - JsObjectRef toReturn = ref.get(); - if (toReturn != null) { - return toReturn; - } - } - - JsObjectRef.checkIdMap(refId); - JsObjectRef toReturn = new JsObjectRef(refId); - Reference<JsObjectRef> ref = new WeakReference<JsObjectRef>(toReturn, - JSOBJECT_REF_QUEUE.get()); - map.put(refId, ref); - REFERENCE_ID_MAP.get().put(ref, refId); - return toReturn; + protected static JavaObjectRef getJavaObjectRef(int refId) { + return new JavaObjectRef(refId); } protected static String readUtf8String(DataInputStream stream) @@ -1346,60 +1383,6 @@ stream.readFully(data); return new String(data, "UTF8"); } - - protected static Value readValue(DataInputStream stream) throws IOException { - ValueType tag; - try { - tag = readValueType(stream); - } catch (BrowserChannelException e) { - IOException ee = new IOException(); - ee.initCause(e); - throw ee; - } - Value value = new Value(); - switch (tag) { - case NULL: - value.setNull(); - break; - case UNDEFINED: - value.setUndefined(); - break; - case BOOLEAN: - value.setBoolean(stream.readByte() != 0); - break; - case BYTE: - value.setByte(stream.readByte()); - break; - case CHAR: - value.setChar(stream.readChar()); - break; - case FLOAT: - value.setFloat(stream.readFloat()); - break; - case INT: - value.setInt(stream.readInt()); - break; - case LONG: - value.setLong(stream.readLong()); - break; - case DOUBLE: - value.setDouble(stream.readDouble()); - break; - case SHORT: - value.setShort(stream.readShort()); - break; - case STRING: - value.setString(readUtf8String(stream)); - break; - case JS_OBJECT: - value.setJsObject(getJsObjectRef(stream.readInt())); - break; - case JAVA_OBJECT: - value.setJavaObject(new JavaObjectRef(stream.readInt())); - break; - } - return value; - } protected static ValueType readValueType(DataInputStream stream) throws IOException, BrowserChannelException { @@ -1486,60 +1469,35 @@ throw new RuntimeException(); } } - - protected static void writeValue(DataOutputStream stream, Value value) - throws IOException { - if (value.isNull()) { - writeNull(stream); - } else if (value.isUndefined()) { - writeUndefined(stream); - } else if (value.isJsObject()) { - writeJsObject(stream, value.getJsObject()); - } else if (value.isJavaObject()) { - writeJavaObject(stream, value.getJavaObject()); - } else if (value.isBoolean()) { - writeTaggedBoolean(stream, value.getBoolean()); - } else if (value.isByte()) { - writeTaggedByte(stream, value.getByte()); - } else if (value.isChar()) { - writeTaggedChar(stream, value.getChar()); - } else if (value.isShort()) { - writeTaggedShort(stream, value.getShort()); - } else if (value.isDouble()) { - writeTaggedDouble(stream, value.getDouble()); - } else if (value.isFloat()) { - writeTaggedFloat(stream, value.getFloat()); - } else if (value.isInt()) { - writeTaggedInt(stream, value.getInt()); - } else if (value.isString()) { - writeTaggedString(stream, value.getString()); - } else { - assert false; - } - } private static void writeUndefined(DataOutputStream stream) throws IOException { stream.writeByte(ValueType.UNDEFINED.getTag()); } + + private final ObjectRefFactory objectRefFactory; + + private Socket socket; private final DataInputStream streamFromOtherSide; private final DataOutputStream streamToOtherSide; - private Socket socket; - - public BrowserChannel(Socket socket) throws IOException { + public BrowserChannel(Socket socket, ObjectRefFactory objectRefFactory) + throws IOException { this(new BufferedInputStream(socket.getInputStream()), - new BufferedOutputStream(socket.getOutputStream())); + new BufferedOutputStream(socket.getOutputStream()), + objectRefFactory); this.socket = socket; } - protected BrowserChannel(InputStream inputStream, OutputStream outputStream) + protected BrowserChannel(InputStream inputStream, OutputStream outputStream, + ObjectRefFactory objectRefFactory) throws IOException { streamFromOtherSide = new DataInputStream(inputStream); streamToOtherSide = new DataOutputStream(outputStream); socket = null; + this.objectRefFactory = objectRefFactory; } public void endSession() { @@ -1548,38 +1506,11 @@ Utility.close(socket); } + /** + * @return a set of remote object reference IDs to be freed. + */ public Set<Integer> getRefIdsForCleanup() { - // Access to these objects is inherently synchronous - Map<Integer, Reference<JsObjectRef>> objectMap = JSOBJECT_ID_MAP.get(); - Map<Reference<JsObjectRef>, Integer> refIdMap = REFERENCE_ID_MAP.get(); - ReferenceQueue<JsObjectRef> q = JSOBJECT_REF_QUEUE.get(); - Set<Integer> toReturn = new HashSet<Integer>(); - - // Find all refIds associated with previous garbage collection cycles - Reference<? extends JsObjectRef> ref; - while ((ref = q.poll()) != null) { - Integer i = refIdMap.remove(ref); - assert i != null; - toReturn.add(i); - } - - /* - * Check for liveness. This is necessary because the last reference to a - * JsObjectRef could have been cleared and a new reference to that refId - * created before this method has been called. - */ - for (Iterator<Integer> i = toReturn.iterator(); i.hasNext();) { - Integer refId = i.next(); - if (objectMap.containsKey(refId)) { - if (objectMap.get(refId).get() != null) { - i.remove(); - } else { - objectMap.remove(refId); - } - } - } - - return toReturn; + return objectRefFactory.getRefIdsForCleanup(); } public String getRemoteEndpoint() { @@ -1609,8 +1540,10 @@ break; case INVOKE: final InvokeOnServerMessage imsg = InvokeOnServerMessage.receive(this); - ReturnMessage.send(this, handler.invoke(this, imsg.getThis(), - imsg.getMethodDispatchId(), imsg.getArgs())); + ExceptionOrReturnValue result = handler.invoke(this, imsg.getThis(), + imsg.getMethodDispatchId(), imsg.getArgs()); + sendFreedValues(); + ReturnMessage.send(this, result); break; case INVOKE_SPECIAL: handleInvokeSpecial(handler); @@ -1638,8 +1571,10 @@ return ReturnMessage.receive(this); case INVOKE: final InvokeOnServerMessage imsg = InvokeOnServerMessage.receive(this); - ReturnMessage.send(this, handler.invoke(this, imsg.getThis(), - imsg.getMethodDispatchId(), imsg.getArgs())); + ExceptionOrReturnValue result = handler.invoke(this, imsg.getThis(), + imsg.getMethodDispatchId(), imsg.getArgs()); + sendFreedValues(); + ReturnMessage.send(this, result); break; case INVOKE_SPECIAL: handleInvokeSpecial(handler); @@ -1658,6 +1593,92 @@ protected DataOutputStream getStreamToOtherSide() { return streamToOtherSide; } + + protected Value readValue(DataInputStream stream) throws IOException { + ValueType tag; + try { + tag = readValueType(stream); + } catch (BrowserChannelException e) { + IOException ee = new IOException(); + ee.initCause(e); + throw ee; + } + Value value = new Value(); + switch (tag) { + case NULL: + value.setNull(); + break; + case UNDEFINED: + value.setUndefined(); + break; + case BOOLEAN: + value.setBoolean(stream.readByte() != 0); + break; + case BYTE: + value.setByte(stream.readByte()); + break; + case CHAR: + value.setChar(stream.readChar()); + break; + case FLOAT: + value.setFloat(stream.readFloat()); + break; + case INT: + value.setInt(stream.readInt()); + break; + case LONG: + value.setLong(stream.readLong()); + break; + case DOUBLE: + value.setDouble(stream.readDouble()); + break; + case SHORT: + value.setShort(stream.readShort()); + break; + case STRING: + value.setString(readUtf8String(stream)); + break; + case JS_OBJECT: + value.setJsObject(objectRefFactory.getJsObjectRef(stream.readInt())); + break; + case JAVA_OBJECT: + value.setJavaObject(objectRefFactory.getJavaObjectRef( + stream.readInt())); + break; + } + return value; + } + + protected void writeValue(DataOutputStream stream, Value value) + throws IOException { + if (value.isNull()) { + writeNull(stream); + } else if (value.isUndefined()) { + writeUndefined(stream); + } else if (value.isJsObject()) { + writeJsObject(stream, value.getJsObject()); + } else if (value.isJavaObject()) { + writeJavaObject(stream, value.getJavaObject()); + } else if (value.isBoolean()) { + writeTaggedBoolean(stream, value.getBoolean()); + } else if (value.isByte()) { + writeTaggedByte(stream, value.getByte()); + } else if (value.isChar()) { + writeTaggedChar(stream, value.getChar()); + } else if (value.isShort()) { + writeTaggedShort(stream, value.getShort()); + } else if (value.isDouble()) { + writeTaggedDouble(stream, value.getDouble()); + } else if (value.isFloat()) { + writeTaggedFloat(stream, value.getFloat()); + } else if (value.isInt()) { + writeTaggedInt(stream, value.getInt()); + } else if (value.isString()) { + writeTaggedString(stream, value.getString()); + } else { + assert false; + } + } private void handleInvokeSpecial(SessionHandler handler) throws IOException, BrowserChannelException { @@ -1680,4 +1701,17 @@ } ReturnMessage.send(this, retExc); } -} + + private void sendFreedValues() throws IOException { + Set<Integer> freed = objectRefFactory.getRefIdsForCleanup(); + int n = freed.size(); + if (n > 0) { + int[] ids = new int[n]; + int i = 0; + for (Integer id : freed) { + ids[i++] = id; + } + FreeMessage.send(this, ids); + } + } +} ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelClient.java Thu Sep 10 16:26:01 2009 +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelClient.java Fri Sep 25 11:00:02 2009 @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.Socket; +import java.util.Set; /** * Implementation of the BrowserChannel for the client side. @@ -28,7 +29,32 @@ */ public class BrowserChannelClient extends BrowserChannel { - private static final int PROTOCOL_VERSION = 2; + private static class ClientObjectRefFactory implements ObjectRefFactory { + + private final RemoteObjectTable<JavaObjectRef> remoteObjectTable; + + public ClientObjectRefFactory() { + remoteObjectTable = new RemoteObjectTable<JavaObjectRef>(); + } + + public JavaObjectRef getJavaObjectRef(int refId) { + JavaObjectRef objectRef = remoteObjectTable.getRemoteObjectRef(refId); + if (objectRef == null) { + objectRef = new JavaObjectRef(refId); + remoteObjectTable.putRemoteObjectRef(refId, objectRef); + } + return objectRef; + } + + public JsObjectRef getJsObjectRef(int refId) { + return new JsObjectRef(refId); + } + + public Set<Integer> getRefIdsForCleanup() { + return remoteObjectTable.getRefIdsForCleanup(); + } + } + private final HtmlUnitSessionHandler htmlUnitSessionHandler; private final PrintWriterTreeLogger logger = new PrintWriterTreeLogger(); private final String moduleName; @@ -41,7 +67,8 @@ public BrowserChannelClient(String addressParts[], String url, String sessionKey, String moduleName, String versionString, HtmlUnitSessionHandler htmlUnitSessionHandler) throws IOException { - super(new Socket(addressParts[0], Integer.parseInt(addressParts[1]))); + super(new Socket(addressParts[0], Integer.parseInt(addressParts[1])), + new ClientObjectRefFactory()); connected = true; this.url = url; this.sessionKey = sessionKey; @@ -108,8 +135,8 @@ private boolean init() throws IOException, BrowserChannelException { logger.log(TreeLogger.DEBUG, "sending " + MessageType.CHECK_VERSIONS + " message"); - new CheckVersionsMessage(this, PROTOCOL_VERSION, PROTOCOL_VERSION, - versionString).send(); + new CheckVersionsMessage(this, BROWSERCHANNEL_PROTOCOL_VERSION, + BROWSERCHANNEL_PROTOCOL_VERSION, versionString).send(); MessageType type = Message.readMessageType(getStreamFromOtherSide()); switch (type) { case PROTOCOL_VERSION: ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java Sat Aug 22 16:01:38 2009 +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/BrowserChannelServer.java Fri Sep 25 11:00:02 2009 @@ -20,6 +20,7 @@ import java.io.IOException; import java.net.Socket; +import java.util.Set; /** * @@ -27,11 +28,37 @@ public final class BrowserChannelServer extends BrowserChannel implements Runnable { + private static class ServerObjectRefFactory implements ObjectRefFactory { + + private final RemoteObjectTable<JsObjectRef> remoteObjectTable; + + public ServerObjectRefFactory() { + remoteObjectTable = new RemoteObjectTable<JsObjectRef>(); + } + + public JavaObjectRef getJavaObjectRef(int refId) { + return new JavaObjectRef(refId); + } + + public JsObjectRef getJsObjectRef(int refId) { + JsObjectRef objectRef = remoteObjectTable.getRemoteObjectRef(refId); + if (objectRef == null) { + objectRef = new JsObjectRef(refId); + remoteObjectTable.putRemoteObjectRef(refId, objectRef); + } + return objectRef; + } + + public Set<Integer> getRefIdsForCleanup() { + return remoteObjectTable.getRefIdsForCleanup(); + } + } + public static final String JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject"; private SessionHandler handler; - private final ObjectsTable javaObjectsInBrowser = new ObjectsTable(); + private final ServerObjectsTable javaObjectsInBrowser = new ServerObjectsTable(); private TreeLogger logger; @@ -43,7 +70,7 @@ public BrowserChannelServer(TreeLogger initialLogger, Socket socket, SessionHandler handler) throws IOException { - super(socket); + super(socket, new ServerObjectRefFactory()); this.handler = handler; this.logger = initialLogger; Thread thread = new Thread(this); @@ -62,7 +89,7 @@ } } - public ObjectsTable getJavaObjectsExposedInBrowser() { + public ServerObjectsTable getJavaObjectsExposedInBrowser() { return javaObjectsInBrowser; } @@ -84,7 +111,7 @@ public void invokeJavascript(CompilingClassLoader ccl, JsValueOOPHM jsthis, String methodName, JsValueOOPHM[] args, JsValueOOPHM returnJsValue) throws Throwable { - final ObjectsTable remoteObjects = getJavaObjectsExposedInBrowser(); + final ServerObjectsTable remoteObjects = getJavaObjectsExposedInBrowser(); Value vthis = convertFromJsValue(remoteObjects, jsthis); Value[] vargs = new Value[args.length]; for (int i = 0; i < args.length; ++i) { @@ -175,7 +202,7 @@ * @param jsval value to convert * @return jsval as a Value object. */ - Value convertFromJsValue(ObjectsTable localObjects, JsValueOOPHM jsval) { + Value convertFromJsValue(ServerObjectsTable localObjects, JsValueOOPHM jsval) { Value value = new Value(); if (jsval.isNull()) { value.setNull(); @@ -214,7 +241,7 @@ * @param val Value to convert * @param jsval JsValue object to receive converted value. */ - void convertToJsValue(CompilingClassLoader ccl, ObjectsTable localObjects, + void convertToJsValue(CompilingClassLoader ccl, ServerObjectsTable localObjects, Value val, JsValueOOPHM jsval) { switch (val.getType()) { case NULL: ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java Thu Sep 10 16:26:01 2009 +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/JsValueOOPHM.java Fri Sep 25 11:00:02 2009 @@ -144,7 +144,6 @@ * @param jsRefId pointer to underlying JsRootedValue as an integer. */ public JsValueOOPHM(int jsRefId) { - JsObjectRef.checkIdMap(jsRefId); this.value = new JsObjectRef(jsRefId); } ======================================= --- /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java Sat Aug 22 16:01:38 2009 +++ /branches/farewellSwt/dev/oophm/src/com/google/gwt/dev/shell/OophmSessionHandler.java Fri Sep 25 11:00:02 2009 @@ -48,7 +48,7 @@ @Override public void freeValue(BrowserChannel channel, int[] ids) { BrowserChannelServer serverChannel = (BrowserChannelServer) channel; - ObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); + ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); for (int id : ids) { localObjects.free(id); } @@ -60,7 +60,7 @@ BrowserChannelServer serverChannel = (BrowserChannelServer) channel; ModuleSpace moduleSpace = moduleMap.get(serverChannel); assert moduleSpace != null; - ObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); + ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); try { JsValueOOPHM obj = new JsValueOOPHM(); DispatchObject dispObj; @@ -91,7 +91,7 @@ public ExceptionOrReturnValue invoke(BrowserChannel channel, Value thisVal, int methodDispatchId, Value[] args) { BrowserChannelServer serverChannel = (BrowserChannelServer) channel; - ObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); + ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); ModuleSpace moduleSpace = moduleMap.get(serverChannel); assert moduleSpace != null; CompilingClassLoader cl = moduleSpace.getIsolatedClassLoader(); @@ -190,7 +190,7 @@ BrowserChannelServer serverChannel = (BrowserChannelServer) channel; ModuleSpace moduleSpace = moduleMap.get(serverChannel); assert moduleSpace != null; - ObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); + ServerObjectsTable localObjects = serverChannel.getJavaObjectsExposedInBrowser(); try { JsValueOOPHM obj = new JsValueOOPHM(); DispatchObject dispObj; ======================================= --- /branches/farewellSwt/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java Mon Aug 10 16:15:14 2009 +++ /branches/farewellSwt/dev/oophm/test/com/google/gwt/dev/shell/BrowserChannelTest.java Fri Sep 25 11:00:02 2009 @@ -44,6 +44,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; +import java.util.Set; /** * Test for {...@link BrowserChannel}. @@ -55,7 +56,22 @@ private class TestBrowserChannel extends BrowserChannel { public TestBrowserChannel(InputStream inputStream, OutputStream outputStream) throws IOException { - super(inputStream, outputStream); + super(inputStream, outputStream, new ObjectRefFactory() { + public JavaObjectRef getJavaObjectRef(int refId) { + fail("getJavaObjectRef mocked"); + return null; + } + + public JsObjectRef getJsObjectRef(int refId) { + fail("getJsObjectRef mocked"); + return null; + } + + public Set<Integer> getRefIdsForCleanup() { + fail("getRefIdsForCleanup mocked"); + return null; + } + }); } public MessageType readMessageType() throws IOException, @@ -80,13 +96,13 @@ public void testBooleanValue() throws IOException { Value val = new Value(); val.setBoolean(true); - BrowserChannel.writeValue(oStr, val); - val = BrowserChannel.readValue(iStr); + channel.writeValue(oStr, val); + val = channel.readValue(iStr); assertEquals(ValueType.BOOLEAN, val.getType()); assertEquals(true, val.getBoolean()); val.setBoolean(false); - BrowserChannel.writeValue(oStr, val); - val = BrowserChannel.readValue(iStr); + channel.writeValue(oStr, val); + val = channel.readValue(iStr); assertEquals(ValueType.BOOLEAN, val.getType()); assertEquals(false, val.getBoolean()); } ======================================= --- /branches/farewellSwt/user/src/com/google/gwt/junit/RunStyle.java Sun Sep 20 12:33:31 2009 +++ /branches/farewellSwt/user/src/com/google/gwt/junit/RunStyle.java Fri Sep 25 11:00:02 2009 @@ -33,10 +33,7 @@ * the same signature since this will be how the RunStyle is created via * reflection. * - * @param logger TreeLogger instance * @param shell the containing shell - * @param args arguments (after the colon in the argument to -runStyle) - * may be null if no argument is supplied */ public RunStyle(JUnitShell shell) { this.shell = shell; --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---