/* * Copyright 2016, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.smalidea.debugging.value; import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.engine.DebugProcessImpl; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.impl.DebuggerContextImpl; import com.intellij.debugger.jdi.VirtualMachineProxyImpl; import com.intellij.openapi.project.Project; import com.sun.jdi.Type; import com.sun.jdi.Value; import com.sun.jdi.VirtualMachine; import org.jf.smalidea.debugging.SmaliCodeFragmentFactory; import org.jf.smalidea.psi.impl.SmaliMethod; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class LazyValue<T extends Value> implements Value { protected final int registerNumber; protected final Project project; protected final SmaliMethod method; protected final String type; private EvaluationContext evaluationContext; private Value value; public LazyValue(SmaliMethod method, Project project, int registerNumber, String type) { this.method = method; this.project = project; this.registerNumber = registerNumber; this.type = type; } public static LazyValue create(@Nonnull SmaliMethod method, @Nonnull Project project, int registerNumber, @Nonnull String type) { if (type.equals("B")) { return new LazyByteValue(method, project, registerNumber, type); } else if (type.equals("S")) { return new LazyShortValue(method, project, registerNumber, type); } else if (type.equals("J")) { return new LazyLongValue(method, project, registerNumber, type); } else if (type.equals("I")) { return new LazyIntegerValue(method, project, registerNumber, type); } else if (type.equals("F")) { return new LazyFloatValue(method, project, registerNumber, type); } else if (type.equals("D")) { return new LazyDoubleValue(method, project, registerNumber, type); } else if (type.equals("Z")) { return new LazyBooleanValue(method, project, registerNumber, type); } else if (type.equals("C")) { return new LazyCharValue(method, project, registerNumber, type); } else if (type.equals("V")) { return new LazyVoidValue(method, project, registerNumber, type); } else if (type.startsWith("[")) { return new LazyArrayReference(method, project, registerNumber, type); } else if (type.equals("Ljava/lang/String;")) { return new LazyStringReference(method, project, registerNumber, type); } else if (type.equals("Ljava/lang/Class;")) { return new LazyClassObjectReference(method, project, registerNumber, type); } else if (type.equals("Ljava/lang/ThreadGroup;")) { return new LazyThreadGroupReference(method, project, registerNumber, type); } else if (type.equals("Ljava/lang/Thread;")) { return new LazyThreadReference(method, project, registerNumber, type); } else if (type.equals("Ljava/lang/ClassLoader;")) { return new LazyClassLoaderReference(method, project, registerNumber, type); } else if (type.startsWith("L")) { return new LazyObjectReference(method, project, registerNumber, type); } return new LazyValue(method, project, registerNumber, type); } @Nullable protected T getNullableValue(boolean allowNull) { if (value == null) { try { if (evaluationContext == null) { final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext(); evaluationContext = debuggerContext.createEvaluationContext(); if (evaluationContext == null) { if (!allowNull) { throw new IllegalStateException("Can't create evaluation context"); } return null; } } value = SmaliCodeFragmentFactory.evaluateRegister(evaluationContext, method, registerNumber, type); evaluationContext = null; } catch (EvaluateException ex) { if (!allowNull) { throw new IllegalStateException(ex); } return null; } } return (T)value; } @Nonnull protected T getValue() { T value = getNullableValue(false); assert value != null; return value; } @Override public Type type() { return getValue().type(); } @Override public VirtualMachine virtualMachine() { if (evaluationContext != null) { return ((VirtualMachineProxyImpl)evaluationContext.getDebugProcess().getVirtualMachineProxy()) .getVirtualMachine(); } else { final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext(); final DebugProcessImpl process = debuggerContext.getDebugProcess(); if (process != null) { return process.getVirtualMachineProxy().getVirtualMachine(); } } return null; } public void setEvaluationContext(@Nonnull EvaluationContext evaluationContext) { this.evaluationContext = evaluationContext; } @Override public boolean equals(Object obj) { Value value = getNullableValue(true); if (value != null) { return value.equals(obj); } return super.equals(obj); } @Override public int hashCode() { Value value = getNullableValue(true); if (value != null) { return value.hashCode(); } return super.hashCode(); } @Override public String toString() { Value value = getNullableValue(true); if (value != null) { return value.toString(); } return super.toString(); } }