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
12namespace 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.
16static 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.
71DEFINE_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.
119DEFINE_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.
155DEFINE_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.
176DEFINE_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.
201DEFINE_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.
225DEFINE_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