| // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/kernel_binary_flowgraph.h" |
| |
| #include "vm/object_store.h" |
| |
| #if !defined(DART_PRECOMPILED_RUNTIME) |
| |
| namespace dart { |
| namespace kernel { |
| |
| #define Z (zone_) |
| #define H (translation_helper_) |
| #define I Isolate::Current() |
| |
| StreamingConstantEvaluator::StreamingConstantEvaluator( |
| StreamingFlowGraphBuilder* builder, |
| Zone* zone, |
| TranslationHelper* h, |
| DartTypeTranslator* type_translator) |
| : builder_(builder), |
| isolate_(Isolate::Current()), |
| zone_(zone), |
| translation_helper_(*h), |
| // type_translator_(*type_translator), |
| script_(Script::Handle( |
| zone, |
| builder == NULL ? Script::null() |
| : builder_->parsed_function()->function().script())), |
| result_(Instance::Handle(zone)) {} |
| |
| |
| Instance& StreamingConstantEvaluator::EvaluateExpression() { |
| intptr_t offset = builder_->ReaderOffset(); |
| if (!GetCachedConstant(offset, &result_)) { |
| uint8_t payload = 0; |
| Tag tag = builder_->ReadTag(&payload); |
| switch (tag) { |
| case kStaticGet: |
| EvaluateStaticGet(); |
| break; |
| case kSymbolLiteral: |
| EvaluateSymbolLiteral(); |
| break; |
| case kDoubleLiteral: |
| EvaluateDoubleLiteral(); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| CacheConstantValue(offset, result_); |
| } |
| // We return a new `ZoneHandle` here on purpose: The intermediate language |
| // instructions do not make a copy of the handle, so we do it. |
| return dart::Instance::ZoneHandle(Z, result_.raw()); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateStaticGet() { |
| builder_->ReadPosition(); |
| int canonical_name_index = builder_->ReadUInt(); |
| CanonicalName* target = builder_->GetCanonicalName(canonical_name_index); |
| |
| if (H.IsField(target)) { |
| const dart::Field& field = |
| dart::Field::Handle(Z, H.LookupFieldByKernelField(target)); |
| if (field.StaticValue() == Object::sentinel().raw() || |
| field.StaticValue() == Object::transition_sentinel().raw()) { |
| field.EvaluateInitializer(); |
| result_ = field.StaticValue(); |
| result_ = H.Canonicalize(result_); |
| field.SetStaticValue(result_, true); |
| } else { |
| result_ = field.StaticValue(); |
| } |
| } else if (H.IsProcedure(target)) { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (H.IsMethod(target)) { |
| Function& closure_function = |
| Function::ZoneHandle(Z, function.ImplicitClosureFunction()); |
| closure_function.set_kernel_function(function.kernel_function()); |
| result_ = closure_function.ImplicitStaticClosure(); |
| result_ = H.Canonicalize(result_); |
| } else if (H.IsGetter(target)) { |
| UNIMPLEMENTED(); |
| } else { |
| UNIMPLEMENTED(); |
| } |
| } |
| } |
| |
| void StreamingConstantEvaluator::EvaluateSymbolLiteral() { |
| int str_index = builder_->ReadUInt(); |
| const dart::String& symbol_value = builder_->DartSymbol(str_index); |
| |
| const dart::Class& symbol_class = |
| dart::Class::ZoneHandle(Z, I->object_store()->symbol_class()); |
| ASSERT(!symbol_class.IsNull()); |
| const dart::Function& symbol_constructor = Function::ZoneHandle( |
| Z, symbol_class.LookupConstructor(Symbols::SymbolCtor())); |
| ASSERT(!symbol_constructor.IsNull()); |
| result_ ^= EvaluateConstConstructorCall( |
| symbol_class, TypeArguments::Handle(Z), symbol_constructor, symbol_value); |
| } |
| |
| void StreamingConstantEvaluator::EvaluateDoubleLiteral() { |
| int str_index = builder_->ReadUInt(); |
| result_ = dart::Double::New(builder_->DartString(str_index), Heap::kOld); |
| result_ = H.Canonicalize(result_); |
| } |
| |
| RawObject* StreamingConstantEvaluator::EvaluateConstConstructorCall( |
| const dart::Class& type_class, |
| const TypeArguments& type_arguments, |
| const Function& constructor, |
| const Object& argument) { |
| // Factories have one extra argument: the type arguments. |
| // Constructors have 1 extra arguments: receiver. |
| const int kNumArgs = 1; |
| const int kNumExtraArgs = 1; |
| const int num_arguments = kNumArgs + kNumExtraArgs; |
| const Array& arg_values = |
| Array::Handle(Z, Array::New(num_arguments, Heap::kOld)); |
| Instance& instance = Instance::Handle(Z); |
| if (!constructor.IsFactory()) { |
| instance = Instance::New(type_class, Heap::kOld); |
| if (!type_arguments.IsNull()) { |
| ASSERT(type_arguments.IsInstantiated()); |
| instance.SetTypeArguments( |
| TypeArguments::Handle(Z, type_arguments.Canonicalize())); |
| } |
| arg_values.SetAt(0, instance); |
| } else { |
| // Prepend type_arguments to list of arguments to factory. |
| ASSERT(type_arguments.IsZoneHandle()); |
| arg_values.SetAt(0, type_arguments); |
| } |
| arg_values.SetAt((0 + kNumExtraArgs), argument); |
| const Array& args_descriptor = Array::Handle( |
| Z, ArgumentsDescriptor::New(num_arguments, Object::empty_array())); |
| const Object& result = Object::Handle( |
| Z, DartEntry::InvokeFunction(constructor, arg_values, args_descriptor)); |
| ASSERT(!result.IsError()); |
| if (constructor.IsFactory()) { |
| // The factory method returns the allocated object. |
| instance ^= result.raw(); |
| } |
| return H.Canonicalize(instance); |
| } |
| |
| bool StreamingConstantEvaluator::GetCachedConstant(intptr_t kernel_offset, |
| Instance* value) { |
| if (builder_ == NULL) return false; |
| |
| const Function& function = builder_->parsed_function()->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return false; |
| } |
| |
| bool is_present = false; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| return false; |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| *value ^= constants.GetOrNull(kernel_offset, &is_present); |
| // Mutator compiler thread may add constants while background compiler |
| // is running, and thus change the value of 'compile_time_constants'; |
| // do not assert that 'compile_time_constants' has not changed. |
| constants.Release(); |
| if (FLAG_compiler_stats && is_present) { |
| H.thread()->compiler_stats()->num_const_cache_hits++; |
| } |
| return is_present; |
| } |
| |
| |
| void StreamingConstantEvaluator::CacheConstantValue(intptr_t kernel_offset, |
| const Instance& value) { |
| ASSERT(Thread::Current()->IsMutatorThread()); |
| |
| if (builder_ == NULL) return; |
| |
| const Function& function = builder_->parsed_function()->function(); |
| if (function.kind() == RawFunction::kImplicitStaticFinalGetter) { |
| // Don't cache constants in initializer expressions. They get |
| // evaluated only once. |
| return; |
| } |
| const intptr_t kInitialConstMapSize = 16; |
| ASSERT(!script_.InVMHeap()); |
| if (script_.compile_time_constants() == Array::null()) { |
| const Array& array = Array::Handle( |
| HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew)); |
| script_.set_compile_time_constants(array); |
| } |
| KernelConstantsMap constants(script_.compile_time_constants()); |
| constants.InsertNewOrGetValue(kernel_offset, value); |
| script_.set_compile_time_constants(constants.Release()); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildAt(intptr_t kernel_offset) { |
| SetOffset(kernel_offset); |
| |
| uint8_t payload = 0; |
| Tag tag = ReadTag(&payload); |
| switch (tag) { |
| case kInvalidExpression: |
| return BuildInvalidExpression(); |
| // case kVariableGet: |
| // return VariableGet::ReadFrom(reader_); |
| // case kSpecializedVariableGet: |
| // return VariableGet::ReadFrom(reader_, payload); |
| // case kVariableSet: |
| // return VariableSet::ReadFrom(reader_); |
| // case kSpecializedVariableSet: |
| // return VariableSet::ReadFrom(reader_, payload); |
| // case kPropertyGet: |
| // return PropertyGet::ReadFrom(reader_); |
| // case kPropertySet: |
| // return PropertySet::ReadFrom(reader_); |
| // case kDirectPropertyGet: |
| // return DirectPropertyGet::ReadFrom(reader_); |
| // case kDirectPropertySet: |
| // return DirectPropertySet::ReadFrom(reader_); |
| case kStaticGet: |
| return BuildStaticGet(); |
| // case kStaticSet: |
| // return StaticSet::ReadFrom(reader_); |
| // case kMethodInvocation: |
| // return MethodInvocation::ReadFrom(reader_); |
| // case kDirectMethodInvocation: |
| // return DirectMethodInvocation::ReadFrom(reader_); |
| // case kStaticInvocation: |
| // return StaticInvocation::ReadFrom(reader_, false); |
| // case kConstStaticInvocation: |
| // return StaticInvocation::ReadFrom(reader_, true); |
| // case kConstructorInvocation: |
| // return ConstructorInvocation::ReadFrom(reader_, false); |
| // case kConstConstructorInvocation: |
| // return ConstructorInvocation::ReadFrom(reader_, true); |
| // case kNot: |
| // return Not::ReadFrom(reader_); |
| // case kLogicalExpression: |
| // return LogicalExpression::ReadFrom(reader_); |
| // case kConditionalExpression: |
| // return ConditionalExpression::ReadFrom(reader_); |
| // case kStringConcatenation: |
| // return StringConcatenation::ReadFrom(reader_); |
| // case kIsExpression: |
| // return IsExpression::ReadFrom(reader_); |
| // case kAsExpression: |
| // return AsExpression::ReadFrom(reader_); |
| case kSymbolLiteral: |
| return BuildSymbolLiteral(); |
| // case kTypeLiteral: |
| // return TypeLiteral::ReadFrom(reader_); |
| case kThisExpression: |
| return BuildThisExpression(); |
| case kRethrow: |
| return BuildRethrow(); |
| // case kThrow: |
| // return Throw::ReadFrom(reader_); |
| // case kListLiteral: |
| // return ListLiteral::ReadFrom(reader_, false); |
| // case kConstListLiteral: |
| // return ListLiteral::ReadFrom(reader_, true); |
| // case kMapLiteral: |
| // return MapLiteral::ReadFrom(reader_, false); |
| // case kConstMapLiteral: |
| // return MapLiteral::ReadFrom(reader_, true); |
| // case kAwaitExpression: |
| // return AwaitExpression::ReadFrom(reader_); |
| // case kFunctionExpression: |
| // return FunctionExpression::ReadFrom(reader_); |
| // case kLet: |
| // return Let::ReadFrom(reader_); |
| case kBigIntLiteral: |
| return BuildBigIntLiteral(); |
| case kStringLiteral: |
| return BuildStringLiteral(); |
| case kSpecialIntLiteral: |
| return BuildIntLiteral(payload); |
| case kNegativeIntLiteral: |
| return BuildIntLiteral(true); |
| case kPositiveIntLiteral: |
| return BuildIntLiteral(false); |
| case kDoubleLiteral: |
| return BuildDoubleLiteral(); |
| case kTrueLiteral: |
| return BuildBoolLiteral(true); |
| case kFalseLiteral: |
| return BuildBoolLiteral(false); |
| case kNullLiteral: |
| return BuildNullLiteral(); |
| default: |
| UNREACHABLE(); |
| } |
| |
| return Fragment(); |
| } |
| |
| |
| intptr_t StreamingFlowGraphBuilder::GetStringOffset(intptr_t index) { |
| if (string_offsets_ == NULL) { |
| intptr_t saved_offset = ReaderOffset(); |
| reader_->set_offset(4); // Skip kMagicProgramFile to the string table. |
| string_offset_count_ = ReadListLength() + 1; |
| string_offsets_ = new intptr_t[string_offset_count_]; |
| |
| // Build a table of the 0-based string start and end offsets. |
| string_offsets_[0] = 0; |
| for (intptr_t i = 1; i < string_offset_count_; ++i) { |
| string_offsets_[i] = ReadUInt(); |
| } |
| // Mark the start of the string data to use for decoding strings. |
| reader_->MarkStringDataOffset(); |
| SetOffset(saved_offset); |
| } |
| return string_offsets_[index]; |
| } |
| |
| |
| CanonicalName* StreamingFlowGraphBuilder::GetCanonicalName(intptr_t index) { |
| if (index == 0) return NULL; |
| --index; |
| |
| if (canonical_names_ != NULL && canonical_names_entries_read_ > index) { |
| return canonical_names_[index]; |
| } |
| |
| intptr_t saved_offset = ReaderOffset(); |
| if (canonical_names_ == NULL) { |
| // Find offset from where to read canonical names table. |
| |
| // Skip the magic number. |
| reader_->set_offset(4); |
| |
| // Skip the string table. The last offset is the end offset of the last |
| // string which gives the length of the string data. |
| intptr_t list_length = ReadListLength(); |
| intptr_t end_offset = 0; |
| for (intptr_t i = 0; i < list_length; ++i) { |
| end_offset = ReadUInt(); |
| } |
| SkipBytes(end_offset); |
| |
| // There is another string table for the source URIs. Skip it as well. |
| list_length = ReadListLength(); |
| end_offset = 0; |
| for (intptr_t i = 0; i < list_length; ++i) { |
| end_offset = ReadUInt(); |
| } |
| SkipBytes(end_offset); |
| |
| // Source code table |
| for (intptr_t i = 0; i < list_length; ++i) { |
| // Source code |
| intptr_t bytes = ReadUInt(); |
| SkipBytes(bytes); |
| |
| // Line starts table |
| intptr_t line_count = ReadUInt(); |
| for (intptr_t j = 0; j < line_count; ++j) { |
| ReadUInt(); |
| } |
| } |
| |
| // Now at canonical names table. |
| canonical_names_size_ = ReadUInt(); |
| canonical_names_ = new CanonicalName*[canonical_names_size_]; |
| canonical_names_next_offset_ = ReaderOffset(); |
| } |
| |
| SetOffset(canonical_names_next_offset_); |
| for (; canonical_names_entries_read_ <= index; |
| ++canonical_names_entries_read_) { |
| intptr_t biased_parent_index = ReadUInt(); |
| CanonicalName* parent; |
| if (biased_parent_index != 0) { |
| parent = canonical_names_[biased_parent_index - 1]; |
| } else { |
| if (canonical_names_entries_read_ == 0) { |
| parent = CanonicalName::NewRoot(); |
| } else { |
| parent = canonical_names_[0]->parent(); |
| } |
| } |
| ASSERT(parent != NULL); |
| intptr_t name_index = ReadUInt(); |
| String* name = KernelString(name_index); |
| CanonicalName* canonical_name = parent->AddChild(name); |
| canonical_names_[canonical_names_entries_read_] = canonical_name; |
| } |
| |
| canonical_names_next_offset_ = ReaderOffset(); |
| |
| SetOffset(saved_offset); |
| return canonical_names_[index]; |
| } |
| |
| |
| intptr_t StreamingFlowGraphBuilder::ReaderOffset() { |
| return reader_->offset(); |
| } |
| |
| |
| void StreamingFlowGraphBuilder::SetOffset(intptr_t offset) { |
| reader_->set_offset(offset); |
| } |
| |
| |
| void StreamingFlowGraphBuilder::SkipBytes(intptr_t bytes) { |
| reader_->set_offset(ReaderOffset() + bytes); |
| } |
| |
| |
| uint32_t StreamingFlowGraphBuilder::ReadUInt() { |
| return reader_->ReadUInt(); |
| } |
| |
| |
| intptr_t StreamingFlowGraphBuilder::ReadListLength() { |
| return reader_->ReadListLength(); |
| } |
| |
| |
| TokenPosition StreamingFlowGraphBuilder::ReadPosition(bool record) { |
| return reader_->ReadPosition(record); |
| } |
| |
| |
| Tag StreamingFlowGraphBuilder::ReadTag(uint8_t* payload) { |
| return reader_->ReadTag(payload); |
| } |
| |
| |
| CatchBlock* StreamingFlowGraphBuilder::catch_block() { |
| return flow_graph_builder_->catch_block_; |
| } |
| |
| |
| ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() { |
| return flow_graph_builder_->scopes_; |
| } |
| |
| |
| ParsedFunction* StreamingFlowGraphBuilder::parsed_function() { |
| return flow_graph_builder_->parsed_function_; |
| } |
| |
| |
| dart::String& StreamingFlowGraphBuilder::DartSymbol(intptr_t index) { |
| intptr_t start = GetStringOffset(index); |
| intptr_t end = GetStringOffset(index + 1); |
| return H.DartSymbol(reader_->buffer() + reader_->string_data_offset() + start, |
| end - start); |
| } |
| |
| |
| dart::String& StreamingFlowGraphBuilder::DartString(intptr_t index) { |
| intptr_t start = GetStringOffset(index); |
| intptr_t end = GetStringOffset(index + 1); |
| return H.DartString(reader_->buffer() + reader_->string_data_offset() + start, |
| end - start); |
| } |
| |
| |
| String* StreamingFlowGraphBuilder::KernelString(intptr_t index) { |
| intptr_t start = GetStringOffset(index); |
| intptr_t end = GetStringOffset(index + 1); |
| return new String(start, end - start); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) { |
| return flow_graph_builder_->DebugStepCheck(position); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
| return flow_graph_builder_->LoadLocal(variable); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::PushArgument() { |
| return flow_graph_builder_->PushArgument(); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position, |
| int catch_try_index) { |
| return flow_graph_builder_->RethrowException(position, catch_try_index); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError() { |
| return flow_graph_builder_->ThrowNoSuchMethodError(); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::Constant(const Object& value) { |
| return flow_graph_builder_->Constant(value); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) { |
| return flow_graph_builder_->IntConstant(value); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::LoadStaticField() { |
| return flow_graph_builder_->LoadStaticField(); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position, |
| const Function& target, |
| intptr_t argument_count) { |
| return flow_graph_builder_->StaticCall(position, target, argument_count); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildInvalidExpression() { |
| // The frontend will take care of emitting normal errors (like |
| // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special |
| // situations (e.g. an invalid annotation). |
| return ThrowNoSuchMethodError(); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildStaticGet() { |
| intptr_t saved_offset = ReaderOffset() - 1; // Include the tag. |
| TokenPosition position = ReadPosition(); |
| int canonical_name_index = ReadUInt(); |
| CanonicalName* target = GetCanonicalName(canonical_name_index); |
| |
| if (H.IsField(target)) { |
| const dart::Field& field = |
| dart::Field::ZoneHandle(Z, H.LookupFieldByKernelField(target)); |
| if (field.is_const()) { |
| SetOffset(saved_offset); // EvaluateExpression needs the tag. |
| return Constant(constant_evaluator_.EvaluateExpression()); |
| } else { |
| const dart::Class& owner = dart::Class::Handle(Z, field.Owner()); |
| const dart::String& getter_name = H.DartGetterName(target); |
| const Function& getter = |
| Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name)); |
| if (getter.IsNull() || !field.has_initializer()) { |
| Fragment instructions = Constant(field); |
| return instructions + LoadStaticField(); |
| } else { |
| return StaticCall(position, getter, 0); |
| } |
| } |
| } else { |
| const Function& function = |
| Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target)); |
| |
| if (H.IsGetter(target)) { |
| return StaticCall(position, function, 0); |
| } else if (H.IsMethod(target)) { |
| SetOffset(saved_offset); // EvaluateExpression needs the tag. |
| return Constant(constant_evaluator_.EvaluateExpression()); |
| } else { |
| UNIMPLEMENTED(); |
| } |
| } |
| |
| return Fragment(); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildSymbolLiteral() { |
| SkipBytes(-1); // EvaluateExpression needs the tag. |
| return Constant(constant_evaluator_.EvaluateExpression()); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildThisExpression() { |
| return LoadLocal(scopes()->this_variable); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildRethrow() { |
| TokenPosition position = ReadPosition(); |
| Fragment instructions = DebugStepCheck(position); |
| instructions += LoadLocal(catch_block()->exception_var()); |
| instructions += PushArgument(); |
| instructions += LoadLocal(catch_block()->stack_trace_var()); |
| instructions += PushArgument(); |
| instructions += RethrowException(position, catch_block()->catch_try_index()); |
| |
| return instructions; |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral() { |
| const dart::String& value = DartString(ReadUInt()); |
| return Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld))); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildStringLiteral() { |
| intptr_t str_index = ReadUInt(); |
| return Constant(DartSymbol(str_index)); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload) { |
| int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias; |
| return IntConstant(value); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative) { |
| int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt()) : ReadUInt(); |
| return IntConstant(value); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral() { |
| SkipBytes(-1); // EvaluateExpression needs the tag. |
| return Constant(constant_evaluator_.EvaluateExpression()); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value) { |
| return Constant(Bool::Get(value)); |
| } |
| |
| |
| Fragment StreamingFlowGraphBuilder::BuildNullLiteral() { |
| return Constant(Instance::ZoneHandle(Z, Instance::null())); |
| } |
| |
| |
| } // namespace kernel |
| } // namespace dart |
| |
| #endif // !defined(DART_PRECOMPILED_RUNTIME) |