org.chromium.sdk/src/org/chromium/sdk/internal/ValueLoader.java
changeset 2 e4420d2515f1
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.sdk.internal;
       
     6 
       
     7 import java.util.ArrayList;
       
     8 import java.util.Arrays;
       
     9 import java.util.Collections;
       
    10 import java.util.List;
       
    11 import java.util.concurrent.ConcurrentHashMap;
       
    12 import java.util.concurrent.ConcurrentMap;
       
    13 
       
    14 import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException;
       
    15 import org.chromium.sdk.internal.protocol.ScopeBody;
       
    16 import org.chromium.sdk.internal.protocol.SuccessCommandResponse;
       
    17 import org.chromium.sdk.internal.protocol.data.ObjectValueHandle;
       
    18 import org.chromium.sdk.internal.protocol.data.SomeHandle;
       
    19 import org.chromium.sdk.internal.protocol.data.ValueHandle;
       
    20 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
       
    21 import org.chromium.sdk.internal.tools.v8.V8BlockingCallback;
       
    22 import org.chromium.sdk.internal.tools.v8.V8Helper;
       
    23 import org.chromium.sdk.internal.tools.v8.V8ProtocolUtil;
       
    24 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
       
    25 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory;
       
    26 import org.json.simple.JSONObject;
       
    27 
       
    28 /**
       
    29  * The elaborate factory for {@link ValueMirror}'s, that loads values from remote and
       
    30  * caches them. All the data comes originally in form of JSON strings which may contain
       
    31  * less or more fields, so it creates {@link ValueMirror} or {@link PropertyHoldingValueMirror}
       
    32  * accordingly.
       
    33  */
       
    34 public class ValueLoader {
       
    35   private final ConcurrentMap<Long, ValueMirror> refToMirror =
       
    36       new ConcurrentHashMap<Long, ValueMirror>();
       
    37   private final InternalContext context;
       
    38 
       
    39   ValueLoader(InternalContext context) {
       
    40     this.context = context;
       
    41   }
       
    42 
       
    43   /**
       
    44    * Receives {@link ValueMirror} and makes sure it has its properties loaded.
       
    45    */
       
    46   public PropertyHoldingValueMirror loadSubpropertiesInMirror(ValueMirror mirror) {
       
    47     PropertyHoldingValueMirror references = mirror.getProperties();
       
    48     if (references == null) {
       
    49       // need to look up this value again
       
    50       List<PropertyHoldingValueMirror> loadedMirrors =
       
    51           loadValuesFromRemote(Collections.singletonList(Long.valueOf(mirror.getRef())));
       
    52       references = loadedMirrors.get(0);
       
    53     }
       
    54     return references;
       
    55   }
       
    56 
       
    57   /**
       
    58    * Looks up data for scope on remote.
       
    59    */
       
    60   public List<? extends PropertyReference> loadScopeFields(int scopeNumber, int frameNumber) {
       
    61     DebuggerMessage message = DebuggerMessageFactory.scope(scopeNumber, frameNumber);
       
    62 
       
    63     V8BlockingCallback<List<? extends PropertyReference>> callback =
       
    64         new V8BlockingCallback<List<? extends PropertyReference>>() {
       
    65       @Override
       
    66       protected List<? extends PropertyReference> handleSuccessfulResponse(
       
    67           SuccessCommandResponse response) {
       
    68         return readFromScopeResponse(response);
       
    69       }
       
    70     };
       
    71 
       
    72     try {
       
    73       return V8Helper.callV8Sync(context, message, callback);
       
    74     } catch (ContextDismissedCheckedException e) {
       
    75       context.getDebugSession().maybeRethrowContextException(e);
       
    76       // or
       
    77       return Collections.emptyList();
       
    78     }
       
    79   }
       
    80 
       
    81   private List<? extends PropertyReference> readFromScopeResponse(SuccessCommandResponse response) {
       
    82     List<SomeHandle> refs = response.getRefs();
       
    83 
       
    84     HandleManager handleManager = context.getHandleManager();
       
    85     for (int i = 0; i < refs.size(); i++) {
       
    86       SomeHandle ref = refs.get(i);
       
    87       handleManager.put(ref);
       
    88     }
       
    89     ScopeBody body;
       
    90     try {
       
    91       body = response.getBody().asScopeBody();
       
    92     } catch (JsonProtocolParseException e) {
       
    93       throw new ValueLoadException(e);
       
    94     }
       
    95     ObjectValueHandle objectRef = body.getObject();
       
    96     return V8ProtocolUtil.extractObjectProperties(objectRef);
       
    97   }
       
    98 
       
    99 /**
       
   100    * For each PropertyReference from propertyRefs tries to either: 1. read it from PropertyReference
       
   101    * (possibly cached value) or 2. lookup value by refId from remote
       
   102    */
       
   103   public List<ValueMirror> getOrLoadValueFromRefs(List<? extends PropertyReference> propertyRefs) {
       
   104     ValueMirror[] result = new ValueMirror[propertyRefs.size()];
       
   105     List<Integer> mapForLoadResults = new ArrayList<Integer>();
       
   106     List<PropertyReference> needsLoading = new ArrayList<PropertyReference>();
       
   107 
       
   108     for (int i = 0; i < propertyRefs.size(); i++) {
       
   109       PropertyReference ref = propertyRefs.get(i);
       
   110       ValueMirror mirror = readFromPropertyReference(ref);
       
   111       if (mirror == null) {
       
   112         // We don't have the data (enough) right now. We are requesting them from server.
       
   113         // There might be simultaneous request for the same value, which is a normal though
       
   114         // undesired case.
       
   115         needsLoading.add(ref);
       
   116         mapForLoadResults.add(i);
       
   117       }
       
   118       result[i] = mirror;
       
   119     }
       
   120 
       
   121     List<Long> refIds = getRefIdFromReferences(needsLoading);
       
   122     List<PropertyHoldingValueMirror> loadedMirrors = loadValuesFromRemote(refIds);
       
   123     assert refIds.size() == loadedMirrors.size();
       
   124     for (int i = 0; i < loadedMirrors.size(); i++) {
       
   125       int pos = mapForLoadResults.get(i);
       
   126       result[pos] = loadedMirrors.get(i).getValueMirror();
       
   127     }
       
   128     return Arrays.asList(result);
       
   129   }
       
   130 
       
   131   private static List<Long> getRefIdFromReferences(final List<PropertyReference> propertyRefs) {
       
   132     List<Long> result = new ArrayList<Long>(propertyRefs.size());
       
   133     for (PropertyReference ref : propertyRefs) {
       
   134       result.add(Long.valueOf(ref.getRef()));
       
   135     }
       
   136     return result;
       
   137   }
       
   138 
       
   139   /**
       
   140    * Reads data from caches or from JSON from propertyReference. Never accesses remote.
       
   141    */
       
   142   private ValueMirror readFromPropertyReference(PropertyReference propertyReference) {
       
   143     Long refIdObject = propertyReference.getRef();
       
   144 
       
   145     ValueMirror mirror = refToMirror.get(refIdObject);
       
   146     if (mirror != null) {
       
   147       return mirror;
       
   148     }
       
   149     SomeHandle cachedHandle = context.getHandleManager().getHandle(refIdObject);
       
   150     // If we have cached handle, we reads cached handle, not using one from propertyeReference
       
   151     // because we expect to find more complete version in cache. Is it ok?
       
   152     if (cachedHandle != null) {
       
   153       ValueHandle valueHandle;
       
   154       try {
       
   155         valueHandle = cachedHandle.asValueHandle();
       
   156       } catch (JsonProtocolParseException e) {
       
   157         throw new RuntimeException(e);
       
   158       }
       
   159       mirror = V8Helper.createValueMirrorOptional(valueHandle);
       
   160     } else {
       
   161       DataWithRef handleFromProperty = propertyReference.getValueObject();
       
   162 
       
   163       mirror = V8Helper.createValueMirrorOptional(handleFromProperty);
       
   164     }
       
   165     if (mirror != null) {
       
   166       ValueMirror mirror2 = refToMirror.putIfAbsent(refIdObject, mirror);
       
   167       if (mirror2 != null) {
       
   168         mergeMirrors(mirror2, mirror);
       
   169       }
       
   170     }
       
   171 
       
   172     return mirror;
       
   173   }
       
   174 
       
   175   /**
       
   176    * Requests values from remote via "lookup" command. Automatically caches JSON objects
       
   177    * in {@link HandleManager}.
       
   178    * @param propertyRefIds list of ref ids we need to look up
       
   179    * @return loaded value mirrors in the same order as in propertyRefIds
       
   180    */
       
   181   public List<PropertyHoldingValueMirror> loadValuesFromRemote(final List<Long> propertyRefIds) {
       
   182     if (propertyRefIds.isEmpty()) {
       
   183       return Collections.emptyList();
       
   184     }
       
   185 
       
   186     DebuggerMessage message = DebuggerMessageFactory.lookup(propertyRefIds, false);
       
   187 
       
   188     V8BlockingCallback<List<PropertyHoldingValueMirror>> callback =
       
   189         new V8BlockingCallback<List<PropertyHoldingValueMirror>>() {
       
   190       @Override
       
   191       protected List<PropertyHoldingValueMirror> handleSuccessfulResponse(
       
   192           SuccessCommandResponse response) {
       
   193         return readResponseFromLookup(response, propertyRefIds);
       
   194       }
       
   195     };
       
   196 
       
   197     try {
       
   198       return V8Helper.callV8Sync(context, message, callback);
       
   199     } catch (ContextDismissedCheckedException e) {
       
   200       context.getDebugSession().maybeRethrowContextException(e);
       
   201       // or
       
   202       throw new ValueLoadException("Invalid context", e);
       
   203     }
       
   204   }
       
   205 
       
   206   private List<PropertyHoldingValueMirror> readResponseFromLookup(
       
   207       SuccessCommandResponse successResponse, List<Long> propertyRefIds) {
       
   208     List<PropertyHoldingValueMirror> result =
       
   209         new ArrayList<PropertyHoldingValueMirror>(propertyRefIds.size());
       
   210     JSONObject body;
       
   211     try {
       
   212       body = successResponse.getBody().asLookupMap();
       
   213     } catch (JsonProtocolParseException e) {
       
   214       throw new ValueLoadException(e);
       
   215     }
       
   216     for (int i = 0; i < propertyRefIds.size(); i++) {
       
   217       int ref = propertyRefIds.get(i).intValue();
       
   218       JSONObject value = JsonUtil.getAsJSON(body, String.valueOf(ref));
       
   219       if (value == null) {
       
   220         throw new ValueLoadException("Failed to find value for ref=" + ref);
       
   221       }
       
   222       SomeHandle smthHandle = context.getHandleManager().put((long)ref, value);
       
   223       ValueHandle valueHandle;
       
   224       try {
       
   225         valueHandle = smthHandle.asValueHandle();
       
   226       } catch (JsonProtocolParseException e) {
       
   227         throw new ValueLoadException(e);
       
   228       }
       
   229 
       
   230       result.add(readMirrorFromLookup(ref, valueHandle));
       
   231     }
       
   232     return result;
       
   233   }
       
   234 
       
   235   /**
       
   236    * Constructs a ValueMirror given a V8 debugger object specification and the
       
   237    * value name.
       
   238    *
       
   239    * @param jsonValue containing the object specification from the V8 debugger
       
   240    * @param ref
       
   241    * @return a ValueMirror instance with the specified name, containing data
       
   242    *         from handle, or {@code null} if {@code handle} is not a handle
       
   243    */
       
   244   private PropertyHoldingValueMirror readMirrorFromLookup(int ref, ValueHandle jsonValue) {
       
   245     PropertyHoldingValueMirror propertiesMirror = V8Helper.createMirrorFromLookup(jsonValue);
       
   246     ValueMirror newMirror = propertiesMirror.getValueMirror();
       
   247 
       
   248     ValueMirror oldMirror = refToMirror.putIfAbsent((long)ref, newMirror);
       
   249     if (oldMirror != null) {
       
   250       mergeMirrors(oldMirror, newMirror);
       
   251     }
       
   252     return propertiesMirror;
       
   253   }
       
   254 
       
   255   private static void mergeMirrors(ValueMirror baseMirror, ValueMirror alternativeMirror) {
       
   256     baseMirror.mergeFrom(alternativeMirror);
       
   257   }
       
   258 }