1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/bootstrap_natives.h" |
6 | #include "vm/exceptions.h" |
7 | #include "vm/object_store.h" |
8 | #include "vm/runtime_entry.h" |
9 | #include "vm/stack_frame.h" |
10 | #include "vm/symbols.h" |
11 | |
12 | namespace dart { |
13 | |
14 | // Scan the stack until we hit the first function in the _AssertionError |
15 | // class. We then return the next frame's script taking inlining into account. |
16 | static ScriptPtr FindScript(DartFrameIterator* iterator) { |
17 | #if defined(DART_PRECOMPILED_RUNTIME) |
18 | // The precompiled runtime faces two issues in recovering the correct |
19 | // assertion text. First, the precompiled runtime does not include |
20 | // the inlining meta-data so we cannot walk the inline-aware stack trace. |
21 | // Second, the script text itself is missing so whatever script is returned |
22 | // from here will be missing the assertion expression text. |
23 | iterator->NextFrame(); // Skip _AssertionError._evaluateAssertion frame |
24 | return Exceptions::GetCallerScript(iterator); |
25 | #else |
26 | StackFrame* stack_frame = iterator->NextFrame(); |
27 | Code& code = Code::Handle(); |
28 | Function& func = Function::Handle(); |
29 | const Class& assert_error_class = |
30 | Class::Handle(Library::LookupCoreClass(Symbols::AssertionError())); |
31 | ASSERT(!assert_error_class.IsNull()); |
32 | bool hit_assertion_error = false; |
33 | for (; stack_frame != NULL; stack_frame = iterator->NextFrame()) { |
34 | if (stack_frame->is_interpreted()) { |
35 | func = stack_frame->LookupDartFunction(); |
36 | } else { |
37 | code = stack_frame->LookupDartCode(); |
38 | if (code.is_optimized()) { |
39 | InlinedFunctionsIterator inlined_iterator(code, stack_frame->pc()); |
40 | while (!inlined_iterator.Done()) { |
41 | func = inlined_iterator.function(); |
42 | if (hit_assertion_error) { |
43 | return func.script(); |
44 | } |
45 | ASSERT(!hit_assertion_error); |
46 | hit_assertion_error = (func.Owner() == assert_error_class.raw()); |
47 | inlined_iterator.Advance(); |
48 | } |
49 | continue; |
50 | } else { |
51 | func = code.function(); |
52 | } |
53 | } |
54 | ASSERT(!func.IsNull()); |
55 | if (hit_assertion_error) { |
56 | return func.script(); |
57 | } |
58 | ASSERT(!hit_assertion_error); |
59 | hit_assertion_error = (func.Owner() == assert_error_class.raw()); |
60 | } |
61 | UNREACHABLE(); |
62 | return Script::null(); |
63 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
64 | } |
65 | |
66 | // Allocate and throw a new AssertionError. |
67 | // Arg0: index of the first token of the failed assertion. |
68 | // Arg1: index of the first token after the failed assertion. |
69 | // Arg2: Message object or null. |
70 | // Return value: none, throws an exception. |
71 | DEFINE_NATIVE_ENTRY(AssertionError_throwNew, 0, 3) { |
72 | // No need to type check the arguments. This function can only be called |
73 | // internally from the VM. |
74 | const TokenPosition assertion_start = TokenPosition( |
75 | Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value()); |
76 | const TokenPosition assertion_end = TokenPosition( |
77 | Smi::CheckedHandle(zone, arguments->NativeArgAt(1)).Value()); |
78 | |
79 | const Instance& message = |
80 | Instance::CheckedHandle(zone, arguments->NativeArgAt(2)); |
81 | const Array& args = Array::Handle(zone, Array::New(5)); |
82 | |
83 | DartFrameIterator iterator(thread, |
84 | StackFrameIterator::kNoCrossThreadIteration); |
85 | iterator.NextFrame(); // Skip native call. |
86 | const Script& script = Script::Handle(FindScript(&iterator)); |
87 | |
88 | // Initialize argument 'failed_assertion' with source snippet. |
89 | intptr_t from_line, from_column; |
90 | script.GetTokenLocation(assertion_start, &from_line, &from_column); |
91 | intptr_t to_line, to_column; |
92 | script.GetTokenLocation(assertion_end, &to_line, &to_column); |
93 | // Extract the assertion condition text (if source is available). |
94 | auto& condition_text = String::Handle( |
95 | script.GetSnippet(from_line, from_column, to_line, to_column)); |
96 | if (condition_text.IsNull()) { |
97 | condition_text = Symbols::OptimizedOut().raw(); |
98 | } |
99 | args.SetAt(0, condition_text); |
100 | |
101 | // Initialize location arguments starting at position 1. |
102 | // Do not set a column if the source has been generated as it will be wrong. |
103 | args.SetAt(1, String::Handle(script.url())); |
104 | args.SetAt(2, Smi::Handle(Smi::New(from_line))); |
105 | args.SetAt(3, Smi::Handle(Smi::New(script.HasSource() ? from_column : -1))); |
106 | args.SetAt(4, message); |
107 | |
108 | Exceptions::ThrowByType(Exceptions::kAssertion, args); |
109 | UNREACHABLE(); |
110 | return Object::null(); |
111 | } |
112 | |
113 | // Allocate and throw a new AssertionError. |
114 | // Arg0: Source code snippet of failed assertion. |
115 | // Arg1: Line number. |
116 | // Arg2: Column number. |
117 | // Arg3: Message object or null. |
118 | // Return value: none, throws an exception. |
119 | DEFINE_NATIVE_ENTRY(AssertionError_throwNewSource, 0, 4) { |
120 | // No need to type check the arguments. This function can only be called |
121 | // internally from the VM. |
122 | const String& failed_assertion = |
123 | String::CheckedHandle(zone, arguments->NativeArgAt(0)); |
124 | const intptr_t line = |
125 | Smi::CheckedHandle(zone, arguments->NativeArgAt(1)).Value(); |
126 | const intptr_t column = |
127 | Smi::CheckedHandle(zone, arguments->NativeArgAt(2)).Value(); |
128 | const Instance& message = |
129 | Instance::CheckedHandle(zone, arguments->NativeArgAt(3)); |
130 | |
131 | const Array& args = Array::Handle(zone, Array::New(5)); |
132 | |
133 | DartFrameIterator iterator(thread, |
134 | StackFrameIterator::kNoCrossThreadIteration); |
135 | iterator.NextFrame(); // Skip native call. |
136 | const Script& script = Script::Handle(zone, FindScript(&iterator)); |
137 | |
138 | args.SetAt(0, failed_assertion); |
139 | args.SetAt(1, String::Handle(zone, script.url())); |
140 | args.SetAt(2, Smi::Handle(zone, Smi::New(line))); |
141 | args.SetAt(3, Smi::Handle(zone, Smi::New(column))); |
142 | args.SetAt(4, message); |
143 | |
144 | Exceptions::ThrowByType(Exceptions::kAssertion, args); |
145 | UNREACHABLE(); |
146 | return Object::null(); |
147 | } |
148 | |
149 | // Allocate and throw a new TypeError or CastError. |
150 | // Arg0: index of the token of the failed type check. |
151 | // Arg1: src value. |
152 | // Arg2: dst type. |
153 | // Arg3: dst name. |
154 | // Return value: none, throws an exception. |
155 | DEFINE_NATIVE_ENTRY(TypeError_throwNew, 0, 4) { |
156 | // No need to type check the arguments. This function can only be called |
157 | // internally from the VM. |
158 | const TokenPosition location = TokenPosition( |
159 | Smi::CheckedHandle(zone, arguments->NativeArgAt(0)).Value()); |
160 | const Instance& src_value = |
161 | Instance::CheckedHandle(zone, arguments->NativeArgAt(1)); |
162 | const AbstractType& dst_type = |
163 | AbstractType::CheckedHandle(zone, arguments->NativeArgAt(2)); |
164 | const String& dst_name = |
165 | String::CheckedHandle(zone, arguments->NativeArgAt(3)); |
166 | const AbstractType& src_type = |
167 | AbstractType::Handle(src_value.GetType(Heap::kNew)); |
168 | Exceptions::CreateAndThrowTypeError(location, src_type, dst_type, dst_name); |
169 | UNREACHABLE(); |
170 | return Object::null(); |
171 | } |
172 | |
173 | // Allocate and throw a new FallThroughError. |
174 | // Arg0: index of the case clause token into which we fall through. |
175 | // Return value: none, throws an exception. |
176 | DEFINE_NATIVE_ENTRY(FallThroughError_throwNew, 0, 1) { |
177 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0)); |
178 | TokenPosition fallthrough_pos = TokenPosition(smi_pos.Value()); |
179 | |
180 | const Array& args = Array::Handle(Array::New(2)); |
181 | |
182 | // Initialize 'url' and 'line' arguments. |
183 | DartFrameIterator iterator(thread, |
184 | StackFrameIterator::kNoCrossThreadIteration); |
185 | iterator.NextFrame(); // Skip native call. |
186 | const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator)); |
187 | args.SetAt(0, String::Handle(script.url())); |
188 | intptr_t line; |
189 | script.GetTokenLocation(fallthrough_pos, &line, NULL); |
190 | args.SetAt(1, Smi::Handle(Smi::New(line))); |
191 | |
192 | Exceptions::ThrowByType(Exceptions::kFallThrough, args); |
193 | UNREACHABLE(); |
194 | return Object::null(); |
195 | } |
196 | |
197 | // Allocate and throw a new AbstractClassInstantiationError. |
198 | // Arg0: Token position of allocation statement. |
199 | // Arg1: class name of the abstract class that cannot be instantiated. |
200 | // Return value: none, throws an exception. |
201 | DEFINE_NATIVE_ENTRY(AbstractClassInstantiationError_throwNew, 0, 2) { |
202 | GET_NON_NULL_NATIVE_ARGUMENT(Smi, smi_pos, arguments->NativeArgAt(0)); |
203 | GET_NON_NULL_NATIVE_ARGUMENT(String, class_name, arguments->NativeArgAt(1)); |
204 | TokenPosition error_pos = TokenPosition(smi_pos.Value()); |
205 | |
206 | const Array& args = Array::Handle(Array::New(3)); |
207 | |
208 | // Initialize 'className', 'url' and 'line' arguments. |
209 | DartFrameIterator iterator(thread, |
210 | StackFrameIterator::kNoCrossThreadIteration); |
211 | iterator.NextFrame(); // Skip native call. |
212 | const Script& script = Script::Handle(Exceptions::GetCallerScript(&iterator)); |
213 | args.SetAt(0, class_name); |
214 | args.SetAt(1, String::Handle(script.url())); |
215 | intptr_t line; |
216 | script.GetTokenLocation(error_pos, &line, NULL); |
217 | args.SetAt(2, Smi::Handle(Smi::New(line))); |
218 | |
219 | Exceptions::ThrowByType(Exceptions::kAbstractClassInstantiation, args); |
220 | UNREACHABLE(); |
221 | return Object::null(); |
222 | } |
223 | |
224 | // Rethrow an error with a stacktrace. |
225 | DEFINE_NATIVE_ENTRY(Async_rethrow, 0, 2) { |
226 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, error, arguments->NativeArgAt(0)); |
227 | GET_NON_NULL_NATIVE_ARGUMENT(Instance, stacktrace, arguments->NativeArgAt(1)); |
228 | Exceptions::ReThrow(thread, error, stacktrace); |
229 | return Object::null(); |
230 | } |
231 | |
232 | } // namespace dart |
233 | |