--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8Helper.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,355 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.sdk.internal.tools.v8;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import org.chromium.sdk.CallbackSemaphore;
+import org.chromium.sdk.SyncCallback;
+import org.chromium.sdk.JsValue.Type;
+import org.chromium.sdk.internal.DataWithRef;
+import org.chromium.sdk.internal.DebugSession;
+import org.chromium.sdk.internal.FunctionAdditionalProperties;
+import org.chromium.sdk.internal.JsDataTypeUtil;
+import org.chromium.sdk.internal.PropertyHoldingValueMirror;
+import org.chromium.sdk.internal.PropertyReference;
+import org.chromium.sdk.internal.ScopeMirror;
+import org.chromium.sdk.internal.ScriptManager;
+import org.chromium.sdk.internal.SubpropertiesMirror;
+import org.chromium.sdk.internal.ValueLoadException;
+import org.chromium.sdk.internal.ValueMirror;
+import org.chromium.sdk.internal.protocol.CommandResponse;
+import org.chromium.sdk.internal.protocol.FrameObject;
+import org.chromium.sdk.internal.protocol.ScopeRef;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.FunctionValueHandle;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocol.data.PropertyObject;
+import org.chromium.sdk.internal.protocol.data.RefWithDisplayData;
+import org.chromium.sdk.internal.protocol.data.ScriptHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+import org.chromium.sdk.internal.tools.v8.request.ScriptsMessage;
+
+/**
+ * A helper class for performing complex V8-related operations.
+ */
+public class V8Helper {
+
+ /**
+ * The debug context in which the operations are performed.
+ */
+ private final DebugSession debugSession;
+
+ /**
+ * A semaphore that prevents concurrent script reloading (which may effectively
+ * double the efforts.)
+ */
+ private final Semaphore scriptsReloadSemaphore = new Semaphore(1);
+
+ public V8Helper(DebugSession debugSession) {
+ this.debugSession = debugSession;
+ }
+
+ /**
+ * Reloads all normal scripts found in the page. First, all scripts without
+ * their sources are retrieved to save bandwidth (script list change during a
+ * page lifetime is a relatively rare event.) If at least one script has been
+ * added, the script cache is dropped and re-populated with new scripts that
+ * are re-requested together with their sources.
+ *
+ * @param callback to invoke when the script reloading has completed
+ */
+ public void reloadAllScriptsAsync(V8CommandProcessor.V8HandlerCallback callback,
+ SyncCallback syncCallback) {
+ final V8CommandProcessor.V8HandlerCallback finalCallback = callback != null
+ ? callback
+ : V8CommandProcessor.V8HandlerCallback.NULL_CALLBACK;
+ lock();
+ debugSession.sendMessageAsync(
+ DebuggerMessageFactory.scripts(ScriptsMessage.SCRIPTS_NORMAL, true),
+ true,
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void failure(String message) {
+ unlock();
+ finalCallback.failure(message);
+ }
+
+ public void messageReceived(CommandResponse response) {
+ SuccessCommandResponse successResponse = response.asSuccess();
+
+ // TODO(peter.rybin): add try/finally for unlock, with some error reporting probably.
+ List<ScriptHandle> body;
+ try {
+ body = successResponse.getBody().asScripts();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ ScriptManager scriptManager = debugSession.getScriptManager();
+ for (int i = 0; i < body.size(); ++i) {
+ ScriptHandle scriptHandle = body.get(i);
+ Long id = V8ProtocolUtil.getScriptIdFromResponse(scriptHandle);
+ if (scriptManager.findById(id) == null &&
+ !ChromeDevToolSessionManager.JAVASCRIPT_VOID.equals(scriptHandle.source())) {
+ scriptManager.addScript(
+ scriptHandle,
+ successResponse.getRefs());
+ }
+ }
+ unlock();
+ finalCallback.messageReceived(response);
+ }
+ },
+ syncCallback);
+ }
+
+ protected void lock() {
+ try {
+ scriptsReloadSemaphore.acquire();
+ } catch (InterruptedException e) {
+ // consider it a successful acquisition
+ }
+ }
+
+ protected void unlock() {
+ scriptsReloadSemaphore.release();
+ }
+
+ /**
+ * Gets all resolved locals for the call frame, caches scripts and objects in
+ * the scriptManager and handleManager.
+ *
+ * @param frame to get the data for
+ * @return the mirrors corresponding to the frame locals
+ */
+ public static List<PropertyReference> computeLocals(FrameObject frame) {
+ List<PropertyObject> args = frame.getArguments();
+ List<PropertyObject> locals = frame.getLocals();
+
+ int maxLookups = args.size() + locals.size() + 1 /* "this" */;
+
+ List<PropertyReference> localRefs = new ArrayList<PropertyReference>(maxLookups);
+
+ {
+ // Receiver ("this")
+ RefWithDisplayData receiverObject = frame.getReceiver().asWithDisplayData();
+ V8ProtocolUtil.putMirror(localRefs, receiverObject,
+ V8ProtocolUtil.PropertyNameGetter.THIS);
+ }
+
+ // Arguments
+ for (int i = 0; i < args.size(); i++) {
+ PropertyObject arg = args.get(i);
+ V8ProtocolUtil.putMirror(localRefs, arg, V8ProtocolUtil.PropertyNameGetter.SUBPROPERTY);
+ }
+
+ // Locals
+ for (int i = 0; i < locals.size(); i++) {
+ PropertyObject local = locals.get(i);
+ V8ProtocolUtil.putMirror(localRefs, local, V8ProtocolUtil.PropertyNameGetter.SUBPROPERTY);
+ }
+
+ return localRefs;
+ }
+
+ public static List<ScopeMirror> computeScopes(FrameObject frame) {
+ List<ScopeRef> scopes = frame.getScopes();
+
+ final List<ScopeMirror> result = new ArrayList<ScopeMirror>(scopes.size());
+
+ for (int i = 0; i < scopes.size(); i++) {
+ ScopeRef scope = scopes.get(i);
+ int type = (int) scope.type();
+ int index = (int) scope.index();
+
+ result.add(new ScopeMirror(type, index));
+ }
+
+ return result;
+ }
+
+ public static PropertyReference computeReceiverRef(FrameObject frame) {
+ RefWithDisplayData receiverObject = frame.getReceiver().asWithDisplayData();
+ return V8ProtocolUtil.extractProperty(receiverObject,
+ V8ProtocolUtil.PropertyNameGetter.THIS);
+ }
+
+ /**
+ * Constructs a ValueMirror given a V8 debugger object specification.
+ *
+ * @param jsonValue containing the object specification from the V8 debugger
+ * @return a {@link PropertyHoldingValueMirror} instance, containing data
+ * from jsonValue; not null
+ */
+ public static PropertyHoldingValueMirror createMirrorFromLookup(ValueHandle valueHandle) {
+ String text = valueHandle.text();
+ if (text == null) {
+ throw new ValueLoadException("Bad lookup result");
+ }
+ String typeString = valueHandle.type();
+ String className = valueHandle.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(typeString, className);
+ if (type == null) {
+ throw new ValueLoadException("Bad lookup result: type field not recognized: " + typeString);
+ }
+ return createMirrorFromLookup(valueHandle, type);
+ }
+
+ /**
+ * Constructs a {@link ValueMirror} given a V8 debugger object specification if it's possible.
+ * @return a {@link ValueMirror} instance, containing data
+ * from {@code jsonValue}; or {@code null} if {@code jsonValue} is not a handle
+ */
+ public static ValueMirror createValueMirrorOptional(DataWithRef handleFromProperty) {
+ RefWithDisplayData withData = handleFromProperty.getWithDisplayData();
+ if (withData == null) {
+ return null;
+ }
+ return createValueMirror(withData);
+ }
+ public static ValueMirror createValueMirrorOptional(ValueHandle valueHandle) {
+ return createValueMirror(valueHandle);
+ }
+
+ private static ValueMirror createValueMirror(ValueHandle valueHandle) {
+ String className = valueHandle.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(valueHandle.type(), className);
+ if (type == null) {
+ throw new ValueLoadException("Bad value object");
+ }
+ String text = valueHandle.text();
+ return createMirrorFromLookup(valueHandle, type).getValueMirror();
+ }
+
+ private static ValueMirror createValueMirror(RefWithDisplayData jsonValue) {
+ String className = jsonValue.className();
+ Type type = JsDataTypeUtil.fromJsonTypeAndClassName(jsonValue.type(), className);
+ if (type == null) {
+ throw new ValueLoadException("Bad value object");
+ }
+ { // try another format
+ if (Type.isObjectType(type)) {
+ int refId = (int) jsonValue.ref();
+ return ValueMirror.createObjectUnknownProperties(refId, type, className);
+ } else {
+ // try another format
+ Object valueObj = jsonValue.value();
+ String valueStr;
+ if (valueObj == null) {
+ valueStr = jsonValue.type(); // e.g. "undefined"
+ } else {
+ valueStr = valueObj.toString();
+ }
+ return ValueMirror.createScalar(valueStr, type, className).getValueMirror();
+ }
+ }
+ }
+
+ private static PropertyHoldingValueMirror createMirrorFromLookup(ValueHandle valueHandle,
+ Type type) {
+ if (Type.isObjectType(type)) {
+ ObjectValueHandle objectValueHandle = valueHandle.asObject();
+ int refId = (int) valueHandle.handle();
+ SubpropertiesMirror subpropertiesMirror;
+ if (type == Type.TYPE_FUNCTION) {
+ FunctionValueHandle functionValueHandle = objectValueHandle.asFunction();
+ subpropertiesMirror = new SubpropertiesMirror.FunctionValueBased(functionValueHandle,
+ FUNCTION_PROPERTY_FACTORY2);
+ } else {
+ subpropertiesMirror =
+ new SubpropertiesMirror.ObjectValueBased(objectValueHandle, null);
+ }
+ return ValueMirror.createObject(refId, subpropertiesMirror, type, valueHandle.className());
+ } else {
+ return ValueMirror.createScalar(valueHandle.text(), type, valueHandle.className());
+ }
+ }
+
+ // TODO(peter.rybin): Get rid of this monstrosity once we switched to type JSON interfaces.
+ private static final
+ SubpropertiesMirror.JsonBased.AdditionalPropertyFactory<FunctionValueHandle>
+ FUNCTION_PROPERTY_FACTORY2 =
+ new SubpropertiesMirror.JsonBased.AdditionalPropertyFactory<FunctionValueHandle>() {
+ public Object createAdditionalProperties(FunctionValueHandle jsonWithProperties) {
+ Long pos = jsonWithProperties.position();
+ if (pos == null) {
+ pos = Long.valueOf(FunctionAdditionalProperties.NO_POSITION);
+ }
+ Long scriptId = jsonWithProperties.scriptId();
+ if (scriptId == null) {
+ scriptId = Long.valueOf(FunctionAdditionalProperties.NO_SCRIPT_ID);
+ }
+ return new FunctionAdditionalProperties(pos.intValue(), scriptId.intValue());
+ }
+ };
+
+ public static <MESSAGE, RES, EX extends Exception> RES callV8Sync(
+ V8CommandSender<MESSAGE, EX> commandSender, MESSAGE message,
+ V8BlockingCallback<RES> callback) throws EX {
+ return callV8Sync(commandSender, message, callback,
+ CallbackSemaphore.OPERATION_TIMEOUT_MS);
+ }
+
+ public static <MESSAGE, RES, EX extends Exception> RES callV8Sync(
+ V8CommandSender<MESSAGE, EX> commandSender,
+ MESSAGE message, final V8BlockingCallback<RES> callback, long timeoutMs) throws EX {
+ CallbackSemaphore syncCallback = new CallbackSemaphore();
+ final Exception [] exBuff = { null };
+ // A long way of creating buffer for generic type without warnings.
+ final List<RES> resBuff = new ArrayList<RES>(Collections.nCopies(1, (RES)null));
+ V8CommandProcessor.V8HandlerCallback callbackWrapper =
+ new V8CommandProcessor.V8HandlerCallback() {
+ public void failure(String message) {
+ exBuff[0] = new Exception("Failure: " + message);
+ }
+
+ public void messageReceived(CommandResponse response) {
+ RES result = callback.messageReceived(response);
+ resBuff.set(0, result);
+ }
+ };
+ commandSender.sendV8CommandAsync(message, true, callbackWrapper, syncCallback);
+
+ boolean waitRes;
+ try {
+ waitRes = syncCallback.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (RuntimeException e) {
+ throw new CallbackException(e);
+ }
+
+ if (!waitRes) {
+ throw new CallbackException("Timeout");
+ }
+
+ if (exBuff[0] != null) {
+ throw new CallbackException(exBuff[0]);
+ }
+
+ return resBuff.get(0);
+ }
+
+ /**
+ * Special kind of exceptions for problems in receiving or waiting for the answer.
+ * Clients may try to catch it.
+ */
+ public static class CallbackException extends RuntimeException {
+ CallbackException() {
+ }
+ CallbackException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ CallbackException(String message) {
+ super(message);
+ }
+ CallbackException(Throwable cause) {
+ super(cause);
+ }
+ }
+}