/* * 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.github.sdbg.debug.core.internal.webkit.model; import com.github.sdbg.debug.core.SDBGDebugCorePlugin; import com.github.sdbg.debug.core.internal.webkit.protocol.WebkitCallback; import com.github.sdbg.debug.core.internal.webkit.protocol.WebkitPropertyDescriptor; import com.github.sdbg.debug.core.internal.webkit.protocol.WebkitRemoteObject; import com.github.sdbg.debug.core.internal.webkit.protocol.WebkitResult; import com.github.sdbg.debug.core.internal.webkit.protocol.WebkitScope; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import org.eclipse.debug.core.model.IVariable; /** * A helper class to asynchronously collect variable values for the WebkitDebugStackFrame class. */ class VariableCollector { private WebkitDebugTarget target; private WebkitDebugVariable parentVariable; private CountDownLatch latch; private List<IVariable> variables = new ArrayList<IVariable>(); private List<WebkitPropertyDescriptor> webkitProperties = new ArrayList<WebkitPropertyDescriptor>(); public static VariableCollector createCollector(WebkitDebugTarget target, final WebkitDebugVariable 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(), variable != null && variable.isScope() && "global".equals(variable.getName()), variable != null && variable.isScope() && "local".equals(variable.getName())); } catch (Throwable t) { SDBGDebugCorePlugin.logError(t); collector.worked(); } } }); } catch (Throwable e) { SDBGDebugCorePlugin.logError(e); collector.worked(); } } return collector; } public static VariableCollector createCollector(WebkitDebugTarget target, WebkitRemoteObject thisObject, WebkitRemoteObject exception, boolean flattenLocalScope, WebkitScope... scopes) { final VariableCollector collector = new VariableCollector(target, flattenLocalScope ? 1 : 0); if (thisObject != null) { collector.createThisVariable(thisObject); } if (exception != null) { collector.createExceptionVariable(exception); } if (flattenLocalScope) { for (WebkitScope scope : scopes) { if (scope.isLocal()) { try { target.getConnection().getRuntime().getProperties( scope.getObject(), true, false, new WebkitCallback<WebkitPropertyDescriptor[]>() { @Override public void handleResult(WebkitResult<WebkitPropertyDescriptor[]> result) { try { collector.collectFields(result, true, false, true); } catch (Throwable t) { SDBGDebugCorePlugin.logError(t); collector.worked(); } } }); } catch (Throwable e) { SDBGDebugCorePlugin.logError(e); collector.worked(); } } } } for (WebkitScope scope : scopes) { if (!flattenLocalScope || !scope.isLocal()) { collector.createScopeVariable(scope.getObject(), scope.getType()); } } return collector; } public static VariableCollector empty() { return new VariableCollector(null, 0); } public static VariableCollector fixed(WebkitDebugTarget target, List<IVariable> variables) { return new VariableCollector(target, variables); } public VariableCollector(WebkitDebugTarget target, List<IVariable> variables) { this.target = target; this.variables.addAll(variables); this.latch = new CountDownLatch(0); } private VariableCollector(WebkitDebugTarget target, int work) { this(target, work, null); } private VariableCollector(WebkitDebugTarget target, int work, WebkitDebugVariable parentVariable) { this.target = target; this.parentVariable = parentVariable; this.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 isStatic, boolean isLocal) { if (!results.isError()) { WebkitPropertyDescriptor[] properties = results.getResult(); if (shouldSort) { properties = sort(properties); } webkitProperties = Arrays.asList(properties); for (WebkitPropertyDescriptor descriptor : properties) { if (descriptor.isEnumerable() || "__proto__".equals(descriptor.getName())) { if (!shouldFilter(descriptor)) { WebkitDebugVariable variable = new WebkitDebugVariable(target, descriptor); if (parentVariable != null) { variable.setParent(parentVariable); } variable.setIsStatic(isStatic); variable.setIsLocal(isLocal); variables.add(variable); } } } } 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()) { WebkitDebugVariable variable = new WebkitDebugVariable(target, descriptor); variable.setIsStatic(true); if (parentVariable != null) { variable.setParent(parentVariable); } variables.add(variable); } } } } finally { latch.countDown(); } } private void createExceptionVariable(WebkitRemoteObject thisObject) { WebkitDebugVariable variable = new WebkitDebugVariable( target, WebkitPropertyDescriptor.createObjectDescriptor(thisObject, "exception"), true); variables.add(variable); } // private void createLibraryVariable(WebkitRemoteObject libraryObject) { // WebkitDebugVariable variable = new WebkitDebugVariable( // target, // WebkitPropertyDescriptor.createObjectDescriptor(libraryObject, DebuggerUtils.TOP_LEVEL_NAME)); // variable.setIsLibraryObject(true); // variables.add(variable); // } private void createScopeVariable(WebkitRemoteObject object, String name) { WebkitDebugVariable variable = new WebkitDebugVariable( target, WebkitPropertyDescriptor.createObjectDescriptor(object, name), true); variables.add(variable); } private void createThisVariable(WebkitRemoteObject thisObject) { variables.add(new WebkitDebugVariable(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; } } return false; } private WebkitPropertyDescriptor[] sort(WebkitPropertyDescriptor[] properties) { Arrays.sort(properties); return properties; } private void worked() { latch.countDown(); } }