|
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.Collection; |
|
9 import java.util.Collections; |
|
10 import java.util.List; |
|
11 |
|
12 import org.chromium.sdk.CallFrame; |
|
13 import org.chromium.sdk.CallbackSemaphore; |
|
14 import org.chromium.sdk.JsScope; |
|
15 import org.chromium.sdk.JsVariable; |
|
16 import org.chromium.sdk.Script; |
|
17 import org.chromium.sdk.SyncCallback; |
|
18 import org.chromium.sdk.internal.InternalContext.ContextDismissedCheckedException; |
|
19 import org.chromium.sdk.internal.protocol.CommandResponse; |
|
20 import org.chromium.sdk.internal.protocol.SuccessCommandResponse; |
|
21 import org.chromium.sdk.internal.protocol.data.ValueHandle; |
|
22 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException; |
|
23 import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException; |
|
24 import org.chromium.sdk.internal.tools.v8.V8CommandProcessor; |
|
25 import org.chromium.sdk.internal.tools.v8.V8Helper; |
|
26 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage; |
|
27 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessageFactory; |
|
28 |
|
29 /** |
|
30 * A generic implementation of the CallFrame interface. |
|
31 */ |
|
32 public class CallFrameImpl implements CallFrame { |
|
33 |
|
34 /** The frame ID as reported by the JavaScript VM. */ |
|
35 private final int frameId; |
|
36 |
|
37 /** The debug context this call frame belongs in. */ |
|
38 private final InternalContext context; |
|
39 |
|
40 /** The underlying frame data from the JavaScript VM. */ |
|
41 private final FrameMirror frameMirror; |
|
42 |
|
43 /** The variables known in this call frame. */ |
|
44 private Collection<JsVariableImpl> variables = null; |
|
45 |
|
46 /** The scopes known in this call frame. */ |
|
47 private List<? extends JsScope> scopes = null; |
|
48 |
|
49 /** The receiver variable known in this call frame. May be null. */ |
|
50 private JsVariable receiverVariable; |
|
51 private boolean receiverVariableLoaded = false; |
|
52 |
|
53 /** |
|
54 * Constructs a call frame for the given handler using the FrameMirror data |
|
55 * from the remote JavaScript VM. |
|
56 * |
|
57 * @param mirror frame in the VM |
|
58 * @param index call frame id (0 is the stack top) |
|
59 * @param context in which the call frame is created |
|
60 */ |
|
61 public CallFrameImpl(FrameMirror mirror, int index, InternalContext context) { |
|
62 this.context = context; |
|
63 this.frameId = index; |
|
64 this.frameMirror = mirror; |
|
65 } |
|
66 |
|
67 public InternalContext getInternalContext() { |
|
68 return context; |
|
69 } |
|
70 |
|
71 @Deprecated |
|
72 public Collection<JsVariableImpl> getVariables() { |
|
73 ensureVariables(); |
|
74 return variables; |
|
75 } |
|
76 |
|
77 public List<? extends JsScope> getVariableScopes() { |
|
78 ensureScopes(); |
|
79 return scopes; |
|
80 } |
|
81 |
|
82 public JsVariable getReceiverVariable() { |
|
83 ensureReceiver(); |
|
84 return this.receiverVariable; |
|
85 } |
|
86 |
|
87 private void ensureVariables() { |
|
88 if (variables == null) { |
|
89 this.variables = Collections.unmodifiableCollection(createVariables()); |
|
90 } |
|
91 } |
|
92 |
|
93 private void ensureScopes() { |
|
94 if (scopes == null) { |
|
95 this.scopes = Collections.unmodifiableList(createScopes()); |
|
96 } |
|
97 } |
|
98 |
|
99 private void ensureReceiver() { |
|
100 if (!receiverVariableLoaded) { |
|
101 PropertyReference ref = frameMirror.getReceiverRef(); |
|
102 if (ref == null) { |
|
103 this.receiverVariable = null; |
|
104 } else { |
|
105 ValueLoader valueLoader = context.getValueLoader(); |
|
106 ValueMirror mirror = |
|
107 valueLoader.getOrLoadValueFromRefs(Collections.singletonList(ref)).get(0); |
|
108 this.receiverVariable = new JsVariableImpl(this, mirror, ref.getName()); |
|
109 } |
|
110 this.receiverVariableLoaded = true; |
|
111 } |
|
112 } |
|
113 |
|
114 public int getLineNumber() { |
|
115 Script script = frameMirror.getScript(); |
|
116 // Recalculate respective to the script start |
|
117 // (frameMirror.getLine() returns the line offset in the resource). |
|
118 return script != null |
|
119 ? frameMirror.getLine() - script.getStartLine() |
|
120 : -1; |
|
121 } |
|
122 |
|
123 public int getCharStart() { |
|
124 return -1; |
|
125 } |
|
126 |
|
127 public int getCharEnd() { |
|
128 return -1; |
|
129 } |
|
130 |
|
131 public String getFunctionName() { |
|
132 return frameMirror.getFunctionName(); |
|
133 } |
|
134 |
|
135 public Script getScript() { |
|
136 return frameMirror.getScript(); |
|
137 } |
|
138 |
|
139 public void evaluateSync(String expression, EvaluateCallback evaluateCallback) |
|
140 throws MethodIsBlockingException { |
|
141 CallbackSemaphore callbackSemaphore = new CallbackSemaphore(); |
|
142 evaluateAsync(expression, evaluateCallback, callbackSemaphore); |
|
143 boolean res = callbackSemaphore.tryAcquireDefault(); |
|
144 if (!res) { |
|
145 evaluateCallback.failure("Timeout"); |
|
146 } |
|
147 } |
|
148 |
|
149 public void evaluateAsync(final String expression, final EvaluateCallback callback, |
|
150 SyncCallback syncCallback) { |
|
151 try { |
|
152 evaluateAsyncImpl(expression, callback, syncCallback); |
|
153 } catch (ContextDismissedCheckedException e) { |
|
154 getInternalContext().getDebugSession().maybeRethrowContextException(e); |
|
155 // or |
|
156 try { |
|
157 callback.failure(e.getMessage()); |
|
158 } finally { |
|
159 syncCallback.callbackDone(null); |
|
160 } |
|
161 } |
|
162 } |
|
163 public void evaluateAsyncImpl(final String expression, final EvaluateCallback callback, |
|
164 SyncCallback syncCallback) throws ContextDismissedCheckedException { |
|
165 DebuggerMessage message = |
|
166 DebuggerMessageFactory.evaluate(expression, getIdentifier(), null, null); |
|
167 |
|
168 V8CommandProcessor.V8HandlerCallback commandCallback = callback == null |
|
169 ? null |
|
170 : new V8CommandProcessor.V8HandlerCallback() { |
|
171 public void messageReceived(CommandResponse response) { |
|
172 SuccessCommandResponse successResponse = response.asSuccess(); |
|
173 if (successResponse != null) { |
|
174 ValueHandle body; |
|
175 try { |
|
176 body = successResponse.getBody().asEvaluateBody(); |
|
177 } catch (JsonProtocolParseException e) { |
|
178 throw new RuntimeException(e); |
|
179 } |
|
180 JsVariable variable = |
|
181 new JsVariableImpl(CallFrameImpl.this, V8Helper.createMirrorFromLookup( |
|
182 body).getValueMirror(), expression); |
|
183 if (variable != null) { |
|
184 callback.success(variable); |
|
185 } else { |
|
186 callback.failure("Evaluation failed"); |
|
187 } |
|
188 } else { |
|
189 callback.failure(response.asFailure().getMessage()); |
|
190 } |
|
191 } |
|
192 |
|
193 public void failure(String message) { |
|
194 callback.failure(message); |
|
195 } |
|
196 }; |
|
197 |
|
198 getInternalContext().sendV8CommandAsync(message, true, commandCallback, |
|
199 syncCallback); |
|
200 } |
|
201 |
|
202 /** |
|
203 * @return this call frame's unique identifier within the V8 VM (0 is the top |
|
204 * frame) |
|
205 */ |
|
206 int getIdentifier() { |
|
207 return frameId; |
|
208 } |
|
209 |
|
210 /** |
|
211 * Initializes this frame with variables based on the frameMirror locals. |
|
212 */ |
|
213 private Collection<JsVariableImpl> createVariables() { |
|
214 List<PropertyReference> refs = frameMirror.getLocals(); |
|
215 List<ValueMirror> mirrors = context.getValueLoader().getOrLoadValueFromRefs(refs); |
|
216 Collection<JsVariableImpl> result = new ArrayList<JsVariableImpl>(refs.size()); |
|
217 for (int i = 0; i < refs.size(); i++) { |
|
218 result.add(new JsVariableImpl(this, mirrors.get(i), refs.get(i).getName())); |
|
219 } |
|
220 return result; |
|
221 } |
|
222 |
|
223 private List<JsScopeImpl> createScopes() { |
|
224 List<ScopeMirror> scopes = frameMirror.getScopes(); |
|
225 List<JsScopeImpl> result = new ArrayList<JsScopeImpl>(scopes.size()); |
|
226 for (ScopeMirror mirror : scopes) { |
|
227 result.add(new JsScopeImpl(this, mirror)); |
|
228 } |
|
229 return result; |
|
230 } |
|
231 } |