56a1cfe7aa
Rationale: As shown in the comment, I was already not sure about what argument to use. Clearly one of the two type arguments is not needed. This fixes the language_2/const_inference_test failure that was temporarily approved. https://github.com/dart-lang/sdk/issues/36220 Change-Id: I3dd8ddf0e3cf0e992164c005b08ffd59306041f7 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107064 Reviewed-by: Aart Bik <ajcbik@google.com> Commit-Queue: Aart Bik <ajcbik@google.com> Auto-Submit: Aart Bik <ajcbik@google.com>
1348 lines
51 KiB
C++
1348 lines
51 KiB
C++
// Copyright (c) 2018, 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/compiler/frontend/constant_evaluator.h"
|
|
|
|
#include "vm/compiler/aot/precompiler.h"
|
|
#include "vm/compiler/frontend/kernel_to_il.h"
|
|
#include "vm/compiler/jit/compiler.h"
|
|
#include "vm/dart_entry.h"
|
|
#include "vm/longjump.h"
|
|
#include "vm/object_store.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
namespace kernel {
|
|
|
|
#define Z (zone_)
|
|
#define H (translation_helper_)
|
|
#define T (type_translator_)
|
|
#define I Isolate::Current()
|
|
|
|
ConstantEvaluator::ConstantEvaluator(KernelReaderHelper* helper,
|
|
TypeTranslator* type_translator,
|
|
ActiveClass* active_class,
|
|
FlowGraphBuilder* flow_graph_builder)
|
|
: helper_(helper),
|
|
isolate_(Isolate::Current()),
|
|
zone_(helper->zone_),
|
|
translation_helper_(helper->translation_helper_),
|
|
type_translator_(*type_translator),
|
|
active_class_(active_class),
|
|
flow_graph_builder_(flow_graph_builder),
|
|
script_(helper->script()),
|
|
result_(Instance::Handle(zone_)) {}
|
|
|
|
bool ConstantEvaluator::IsCached(intptr_t offset) {
|
|
return GetCachedConstant(offset, &result_);
|
|
}
|
|
|
|
RawInstance* ConstantEvaluator::EvaluateExpression(intptr_t offset,
|
|
bool reset_position) {
|
|
ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
BailoutIfBackgroundCompilation();
|
|
|
|
ASSERT(IsAllowedToEvaluate());
|
|
intptr_t original_offset = helper_->ReaderOffset();
|
|
helper_->SetOffset(offset);
|
|
uint8_t payload = 0;
|
|
Tag tag = helper_->ReadTag(&payload); // read tag.
|
|
switch (tag) {
|
|
case kVariableGet:
|
|
EvaluateVariableGet(/* is_specialized = */ false);
|
|
break;
|
|
case kSpecializedVariableGet:
|
|
EvaluateVariableGet(/* is_specialized = */ true);
|
|
break;
|
|
case kPropertyGet:
|
|
EvaluatePropertyGet();
|
|
break;
|
|
case kDirectPropertyGet:
|
|
EvaluateDirectPropertyGet();
|
|
break;
|
|
case kStaticGet:
|
|
EvaluateStaticGet();
|
|
break;
|
|
case kMethodInvocation:
|
|
EvaluateMethodInvocation();
|
|
break;
|
|
case kDirectMethodInvocation:
|
|
EvaluateDirectMethodInvocation();
|
|
break;
|
|
case kSuperMethodInvocation:
|
|
EvaluateSuperMethodInvocation();
|
|
break;
|
|
case kStaticInvocation:
|
|
case kConstStaticInvocation:
|
|
EvaluateStaticInvocation();
|
|
break;
|
|
case kConstConstructorInvocation:
|
|
EvaluateConstructorInvocationInternal();
|
|
break;
|
|
case kNot:
|
|
EvaluateNot();
|
|
break;
|
|
case kLogicalExpression:
|
|
EvaluateLogicalExpression();
|
|
break;
|
|
case kConditionalExpression:
|
|
EvaluateConditionalExpression();
|
|
break;
|
|
case kStringConcatenation:
|
|
EvaluateStringConcatenation();
|
|
break;
|
|
case kListConcatenation:
|
|
case kSetConcatenation:
|
|
case kMapConcatenation:
|
|
case kInstanceCreation:
|
|
// These only occur inside unevaluated constants, so if we decide to
|
|
// remove support for late evaluation of environment constants from
|
|
// dill files in the VM, an implementation here will not be necessary.
|
|
H.ReportError(
|
|
script_, TokenPosition::kNoSource,
|
|
"Unexpected unevaluated constant, All constant expressions"
|
|
" are expected to be evaluated at this point %s (%d)",
|
|
Reader::TagName(tag), tag);
|
|
break;
|
|
case kSymbolLiteral:
|
|
EvaluateSymbolLiteral();
|
|
break;
|
|
case kTypeLiteral:
|
|
EvaluateTypeLiteral();
|
|
break;
|
|
case kAsExpression:
|
|
EvaluateAsExpression();
|
|
break;
|
|
case kConstListLiteral:
|
|
EvaluateListLiteralInternal();
|
|
break;
|
|
case kConstSetLiteral:
|
|
// Set literals are currently desugared in the frontend and will not
|
|
// reach the VM. See http://dartbug.com/35124 for discussion.
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Unexpected set literal constant, this constant"
|
|
" is expected to be evaluated at this point %s (%d)",
|
|
Reader::TagName(tag), tag);
|
|
break;
|
|
case kConstMapLiteral:
|
|
EvaluateMapLiteralInternal();
|
|
break;
|
|
case kLet:
|
|
EvaluateLet();
|
|
break;
|
|
case kBlockExpression: {
|
|
UNIMPLEMENTED();
|
|
break;
|
|
}
|
|
case kInstantiation:
|
|
EvaluatePartialTearoffInstantiation();
|
|
break;
|
|
case kBigIntLiteral:
|
|
EvaluateBigIntLiteral();
|
|
break;
|
|
case kStringLiteral:
|
|
EvaluateStringLiteral();
|
|
break;
|
|
case kSpecializedIntLiteral:
|
|
EvaluateIntLiteral(payload);
|
|
break;
|
|
case kNegativeIntLiteral:
|
|
EvaluateIntLiteral(true);
|
|
break;
|
|
case kPositiveIntLiteral:
|
|
EvaluateIntLiteral(false);
|
|
break;
|
|
case kDoubleLiteral:
|
|
EvaluateDoubleLiteral();
|
|
break;
|
|
case kTrueLiteral:
|
|
EvaluateBoolLiteral(true);
|
|
break;
|
|
case kFalseLiteral:
|
|
EvaluateBoolLiteral(false);
|
|
break;
|
|
case kNullLiteral:
|
|
EvaluateNullLiteral();
|
|
break;
|
|
case kConstantExpression:
|
|
helper_->ReadPosition();
|
|
helper_->SkipDartType();
|
|
result_ = EvaluateConstantExpression(helper_->ReadUInt());
|
|
break;
|
|
case kDeprecated_ConstantExpression:
|
|
result_ = EvaluateConstantExpression(helper_->ReadUInt());
|
|
break;
|
|
default:
|
|
H.ReportError(
|
|
script_, TokenPosition::kNoSource,
|
|
"Not a constant expression: unexpected kernel tag %s (%d)",
|
|
Reader::TagName(tag), tag);
|
|
}
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) helper_->SetOffset(original_offset);
|
|
} else {
|
|
if (!reset_position) {
|
|
helper_->SetOffset(offset);
|
|
helper_->SkipExpression();
|
|
}
|
|
}
|
|
return result_.raw();
|
|
}
|
|
|
|
Instance& ConstantEvaluator::EvaluateListLiteral(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
BailoutIfBackgroundCompilation();
|
|
|
|
ASSERT(IsAllowedToEvaluate());
|
|
intptr_t original_offset = helper_->ReaderOffset();
|
|
helper_->SetOffset(offset);
|
|
helper_->ReadTag(); // skip tag.
|
|
EvaluateListLiteralInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) helper_->SetOffset(original_offset);
|
|
}
|
|
// 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 Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& ConstantEvaluator::EvaluateMapLiteral(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
BailoutIfBackgroundCompilation();
|
|
|
|
ASSERT(IsAllowedToEvaluate());
|
|
intptr_t original_offset = helper_->ReaderOffset();
|
|
helper_->SetOffset(offset);
|
|
helper_->ReadTag(); // skip tag.
|
|
EvaluateMapLiteralInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) helper_->SetOffset(original_offset);
|
|
}
|
|
// 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 Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& ConstantEvaluator::EvaluateConstructorInvocation(
|
|
intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
BailoutIfBackgroundCompilation();
|
|
|
|
ASSERT(IsAllowedToEvaluate());
|
|
intptr_t original_offset = helper_->ReaderOffset();
|
|
helper_->SetOffset(offset);
|
|
helper_->ReadTag(); // skip tag.
|
|
EvaluateConstructorInvocationInternal();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) helper_->SetOffset(original_offset);
|
|
}
|
|
// 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 Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
Instance& ConstantEvaluator::EvaluateStaticInvocation(intptr_t offset,
|
|
bool reset_position) {
|
|
if (!GetCachedConstant(offset, &result_)) {
|
|
BailoutIfBackgroundCompilation();
|
|
|
|
ASSERT(IsAllowedToEvaluate());
|
|
intptr_t original_offset = helper_->ReaderOffset();
|
|
helper_->SetOffset(offset);
|
|
helper_->ReadTag(); // skip tag.
|
|
EvaluateStaticInvocation();
|
|
|
|
CacheConstantValue(offset, result_);
|
|
if (reset_position) helper_->SetOffset(original_offset);
|
|
}
|
|
// 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 Instance::ZoneHandle(Z, result_.raw());
|
|
}
|
|
|
|
RawObject* ConstantEvaluator::EvaluateExpressionSafe(intptr_t offset) {
|
|
LongJumpScope jump;
|
|
if (setjmp(*jump.Set()) == 0) {
|
|
return EvaluateExpression(offset);
|
|
} else {
|
|
return H.thread()->StealStickyError();
|
|
}
|
|
}
|
|
|
|
RawObject* ConstantEvaluator::EvaluateAnnotations() {
|
|
intptr_t list_length = helper_->ReadListLength(); // read list length.
|
|
const Array& metadata_values =
|
|
Array::Handle(Z, Array::New(list_length, H.allocation_space()));
|
|
Instance& value = Instance::Handle(Z);
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
// this will (potentially) read the expression, but reset the position.
|
|
value = EvaluateExpression(helper_->ReaderOffset());
|
|
helper_->SkipExpression(); // read (actual) initializer.
|
|
metadata_values.SetAt(i, value);
|
|
}
|
|
return metadata_values.raw();
|
|
}
|
|
|
|
RawInstance* ConstantEvaluator::EvaluateConstantExpression(
|
|
intptr_t constant_offset) {
|
|
ASSERT(!H.constants().IsNull());
|
|
ASSERT(!H.constants_table().IsNull()); // raw bytes
|
|
|
|
// For kernel-level cache (in contrast with script-level caching),
|
|
// we need to access the raw constants array inside the shared
|
|
// KernelProgramInfo directly, so that all scripts will see the
|
|
// results after new insertions. These accesses at kernel-level
|
|
// must be locked since mutator and background compiler can
|
|
// access the array at the same time.
|
|
{
|
|
SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
|
|
KernelConstantsMap constant_map(H.info().constants());
|
|
result_ ^= constant_map.GetOrNull(constant_offset);
|
|
ASSERT(constant_map.Release().raw() == H.info().constants());
|
|
}
|
|
|
|
// On miss, evaluate, and insert value.
|
|
if (result_.IsNull()) {
|
|
result_ = EvaluateConstant(constant_offset);
|
|
SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
|
|
KernelConstantsMap constant_map(H.info().constants());
|
|
auto insert = constant_map.InsertNewOrGetValue(constant_offset, result_);
|
|
ASSERT(insert == result_.raw());
|
|
H.info().set_constants(constant_map.Release()); // update!
|
|
}
|
|
return result_.raw();
|
|
}
|
|
|
|
RawInstance* ConstantEvaluator::EvaluateConstant(intptr_t constant_offset) {
|
|
// Get reader directly into raw bytes of constant table.
|
|
KernelReaderHelper reader(Z, &H, script_, H.constants_table(), 0);
|
|
reader.ReadUInt(); // skip variable-sized int for adjusted constant offset
|
|
reader.SetOffset(reader.ReaderOffset() + constant_offset);
|
|
// Construct constant from raw bytes.
|
|
Instance& instance = Instance::Handle(Z);
|
|
const intptr_t constant_tag = reader.ReadByte();
|
|
switch (constant_tag) {
|
|
case kNullConstant:
|
|
instance = Instance::null();
|
|
break;
|
|
case kBoolConstant:
|
|
instance = reader.ReadByte() == 1 ? Object::bool_true().raw()
|
|
: Object::bool_false().raw();
|
|
break;
|
|
case kIntConstant: {
|
|
uint8_t payload = 0;
|
|
Tag integer_tag = reader.ReadTag(&payload); // read tag.
|
|
switch (integer_tag) {
|
|
case kBigIntLiteral: {
|
|
const String& value = H.DartString(reader.ReadStringReference());
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kSpecializedIntLiteral: {
|
|
const int64_t value =
|
|
static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kNegativeIntLiteral: {
|
|
const int64_t value = -static_cast<int64_t>(reader.ReadUInt());
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
case kPositiveIntLiteral: {
|
|
const int64_t value = reader.ReadUInt();
|
|
instance = Integer::New(value, Heap::kOld);
|
|
break;
|
|
}
|
|
default:
|
|
H.ReportError(
|
|
script_, TokenPosition::kNoSource,
|
|
"Cannot lazily read integer: unexpected kernel tag %s (%d)",
|
|
Reader::TagName(integer_tag), integer_tag);
|
|
}
|
|
break;
|
|
}
|
|
case kDoubleConstant:
|
|
instance = Double::New(reader.ReadDouble(), Heap::kOld);
|
|
break;
|
|
case kStringConstant:
|
|
instance = H.DartSymbolPlain(reader.ReadStringReference()).raw();
|
|
break;
|
|
case kSymbolConstant: {
|
|
Library& library = Library::Handle(Z);
|
|
library = Library::InternalLibrary();
|
|
const Class& symbol_class =
|
|
Class::Handle(Z, library.LookupClass(Symbols::Symbol()));
|
|
const Field& symbol_name_field = Field::Handle(
|
|
Z, symbol_class.LookupInstanceFieldAllowPrivate(Symbols::_name()));
|
|
ASSERT(!symbol_name_field.IsNull());
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
if (index == -1) {
|
|
library = Library::null();
|
|
} else {
|
|
library = H.LookupLibraryByKernelLibrary(index);
|
|
}
|
|
const String& symbol =
|
|
H.DartIdentifier(library, reader.ReadStringReference());
|
|
instance = Instance::New(symbol_class, Heap::kOld);
|
|
instance.SetField(symbol_name_field, symbol);
|
|
break;
|
|
}
|
|
case kListConstant: {
|
|
const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
|
|
const Class& list_class =
|
|
Class::Handle(Z, corelib.LookupClassAllowPrivate(Symbols::_List()));
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(&reader, active_class_, true);
|
|
TypeArguments& type_arguments =
|
|
TypeArguments::ZoneHandle(Z, TypeArguments::New(1, Heap::kOld));
|
|
AbstractType& type = type_translator.BuildType();
|
|
type_arguments.SetTypeAt(0, type);
|
|
// Instantiate class.
|
|
type = Type::New(list_class, type_arguments, TokenPosition::kNoSource);
|
|
type = ClassFinalizer::FinalizeType(*active_class_->klass, type,
|
|
ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
// Fill array with constant elements.
|
|
const intptr_t length = reader.ReadUInt();
|
|
const Array& array =
|
|
Array::Handle(Z, ImmutableArray::New(length, Heap::kOld));
|
|
array.SetTypeArguments(type_arguments);
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < length; ++j) {
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
constant = EvaluateConstantExpression(entry_offset);
|
|
array.SetAt(j, constant);
|
|
}
|
|
instance = array.raw();
|
|
break;
|
|
}
|
|
case kInstanceConstant: {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
const Class& klass = Class::Handle(Z, H.LookupClassByKernelClass(index));
|
|
const Object& obj =
|
|
Object::Handle(Z, klass.EnsureIsFinalized(H.thread()));
|
|
ASSERT(obj.IsNull());
|
|
instance = Instance::New(klass, Heap::kOld);
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(&reader, active_class_, true);
|
|
const intptr_t number_of_type_arguments = reader.ReadUInt();
|
|
if (klass.NumTypeArguments() > 0) {
|
|
TypeArguments& type_arguments = TypeArguments::ZoneHandle(
|
|
Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
|
|
for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
|
|
type_arguments.SetTypeAt(j, type_translator.BuildType());
|
|
}
|
|
// Instantiate class.
|
|
AbstractType& type = AbstractType::Handle(
|
|
Z, Type::New(klass, type_arguments, TokenPosition::kNoSource));
|
|
type = ClassFinalizer::FinalizeType(*active_class_->klass, type,
|
|
ClassFinalizer::kCanonicalize);
|
|
type_arguments = type.arguments();
|
|
instance.SetTypeArguments(type_arguments);
|
|
} else {
|
|
ASSERT(number_of_type_arguments == 0);
|
|
}
|
|
// Set the fields.
|
|
const intptr_t number_of_fields = reader.ReadUInt();
|
|
Field& field = Field::Handle(Z);
|
|
Instance& constant = Instance::Handle(Z);
|
|
for (intptr_t j = 0; j < number_of_fields; ++j) {
|
|
field = H.LookupFieldByKernelField(reader.ReadCanonicalNameReference());
|
|
// Recurse into lazily evaluating all "sub" constants
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
constant = EvaluateConstantExpression(entry_offset);
|
|
instance.SetField(field, constant);
|
|
}
|
|
break;
|
|
}
|
|
case kPartialInstantiationConstant: {
|
|
// Recurse into lazily evaluating the "sub" constant
|
|
// needed to evaluate the current constant.
|
|
const intptr_t entry_offset = reader.ReadUInt();
|
|
ASSERT(entry_offset < constant_offset); // DAG!
|
|
Instance& constant =
|
|
Instance::Handle(Z, EvaluateConstantExpression(entry_offset));
|
|
// Happens if the tearoff was in the vmservice library and we have
|
|
// [skip_vm_service_library] enabled.
|
|
// TODO(ajcbik): probably ASSERT that this no longer happens
|
|
if (constant.IsNull()) {
|
|
instance = Instance::null();
|
|
break;
|
|
}
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(&reader, active_class_, true);
|
|
const intptr_t number_of_type_arguments = reader.ReadUInt();
|
|
ASSERT(number_of_type_arguments > 0);
|
|
TypeArguments& type_arguments = TypeArguments::ZoneHandle(
|
|
Z, TypeArguments::New(number_of_type_arguments, Heap::kOld));
|
|
for (intptr_t j = 0; j < number_of_type_arguments; ++j) {
|
|
type_arguments.SetTypeAt(j, type_translator.BuildType());
|
|
}
|
|
type_arguments = type_arguments.Canonicalize();
|
|
// Make a copy of the old closure, and set delayed type arguments.
|
|
Closure& closure = Closure::Handle(Z, Closure::RawCast(constant.raw()));
|
|
Function& function = Function::Handle(Z, closure.function());
|
|
TypeArguments& type_arguments2 =
|
|
TypeArguments::ZoneHandle(Z, closure.instantiator_type_arguments());
|
|
// TODO(ajcbik): why was this here in original reader?
|
|
// TypeArguments& type_arguments3 =
|
|
// TypeArguments::ZoneHandle(Z, closure.function_type_arguments());
|
|
Context& context = Context::Handle(Z, closure.context());
|
|
instance = Closure::New(type_arguments2, Object::null_type_arguments(),
|
|
type_arguments, function, context, Heap::kOld);
|
|
break;
|
|
}
|
|
case kTearOffConstant: {
|
|
const NameIndex index = reader.ReadCanonicalNameReference();
|
|
Function& function =
|
|
Function::Handle(Z, H.LookupStaticMethodByKernelProcedure(index));
|
|
function = function.ImplicitClosureFunction();
|
|
instance = function.ImplicitStaticClosure();
|
|
break;
|
|
}
|
|
case kTypeLiteralConstant: {
|
|
// Build type from the raw bytes (needs temporary translator).
|
|
TypeTranslator type_translator(&reader, active_class_, true);
|
|
instance = type_translator.BuildType().raw();
|
|
break;
|
|
}
|
|
default:
|
|
// Set literals (kSetConstant) are currently desugared in the frontend
|
|
// and will not reach the VM. See http://dartbug.com/35124 for some
|
|
// discussion. Map constants (kMapConstant ) are already lowered to
|
|
// InstanceConstant or ListConstant. We should never see unevaluated
|
|
// constants (kUnevaluatedConstant) in the constant table, they should
|
|
// have been fully evaluated before we get them.
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Cannot lazily read constant: unexpected kernel tag (%" Pd
|
|
")",
|
|
constant_tag);
|
|
}
|
|
return H.Canonicalize(instance);
|
|
}
|
|
|
|
void ConstantEvaluator::BailoutIfBackgroundCompilation() {
|
|
if (Compiler::IsBackgroundCompilation()) {
|
|
Compiler::AbortBackgroundCompilation(
|
|
DeoptId::kNone, "Cannot evaluate annotations in background compiler.");
|
|
}
|
|
}
|
|
|
|
bool ConstantEvaluator::IsBuildingFlowGraph() const {
|
|
return flow_graph_builder_ != nullptr;
|
|
}
|
|
|
|
bool ConstantEvaluator::IsAllowedToEvaluate() const {
|
|
return FLAG_precompiled_mode || !IsBuildingFlowGraph() ||
|
|
!flow_graph_builder_->optimizing_;
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateVariableGet(bool is_specialized) {
|
|
ASSERT(IsBuildingFlowGraph());
|
|
// When we see a [VariableGet] the corresponding [VariableDeclaration] must've
|
|
// been executed already. It therefore must have a constant object associated
|
|
// with it.
|
|
const TokenPosition position = helper_->ReadPosition(); // read position.
|
|
const intptr_t variable_kernel_position =
|
|
helper_->ReadUInt(); // read kernel position.
|
|
if (!is_specialized) {
|
|
helper_->ReadUInt(); // read relative variable index.
|
|
helper_->SkipOptionalDartType(); // read promoted type.
|
|
}
|
|
LocalVariable* variable =
|
|
flow_graph_builder_->LookupVariable(variable_kernel_position);
|
|
if (!variable->IsConst()) {
|
|
H.ReportError(script_, position, "Not a constant expression.");
|
|
}
|
|
result_ = variable->ConstValue()->raw();
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateGetStringLength(intptr_t expression_offset,
|
|
TokenPosition position) {
|
|
EvaluateExpression(expression_offset);
|
|
if (result_.IsString()) {
|
|
const String& str = String::Handle(Z, String::RawCast(result_.raw()));
|
|
result_ = Integer::New(str.Length(), H.allocation_space());
|
|
} else {
|
|
H.ReportError(
|
|
script_, position,
|
|
"Constant expressions can only call 'length' on string constants.");
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluatePropertyGet() {
|
|
const TokenPosition position = helper_->ReadPosition(); // read position.
|
|
intptr_t expression_offset = helper_->ReaderOffset();
|
|
helper_->SkipExpression(); // read receiver.
|
|
StringIndex name = helper_->ReadNameAsStringIndex(); // read name.
|
|
helper_->SkipCanonicalNameReference(); // read interface_target_reference.
|
|
|
|
if (H.StringEquals(name, "length")) {
|
|
EvaluateGetStringLength(expression_offset, position);
|
|
} else {
|
|
H.ReportError(
|
|
script_, position,
|
|
"Constant expressions can only call 'length' on string constants.");
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateDirectPropertyGet() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
intptr_t expression_offset = helper_->ReaderOffset();
|
|
helper_->SkipExpression(); // read receiver.
|
|
NameIndex kernel_name =
|
|
helper_->ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
// TODO(vegorov): add check based on the complete canonical name.
|
|
if (H.IsGetter(kernel_name) &&
|
|
H.StringEquals(H.CanonicalNameString(kernel_name), "length")) {
|
|
EvaluateGetStringLength(expression_offset, position);
|
|
} else {
|
|
H.ReportError(
|
|
script_, position,
|
|
"Constant expressions can only call 'length' on string constants.");
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateStaticGet() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
NameIndex target =
|
|
helper_->ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
|
|
|
|
if (H.IsField(target)) {
|
|
const Field& field = Field::Handle(Z, H.LookupFieldByKernelField(target));
|
|
if (!field.is_const()) {
|
|
H.ReportError(script_, position, "Not a constant field.");
|
|
}
|
|
if (field.StaticValue() == Object::transition_sentinel().raw()) {
|
|
if (IsBuildingFlowGraph()) {
|
|
flow_graph_builder_->InlineBailout(
|
|
"kernel::ConstantEvaluator::EvaluateStaticGet::Cyclic");
|
|
}
|
|
H.ReportError(script_, position, "Not a constant expression.");
|
|
} else if (field.StaticValue() == Object::sentinel().raw()) {
|
|
field.SetStaticValue(Object::transition_sentinel());
|
|
const Object& value = Object::Handle(Z, field.EvaluateInitializer());
|
|
if (value.IsError()) {
|
|
field.SetStaticValue(Object::null_instance());
|
|
H.ReportError(Error::Cast(value), script_, position,
|
|
"Not a constant expression.");
|
|
}
|
|
Thread* thread = H.thread();
|
|
const Error& error =
|
|
Error::Handle(thread->zone(), thread->StealStickyError());
|
|
if (!error.IsNull()) {
|
|
field.SetStaticValue(Object::null_instance());
|
|
H.ReportError(error, script_, position, "Not a constant expression.");
|
|
}
|
|
ASSERT(value.IsNull() || value.IsInstance());
|
|
field.SetStaticValue(value.IsNull() ? Instance::null_instance()
|
|
: Instance::Cast(value));
|
|
|
|
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());
|
|
result_ = closure_function.ImplicitStaticClosure();
|
|
result_ = H.Canonicalize(result_);
|
|
} else if (H.IsGetter(target)) {
|
|
H.ReportError(script_, position, "Not a constant expression.");
|
|
} else {
|
|
H.ReportError(script_, position, "Not a constant expression.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateMethodInvocation() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
// This method call wasn't cached, so receiver et al. isn't cached either.
|
|
const Instance& receiver = Instance::Handle(
|
|
Z, EvaluateExpression(helper_->ReaderOffset(), false)); // read receiver.
|
|
Class& klass =
|
|
Class::Handle(Z, isolate_->class_table()->At(receiver.GetClassId()));
|
|
ASSERT(!klass.IsNull());
|
|
|
|
// Search the superclass chain for the selector.
|
|
const String& method_name = helper_->ReadNameAsMethodName(); // read name.
|
|
Function& function =
|
|
Function::Handle(Z, H.LookupDynamicFunction(klass, method_name));
|
|
|
|
// The frontend should guarantee that [MethodInvocation]s inside constant
|
|
// expressions are always valid.
|
|
ASSERT(!function.IsNull());
|
|
|
|
// Read arguments, run the method and canonicalize the result.
|
|
const Object& result = RunMethodCall(position, function, &receiver);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
|
|
helper_->SkipCanonicalNameReference(); // read interface_target_reference.
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateDirectMethodInvocation() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
|
|
const Instance& receiver = Instance::Handle(
|
|
Z, EvaluateExpression(helper_->ReaderOffset(), false)); // read receiver.
|
|
|
|
NameIndex kernel_name =
|
|
helper_->ReadCanonicalNameReference(); // read target_reference.
|
|
|
|
const Function& function = Function::ZoneHandle(
|
|
Z, H.LookupMethodByMember(kernel_name, H.DartProcedureName(kernel_name)));
|
|
|
|
// Read arguments, run the method and canonicalize the result.
|
|
const Object& result = RunMethodCall(position, function, &receiver);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateSuperMethodInvocation() {
|
|
ASSERT(IsBuildingFlowGraph());
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
|
|
const LocalVariable* receiver_variable =
|
|
flow_graph_builder_->parsed_function_->receiver_var();
|
|
ASSERT(receiver_variable->IsConst());
|
|
const Instance& receiver =
|
|
Instance::Handle(Z, receiver_variable->ConstValue()->raw());
|
|
ASSERT(!receiver.IsNull());
|
|
|
|
Class& klass = Class::Handle(Z, active_class_->klass->SuperClass());
|
|
ASSERT(!klass.IsNull());
|
|
|
|
const String& method_name = helper_->ReadNameAsMethodName(); // read name.
|
|
Function& function =
|
|
Function::Handle(Z, H.LookupDynamicFunction(klass, method_name));
|
|
|
|
// The frontend should guarantee that [MethodInvocation]s inside constant
|
|
// expressions are always valid.
|
|
ASSERT(!function.IsNull());
|
|
|
|
// Read arguments, run the method and canonicalize the result.
|
|
const Object& result = RunMethodCall(position, function, &receiver);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
|
|
helper_->SkipCanonicalNameReference(); // read interface_target_reference.
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateStaticInvocation() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
NameIndex procedure_reference =
|
|
helper_->ReadCanonicalNameReference(); // read procedure reference.
|
|
|
|
const Function& function = Function::ZoneHandle(
|
|
Z, H.LookupStaticMethodByKernelProcedure(procedure_reference));
|
|
Class& klass = Class::Handle(Z, function.Owner());
|
|
|
|
intptr_t argument_count =
|
|
helper_->ReadUInt(); // read arguments part #1: arguments count.
|
|
|
|
// Build the type arguments vector (if necessary).
|
|
const TypeArguments* type_arguments =
|
|
TranslateTypeArguments(function, &klass); // read argument types.
|
|
|
|
// read positional and named parameters.
|
|
const Object& result =
|
|
RunFunction(position, function, argument_count, NULL, type_arguments);
|
|
result_ ^= result.raw();
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateConstructorInvocationInternal() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
|
|
NameIndex target = helper_->ReadCanonicalNameReference(); // read target.
|
|
const Function& constructor =
|
|
Function::Handle(Z, H.LookupConstructorByKernelConstructor(target));
|
|
Class& klass = Class::Handle(Z, constructor.Owner());
|
|
|
|
intptr_t argument_count =
|
|
helper_->ReadUInt(); // read arguments part #1: arguments count.
|
|
|
|
// Build the type arguments vector (if necessary).
|
|
const TypeArguments* type_arguments =
|
|
TranslateTypeArguments(constructor, &klass); // read argument types.
|
|
|
|
if (klass.NumTypeArguments() > 0 && !klass.IsGeneric()) {
|
|
Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw());
|
|
// TODO(27590): Can we move this code into [ReceiverType]?
|
|
type ^= ClassFinalizer::FinalizeType(*active_class_->klass, type,
|
|
ClassFinalizer::kFinalize);
|
|
TypeArguments& canonicalized_type_arguments =
|
|
TypeArguments::ZoneHandle(Z, type.arguments());
|
|
canonicalized_type_arguments = canonicalized_type_arguments.Canonicalize();
|
|
type_arguments = &canonicalized_type_arguments;
|
|
}
|
|
|
|
// Prepare either the instance or the type argument vector for the constructor
|
|
// call.
|
|
Instance* receiver = NULL;
|
|
const TypeArguments* type_arguments_argument = NULL;
|
|
if (!constructor.IsFactory()) {
|
|
receiver = &Instance::Handle(Z, Instance::New(klass, Heap::kOld));
|
|
if (type_arguments != NULL) {
|
|
receiver->SetTypeArguments(*type_arguments);
|
|
}
|
|
} else {
|
|
type_arguments_argument = type_arguments;
|
|
}
|
|
|
|
// read positional and named parameters.
|
|
const Object& result = RunFunction(position, constructor, argument_count,
|
|
receiver, type_arguments_argument);
|
|
|
|
if (constructor.IsFactory()) {
|
|
// Factories return the new object.
|
|
result_ ^= result.raw();
|
|
} else {
|
|
ASSERT(!receiver->IsNull());
|
|
result_ = (*receiver).raw();
|
|
}
|
|
if (I->obfuscate() &&
|
|
(result_.clazz() == I->object_store()->symbol_class())) {
|
|
Obfuscator::ObfuscateSymbolInstance(H.thread(), result_);
|
|
}
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateNot() {
|
|
result_ = Bool::Get(!EvaluateBooleanExpressionHere()).raw();
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateLogicalExpression() {
|
|
bool left = EvaluateBooleanExpressionHere(); // read left.
|
|
LogicalOperator op = static_cast<LogicalOperator>(helper_->ReadByte());
|
|
if (op == kAnd) {
|
|
if (left) {
|
|
EvaluateBooleanExpressionHere(); // read right.
|
|
} else {
|
|
helper_->SkipExpression(); // read right.
|
|
}
|
|
} else {
|
|
ASSERT(op == kOr);
|
|
if (!left) {
|
|
EvaluateBooleanExpressionHere(); // read right.
|
|
} else {
|
|
helper_->SkipExpression(); // read right.
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateAsExpression() {
|
|
TokenPosition position = helper_->ReadPosition();
|
|
const uint8_t flags = helper_->ReadFlags();
|
|
const bool is_type_error = (flags & (1 << 0)) != 0;
|
|
|
|
// Check that this AsExpression was inserted by the front-end.
|
|
if (!is_type_error) {
|
|
H.ReportError(
|
|
script_, position,
|
|
"explicit as operator is not permitted in constant expression");
|
|
}
|
|
|
|
EvaluateExpression(helper_->ReaderOffset(), false);
|
|
|
|
const AbstractType& type = T.BuildType();
|
|
if (!type.IsInstantiated()) {
|
|
const String& type_str = String::Handle(type.UserVisibleName());
|
|
H.ReportError(
|
|
script_, position,
|
|
"Not a constant expression: right hand side of an implicit "
|
|
"as-expression is expected to be an instantiated type, got %s",
|
|
type_str.ToCString());
|
|
}
|
|
|
|
const TypeArguments& instantiator_type_arguments = TypeArguments::Handle();
|
|
const TypeArguments& function_type_arguments = TypeArguments::Handle();
|
|
if (!result_.IsInstanceOf(type, instantiator_type_arguments,
|
|
function_type_arguments)) {
|
|
const AbstractType& rtype =
|
|
AbstractType::Handle(result_.GetType(Heap::kNew));
|
|
const String& result_str = String::Handle(rtype.UserVisibleName());
|
|
const String& type_str = String::Handle(type.UserVisibleName());
|
|
H.ReportError(
|
|
script_, position,
|
|
"Not a constant expression: Type '%s' is not a subtype of type '%s'",
|
|
result_str.ToCString(), type_str.ToCString());
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateConditionalExpression() {
|
|
bool condition = EvaluateBooleanExpressionHere();
|
|
if (condition) {
|
|
EvaluateExpression(helper_->ReaderOffset(), false); // read then.
|
|
helper_->SkipExpression(); // read otherwise.
|
|
} else {
|
|
helper_->SkipExpression(); // read then.
|
|
EvaluateExpression(helper_->ReaderOffset(), false); // read otherwise.
|
|
}
|
|
helper_->SkipOptionalDartType(); // read unused static type.
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateStringConcatenation() {
|
|
TokenPosition position = helper_->ReadPosition(); // read position.
|
|
intptr_t length = helper_->ReadListLength(); // read list length.
|
|
|
|
bool all_string = true;
|
|
const Array& strings =
|
|
Array::Handle(Z, Array::New(length, H.allocation_space()));
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
EvaluateExpression(helper_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
strings.SetAt(i, result_);
|
|
all_string = all_string && result_.IsString();
|
|
}
|
|
if (all_string) {
|
|
result_ = String::ConcatAll(strings, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
} else {
|
|
// Get string interpolation function.
|
|
const Class& cls =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::StringBase()));
|
|
ASSERT(!cls.IsNull());
|
|
const Function& func = Function::Handle(
|
|
Z, cls.LookupStaticFunction(
|
|
Library::PrivateCoreLibName(Symbols::Interpolate())));
|
|
ASSERT(!func.IsNull());
|
|
|
|
// Build argument array to pass to the interpolation function.
|
|
const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld));
|
|
interpolate_arg.SetAt(0, strings);
|
|
|
|
// Run and canonicalize.
|
|
const Object& result =
|
|
RunFunction(position, func, interpolate_arg, Array::null_array());
|
|
result_ = H.Canonicalize(String::Cast(result));
|
|
}
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateSymbolLiteral() {
|
|
const Class& owner = *active_class_->klass;
|
|
const Library& lib = Library::Handle(Z, owner.library());
|
|
String& symbol_value = H.DartIdentifier(lib, helper_->ReadStringReference());
|
|
const Class& symbol_class =
|
|
Class::ZoneHandle(Z, I->object_store()->symbol_class());
|
|
ASSERT(!symbol_class.IsNull());
|
|
const 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 ConstantEvaluator::EvaluateTypeLiteral() {
|
|
const AbstractType& type = T.BuildType();
|
|
result_ = type.raw();
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateListLiteralInternal() {
|
|
helper_->ReadPosition(); // read position.
|
|
const TypeArguments& type_arguments = T.BuildTypeArguments(1); // read type.
|
|
intptr_t length = helper_->ReadListLength(); // read list length.
|
|
const Array& const_list =
|
|
Array::ZoneHandle(Z, Array::New(length, Heap::kOld));
|
|
const_list.SetTypeArguments(type_arguments);
|
|
Instance& expression = Instance::Handle(Z);
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
expression = EvaluateExpression(helper_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
const_list.SetAt(i, expression);
|
|
}
|
|
const_list.MakeImmutable();
|
|
result_ = H.Canonicalize(const_list);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateMapLiteralInternal() {
|
|
helper_->ReadPosition(); // read position.
|
|
const TypeArguments& type_arguments =
|
|
T.BuildTypeArguments(2); // read key type and value type.
|
|
|
|
intptr_t length = helper_->ReadListLength(); // read length of entries.
|
|
|
|
// This MapLiteral wasn't cached, so content isn't cached either.
|
|
Array& const_kv_array = Array::Handle(Z, Array::New(2 * length, Heap::kOld));
|
|
Instance& temp = Instance::Handle(Z);
|
|
for (intptr_t i = 0; i < length; ++i) {
|
|
temp = EvaluateExpression(helper_->ReaderOffset(), false); // read key.
|
|
const_kv_array.SetAt(2 * i + 0, temp);
|
|
temp = EvaluateExpression(helper_->ReaderOffset(), false); // read value.
|
|
const_kv_array.SetAt(2 * i + 1, temp);
|
|
}
|
|
|
|
const_kv_array.MakeImmutable();
|
|
const_kv_array ^= H.Canonicalize(const_kv_array);
|
|
|
|
const Class& map_class =
|
|
Class::Handle(Z, Library::LookupCoreClass(Symbols::ImmutableMap()));
|
|
ASSERT(!map_class.IsNull());
|
|
ASSERT(map_class.NumTypeArguments() == 2);
|
|
|
|
const Field& field =
|
|
Field::Handle(Z, map_class.LookupInstanceFieldAllowPrivate(
|
|
H.DartSymbolObfuscate("_kvPairs")));
|
|
ASSERT(!field.IsNull());
|
|
|
|
// NOTE: This needs to be kept in sync with `runtime/lib/immutable_map.dart`!
|
|
result_ = Instance::New(map_class, Heap::kOld);
|
|
ASSERT(!result_.IsNull());
|
|
result_.SetTypeArguments(type_arguments);
|
|
result_.SetField(field, const_kv_array);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateLet() {
|
|
ASSERT(IsBuildingFlowGraph());
|
|
intptr_t kernel_position =
|
|
helper_->ReaderOffset() + helper_->data_program_offset_;
|
|
|
|
LocalVariable* local = flow_graph_builder_->LookupVariable(kernel_position);
|
|
|
|
// read variable declaration.
|
|
VariableDeclarationHelper helper(helper_);
|
|
helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
|
|
Tag tag = helper_->ReadTag(); // read (first part of) initializer.
|
|
if (tag == kNothing) {
|
|
local->SetConstValue(Instance::ZoneHandle(Z, Instance::null()));
|
|
} else {
|
|
local->SetConstValue(Instance::ZoneHandle(
|
|
Z, EvaluateExpression(helper_->ReaderOffset(),
|
|
false))); // read rest of initializer.
|
|
}
|
|
|
|
EvaluateExpression(helper_->ReaderOffset(), false); // read body
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluatePartialTearoffInstantiation() {
|
|
// This method call wasn't cached, so receiver et al. isn't cached either.
|
|
const Instance& receiver = Instance::Handle(
|
|
Z, EvaluateExpression(helper_->ReaderOffset(), false)); // read receiver.
|
|
if (!receiver.IsClosure()) {
|
|
H.ReportError(script_, TokenPosition::kNoSource, "Expected closure.");
|
|
}
|
|
const Closure& old_closure = Closure::Cast(receiver);
|
|
|
|
// read type arguments.
|
|
intptr_t num_type_args = helper_->ReadListLength();
|
|
const TypeArguments* type_args = &T.BuildTypeArguments(num_type_args);
|
|
if (!type_args->IsNull() && !type_args->IsInstantiated()) {
|
|
H.ReportError(
|
|
script_, TokenPosition::kNoSource,
|
|
"Type arguments in partial instantiations must be instantiated and are "
|
|
"therefore not allowed to depend on type parameters.");
|
|
}
|
|
|
|
// Create new closure with the type arguments inserted, and other things
|
|
// copied over.
|
|
Closure& new_closure = Closure::Handle(
|
|
Z,
|
|
Closure::New(
|
|
TypeArguments::Handle(Z, old_closure.instantiator_type_arguments()),
|
|
TypeArguments::Handle(old_closure.function_type_arguments()),
|
|
*type_args, Function::Handle(Z, old_closure.function()),
|
|
Context::Handle(Z, old_closure.context()), Heap::kOld));
|
|
result_ = H.Canonicalize(new_closure);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateBigIntLiteral() {
|
|
const String& value =
|
|
H.DartString(helper_->ReadStringReference()); // read string reference.
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
if (result_.IsNull()) {
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Integer literal %s is out of range", value.ToCString());
|
|
}
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateStringLiteral() {
|
|
result_ = H.DartSymbolPlain(helper_->ReadStringReference())
|
|
.raw(); // read string reference.
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateIntLiteral(uint8_t payload) {
|
|
int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateIntLiteral(bool is_negative) {
|
|
int64_t value = is_negative ? -static_cast<int64_t>(helper_->ReadUInt())
|
|
: helper_->ReadUInt(); // read value.
|
|
result_ = Integer::New(value, Heap::kOld);
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateDoubleLiteral() {
|
|
result_ = Double::New(helper_->ReadDouble(), Heap::kOld); // read value.
|
|
result_ = H.Canonicalize(result_);
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateBoolLiteral(bool value) {
|
|
result_ = Bool::Get(value).raw();
|
|
}
|
|
|
|
void ConstantEvaluator::EvaluateNullLiteral() {
|
|
result_ = Instance::null();
|
|
}
|
|
|
|
// This depends on being about to read the list of positionals on arguments.
|
|
const Object& ConstantEvaluator::RunFunction(TokenPosition position,
|
|
const Function& function,
|
|
intptr_t argument_count,
|
|
const Instance* receiver,
|
|
const TypeArguments* type_args) {
|
|
// We use a kernel2kernel constant evaluator in Dart 2.0 AOT compilation, so
|
|
// we should never end up evaluating constants using the VM's constant
|
|
// evaluator.
|
|
if (FLAG_precompiled_mode) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// We do not support generic methods yet.
|
|
ASSERT((receiver == NULL) || (type_args == NULL));
|
|
intptr_t extra_arguments =
|
|
(receiver != NULL ? 1 : 0) + (type_args != NULL ? 1 : 0);
|
|
|
|
// Build up arguments.
|
|
const Array& arguments = Array::Handle(
|
|
Z, Array::New(extra_arguments + argument_count, H.allocation_space()));
|
|
intptr_t pos = 0;
|
|
if (receiver != NULL) {
|
|
arguments.SetAt(pos++, *receiver);
|
|
}
|
|
if (type_args != NULL) {
|
|
arguments.SetAt(pos++, *type_args);
|
|
}
|
|
|
|
// List of positional.
|
|
intptr_t list_length = helper_->ReadListLength(); // read list length.
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
EvaluateExpression(helper_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
arguments.SetAt(pos++, result_);
|
|
}
|
|
|
|
// List of named.
|
|
list_length = helper_->ReadListLength(); // read list length.
|
|
const Array& names =
|
|
Array::Handle(Z, Array::New(list_length, H.allocation_space()));
|
|
for (intptr_t i = 0; i < list_length; ++i) {
|
|
String& name = H.DartSymbolObfuscate(
|
|
helper_->ReadStringReference()); // read ith name index.
|
|
names.SetAt(i, name);
|
|
EvaluateExpression(helper_->ReaderOffset(),
|
|
false); // read ith expression.
|
|
arguments.SetAt(pos++, result_);
|
|
}
|
|
|
|
return RunFunction(position, function, arguments, names);
|
|
}
|
|
|
|
const Object& ConstantEvaluator::RunFunction(const TokenPosition position,
|
|
const Function& function,
|
|
const Array& arguments,
|
|
const Array& names) {
|
|
// We do not support generic methods yet.
|
|
const int kTypeArgsLen = 0;
|
|
const Array& args_descriptor = Array::Handle(
|
|
Z, ArgumentsDescriptor::New(kTypeArgsLen, arguments.Length(), names));
|
|
const Object& result = Object::Handle(
|
|
Z, DartEntry::InvokeFunction(function, arguments, args_descriptor));
|
|
if (result.IsError()) {
|
|
H.ReportError(Error::Cast(result), script_, position,
|
|
"error evaluating constant constructor");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const Object& ConstantEvaluator::RunMethodCall(const TokenPosition position,
|
|
const Function& function,
|
|
const Instance* receiver) {
|
|
intptr_t argument_count = helper_->ReadUInt(); // read arguments count.
|
|
|
|
// TODO(28109) Support generic methods in the VM or reify them away.
|
|
ASSERT(helper_->PeekListLength() == 0);
|
|
helper_->SkipListOfDartTypes(); // read list of types.
|
|
|
|
// Run the method.
|
|
return RunFunction(position, function, argument_count, receiver, NULL);
|
|
}
|
|
|
|
RawObject* ConstantEvaluator::EvaluateConstConstructorCall(
|
|
const Class& type_class,
|
|
const TypeArguments& type_arguments,
|
|
const Function& constructor,
|
|
const Object& argument) {
|
|
// We use a kernel2kernel constant evaluator in Dart 2.0 AOT compilation, so
|
|
// we should never end up evaluating constants using the VM's constant
|
|
// evaluator.
|
|
if (FLAG_precompiled_mode) {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
// Factories have one extra argument: the type arguments.
|
|
// Constructors have 1 extra arguments: receiver.
|
|
const int kTypeArgsLen = 0;
|
|
const int kNumArgs = 1;
|
|
const int kNumExtraArgs = 1;
|
|
const int argument_count = kNumArgs + kNumExtraArgs;
|
|
const Array& arg_values =
|
|
Array::Handle(Z, Array::New(argument_count, 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(kTypeArgsLen, argument_count,
|
|
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();
|
|
}
|
|
if (I->obfuscate() &&
|
|
(instance.clazz() == I->object_store()->symbol_class())) {
|
|
Obfuscator::ObfuscateSymbolInstance(H.thread(), instance);
|
|
}
|
|
return H.Canonicalize(instance);
|
|
}
|
|
|
|
const TypeArguments* ConstantEvaluator::TranslateTypeArguments(
|
|
const Function& target,
|
|
Class* target_klass) {
|
|
intptr_t type_count = helper_->ReadListLength(); // read type count.
|
|
|
|
const TypeArguments* type_arguments = NULL;
|
|
if (type_count > 0) {
|
|
type_arguments = &T.BuildInstantiatedTypeArguments(
|
|
*target_klass, type_count); // read types.
|
|
|
|
if (!(type_arguments->IsNull() || type_arguments->IsInstantiated())) {
|
|
H.ReportError(script_, TokenPosition::kNoSource,
|
|
"Type must be constant in const constructor.");
|
|
}
|
|
} else if (target.IsFactory() && type_arguments == NULL) {
|
|
// All factories take a type arguments vector as first argument (independent
|
|
// of whether the class is generic or not).
|
|
type_arguments = &TypeArguments::ZoneHandle(Z, TypeArguments::null());
|
|
}
|
|
return type_arguments;
|
|
}
|
|
|
|
bool ConstantEvaluator::EvaluateBooleanExpressionHere() {
|
|
EvaluateExpression(helper_->ReaderOffset(), false);
|
|
AssertBool();
|
|
return result_.raw() == Bool::True().raw();
|
|
}
|
|
|
|
bool ConstantEvaluator::GetCachedConstant(intptr_t kernel_offset,
|
|
Instance* value) {
|
|
if (!IsBuildingFlowGraph()) return false;
|
|
|
|
const Function& function = flow_graph_builder_->parsed_function_->function();
|
|
if ((function.kind() == RawFunction::kImplicitStaticGetter ||
|
|
function.kind() == RawFunction::kStaticFieldInitializer) &&
|
|
!I->CanOptimizeImmediately()) {
|
|
// Don't cache constants in initializer expressions. They get
|
|
// evaluated only once.
|
|
return false;
|
|
}
|
|
|
|
bool is_present = false;
|
|
ASSERT(!script_.InVMIsolateHeap());
|
|
if (script_.compile_time_constants() == Array::null()) {
|
|
return false;
|
|
}
|
|
{
|
|
// Any access to constants arrays must be locked since mutator and
|
|
// background compiler can access the array at the same time.
|
|
SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
|
|
|
|
KernelConstantsMap constants(script_.compile_time_constants());
|
|
*value ^= constants.GetOrNull(kernel_offset + helper_->data_program_offset_,
|
|
&is_present);
|
|
constants.Release();
|
|
}
|
|
return is_present;
|
|
}
|
|
|
|
void ConstantEvaluator::CacheConstantValue(intptr_t kernel_offset,
|
|
const Instance& value) {
|
|
ASSERT(Thread::Current()->IsMutatorThread());
|
|
|
|
if (!IsBuildingFlowGraph()) return;
|
|
|
|
const Function& function = flow_graph_builder_->parsed_function_->function();
|
|
if ((function.kind() == RawFunction::kImplicitStaticGetter ||
|
|
function.kind() == RawFunction::kStaticFieldInitializer) &&
|
|
!I->CanOptimizeImmediately()) {
|
|
// Don't cache constants in initializer expressions. They get
|
|
// evaluated only once.
|
|
return;
|
|
}
|
|
const intptr_t kInitialConstMapSize = 16;
|
|
ASSERT(!script_.InVMIsolateHeap());
|
|
if (script_.compile_time_constants() == Array::null()) {
|
|
const Array& array = Array::Handle(
|
|
HashTables::New<KernelConstantsMap>(kInitialConstMapSize, Heap::kNew));
|
|
script_.set_compile_time_constants(array);
|
|
}
|
|
{
|
|
// Any access to constants arrays must be locked since mutator and
|
|
// background compiler can access the array at the same time.
|
|
SafepointMutexLocker ml(H.thread()->isolate()->kernel_constants_mutex());
|
|
|
|
KernelConstantsMap constants(script_.compile_time_constants());
|
|
constants.InsertNewOrGetValue(kernel_offset + helper_->data_program_offset_,
|
|
value);
|
|
script_.set_compile_time_constants(constants.Release());
|
|
}
|
|
}
|
|
|
|
} // namespace kernel
|
|
} // namespace dart
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|