// 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(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(payload) - SpecializedIntLiteralBias; return IntConstant(value); } Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative) { int64_t value = is_negative ? -static_cast(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)