| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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.wip; | |
| 6 | |
| 7 import java.util.ArrayList; | |
| 8 import java.util.Arrays; | |
| 9 import java.util.Collection; | |
| 10 import java.util.Collections; | |
| 11 import java.util.HashMap; | |
| 12 import java.util.List; | |
| 13 import java.util.Map; | |
| 14 import java.util.concurrent.atomic.AtomicInteger; | |
| 15 | |
| 16 import org.chromium.sdk.CallbackSemaphore; | |
| 17 import org.chromium.sdk.JsObjectProperty; | |
| 18 import org.chromium.sdk.JsVariable; | |
| 19 import org.chromium.sdk.RelayOk; | |
| 20 import org.chromium.sdk.RemoteValueMapping; | |
| 21 import org.chromium.sdk.SyncCallback; | |
| 22 import org.chromium.sdk.internal.wip.WipExpressionBuilder.PropertyNameBuilder; | |
| 23 import org.chromium.sdk.internal.wip.WipExpressionBuilder.ValueNameBuilder; | |
| 24 import org.chromium.sdk.internal.wip.protocol.input.debugger.FunctionDetailsValu
e; | |
| 25 import org.chromium.sdk.internal.wip.protocol.input.debugger.GetFunctionDetailsD
ata; | |
| 26 import org.chromium.sdk.internal.wip.protocol.input.debugger.LocationValue; | |
| 27 import org.chromium.sdk.internal.wip.protocol.input.runtime.GetPropertiesData; | |
| 28 import org.chromium.sdk.internal.wip.protocol.input.runtime.PropertyDescriptorVa
lue; | |
| 29 import org.chromium.sdk.internal.wip.protocol.output.debugger.GetFunctionDetails
Params; | |
| 30 import org.chromium.sdk.internal.wip.protocol.output.runtime.GetPropertiesParams
; | |
| 31 import org.chromium.sdk.util.AsyncFuture; | |
| 32 import org.chromium.sdk.util.AsyncFuture.Callback; | |
| 33 import org.chromium.sdk.util.AsyncFutureRef; | |
| 34 import org.chromium.sdk.util.GenericCallback; | |
| 35 import org.chromium.sdk.util.MethodIsBlockingException; | |
| 36 | |
| 37 /** | |
| 38 * Responsible for loading values of properties. It works in pair with {@link Wi
pValueBuilder}. | |
| 39 * TODO: add a cache for already loaded values if remote protocol ever has | |
| 40 * permanent object ids (same object reported under the same id within a debug c
ontext). | |
| 41 */ | |
| 42 public abstract class WipValueLoader implements RemoteValueMapping { | |
| 43 private final WipTabImpl tabImpl; | |
| 44 private final AtomicInteger cacheStateRef = new AtomicInteger(1); | |
| 45 private final WipValueBuilder valueBuilder = new WipValueBuilder(this); | |
| 46 | |
| 47 public WipValueLoader(WipTabImpl tabImpl) { | |
| 48 this.tabImpl = tabImpl; | |
| 49 } | |
| 50 | |
| 51 @Override | |
| 52 public void clearCaches() { | |
| 53 cacheStateRef.incrementAndGet(); | |
| 54 } | |
| 55 | |
| 56 WipValueBuilder getValueBuilder() { | |
| 57 return valueBuilder; | |
| 58 } | |
| 59 | |
| 60 WipTabImpl getTabImpl() { | |
| 61 return tabImpl; | |
| 62 } | |
| 63 | |
| 64 /** | |
| 65 * Loads object properties. It starts and executes a load operation of a corre
sponding | |
| 66 * {@link AsyncFuture}. The operation is fully synchronous, so this method nor
mally | |
| 67 * blocks. There is a chance that other thread is already executing load opera
tion. | |
| 68 * In this case the method will return immediately, but {@link AsyncFuture} wi
ll hold | |
| 69 * thread until the operation finishes. | |
| 70 * @param innerNameBuilder name builder for qualified names of all properties
and subproperties | |
| 71 * @param futureRef future reference that will hold result of load operation | |
| 72 */ | |
| 73 void loadJsObjectPropertiesInFuture(final String objectId, | |
| 74 PropertyNameBuilder innerNameBuilder, boolean reload, int currentCacheStat
e, | |
| 75 AsyncFutureRef<Getter<ObjectProperties>> futureRef) throws MethodIsBlockin
gException { | |
| 76 ObjectPropertyProcessor propertyProcessor = | |
| 77 new ObjectPropertyProcessor(innerNameBuilder, objectId); | |
| 78 loadPropertiesInFuture(objectId, propertyProcessor, reload, currentCacheStat
e, futureRef); | |
| 79 } | |
| 80 | |
| 81 int getCacheState() { | |
| 82 return cacheStateRef.get(); | |
| 83 } | |
| 84 | |
| 85 abstract String getObjectGroupId(); | |
| 86 | |
| 87 /** | |
| 88 * A utility method that initializes {@link AsyncFuture} of an object without
properties. | |
| 89 */ | |
| 90 static void setEmptyJsObjectProperties(AsyncFutureRef<Getter<ObjectProperties>
> output) { | |
| 91 output.initializeTrivial(EMPTY_OBJECT_PROPERTIES_GETTER); | |
| 92 } | |
| 93 | |
| 94 /** | |
| 95 * A getter that either returns a value or throws an exception with some failu
re description. | |
| 96 * Exception is the only means of passing message about some problem to user i
n SDK API. | |
| 97 */ | |
| 98 static abstract class Getter<T> { | |
| 99 abstract T get(); | |
| 100 | |
| 101 static <V> Getter<V> newNormal(final V value) { | |
| 102 return new Getter<V>() { | |
| 103 @Override | |
| 104 V get() { | |
| 105 return value; | |
| 106 } | |
| 107 }; | |
| 108 } | |
| 109 | |
| 110 static <S> Getter<S> newFailure(final Exception cause) { | |
| 111 return new Getter<S>() { | |
| 112 @Override | |
| 113 S get() { | |
| 114 throw new RuntimeException("Failed to load properties", cause); | |
| 115 } | |
| 116 }; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 interface ObjectProperties { | |
| 121 List<? extends JsObjectProperty> properties(); | |
| 122 JsVariable getProperty(String name); | |
| 123 | |
| 124 List<? extends JsVariable> internalProperties(); | |
| 125 int getCacheState(); | |
| 126 } | |
| 127 | |
| 128 /** | |
| 129 * An abstract processor that is reads protocol response and creates proper | |
| 130 * property objects. This may be implemented differently for objects, function
s or | |
| 131 * scopes. | |
| 132 */ | |
| 133 interface LoadPostprocessor<RES> { | |
| 134 RES process(List<? extends PropertyDescriptorValue> propertyList, int curren
tCacheState); | |
| 135 RES getEmptyResult(); | |
| 136 RES forException(Exception exception); | |
| 137 } | |
| 138 | |
| 139 private class ObjectPropertyProcessor implements LoadPostprocessor<Getter<Obje
ctProperties>> { | |
| 140 private final PropertyNameBuilder propertyNameBuilder; | |
| 141 private final String objectId; | |
| 142 | |
| 143 ObjectPropertyProcessor(PropertyNameBuilder propertyNameBuilder, String obje
ctId) { | |
| 144 this.propertyNameBuilder = propertyNameBuilder; | |
| 145 this.objectId = objectId; | |
| 146 } | |
| 147 | |
| 148 @Override | |
| 149 public Getter<ObjectProperties> process( | |
| 150 List<? extends PropertyDescriptorValue> propertyList, final int currentC
acheState) { | |
| 151 final List<JsObjectProperty> properties = | |
| 152 new ArrayList<JsObjectProperty>(propertyList.size()); | |
| 153 final List<JsVariable> internalProperties = new ArrayList<JsVariable>(2); | |
| 154 | |
| 155 for (PropertyDescriptorValue propertyDescriptor : propertyList) { | |
| 156 String name = propertyDescriptor.name(); | |
| 157 boolean isInternal = INTERNAL_PROPERTY_NAME.contains(name); | |
| 158 | |
| 159 ValueNameBuilder valueNameBuilder = | |
| 160 WipExpressionBuilder.createValueOfPropertyNameBuilder(name, property
NameBuilder); | |
| 161 | |
| 162 JsObjectProperty property = valueBuilder.createObjectProperty(propertyDe
scriptor, | |
| 163 objectId, valueNameBuilder); | |
| 164 if (isInternal) { | |
| 165 internalProperties.add(property); | |
| 166 } else { | |
| 167 properties.add(property); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 final ObjectProperties result = new ObjectProperties() { | |
| 172 private volatile Map<String, JsVariable> propertyMap = null; | |
| 173 | |
| 174 @Override | |
| 175 public List<? extends JsObjectProperty> properties() { | |
| 176 return properties; | |
| 177 } | |
| 178 | |
| 179 @Override | |
| 180 public List<? extends JsVariable> internalProperties() { | |
| 181 return internalProperties; | |
| 182 } | |
| 183 | |
| 184 @Override | |
| 185 public JsVariable getProperty(String name) { | |
| 186 Map<String, JsVariable> map = propertyMap; | |
| 187 if (map == null) { | |
| 188 List<? extends JsVariable> list = properties(); | |
| 189 map = new HashMap<String, JsVariable>(list.size()); | |
| 190 for (JsVariable property : list) { | |
| 191 map.put(property.getName(), property); | |
| 192 } | |
| 193 // Possibly overwrite other already created map, but we don't care a
bout instance here. | |
| 194 propertyMap = map; | |
| 195 } | |
| 196 return map.get(name); | |
| 197 } | |
| 198 | |
| 199 @Override | |
| 200 public int getCacheState() { | |
| 201 return currentCacheState; | |
| 202 } | |
| 203 }; | |
| 204 return Getter.newNormal(result); | |
| 205 } | |
| 206 | |
| 207 @Override | |
| 208 public Getter<ObjectProperties> getEmptyResult() { | |
| 209 return EMPTY_OBJECT_PROPERTIES_GETTER; | |
| 210 } | |
| 211 | |
| 212 @Override | |
| 213 public Getter<ObjectProperties> forException(Exception exception) { | |
| 214 return Getter.newFailure(exception); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 private static final Getter<ObjectProperties> EMPTY_OBJECT_PROPERTIES_GETTER = | |
| 219 Getter.newNormal(((ObjectProperties) new ObjectProperties() { | |
| 220 @Override public List<? extends JsObjectProperty> properties() { | |
| 221 return Collections.emptyList(); | |
| 222 } | |
| 223 @Override public JsVariable getProperty(String name) { | |
| 224 return null; | |
| 225 } | |
| 226 @Override public List<? extends JsVariable> internalProperties() { | |
| 227 return Collections.emptyList(); | |
| 228 } | |
| 229 @Override public int getCacheState() { | |
| 230 return Integer.MAX_VALUE; | |
| 231 } | |
| 232 })); | |
| 233 | |
| 234 <RES> void loadPropertiesInFuture(final String objectId, | |
| 235 final LoadPostprocessor<RES> propertyPostprocessor, boolean reload, | |
| 236 final int currentCacheState, AsyncFutureRef<RES> futureRef) | |
| 237 throws MethodIsBlockingException { | |
| 238 if (objectId == null) { | |
| 239 futureRef.initializeTrivial(propertyPostprocessor.getEmptyResult()); | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 // The entire operation that first loads properties from remote and then pos
tprocess them | |
| 244 // (without occupying Dispatch thread). | |
| 245 // The operation is sync because we don't want to do postprocessing in Dispa
tch thread. | |
| 246 AsyncFuture.SyncOperation<RES> syncOperation = new AsyncFuture.SyncOperation
<RES>() { | |
| 247 @Override | |
| 248 protected RES runSync() throws MethodIsBlockingException { | |
| 249 // Get response from remote. | |
| 250 LoadPropertiesResponse response = loadRawPropertiesSync(objectId); | |
| 251 | |
| 252 // Process result. | |
| 253 return response.accept(new LoadPropertiesResponse.Visitor<RES>() { | |
| 254 @Override | |
| 255 public RES visitData(GetPropertiesData data) { | |
| 256 // TODO: check exception. | |
| 257 return propertyPostprocessor.process(data.result(), currentCacheStat
e); | |
| 258 } | |
| 259 | |
| 260 @Override | |
| 261 public RES visitFailure(final Exception exception) { | |
| 262 return propertyPostprocessor.forException(new RuntimeException( | |
| 263 "Failed to read properties from remote", exception)); | |
| 264 } | |
| 265 }); | |
| 266 } | |
| 267 }; | |
| 268 | |
| 269 if (reload) { | |
| 270 futureRef.reinitializeRunning(syncOperation.asAsyncOperation()); | |
| 271 } else { | |
| 272 futureRef.initializeRunning(syncOperation.asAsyncOperation()); | |
| 273 } | |
| 274 syncOperation.execute(); | |
| 275 } | |
| 276 | |
| 277 void loadFunctionLocationInFuture(final String objectId, | |
| 278 AsyncFutureRef<Getter<FunctionDetailsValue>> loadedPositionRef) | |
| 279 throws MethodIsBlockingException { | |
| 280 | |
| 281 AsyncFuture.Operation<Getter<FunctionDetailsValue>> operation = | |
| 282 new AsyncFuture.Operation<Getter<FunctionDetailsValue>>() { | |
| 283 @Override | |
| 284 public RelayOk start(final Callback<Getter<FunctionDetailsValue>> callback
, | |
| 285 SyncCallback syncCallback) { | |
| 286 GetFunctionDetailsParams request = new GetFunctionDetailsParams(objectId
); | |
| 287 GenericCallback<GetFunctionDetailsData> wrappedCallback = | |
| 288 new GenericCallback<GetFunctionDetailsData>() { | |
| 289 @Override public void success(GetFunctionDetailsData value) { | |
| 290 callback.done(Getter.newNormal(value.details())); | |
| 291 } | |
| 292 | |
| 293 @Override public void failure(Exception exception) { | |
| 294 callback.done(Getter.<FunctionDetailsValue>newFailure(exception)); | |
| 295 } | |
| 296 }; | |
| 297 return tabImpl.getCommandProcessor().send(request, wrappedCallback, sync
Callback); | |
| 298 } | |
| 299 }; | |
| 300 | |
| 301 loadedPositionRef.initializeRunning(operation); | |
| 302 } | |
| 303 | |
| 304 /** | |
| 305 * Response is either data or error message. We wrap whatever it is for postpr
ocessing | |
| 306 * that is conducted off Dispatch thread. | |
| 307 */ | |
| 308 private static abstract class LoadPropertiesResponse { | |
| 309 interface Visitor<R> { | |
| 310 R visitData(GetPropertiesData response); | |
| 311 | |
| 312 R visitFailure(Exception exception); | |
| 313 } | |
| 314 abstract <R> R accept(Visitor<R> visitor); | |
| 315 } | |
| 316 | |
| 317 private LoadPropertiesResponse loadRawPropertiesSync(String objectId) | |
| 318 throws MethodIsBlockingException { | |
| 319 final LoadPropertiesResponse[] result = { null }; | |
| 320 GenericCallback<GetPropertiesData> callback = | |
| 321 new GenericCallback<GetPropertiesData>() { | |
| 322 @Override | |
| 323 public void success(final GetPropertiesData value) { | |
| 324 result[0] = new LoadPropertiesResponse() { | |
| 325 @Override | |
| 326 <R> R accept(Visitor<R> visitor) { | |
| 327 return visitor.visitData(value); | |
| 328 } | |
| 329 }; | |
| 330 } | |
| 331 | |
| 332 @Override | |
| 333 public void failure(final Exception exception) { | |
| 334 result[0] = new LoadPropertiesResponse() { | |
| 335 @Override | |
| 336 <R> R accept(Visitor<R> visitor) { | |
| 337 return visitor.visitFailure(exception); | |
| 338 } | |
| 339 }; | |
| 340 } | |
| 341 }; | |
| 342 | |
| 343 final GetPropertiesParams request; | |
| 344 { | |
| 345 boolean ownProperties = true; | |
| 346 request = new GetPropertiesParams(objectId, ownProperties); | |
| 347 } | |
| 348 | |
| 349 CallbackSemaphore callbackSemaphore = new CallbackSemaphore(); | |
| 350 RelayOk relayOk = | |
| 351 tabImpl.getCommandProcessor().send(request, callback, callbackSemaphore)
; | |
| 352 callbackSemaphore.acquireDefault(relayOk); | |
| 353 | |
| 354 return result[0]; | |
| 355 } | |
| 356 | |
| 357 static WipValueLoader castArgument(RemoteValueMapping mapping) { | |
| 358 try { | |
| 359 return (WipValueLoader) mapping; | |
| 360 } catch (ClassCastException e) { | |
| 361 throw new IllegalArgumentException("Incorrect evaluate context argument",
e); | |
| 362 } | |
| 363 } | |
| 364 | |
| 365 // List is too short to use HashSet. | |
| 366 private static final Collection<String> INTERNAL_PROPERTY_NAME = | |
| 367 Arrays.asList("__proto__", "constructor"); | |
| 368 } | |
| OLD | NEW |