/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.tools.debug.core.dartium;
import com.google.dart.tools.debug.core.DartDebugCorePlugin;
import com.google.dart.tools.debug.core.webkit.WebkitCallback;
import com.google.dart.tools.debug.core.webkit.WebkitPropertyDescriptor;
import com.google.dart.tools.debug.core.webkit.WebkitRemoteObject;
import com.google.dart.tools.debug.core.webkit.WebkitResult;
import org.eclipse.debug.core.model.IVariable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* A helper class to asynchronously collect variable values for the DartiumDebugStackFrame class.
*/
class VariableCollector {
public static VariableCollector createCollector(DartiumDebugTarget target,
DartiumDebugVariable variable, List<WebkitRemoteObject> remoteObjects) {
final VariableCollector collector = new VariableCollector(
target,
remoteObjects.size(),
variable);
for (final WebkitRemoteObject obj : remoteObjects) {
try {
target.getConnection().getRuntime().getProperties(
obj,
true,
false,
new WebkitCallback<WebkitPropertyDescriptor[]>() {
@Override
public void handleResult(WebkitResult<WebkitPropertyDescriptor[]> result) {
try {
collector.collectFields(result, !obj.isList(), !obj.isList(), false);
} catch (Throwable t) {
DartDebugCorePlugin.logError(t);
collector.worked();
}
}
});
} catch (Throwable e) {
DartDebugCorePlugin.logError(e);
collector.worked();
}
}
return collector;
}
public static VariableCollector createCollector(DartiumDebugTarget target,
WebkitRemoteObject thisObject, List<WebkitRemoteObject> remoteObjects,
WebkitRemoteObject libraryObject, WebkitRemoteObject exception) {
final VariableCollector collector = new VariableCollector(target, remoteObjects.size());
if (exception != null) {
collector.createExceptionVariable(exception);
}
// if (libraryObject != null) {
// collector.createLibraryVariable(libraryObject);
// }
if (thisObject != null) {
collector.createThisVariable(thisObject);
}
for (final WebkitRemoteObject obj : remoteObjects) {
try {
target.getConnection().getRuntime().getProperties(
obj,
true,
false,
new WebkitCallback<WebkitPropertyDescriptor[]>() {
@Override
public void handleResult(WebkitResult<WebkitPropertyDescriptor[]> result) {
try {
collector.collectFields(result, false, !obj.isList(), true);
} catch (Throwable t) {
DartDebugCorePlugin.logError(t);
collector.worked();
}
}
});
} catch (Throwable e) {
DartDebugCorePlugin.logError(e);
collector.worked();
}
}
return collector;
}
public static VariableCollector empty() {
return new VariableCollector(null, 0);
}
public static VariableCollector fixed(DartiumDebugTarget target, List<IVariable> variables) {
return new VariableCollector(target, variables);
}
private DartiumDebugTarget target;
private DartiumDebugVariable parentVariable;
private CountDownLatch latch;
private List<IVariable> variables = new ArrayList<IVariable>();
private List<WebkitPropertyDescriptor> webkitProperties = new ArrayList<WebkitPropertyDescriptor>();
public VariableCollector(DartiumDebugTarget target, List<IVariable> variables) {
this.target = target;
this.variables.addAll(variables);
latch = new CountDownLatch(0);
}
private VariableCollector(DartiumDebugTarget target, int work) {
this(target, work, null);
}
private VariableCollector(DartiumDebugTarget target, int work, DartiumDebugVariable parentVariable) {
this.target = target;
this.parentVariable = parentVariable;
latch = new CountDownLatch(work);
}
public IVariable[] getVariables() throws InterruptedException {
latch.await();
return variables.toArray(new IVariable[variables.size()]);
}
public List<WebkitPropertyDescriptor> getWebkitProperties() throws InterruptedException {
latch.await();
return webkitProperties;
}
private void collectFields(WebkitResult<WebkitPropertyDescriptor[]> results, boolean shouldSort,
boolean collectStatics, boolean isLocal) {
boolean gettingStaticFields = false;
if (!results.isError()) {
WebkitPropertyDescriptor[] properties = results.getResult();
if (shouldSort) {
properties = sort(properties);
}
webkitProperties = Arrays.asList(properties);
for (WebkitPropertyDescriptor descriptor : properties) {
if (descriptor.isEnumerable()) {
if (!shouldFilter(descriptor)) {
DartiumDebugVariable variable = new DartiumDebugVariable(target, descriptor);
// TODO(devoncarew): Dartium sends us lots of properties as locals that aren't really.
//variable.setIsLocal(isLocal);
if (parentVariable != null) {
variable.setParent(parentVariable);
}
variables.add(variable);
}
} else {
// Static fields are now shown using the object inspector (Inspect Type...).
// if (parentVariable != null && collectStatics) {
// if (WebkitPropertyDescriptor.STATIC_FIELDS.equals(descriptor.getName())) {
// gettingStaticFields = collectStaticFields(descriptor.getValue(), latch);
// }
// }
}
}
}
if (!gettingStaticFields) {
latch.countDown();
}
}
@SuppressWarnings("unused")
private boolean collectStaticFields(final WebkitRemoteObject classInfo, final CountDownLatch latch) {
try {
target.getConnection().getRuntime().getProperties(
classInfo,
true,
false,
new WebkitCallback<WebkitPropertyDescriptor[]>() {
@Override
public void handleResult(WebkitResult<WebkitPropertyDescriptor[]> result) {
collectStaticFieldsResults(result, latch);
}
});
return true;
} catch (IOException e) {
return false;
}
}
private void collectStaticFieldsResults(WebkitResult<WebkitPropertyDescriptor[]> results,
CountDownLatch latch) {
try {
if (!results.isError()) {
for (WebkitPropertyDescriptor descriptor : sort(results.getResult())) {
if (descriptor.isEnumerable()) {
DartiumDebugVariable variable = new DartiumDebugVariable(target, descriptor);
variable.setIsStatic(true);
if (parentVariable != null) {
variable.setParent(parentVariable);
}
variables.add(variable);
}
}
}
} finally {
latch.countDown();
}
}
private void createExceptionVariable(WebkitRemoteObject thisObject) {
DartiumDebugVariable variable = new DartiumDebugVariable(
target,
WebkitPropertyDescriptor.createObjectDescriptor(thisObject, "exception"),
true);
variables.add(variable);
}
// private void createLibraryVariable(WebkitRemoteObject libraryObject) {
// DartiumDebugVariable variable = new DartiumDebugVariable(
// target,
// WebkitPropertyDescriptor.createObjectDescriptor(libraryObject, DebuggerUtils.TOP_LEVEL_NAME));
// variable.setIsLibraryObject(true);
// variables.add(variable);
// }
private void createThisVariable(WebkitRemoteObject thisObject) {
variables.add(new DartiumDebugVariable(target, WebkitPropertyDescriptor.createObjectDescriptor(
thisObject,
"this"), true));
}
private boolean isListNonIndex(WebkitPropertyDescriptor descriptor) {
if (parentVariable != null && parentVariable.isListValue()) {
try {
Integer.parseInt(descriptor.getName());
return false;
} catch (NumberFormatException nfe) {
return true;
}
} else {
return false;
}
}
/**
* Some specific property filters, to make up for the fact that the enumerable property is not
* always set correctly.
*
* @param descriptor
* @return
*/
private boolean shouldFilter(WebkitPropertyDescriptor descriptor) {
// array fields which are not indexes
if (isListNonIndex(descriptor)) {
return true;
}
// toString function
if (descriptor.getValue() != null && descriptor.getValue().isFunction()) {
if ("toString".equals(descriptor.getName())) {
return true;
}
}
if (descriptor.getName().equals("this")) {
return true;
}
if (descriptor.getName().startsWith(":")) {
return true;
}
return false;
}
private WebkitPropertyDescriptor[] sort(WebkitPropertyDescriptor[] properties) {
Arrays.sort(properties);
return properties;
}
private void worked() {
latch.countDown();
}
}