|
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 } |