| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/exceptions.h" | 5 #include "vm/exceptions.h" |
| 6 | 6 |
| 7 #include "platform/address_sanitizer.h" | 7 #include "platform/address_sanitizer.h" |
| 8 | 8 |
| 9 #include "vm/dart_api_impl.h" | 9 #include "vm/dart_api_impl.h" |
| 10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 | 137 |
| 138 | 138 |
| 139 // Iterate through the stack frames and try to find a frame with an | 139 // Iterate through the stack frames and try to find a frame with an |
| 140 // exception handler. Once found, set the pc, sp and fp so that execution | 140 // exception handler. Once found, set the pc, sp and fp so that execution |
| 141 // can continue in that frame. Sets 'needs_stacktrace' if there is no | 141 // can continue in that frame. Sets 'needs_stacktrace' if there is no |
| 142 // cath-all handler or if a stack-trace is specified in the catch. | 142 // cath-all handler or if a stack-trace is specified in the catch. |
| 143 static bool FindExceptionHandler(Thread* thread, | 143 static bool FindExceptionHandler(Thread* thread, |
| 144 uword* handler_pc, | 144 uword* handler_pc, |
| 145 uword* handler_sp, | 145 uword* handler_sp, |
| 146 uword* handler_fp, | 146 uword* handler_fp, |
| 147 uword** handler_pp_slot, | |
| 148 bool* needs_stacktrace) { | 147 bool* needs_stacktrace) { |
| 149 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); | 148 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); |
| 150 StackFrame* frame = frames.NextFrame(); | 149 StackFrame* frame = frames.NextFrame(); |
| 151 if (frame == NULL) return false; // No Dart frame. | 150 if (frame == NULL) return false; // No Dart frame. |
| 152 bool handler_pc_set = false; | 151 bool handler_pc_set = false; |
| 153 *needs_stacktrace = false; | 152 *needs_stacktrace = false; |
| 154 bool is_catch_all = false; | 153 bool is_catch_all = false; |
| 155 uword temp_handler_pc = kUwordMax; | 154 uword temp_handler_pc = kUwordMax; |
| 156 uword* saved_pp_slot = 0; | |
| 157 while (!frame->IsEntryFrame()) { | 155 while (!frame->IsEntryFrame()) { |
| 158 if (frame->IsDartFrame()) { | 156 if (frame->IsDartFrame()) { |
| 159 if (frame->FindExceptionHandler(thread, | 157 if (frame->FindExceptionHandler(thread, |
| 160 &temp_handler_pc, | 158 &temp_handler_pc, |
| 161 needs_stacktrace, | 159 needs_stacktrace, |
| 162 &is_catch_all)) { | 160 &is_catch_all)) { |
| 163 if (!handler_pc_set) { | 161 if (!handler_pc_set) { |
| 164 handler_pc_set = true; | 162 handler_pc_set = true; |
| 165 *handler_pc = temp_handler_pc; | 163 *handler_pc = temp_handler_pc; |
| 166 *handler_sp = frame->sp(); | 164 *handler_sp = frame->sp(); |
| 167 *handler_fp = frame->fp(); | 165 *handler_fp = frame->fp(); |
| 168 *handler_pp_slot = saved_pp_slot; | |
| 169 } | 166 } |
| 170 if (*needs_stacktrace || is_catch_all) { | 167 if (*needs_stacktrace || is_catch_all) { |
| 171 return true; | 168 return true; |
| 172 } | 169 } |
| 173 } | 170 } |
| 174 } // if frame->IsDartFrame | 171 } // if frame->IsDartFrame |
| 175 #if !defined(TARGET_ARCH_IA32) && !defined(TARGET_ARCH_DBC) | |
| 176 saved_pp_slot = frame->saved_caller_pp_slot(); | |
| 177 #endif | |
| 178 frame = frames.NextFrame(); | 172 frame = frames.NextFrame(); |
| 179 ASSERT(frame != NULL); | 173 ASSERT(frame != NULL); |
| 180 } // while !frame->IsEntryFrame | 174 } // while !frame->IsEntryFrame |
| 181 ASSERT(frame->IsEntryFrame()); | 175 ASSERT(frame->IsEntryFrame()); |
| 182 if (!handler_pc_set) { | 176 if (!handler_pc_set) { |
| 183 *handler_pc = frame->pc(); | 177 *handler_pc = frame->pc(); |
| 184 *handler_sp = frame->sp(); | 178 *handler_sp = frame->sp(); |
| 185 *handler_fp = frame->fp(); | 179 *handler_fp = frame->fp(); |
| 186 *handler_pp_slot = saved_pp_slot; | |
| 187 } | 180 } |
| 188 // No catch-all encountered, needs stacktrace. | 181 // No catch-all encountered, needs stacktrace. |
| 189 *needs_stacktrace = true; | 182 *needs_stacktrace = true; |
| 190 return handler_pc_set; | 183 return handler_pc_set; |
| 191 } | 184 } |
| 192 | 185 |
| 193 | 186 |
| 194 static void FindErrorHandler(uword* handler_pc, | 187 static void FindErrorHandler(uword* handler_pc, |
| 195 uword* handler_sp, | 188 uword* handler_sp, |
| 196 uword* handler_fp, | 189 uword* handler_fp) { |
| 197 uword** handler_pp_slot) { | |
| 198 // TODO(turnidge): Is there a faster way to get the next entry frame? | 190 // TODO(turnidge): Is there a faster way to get the next entry frame? |
| 199 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); | 191 StackFrameIterator frames(StackFrameIterator::kDontValidateFrames); |
| 200 StackFrame* frame = frames.NextFrame(); | 192 StackFrame* frame = frames.NextFrame(); |
| 201 ASSERT(frame != NULL); | 193 ASSERT(frame != NULL); |
| 202 uword* saved_pp_slot = NULL; | |
| 203 while (!frame->IsEntryFrame()) { | 194 while (!frame->IsEntryFrame()) { |
| 204 #if !defined(TARGET_ARCH_IA32) && !defined(TARGET_ARCH_DBC) | |
| 205 saved_pp_slot = frame->saved_caller_pp_slot(); | |
| 206 #endif | |
| 207 frame = frames.NextFrame(); | 195 frame = frames.NextFrame(); |
| 208 ASSERT(frame != NULL); | 196 ASSERT(frame != NULL); |
| 209 } | 197 } |
| 210 ASSERT(frame->IsEntryFrame()); | 198 ASSERT(frame->IsEntryFrame()); |
| 211 *handler_pc = frame->pc(); | 199 *handler_pc = frame->pc(); |
| 212 *handler_sp = frame->sp(); | 200 *handler_sp = frame->sp(); |
| 213 *handler_fp = frame->fp(); | 201 *handler_fp = frame->fp(); |
| 214 *handler_pp_slot = saved_pp_slot; | |
| 215 } | 202 } |
| 216 | 203 |
| 217 | 204 |
| 218 static void JumpToExceptionHandler(Thread* thread, | 205 static void JumpToExceptionHandler(Thread* thread, |
| 219 uword program_counter, | 206 uword program_counter, |
| 220 uword stack_pointer, | 207 uword stack_pointer, |
| 221 uword frame_pointer, | 208 uword frame_pointer, |
| 222 uword* pool_pointer_slot, | |
| 223 const Object& exception_object, | 209 const Object& exception_object, |
| 224 const Object& stacktrace_object) { | 210 const Object& stacktrace_object) { |
| 225 // The no_gc StackResource is unwound through the tear down of | 211 // The no_gc StackResource is unwound through the tear down of |
| 226 // stack resources below. | 212 // stack resources below. |
| 227 NoSafepointScope no_safepoint; | 213 NoSafepointScope no_safepoint; |
| 228 RawObject* raw_exception = exception_object.raw(); | 214 RawObject* raw_exception = exception_object.raw(); |
| 229 RawObject* raw_stacktrace = stacktrace_object.raw(); | 215 RawObject* raw_stacktrace = stacktrace_object.raw(); |
| 230 | 216 |
| 231 #if !defined(TARGET_ARCH_IA32) && !defined(TARGET_ARCH_DBC) | |
| 232 ASSERT(pool_pointer_slot != NULL); | |
| 233 uword pool_pointer = *pool_pointer_slot; | |
| 234 // TODO(rmacnak): Lazy deopt without patching alters pc and pp here. | |
| 235 #else | |
| 236 uword pool_pointer = 0; | |
| 237 #endif | |
| 238 | |
| 239 #if defined(USING_SIMULATOR) | 217 #if defined(USING_SIMULATOR) |
| 240 // Unwinding of the C++ frames and destroying of their stack resources is done | 218 // Unwinding of the C++ frames and destroying of their stack resources is done |
| 241 // by the simulator, because the target stack_pointer is a simulated stack | 219 // by the simulator, because the target stack_pointer is a simulated stack |
| 242 // pointer and not the C++ stack pointer. | 220 // pointer and not the C++ stack pointer. |
| 243 | 221 |
| 244 // Continue simulating at the given pc in the given frame after setting up the | 222 // Continue simulating at the given pc in the given frame after setting up the |
| 245 // exception object in the kExceptionObjectReg register and the stacktrace | 223 // exception object in the kExceptionObjectReg register and the stacktrace |
| 246 // object (may be raw null) in the kStackTraceObjectReg register. | 224 // object (may be raw null) in the kStackTraceObjectReg register. |
| 247 | 225 |
| 248 Simulator::Current()->Longjmp(program_counter, stack_pointer, frame_pointer, | 226 Simulator::Current()->Longjmp(program_counter, stack_pointer, frame_pointer, |
| 249 pool_pointer, raw_exception, raw_stacktrace, | 227 raw_exception, raw_stacktrace, thread); |
| 250 thread); | |
| 251 #else | 228 #else |
| 252 // Prepare for unwinding frames by destroying all the stack resources | 229 // Prepare for unwinding frames by destroying all the stack resources |
| 253 // in the previous frames. | 230 // in the previous frames. |
| 254 StackResource::Unwind(thread); | 231 StackResource::Unwind(thread); |
| 255 | 232 |
| 256 // Call a stub to set up the exception object in kExceptionObjectReg, | 233 // Call a stub to set up the exception object in kExceptionObjectReg, |
| 257 // to set up the stacktrace object in kStackTraceObjectReg, and to | 234 // to set up the stacktrace object in kStackTraceObjectReg, and to |
| 258 // continue execution at the given pc in the given frame. | 235 // continue execution at the given pc in the given frame. |
| 259 typedef void (*ExcpHandler)(uword, uword, uword, RawObject*, RawObject*, | 236 typedef void (*ExcpHandler)(uword, uword, uword, RawObject*, RawObject*, |
| 260 Thread*, uword); | 237 Thread*); |
| 261 ExcpHandler func = reinterpret_cast<ExcpHandler>( | 238 ExcpHandler func = reinterpret_cast<ExcpHandler>( |
| 262 StubCode::JumpToExceptionHandler_entry()->EntryPoint()); | 239 StubCode::JumpToExceptionHandler_entry()->EntryPoint()); |
| 263 | 240 |
| 264 // Unpoison the stack before we tear it down in the generated stub code. | 241 // Unpoison the stack before we tear it down in the generated stub code. |
| 265 uword current_sp = Thread::GetCurrentStackPointer() - 1024; | 242 uword current_sp = Thread::GetCurrentStackPointer() - 1024; |
| 266 ASAN_UNPOISON(reinterpret_cast<void*>(current_sp), | 243 ASAN_UNPOISON(reinterpret_cast<void*>(current_sp), |
| 267 stack_pointer - current_sp); | 244 stack_pointer - current_sp); |
| 268 | 245 |
| 269 func(program_counter, stack_pointer, frame_pointer, | 246 func(program_counter, stack_pointer, frame_pointer, |
| 270 raw_exception, raw_stacktrace, thread, pool_pointer); | 247 raw_exception, raw_stacktrace, thread); |
| 271 #endif | 248 #endif |
| 272 UNREACHABLE(); | 249 UNREACHABLE(); |
| 273 } | 250 } |
| 274 | 251 |
| 275 | 252 |
| 276 static RawField* LookupStacktraceField(const Instance& instance) { | 253 static RawField* LookupStacktraceField(const Instance& instance) { |
| 277 if (instance.GetClassId() < kNumPredefinedCids) { | 254 if (instance.GetClassId() < kNumPredefinedCids) { |
| 278 // 'class Error' is not a predefined class. | 255 // 'class Error' is not a predefined class. |
| 279 return Field::null(); | 256 return Field::null(); |
| 280 } | 257 } |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 333 if (exception.IsNull()) { | 310 if (exception.IsNull()) { |
| 334 exception ^= Exceptions::Create(Exceptions::kNullThrown, | 311 exception ^= Exceptions::Create(Exceptions::kNullThrown, |
| 335 Object::empty_array()); | 312 Object::empty_array()); |
| 336 } else if (exception.raw() == isolate->object_store()->out_of_memory() || | 313 } else if (exception.raw() == isolate->object_store()->out_of_memory() || |
| 337 exception.raw() == isolate->object_store()->stack_overflow()) { | 314 exception.raw() == isolate->object_store()->stack_overflow()) { |
| 338 use_preallocated_stacktrace = true; | 315 use_preallocated_stacktrace = true; |
| 339 } | 316 } |
| 340 uword handler_pc = 0; | 317 uword handler_pc = 0; |
| 341 uword handler_sp = 0; | 318 uword handler_sp = 0; |
| 342 uword handler_fp = 0; | 319 uword handler_fp = 0; |
| 343 uword* handler_pp_slot = 0; | |
| 344 Instance& stacktrace = Instance::Handle(zone); | 320 Instance& stacktrace = Instance::Handle(zone); |
| 345 bool handler_exists = false; | 321 bool handler_exists = false; |
| 346 bool handler_needs_stacktrace = false; | 322 bool handler_needs_stacktrace = false; |
| 347 if (use_preallocated_stacktrace) { | 323 if (use_preallocated_stacktrace) { |
| 348 stacktrace ^= isolate->object_store()->preallocated_stack_trace(); | 324 stacktrace ^= isolate->object_store()->preallocated_stack_trace(); |
| 349 PreallocatedStacktraceBuilder frame_builder(stacktrace); | 325 PreallocatedStacktraceBuilder frame_builder(stacktrace); |
| 350 handler_exists = FindExceptionHandler(thread, | 326 handler_exists = FindExceptionHandler(thread, |
| 351 &handler_pc, | 327 &handler_pc, |
| 352 &handler_sp, | 328 &handler_sp, |
| 353 &handler_fp, | 329 &handler_fp, |
| 354 &handler_pp_slot, | |
| 355 &handler_needs_stacktrace); | 330 &handler_needs_stacktrace); |
| 356 if (handler_pc == 0) { | 331 if (handler_pc == 0) { |
| 357 // No Dart frame. | 332 // No Dart frame. |
| 358 ASSERT(incoming_exception.raw() == | 333 ASSERT(incoming_exception.raw() == |
| 359 isolate->object_store()->out_of_memory()); | 334 isolate->object_store()->out_of_memory()); |
| 360 const UnhandledException& error = UnhandledException::Handle( | 335 const UnhandledException& error = UnhandledException::Handle( |
| 361 zone, isolate->object_store()->preallocated_unhandled_exception()); | 336 zone, isolate->object_store()->preallocated_unhandled_exception()); |
| 362 thread->long_jump_base()->Jump(1, error); | 337 thread->long_jump_base()->Jump(1, error); |
| 363 UNREACHABLE(); | 338 UNREACHABLE(); |
| 364 } | 339 } |
| 365 if (handler_needs_stacktrace) { | 340 if (handler_needs_stacktrace) { |
| 366 BuildStackTrace(&frame_builder); | 341 BuildStackTrace(&frame_builder); |
| 367 } | 342 } |
| 368 } else { | 343 } else { |
| 369 // Get stacktrace field of class Error. This is needed to determine whether | 344 // Get stacktrace field of class Error. This is needed to determine whether |
| 370 // we have a subclass of Error which carries around its stack trace. | 345 // we have a subclass of Error which carries around its stack trace. |
| 371 const Field& stacktrace_field = | 346 const Field& stacktrace_field = |
| 372 Field::Handle(zone, LookupStacktraceField(exception)); | 347 Field::Handle(zone, LookupStacktraceField(exception)); |
| 373 | 348 |
| 374 // Find the exception handler and determine if the handler needs a | 349 // Find the exception handler and determine if the handler needs a |
| 375 // stacktrace. | 350 // stacktrace. |
| 376 handler_exists = FindExceptionHandler(thread, | 351 handler_exists = FindExceptionHandler(thread, |
| 377 &handler_pc, | 352 &handler_pc, |
| 378 &handler_sp, | 353 &handler_sp, |
| 379 &handler_fp, | 354 &handler_fp, |
| 380 &handler_pp_slot, | |
| 381 &handler_needs_stacktrace); | 355 &handler_needs_stacktrace); |
| 382 if (!existing_stacktrace.IsNull()) { | 356 if (!existing_stacktrace.IsNull()) { |
| 383 // If we have an existing stack trace then this better be a rethrow. The | 357 // If we have an existing stack trace then this better be a rethrow. The |
| 384 // reverse is not necessarily true (e.g. Dart_PropagateError can cause | 358 // reverse is not necessarily true (e.g. Dart_PropagateError can cause |
| 385 // a rethrow being called without an existing stacktrace.) | 359 // a rethrow being called without an existing stacktrace.) |
| 386 ASSERT(is_rethrow); | 360 ASSERT(is_rethrow); |
| 387 ASSERT(stacktrace_field.IsNull() || | 361 ASSERT(stacktrace_field.IsNull() || |
| 388 (exception.GetField(stacktrace_field) != Object::null())); | 362 (exception.GetField(stacktrace_field) != Object::null())); |
| 389 stacktrace = existing_stacktrace.raw(); | 363 stacktrace = existing_stacktrace.raw(); |
| 390 } else if (!stacktrace_field.IsNull() || handler_needs_stacktrace) { | 364 } else if (!stacktrace_field.IsNull() || handler_needs_stacktrace) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 408 if (FLAG_print_stacktrace_at_throw) { | 382 if (FLAG_print_stacktrace_at_throw) { |
| 409 THR_Print("Exception '%s' thrown:\n", exception.ToCString()); | 383 THR_Print("Exception '%s' thrown:\n", exception.ToCString()); |
| 410 THR_Print("%s\n", stacktrace.ToCString()); | 384 THR_Print("%s\n", stacktrace.ToCString()); |
| 411 } | 385 } |
| 412 if (handler_exists) { | 386 if (handler_exists) { |
| 413 // Found a dart handler for the exception, jump to it. | 387 // Found a dart handler for the exception, jump to it. |
| 414 JumpToExceptionHandler(thread, | 388 JumpToExceptionHandler(thread, |
| 415 handler_pc, | 389 handler_pc, |
| 416 handler_sp, | 390 handler_sp, |
| 417 handler_fp, | 391 handler_fp, |
| 418 handler_pp_slot, | |
| 419 exception, | 392 exception, |
| 420 stacktrace); | 393 stacktrace); |
| 421 } else { | 394 } else { |
| 422 // No dart exception handler found in this invocation sequence, | 395 // No dart exception handler found in this invocation sequence, |
| 423 // so we create an unhandled exception object and return to the | 396 // so we create an unhandled exception object and return to the |
| 424 // invocation stub so that it returns this unhandled exception | 397 // invocation stub so that it returns this unhandled exception |
| 425 // object. The C++ code which invoked this dart sequence can check | 398 // object. The C++ code which invoked this dart sequence can check |
| 426 // and do the appropriate thing (rethrow the exception to the | 399 // and do the appropriate thing (rethrow the exception to the |
| 427 // dart invocation sequence above it, print diagnostics and terminate | 400 // dart invocation sequence above it, print diagnostics and terminate |
| 428 // the isolate etc.). | 401 // the isolate etc.). |
| 429 const UnhandledException& unhandled_exception = UnhandledException::Handle( | 402 const UnhandledException& unhandled_exception = UnhandledException::Handle( |
| 430 zone, UnhandledException::New(exception, stacktrace)); | 403 zone, UnhandledException::New(exception, stacktrace)); |
| 431 stacktrace = Stacktrace::null(); | 404 stacktrace = Stacktrace::null(); |
| 432 JumpToExceptionHandler(thread, | 405 JumpToExceptionHandler(thread, |
| 433 handler_pc, | 406 handler_pc, |
| 434 handler_sp, | 407 handler_sp, |
| 435 handler_fp, | 408 handler_fp, |
| 436 handler_pp_slot, | |
| 437 unhandled_exception, | 409 unhandled_exception, |
| 438 stacktrace); | 410 stacktrace); |
| 439 } | 411 } |
| 440 UNREACHABLE(); | 412 UNREACHABLE(); |
| 441 } | 413 } |
| 442 | 414 |
| 443 | 415 |
| 444 // Static helpers for allocating, initializing, and throwing an error instance. | 416 // Static helpers for allocating, initializing, and throwing an error instance. |
| 445 | 417 |
| 446 // Return the script of the Dart function that called the native entry or the | 418 // Return the script of the Dart function that called the native entry or the |
| (...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 610 const Instance& exc = Instance::Handle(zone, uhe.exception()); | 582 const Instance& exc = Instance::Handle(zone, uhe.exception()); |
| 611 const Instance& stk = Instance::Handle(zone, uhe.stacktrace()); | 583 const Instance& stk = Instance::Handle(zone, uhe.stacktrace()); |
| 612 Exceptions::ReThrow(thread, exc, stk); | 584 Exceptions::ReThrow(thread, exc, stk); |
| 613 } else { | 585 } else { |
| 614 // Return to the invocation stub and return this error object. The | 586 // Return to the invocation stub and return this error object. The |
| 615 // C++ code which invoked this dart sequence can check and do the | 587 // C++ code which invoked this dart sequence can check and do the |
| 616 // appropriate thing. | 588 // appropriate thing. |
| 617 uword handler_pc = 0; | 589 uword handler_pc = 0; |
| 618 uword handler_sp = 0; | 590 uword handler_sp = 0; |
| 619 uword handler_fp = 0; | 591 uword handler_fp = 0; |
| 620 uword* handler_pp_slot = NULL; | 592 FindErrorHandler(&handler_pc, &handler_sp, &handler_fp); |
| 621 const Stacktrace& stacktrace = Stacktrace::Handle(zone); // null | 593 JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, error, |
| 622 FindErrorHandler(&handler_pc, &handler_sp, &handler_fp, &handler_pp_slot); | 594 Stacktrace::Handle(zone)); // Null stacktrace. |
| 623 JumpToExceptionHandler(thread, handler_pc, handler_sp, handler_fp, | |
| 624 handler_pp_slot, error, stacktrace); | |
| 625 } | 595 } |
| 626 UNREACHABLE(); | 596 UNREACHABLE(); |
| 627 } | 597 } |
| 628 | 598 |
| 629 | 599 |
| 630 void Exceptions::ThrowByType(ExceptionType type, const Array& arguments) { | 600 void Exceptions::ThrowByType(ExceptionType type, const Array& arguments) { |
| 631 Thread* thread = Thread::Current(); | 601 Thread* thread = Thread::Current(); |
| 632 const Object& result = | 602 const Object& result = |
| 633 Object::Handle(thread->zone(), Create(type, arguments)); | 603 Object::Handle(thread->zone(), Create(type, arguments)); |
| 634 if (result.IsError()) { | 604 if (result.IsError()) { |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 768 } | 738 } |
| 769 | 739 |
| 770 return DartLibraryCalls::InstanceCreate(library, | 740 return DartLibraryCalls::InstanceCreate(library, |
| 771 *class_name, | 741 *class_name, |
| 772 *constructor_name, | 742 *constructor_name, |
| 773 arguments); | 743 arguments); |
| 774 } | 744 } |
| 775 | 745 |
| 776 | 746 |
| 777 } // namespace dart | 747 } // namespace dart |
| OLD | NEW |