Title: [167984] trunk/Source/WebInspectorUI
Revision
167984
Author
b...@cs.washington.edu
Date
2014-04-29 22:52:04 -0700 (Tue, 29 Apr 2014)

Log Message

Web Inspector: reduce per-protocol method function creation in InspectorBackend
https://bugs.webkit.org/show_bug.cgi?id=130701

Reviewed by Timothy Hatcher.

Instead of creating 4 bound functions for every backend method on startup, we
can share common functionality on the InspectorBackendCommand prototype. This
also splits the various client-facing protocol introspection mechanisms from
the message encoding/decoding code.

We use a workaround to make the command instances themselves callable as well
as having .invoke, .promise, and .supports. InspectorAgent.methodName returns a
trampoline function that performs the default method invocation. The trampoline's
__proto__ is patched to point to InspectorBackendClass.prototype, and the command
instance is saved in the closure and on the trampoline function itself.

* UserInterface/Protocol/InspectorBackend.js:
(InspectorBackendClass.prototype._registerPendingResponse):
(InspectorBackendClass.prototype._invokeMethod):
(InspectorBackendClass.prototype.registerCommand):
(InspectorBackendClass.prototype.registerEvent):
(InspectorBackendCommand):
(.callable):
(InspectorBackendCommand.create):
(InspectorBackendCommand.prototype.invoke):
(InspectorBackendCommand.prototype.promise):
(InspectorBackendCommand.prototype.supports):
(InspectorBackendCommand.prototype._invokeWithArguments):
(InspectorBackendClass.prototype._wrap): Deleted.
(InspectorBackendClass.prototype._invoke): Deleted.
(InspectorBackendClass.prototype._promise): Deleted.
(InspectorBackendClass.prototype._supports): Deleted.
(InspectorBackendClass.prototype._sendMessageToBackend): Deleted.
(InspectorBackendClass.prototype._wrapCallbackAndSendMessageObject): Deleted.
(InspectorBackendClass.prototype.sendMessageObjectToBackend): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (167983 => 167984)


--- trunk/Source/WebInspectorUI/ChangeLog	2014-04-30 05:42:57 UTC (rev 167983)
+++ trunk/Source/WebInspectorUI/ChangeLog	2014-04-30 05:52:04 UTC (rev 167984)
@@ -1,5 +1,43 @@
 2014-04-29  Brian J. Burg  <b...@cs.washington.edu>
 
+        Web Inspector: reduce per-protocol method function creation in InspectorBackend
+        https://bugs.webkit.org/show_bug.cgi?id=130701
+
+        Reviewed by Timothy Hatcher.
+
+        Instead of creating 4 bound functions for every backend method on startup, we
+        can share common functionality on the InspectorBackendCommand prototype. This
+        also splits the various client-facing protocol introspection mechanisms from
+        the message encoding/decoding code.
+
+        We use a workaround to make the command instances themselves callable as well
+        as having .invoke, .promise, and .supports. InspectorAgent.methodName returns a
+        trampoline function that performs the default method invocation. The trampoline's
+        __proto__ is patched to point to InspectorBackendClass.prototype, and the command
+        instance is saved in the closure and on the trampoline function itself.
+
+        * UserInterface/Protocol/InspectorBackend.js:
+        (InspectorBackendClass.prototype._registerPendingResponse):
+        (InspectorBackendClass.prototype._invokeMethod):
+        (InspectorBackendClass.prototype.registerCommand):
+        (InspectorBackendClass.prototype.registerEvent):
+        (InspectorBackendCommand):
+        (.callable):
+        (InspectorBackendCommand.create):
+        (InspectorBackendCommand.prototype.invoke):
+        (InspectorBackendCommand.prototype.promise):
+        (InspectorBackendCommand.prototype.supports):
+        (InspectorBackendCommand.prototype._invokeWithArguments):
+        (InspectorBackendClass.prototype._wrap): Deleted.
+        (InspectorBackendClass.prototype._invoke): Deleted.
+        (InspectorBackendClass.prototype._promise): Deleted.
+        (InspectorBackendClass.prototype._supports): Deleted.
+        (InspectorBackendClass.prototype._sendMessageToBackend): Deleted.
+        (InspectorBackendClass.prototype._wrapCallbackAndSendMessageObject): Deleted.
+        (InspectorBackendClass.prototype.sendMessageObjectToBackend): Deleted.
+
+2014-04-29  Brian J. Burg  <b...@cs.washington.edu>
+
         Web Inspector: DataGrid columns should be objects not Maps
         https://bugs.webkit.org/show_bug.cgi?id=129383
 

Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js (167983 => 167984)


--- trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js	2014-04-30 05:42:57 UTC (rev 167983)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorBackend.js	2014-04-30 05:52:04 UTC (rev 167984)
@@ -47,20 +47,37 @@
 }
 
 InspectorBackendClass.prototype = {
-    _wrap: function(callback, method)
+    _registerPendingResponse: function(callback, methodName)
     {
         var callbackId = this._lastCallbackId++;
         if (!callback)
             callback = function() {};
 
         this._callbacks[callbackId] = callback;
-        callback.methodName = method;
+        callback.methodName = methodName;
         if (this.dumpInspectorTimeStats)
             callback.sendRequestTime = Date.now();
 
+        ++this._pendingResponsesCount;
+
         return callbackId;
     },
 
+    _invokeMethod: function(command, parameters, callback)
+    {
+        var messageObject = {};
+        messageObject["method"] = command.methodName;
+        if (parameters)
+            messageObject["params"] = parameters;
+        messageObject["id"] = this._registerPendingResponse(callback, command.methodName);
+
+        var stringifiedMessage = JSON.stringify(messageObject);
+        if (this.dumpInspectorProtocolMessages)
+            console.log("frontend: " + stringifiedMessage);
+
+        InspectorFrontendHost.sendMessageToBackend(stringifiedMessage);
+    },
+
     _getAgent: function(domain)
     {
         var agentName = domain + "Agent";
@@ -73,11 +90,8 @@
     {
         var domainAndMethod = method.split(".");
         var agent = this._getAgent(domainAndMethod[0]);
+        agent[domainAndMethod[1]] = InspectorBackendCommand.create(this, method, signature);
 
-        agent[domainAndMethod[1]] = this._sendMessageToBackend.bind(this, method, signature);
-        agent[domainAndMethod[1]]["invoke"] = this._invoke.bind(this, method, signature);
-        agent[domainAndMethod[1]]["promise"] = this._promise.bind(this, method, signature);
-        agent[domainAndMethod[1]]["supports"] = this._supports.bind(this, method, signature);
         this._replyArgs[method] = replyArgs;
     },
 
@@ -89,105 +103,11 @@
         agent[domainAndMethod[1]] = values;
     },
 
-    registerEvent: function(eventName, params)
+    registerEvent: function(eventName, parameters)
     {
-        this._eventArgs[eventName] = params;
+        this._eventArgs[eventName] = parameters;
     },
 
-    _invoke: function(method, signature, args, callback)
-    {
-        this._wrapCallbackAndSendMessageObject(method, args, callback);
-    },
-
-    _promise: function(method, signature)
-    {
-        var backend = this;
-        var promiseArguments = Array.prototype.slice.call(arguments);
-        return new Promise(function(resolve, reject) {
-            function convertToPromiseCallback(error, payload) {
-                if (error)
-                    reject(error);
-                else
-                    resolve(payload);
-            }
-            promiseArguments.push(convertToPromiseCallback);
-            backend._sendMessageToBackend.apply(backend, promiseArguments);
-        });
-    },
-
-    _supports: function(method, signature, paramName)
-    {
-        for (var i = 0; i < signature.length; ++i) {
-            if (signature[i]["name"] === paramName)
-                return true;
-        }
-
-        return false;
-    },
-
-    _sendMessageToBackend: function(method, signature, vararg)
-    {
-        var args = Array.prototype.slice.call(arguments, 2);
-        var callback = typeof args.lastValue === "function" ? args.pop() : null;
-
-        var params = {};
-        var hasParams = false;
-        for (var i = 0; i < signature.length; ++i) {
-            var param = signature[i];
-            var paramName = param["name"];
-            var typeName = param["type"];
-            var optionalFlag = param["optional"];
-
-            if (!args.length && !optionalFlag) {
-                console.error("Protocol Error: Invalid number of arguments for method '" + method + "' call. It must have the following arguments '" + JSON.stringify(signature) + "'.");
-                return;
-            }
-
-            var value = args.shift();
-            if (optionalFlag && typeof value === "undefined") {
-                continue;
-            }
-
-            if (typeof value !== typeName) {
-                console.error("Protocol Error: Invalid type of argument '" + paramName + "' for method '" + method + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
-                return;
-            }
-
-            params[paramName] = value;
-            hasParams = true;
-        }
-
-        if (args.length === 1 && !callback) {
-            if (typeof args[0] !== "undefined") {
-                console.error("Protocol Error: Optional callback argument for method '" + method + "' call must be a function but its type is '" + typeof args[0] + "'.");
-                return;
-            }
-        }
-
-        this._wrapCallbackAndSendMessageObject(method, hasParams ? params : null, callback);
-    },
-
-    _wrapCallbackAndSendMessageObject: function(method, params, callback)
-    {
-        var messageObject = {};
-        messageObject.method = method;
-        if (params)
-            messageObject.params = params;
-        messageObject.id = this._wrap(callback, method);
-
-        if (this.dumpInspectorProtocolMessages)
-            console.log("frontend: " + JSON.stringify(messageObject));
-
-        ++this._pendingResponsesCount;
-        this.sendMessageObjectToBackend(messageObject);
-    },
-
-    sendMessageObjectToBackend: function(messageObject)
-    {
-        var message = JSON.stringify(messageObject);
-        InspectorFrontendHost.sendMessageToBackend(message);
-    },
-
     registerDomainDispatcher: function(domain, dispatcher)
     {
         this._domainDispatchers[domain] = dispatcher;
@@ -311,3 +231,96 @@
 }
 
 InspectorBackend = new InspectorBackendClass();
+
+InspectorBackendCommand = function(backend, methodName, callSignature)
+{
+    this._backend = backend;
+    this.methodName = methodName;
+    this._callSignature = callSignature;
+    this._instance = this;
+}
+
+InspectorBackendCommand.create = function(backend, methodName, callSignature)
+{
+    var instance = new InspectorBackendCommand(backend, methodName, callSignature);
+
+    function callable() {
+        instance._invokeWithArguments.apply(instance, arguments);
+    }
+    callable._instance = instance;
+    callable.__proto__ = InspectorBackendCommand.prototype;
+    return callable;
+}
+
+// As part of the workaround to make commands callable, these functions use |this._instance|.
+// |this| could refer to the callable trampoline, or the InspectorBackendCommand instance.
+InspectorBackendCommand.prototype = {
+    __proto__: Function.prototype,
+
+    invoke: function(args, callback)
+    {
+        var instance = this._instance;
+        instance._backend._invokeMethod(instance, args, callback);
+    },
+
+    promise: function()
+    {
+        var instance = this._instance;
+        var promiseArguments = Array.prototype.slice.call(arguments);
+        return new Promise(function(resolve, reject) {
+            function convertToPromiseCallback(error, payload) {
+                return error ? reject(error) : resolve(payload);
+            }
+            promiseArguments.push(convertToPromiseCallback);
+            instance._invokeWithArguments.apply(instance, promiseArguments);
+        });
+    },
+
+    supports: function(parameterName)
+    {
+        var instance = this._instance;
+        return instance._callSignature.any(function(parameter) {
+            return parameter["name"] === parameterName
+        });
+    },
+
+    _invokeWithArguments: function()
+    {
+        var instance = this._instance;
+        var args = Array.prototype.slice.call(arguments);
+        var callback = typeof args.lastValue === "function" ? args.pop() : null;
+
+        var parameters = {};
+        for (var i = 0; i < instance._callSignature.length; ++i) {
+            var parameter = instance._callSignature[i];
+            var parameterName = parameter["name"];
+            var typeName = parameter["type"];
+            var optionalFlag = parameter["optional"];
+
+            if (!args.length && !optionalFlag) {
+                console.error("Protocol Error: Invalid number of arguments for method '" + instance.methodName + "' call. It must have the following arguments '" + JSON.stringify(signature) + "'.");
+                return;
+            }
+
+            var value = args.shift();
+            if (optionalFlag && typeof value === "undefined")
+                continue;
+
+            if (typeof value !== typeName) {
+                console.error("Protocol Error: Invalid type of argument '" + parameterName + "' for method '" + instance.methodName + "' call. It must be '" + typeName + "' but it is '" + typeof value + "'.");
+                return;
+            }
+
+            parameters[parameterName] = value;
+        }
+
+        if (args.length === 1 && !callback) {
+            if (typeof args[0] !== "undefined") {
+                console.error("Protocol Error: Optional callback argument for method '" + instance.methodName + "' call must be a function but its type is '" + typeof args[0] + "'.");
+                return;
+            }
+        }
+
+        instance._backend._invokeMethod(instance, Object.keys(parameters).length ? parameters : null, callback);
+    },
+}
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to