--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoader.java Wed Dec 23 17:13:18 2009 -0800
@@ -0,0 +1,258 @@
+// 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
+import org.chromium.sdk.internal.protocol.ScopeBody;
+import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
+import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
+import org.chromium.sdk.internal.protocol.data.SomeHandle;
+import org.chromium.sdk.internal.protocol.data.ValueHandle;
+import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
+import org.chromium.sdk.internal.tools.v8.V8BlockingCallback;
+import org.chromium.sdk.internal.tools.v8.V8Helper;
+import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
+import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
+import org.json.simple.JSONObject;
+
+/**
+ * The elaborate factory for {@link ValueMirror}'s, that loads values from remote and
+ * caches them. All the data comes originally in form of JSON strings which may contain
+ * less or more fields, so it creates {@link ValueMirror} or {@link PropertyHoldingValueMirror}
+ * accordingly.
+ */
+public class ValueLoader {
+ private final ConcurrentMap<Long, ValueMirror> refToMirror =
+ new ConcurrentHashMap<Long, ValueMirror>();
+ private final InternalContext context;
+
+ ValueLoader(InternalContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Receives {@link ValueMirror} and makes sure it has its properties loaded.
+ */
+ public PropertyHoldingValueMirror loadSubpropertiesInMirror(ValueMirror mirror) {
+ PropertyHoldingValueMirror references = mirror.getProperties();
+ if (references == null) {
+ // need to look up this value again
+ List<PropertyHoldingValueMirror> loadedMirrors =
+ loadValuesFromRemote(Collections.singletonList(Long.valueOf(mirror.getRef())));
+ references = loadedMirrors.get(0);
+ }
+ return references;
+ }
+
+ /**
+ * Looks up data for scope on remote.
+ */
+ public List<? extends PropertyReference> loadScopeFields(int scopeNumber, int frameNumber) {
+ DebuggerMessage message = DebuggerMessageFactory.scope(scopeNumber, frameNumber);
+
+ V8BlockingCallback<List<? extends PropertyReference>> callback =
+ new V8BlockingCallback<List<? extends PropertyReference>>() {
+ @Override
+ protected List<? extends PropertyReference> handleSuccessfulResponse(
+ SuccessCommandResponse response) {
+ return readFromScopeResponse(response);
+ }
+ };
+
+ try {
+ return V8Helper.callV8Sync(context, message, callback);
+ } catch (ContextDismissedCheckedException e) {
+ context.getDebugSession().maybeRethrowContextException(e);
+ // or
+ return Collections.emptyList();
+ }
+ }
+
+ private List<? extends PropertyReference> readFromScopeResponse(SuccessCommandResponse response) {
+ List<SomeHandle> refs = response.getRefs();
+
+ HandleManager handleManager = context.getHandleManager();
+ for (int i = 0; i < refs.size(); i++) {
+ SomeHandle ref = refs.get(i);
+ handleManager.put(ref);
+ }
+ ScopeBody body;
+ try {
+ body = response.getBody().asScopeBody();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+ ObjectValueHandle objectRef = body.getObject();
+ return V8ProtocolUtil.extractObjectProperties(objectRef);
+ }
+
+/**
+ * For each PropertyReference from propertyRefs tries to either: 1. read it from PropertyReference
+ * (possibly cached value) or 2. lookup value by refId from remote
+ */
+ public List<ValueMirror> getOrLoadValueFromRefs(List<? extends PropertyReference> propertyRefs) {
+ ValueMirror[] result = new ValueMirror[propertyRefs.size()];
+ List<Integer> mapForLoadResults = new ArrayList<Integer>();
+ List<PropertyReference> needsLoading = new ArrayList<PropertyReference>();
+
+ for (int i = 0; i < propertyRefs.size(); i++) {
+ PropertyReference ref = propertyRefs.get(i);
+ ValueMirror mirror = readFromPropertyReference(ref);
+ if (mirror == null) {
+ // We don't have the data (enough) right now. We are requesting them from server.
+ // There might be simultaneous request for the same value, which is a normal though
+ // undesired case.
+ needsLoading.add(ref);
+ mapForLoadResults.add(i);
+ }
+ result[i] = mirror;
+ }
+
+ List<Long> refIds = getRefIdFromReferences(needsLoading);
+ List<PropertyHoldingValueMirror> loadedMirrors = loadValuesFromRemote(refIds);
+ assert refIds.size() == loadedMirrors.size();
+ for (int i = 0; i < loadedMirrors.size(); i++) {
+ int pos = mapForLoadResults.get(i);
+ result[pos] = loadedMirrors.get(i).getValueMirror();
+ }
+ return Arrays.asList(result);
+ }
+
+ private static List<Long> getRefIdFromReferences(final List<PropertyReference> propertyRefs) {
+ List<Long> result = new ArrayList<Long>(propertyRefs.size());
+ for (PropertyReference ref : propertyRefs) {
+ result.add(Long.valueOf(ref.getRef()));
+ }
+ return result;
+ }
+
+ /**
+ * Reads data from caches or from JSON from propertyReference. Never accesses remote.
+ */
+ private ValueMirror readFromPropertyReference(PropertyReference propertyReference) {
+ Long refIdObject = propertyReference.getRef();
+
+ ValueMirror mirror = refToMirror.get(refIdObject);
+ if (mirror != null) {
+ return mirror;
+ }
+ SomeHandle cachedHandle = context.getHandleManager().getHandle(refIdObject);
+ // If we have cached handle, we reads cached handle, not using one from propertyeReference
+ // because we expect to find more complete version in cache. Is it ok?
+ if (cachedHandle != null) {
+ ValueHandle valueHandle;
+ try {
+ valueHandle = cachedHandle.asValueHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new RuntimeException(e);
+ }
+ mirror = V8Helper.createValueMirrorOptional(valueHandle);
+ } else {
+ DataWithRef handleFromProperty = propertyReference.getValueObject();
+
+ mirror = V8Helper.createValueMirrorOptional(handleFromProperty);
+ }
+ if (mirror != null) {
+ ValueMirror mirror2 = refToMirror.putIfAbsent(refIdObject, mirror);
+ if (mirror2 != null) {
+ mergeMirrors(mirror2, mirror);
+ }
+ }
+
+ return mirror;
+ }
+
+ /**
+ * Requests values from remote via "lookup" command. Automatically caches JSON objects
+ * in {@link HandleManager}.
+ * @param propertyRefIds list of ref ids we need to look up
+ * @return loaded value mirrors in the same order as in propertyRefIds
+ */
+ public List<PropertyHoldingValueMirror> loadValuesFromRemote(final List<Long> propertyRefIds) {
+ if (propertyRefIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ DebuggerMessage message = DebuggerMessageFactory.lookup(propertyRefIds, false);
+
+ V8BlockingCallback<List<PropertyHoldingValueMirror>> callback =
+ new V8BlockingCallback<List<PropertyHoldingValueMirror>>() {
+ @Override
+ protected List<PropertyHoldingValueMirror> handleSuccessfulResponse(
+ SuccessCommandResponse response) {
+ return readResponseFromLookup(response, propertyRefIds);
+ }
+ };
+
+ try {
+ return V8Helper.callV8Sync(context, message, callback);
+ } catch (ContextDismissedCheckedException e) {
+ context.getDebugSession().maybeRethrowContextException(e);
+ // or
+ throw new ValueLoadException("Invalid context", e);
+ }
+ }
+
+ private List<PropertyHoldingValueMirror> readResponseFromLookup(
+ SuccessCommandResponse successResponse, List<Long> propertyRefIds) {
+ List<PropertyHoldingValueMirror> result =
+ new ArrayList<PropertyHoldingValueMirror>(propertyRefIds.size());
+ JSONObject body;
+ try {
+ body = successResponse.getBody().asLookupMap();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+ for (int i = 0; i < propertyRefIds.size(); i++) {
+ int ref = propertyRefIds.get(i).intValue();
+ JSONObject value = JsonUtil.getAsJSON(body, String.valueOf(ref));
+ if (value == null) {
+ throw new ValueLoadException("Failed to find value for ref=" + ref);
+ }
+ SomeHandle smthHandle = context.getHandleManager().put((long)ref, value);
+ ValueHandle valueHandle;
+ try {
+ valueHandle = smthHandle.asValueHandle();
+ } catch (JsonProtocolParseException e) {
+ throw new ValueLoadException(e);
+ }
+
+ result.add(readMirrorFromLookup(ref, valueHandle));
+ }
+ return result;
+ }
+
+ /**
+ * Constructs a ValueMirror given a V8 debugger object specification and the
+ * value name.
+ *
+ * @param jsonValue containing the object specification from the V8 debugger
+ * @param ref
+ * @return a ValueMirror instance with the specified name, containing data
+ * from handle, or {@code null} if {@code handle} is not a handle
+ */
+ private PropertyHoldingValueMirror readMirrorFromLookup(int ref, ValueHandle jsonValue) {
+ PropertyHoldingValueMirror propertiesMirror = V8Helper.createMirrorFromLookup(jsonValue);
+ ValueMirror newMirror = propertiesMirror.getValueMirror();
+
+ ValueMirror oldMirror = refToMirror.putIfAbsent((long)ref, newMirror);
+ if (oldMirror != null) {
+ mergeMirrors(oldMirror, newMirror);
+ }
+ return propertiesMirror;
+ }
+
+ private static void mergeMirrors(ValueMirror baseMirror, ValueMirror alternativeMirror) {
+ baseMirror.mergeFrom(alternativeMirror);
+ }
+}