Files
sdk/runtime/vm/object.cc
T
kmillikin@google.com 8c4e214722 Pass IC data and arguments descriptor to IC miss runtime functions.
Before, we obtained them by pattern matching backwards on the machine
instructions at the call site.  This previous approach becomes unwieldy when
we need to use to use multiple instance call patterns.

R=vegorov@google.com
BUG=

Review URL: https://codereview.chromium.org//11438017

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15737 260f80e4-7a28-3924-810f-c04153c831b5
2012-12-05 15:35:18 +00:00

12129 lines
376 KiB
C++

// Copyright (c) 2012, 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/object.h"
#include "include/dart_api.h"
#include "platform/assert.h"
#include "vm/assembler.h"
#include "vm/bigint_operations.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/code_generator.h"
#include "vm/code_observers.h"
#include "vm/code_patcher.h"
#include "vm/compiler.h"
#include "vm/compiler_stats.h"
#include "vm/dart.h"
#include "vm/dart_api_state.h"
#include "vm/dart_entry.h"
#include "vm/datastream.h"
#include "vm/deopt_instructions.h"
#include "vm/double_conversion.h"
#include "vm/exceptions.h"
#include "vm/growable_array.h"
#include "vm/heap.h"
#include "vm/object_store.h"
#include "vm/parser.h"
#include "vm/runtime_entry.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/timer.h"
#include "vm/unicode.h"
namespace dart {
DEFINE_FLAG(bool, show_internal_names, false,
"Show names of internal classes (e.g. \"OneByteString\") in error messages "
"instead of showing the corresponding interface names (e.g. \"String\")");
DEFINE_FLAG(bool, trace_disabling_optimized_code, false,
"Trace disabling optimized code.");
DECLARE_FLAG(bool, trace_compiler);
DECLARE_FLAG(bool, eliminate_type_checks);
DECLARE_FLAG(bool, enable_type_checks);
static const char* kGetterPrefix = "get:";
static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix);
static const char* kSetterPrefix = "set:";
static const intptr_t kSetterPrefixLength = strlen(kSetterPrefix);
cpp_vtable Object::handle_vtable_ = 0;
cpp_vtable Object::builtin_vtables_[kNumPredefinedCids] = { 0 };
cpp_vtable Smi::handle_vtable_ = 0;
// These are initialized to a value that will force a illegal memory access if
// they are being used.
#if defined(RAW_NULL)
#error RAW_NULL should not be defined.
#endif
#define RAW_NULL kHeapObjectTag
RawObject* Object::null_ = reinterpret_cast<RawInstance*>(RAW_NULL);
RawArray* Object::empty_array_ = reinterpret_cast<RawArray*>(RAW_NULL);
RawInstance* Object::sentinel_ = reinterpret_cast<RawInstance*>(RAW_NULL);
RawInstance* Object::transition_sentinel_ =
reinterpret_cast<RawInstance*>(RAW_NULL);
RawClass* Object::class_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::null_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::dynamic_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::void_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::unresolved_class_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::type_arguments_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::instantiated_type_arguments_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::patch_class_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::function_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::closure_data_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::redirection_data_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::field_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::literal_token_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::token_stream_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::script_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::library_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::library_prefix_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::namespace_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::code_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::instructions_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::pc_descriptors_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::stackmap_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::var_descriptors_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::exception_handlers_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::deopt_info_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::context_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::context_scope_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::icdata_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::subtypetestcache_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::api_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::language_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::unhandled_exception_class_ =
reinterpret_cast<RawClass*>(RAW_NULL);
RawClass* Object::unwind_error_class_ = reinterpret_cast<RawClass*>(RAW_NULL);
#undef RAW_NULL
// Takes a vm internal name and makes it suitable for external user.
//
// Examples:
//
// Internal getter and setter prefixes are changed:
//
// get:foo -> foo
// set:foo -> foo=
//
// Private name mangling is removed, possibly twice:
//
// _ReceivePortImpl@6be832b -> _ReceivePortImpl
// _ReceivePortImpl@6be832b._internal@6be832b -> +ReceivePortImpl._internal
//
// The trailing . on the default constructor name is dropped:
//
// List. -> List
//
// And so forth:
//
// get:foo@6be832b -> foo
// _MyClass@6b3832b. -> _MyClass
// _MyClass@6b3832b.named -> _MyClass.named
//
static RawString* IdentifierPrettyName(const String& name) {
intptr_t len = name.Length();
intptr_t start = 0;
intptr_t at_pos = len; // Position of '@' in the name.
intptr_t dot_pos = len; // Position of '.' in the name.
bool is_setter = false;
for (int i = 0; i < name.Length(); i++) {
if (name.CharAt(i) == ':') {
ASSERT(start == 0);
if (name.CharAt(0) == 's') {
is_setter = true;
}
start = i + 1;
} else if (name.CharAt(i) == '@') {
ASSERT(at_pos == len);
at_pos = i;
} else if (name.CharAt(i) == '.') {
dot_pos = i;
break;
}
}
intptr_t limit = (at_pos < dot_pos ? at_pos : dot_pos);
if (start == 0 && limit == len) {
// This name is fine as it is.
return name.raw();
}
const String& result =
String::Handle(String::SubString(name, start, (limit - start)));
// Look for a second '@' now to correctly handle names like
// "_ReceivePortImpl@6be832b._internal@6be832b".
at_pos = len;
for (int i = dot_pos; i < name.Length(); i++) {
if (name.CharAt(i) == '@') {
ASSERT(at_pos == len);
at_pos = i;
}
}
intptr_t suffix_len = at_pos - dot_pos;
if (suffix_len > 1) {
// This is a named constructor. Add the name back to the string.
const String& suffix =
String::Handle(String::SubString(name, dot_pos, suffix_len));
return String::Concat(result, suffix);
}
if (is_setter) {
// Setters need to end with '='.
const String& suffix = String::Handle(Symbols::Equals());
return String::Concat(result, suffix);
}
return result.raw();
}
template<typename type>
static bool IsSpecialCharacter(type value) {
return ((value == '"') ||
(value == '\n') ||
(value == '\f') ||
(value == '\b') ||
(value == '\t') ||
(value == '\v') ||
(value == '\r'));
}
template<typename type>
static type SpecialCharacter(type value) {
if (value == '"') {
return '"';
} else if (value == '\n') {
return 'n';
} else if (value == '\f') {
return 'f';
} else if (value == '\b') {
return 'b';
} else if (value == '\t') {
return 't';
} else if (value == '\v') {
return 'v';
} else if (value == '\r') {
return 'r';
}
UNREACHABLE();
return '\0';
}
void Object::InitOnce() {
// TODO(iposva): NoGCScope needs to be added here.
ASSERT(class_class() == null_);
// Initialize the static vtable values.
{
Object fake_object;
Smi fake_smi;
Object::handle_vtable_ = fake_object.vtable();
Smi::handle_vtable_ = fake_smi.vtable();
}
Isolate* isolate = Isolate::Current();
Heap* heap = isolate->heap();
// Allocate and initialize the null instance.
// 'null_' must be the first object allocated as it is used in allocation to
// clear the object.
{
uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld);
null_ = reinterpret_cast<RawInstance*>(address + kHeapObjectTag);
// The call below is using 'null_' to initialize itself.
InitializeObject(address, kNullCid, Instance::InstanceSize());
}
// Initialize object_store empty array to null_ in order to be able to check
// if the empty array was allocated (RAW_NULL is not available).
empty_array_ = Array::null();
Class& cls = Class::Handle();
// Allocate and initialize the class class.
{
intptr_t size = Class::InstanceSize();
uword address = heap->Allocate(size, Heap::kOld);
class_class_ = reinterpret_cast<RawClass*>(address + kHeapObjectTag);
InitializeObject(address, Class::kClassId, size);
Class fake;
// Initialization from Class::New<Class>.
// Directly set raw_ to break a circular dependency: SetRaw will attempt
// to lookup class class in the class table where it is not registered yet.
cls.raw_ = class_class_;
cls.set_handle_vtable(fake.vtable());
cls.set_instance_size(Class::InstanceSize());
cls.set_next_field_offset(Class::InstanceSize());
cls.set_id(Class::kClassId);
cls.raw_ptr()->state_bits_ = 0;
cls.set_is_finalized();
cls.raw_ptr()->type_arguments_field_offset_in_words_ =
Class::kNoTypeArguments;
cls.raw_ptr()->num_native_fields_ = 0;
cls.InitEmptyFields();
isolate->class_table()->Register(cls);
}
// Allocate and initialize the null class.
cls = Class::New<Instance>(kNullCid);
cls.set_is_finalized();
null_class_ = cls.raw();
// Allocate and initialize the free list element class.
cls = Class::New<FreeListElement::FakeInstance>(kFreeListElement);
cls.set_is_finalized();
// Allocate and initialize the sentinel values of Null class.
{
Instance& sentinel = Instance::Handle();
sentinel ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
sentinel_ = sentinel.raw();
Instance& transition_sentinel = Instance::Handle();
transition_sentinel ^=
Object::Allocate(kNullCid, Instance::InstanceSize(), Heap::kOld);
transition_sentinel_ = transition_sentinel.raw();
}
cls = Class::New<Instance>(kDynamicCid);
cls.set_is_finalized();
cls.set_is_abstract();
dynamic_class_ = cls.raw();
// Allocate the remaining VM internal classes.
cls = Class::New<UnresolvedClass>();
unresolved_class_class_ = cls.raw();
cls = Class::New<Instance>(kVoidCid);
cls.set_is_finalized();
void_class_ = cls.raw();
cls = Class::New<TypeArguments>();
type_arguments_class_ = cls.raw();
cls = Class::New<InstantiatedTypeArguments>();
instantiated_type_arguments_class_ = cls.raw();
cls = Class::New<PatchClass>();
patch_class_class_ = cls.raw();
cls = Class::New<Function>();
function_class_ = cls.raw();
cls = Class::New<ClosureData>();
closure_data_class_ = cls.raw();
cls = Class::New<RedirectionData>();
redirection_data_class_ = cls.raw();
cls = Class::New<Field>();
field_class_ = cls.raw();
cls = Class::New<LiteralToken>();
literal_token_class_ = cls.raw();
cls = Class::New<TokenStream>();
token_stream_class_ = cls.raw();
cls = Class::New<Script>();
script_class_ = cls.raw();
cls = Class::New<Library>();
library_class_ = cls.raw();
cls = Class::New<LibraryPrefix>();
library_prefix_class_ = cls.raw();
cls = Class::New<Namespace>();
namespace_class_ = cls.raw();
cls = Class::New<Code>();
code_class_ = cls.raw();
cls = Class::New<Instructions>();
instructions_class_ = cls.raw();
cls = Class::New<PcDescriptors>();
pc_descriptors_class_ = cls.raw();
cls = Class::New<Stackmap>();
stackmap_class_ = cls.raw();
cls = Class::New<LocalVarDescriptors>();
var_descriptors_class_ = cls.raw();
cls = Class::New<ExceptionHandlers>();
exception_handlers_class_ = cls.raw();
cls = Class::New<DeoptInfo>();
deopt_info_class_ = cls.raw();
cls = Class::New<Context>();
context_class_ = cls.raw();
cls = Class::New<ContextScope>();
context_scope_class_ = cls.raw();
cls = Class::New<ICData>();
icdata_class_ = cls.raw();
cls = Class::New<SubtypeTestCache>();
subtypetestcache_class_ = cls.raw();
cls = Class::New<ApiError>();
api_error_class_ = cls.raw();
cls = Class::New<LanguageError>();
language_error_class_ = cls.raw();
cls = Class::New<UnhandledException>();
unhandled_exception_class_ = cls.raw();
cls = Class::New<UnwindError>();
unwind_error_class_ = cls.raw();
ASSERT(class_class() != null_);
// Pre-allocate the Array and OneByteString class in the vm isolate so that
// we can create a symbol table and populate it with some frequently used
// strings as symbols.
cls = Class::New<Array>();
isolate->object_store()->set_array_class(cls);
cls = Class::NewStringClass(kOneByteStringCid);
isolate->object_store()->set_one_byte_string_class(cls);
cls = Class::NewStringClass(kTwoByteStringCid);
isolate->object_store()->set_two_byte_string_class(cls);
// Allocate and initialize the empty_array instance.
{
uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld);
empty_array_ = reinterpret_cast<RawArray*>(address + kHeapObjectTag);
InitializeObject(address, kArrayCid, Array::InstanceSize(0));
empty_array_->ptr()->length_ = Smi::New(0);
}
}
#define SET_CLASS_NAME(class_name, name) \
cls = class_name##_class(); \
str = Symbols::name(); \
cls.set_name(str); \
void Object::RegisterSingletonClassNames() {
Class& cls = Class::Handle();
String& str = String::Handle();
// Set up names for all VM singleton classes.
SET_CLASS_NAME(class, Class);
SET_CLASS_NAME(null, Null);
SET_CLASS_NAME(dynamic, Dynamic);
SET_CLASS_NAME(void, Void);
SET_CLASS_NAME(unresolved_class, UnresolvedClass);
SET_CLASS_NAME(type_arguments, TypeArguments);
SET_CLASS_NAME(instantiated_type_arguments, InstantiatedTypeArguments);
SET_CLASS_NAME(patch_class, PatchClass);
SET_CLASS_NAME(function, Function);
SET_CLASS_NAME(closure_data, ClosureData);
SET_CLASS_NAME(redirection_data, RedirectionData);
SET_CLASS_NAME(field, Field);
SET_CLASS_NAME(literal_token, LiteralToken);
SET_CLASS_NAME(token_stream, TokenStream);
SET_CLASS_NAME(script, Script);
SET_CLASS_NAME(library, LibraryClass);
SET_CLASS_NAME(library_prefix, LibraryPrefix);
SET_CLASS_NAME(namespace, Namespace);
SET_CLASS_NAME(code, Code);
SET_CLASS_NAME(instructions, Instructions);
SET_CLASS_NAME(pc_descriptors, PcDescriptors);
SET_CLASS_NAME(stackmap, Stackmap);
SET_CLASS_NAME(var_descriptors, LocalVarDescriptors);
SET_CLASS_NAME(exception_handlers, ExceptionHandlers);
SET_CLASS_NAME(deopt_info, DeoptInfo);
SET_CLASS_NAME(context, Context);
SET_CLASS_NAME(context_scope, ContextScope);
SET_CLASS_NAME(icdata, ICData);
SET_CLASS_NAME(subtypetestcache, SubtypeTestCache);
SET_CLASS_NAME(api_error, ApiError);
SET_CLASS_NAME(language_error, LanguageError);
SET_CLASS_NAME(unhandled_exception, UnhandledException);
SET_CLASS_NAME(unwind_error, UnwindError);
// Set up names for object array and one byte string class which are
// pre-allocated in the vm isolate also.
cls = Dart::vm_isolate()->object_store()->array_class();
str = Symbols::ObjectArray();
cls.set_name(str);
cls = Dart::vm_isolate()->object_store()->one_byte_string_class();
str = Symbols::OneByteString();
cls.set_name(str);
}
// Make unused space in an object whose type has been transformed safe
// for traversing during GC.
// The unused part of the transformed object is marked as an Array
// object or a regular Object so that it can be traversed during garbage
// collection.
void Object::MakeUnusedSpaceTraversable(const Object& obj,
intptr_t original_size,
intptr_t used_size) {
ASSERT(Isolate::Current()->no_gc_scope_depth() > 0);
ASSERT(!obj.IsNull());
ASSERT(original_size >= used_size);
if (original_size > used_size) {
intptr_t leftover_size = original_size - used_size;
uword addr = RawObject::ToAddr(obj.raw()) + used_size;
if (leftover_size >= Array::InstanceSize(0)) {
// As we have enough space to use an array object, update the leftover
// space as an Array object.
RawArray* raw = reinterpret_cast<RawArray*>(RawObject::FromAddr(addr));
uword tags = 0;
tags = RawObject::SizeTag::update(leftover_size, tags);
tags = RawObject::ClassIdTag::update(kArrayCid, tags);
raw->ptr()->tags_ = tags;
intptr_t leftover_len =
((leftover_size - Array::InstanceSize(0)) / kWordSize);
ASSERT(Array::InstanceSize(leftover_len) == leftover_size);
raw->ptr()->tags_ = tags;
raw->ptr()->length_ = Smi::New(leftover_len);
} else {
// Update the leftover space as a basic object.
ASSERT(leftover_size == Object::InstanceSize());
RawObject* raw =
reinterpret_cast<RawObject*>(RawObject::FromAddr(addr));
uword tags = 0;
tags = RawObject::SizeTag::update(leftover_size, tags);
tags = RawObject::ClassIdTag::update(kInstanceCid, tags);
raw->ptr()->tags_ = tags;
}
}
}
void Object::RegisterClass(const Class& cls,
const String& name,
const Library& lib) {
ASSERT(name.Length() > 0);
ASSERT(name.CharAt(0) != '_');
cls.set_name(name);
lib.AddClass(cls);
}
void Object::RegisterPrivateClass(const Class& cls,
const String& public_class_name,
const Library& lib) {
ASSERT(public_class_name.Length() > 0);
ASSERT(public_class_name.CharAt(0) == '_');
String& str = String::Handle();
str = lib.PrivateName(public_class_name);
cls.set_name(str);
lib.AddClass(cls);
}
RawError* Object::Init(Isolate* isolate) {
TIMERSCOPE(time_bootstrap);
ObjectStore* object_store = isolate->object_store();
Class& cls = Class::Handle();
Type& type = Type::Handle();
Array& array = Array::Handle();
// All RawArray fields will be initialized to an empty array, therefore
// initialize array class first.
cls = Class::New<Array>();
object_store->set_array_class(cls);
// Array and ImmutableArray are the only VM classes that are parameterized.
// Since they are pre-finalized, CalculateFieldOffsets() is not called, so we
// need to set the offset of their type_arguments_ field, which is explicitly
// declared in RawArray.
cls.set_type_arguments_field_offset(Array::type_arguments_offset());
// Set up the growable object array class (Has to be done after the array
// class is setup as one of its field is an array object).
cls = Class::New<GrowableObjectArray>();
object_store->set_growable_object_array_class(cls);
cls.set_type_arguments_field_offset(
GrowableObjectArray::type_arguments_offset());
// canonical_type_arguments_ are NULL terminated.
array = Array::New(4);
object_store->set_canonical_type_arguments(array);
// Setup type class early in the process.
cls = Class::New<Type>();
object_store->set_type_class(cls);
cls = Class::New<TypeParameter>();
object_store->set_type_parameter_class(cls);
// Pre-allocate the OneByteString class needed by the symbol table.
cls = Class::NewStringClass(kOneByteStringCid);
object_store->set_one_byte_string_class(cls);
// Pre-allocate the TwoByteString class needed by the symbol table.
cls = Class::NewStringClass(kTwoByteStringCid);
object_store->set_two_byte_string_class(cls);
// Setup the symbol table for the symbols created in the isolate.
Symbols::SetupSymbolTable(isolate);
// Set up the libraries array before initializing the core library.
const GrowableObjectArray& libraries =
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld));
object_store->set_libraries(libraries);
// Basic infrastructure has been setup, initialize the class dictionary.
Library::InitCoreLibrary(isolate);
Library& core_lib = Library::Handle(Library::CoreLibrary());
ASSERT(!core_lib.IsNull());
const GrowableObjectArray& pending_classes =
GrowableObjectArray::Handle(GrowableObjectArray::New(Heap::kOld));
object_store->set_pending_classes(pending_classes);
Context& context = Context::Handle(Context::New(0, Heap::kOld));
object_store->set_empty_context(context);
// Now that the symbol table is initialized and that the core dictionary as
// well as the core implementation dictionary have been setup, preallocate
// remaining classes and register them by name in the dictionaries.
String& name = String::Handle();
cls = Class::New<Bool>();
object_store->set_bool_class(cls);
name = Symbols::Bool();
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = object_store->array_class(); // Was allocated above.
name = Symbols::ObjectArray();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
// We cannot use NewNonParameterizedType(cls), because Array is parameterized.
type ^= Type::New(Object::Handle(cls.raw()),
TypeArguments::Handle(),
Scanner::kDummyTokenIndex);
type.set_is_finalized_instantiated();
type ^= type.Canonicalize();
object_store->set_array_type(type);
cls = object_store->growable_object_array_class(); // Was allocated above.
name = Symbols::GrowableObjectArray();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<ImmutableArray>();
object_store->set_immutable_array_class(cls);
cls.set_type_arguments_field_offset(Array::type_arguments_offset());
ASSERT(object_store->immutable_array_class() != object_store->array_class());
name = Symbols::ImmutableArray();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = object_store->one_byte_string_class(); // Was allocated above.
name = Symbols::OneByteString();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = object_store->two_byte_string_class(); // Was allocated above.
name = Symbols::TwoByteString();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::NewStringClass(kExternalOneByteStringCid);
object_store->set_external_one_byte_string_class(cls);
name = Symbols::ExternalOneByteString();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::NewStringClass(kExternalTwoByteStringCid);
object_store->set_external_two_byte_string_class(cls);
name = Symbols::ExternalTwoByteString();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Stacktrace>();
object_store->set_stacktrace_class(cls);
name = Symbols::Stacktrace();
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
// Super type set below, after Object is allocated.
cls = Class::New<JSRegExp>();
object_store->set_jsregexp_class(cls);
name = Symbols::JSSyntaxRegExp();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
// Initialize the base interfaces used by the core VM classes.
const Script& script = Script::Handle(Bootstrap::LoadCoreScript(false));
// Allocate and initialize the pre-allocated classes in the core library.
cls = Class::New<Instance>(kInstanceCid);
object_store->set_object_class(cls);
name = Symbols::Object();
cls.set_name(name);
cls.set_script(script);
cls.set_is_prefinalized();
core_lib.AddClass(cls);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_object_type(type);
cls = object_store->type_class();
name = Symbols::Type();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = object_store->type_parameter_class();
name = Symbols::TypeParameter();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Integer>();
object_store->set_integer_implementation_class(cls);
name = Symbols::IntegerImplementation();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Smi>();
object_store->set_smi_class(cls);
name = Symbols::Smi();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Mint>();
object_store->set_mint_class(cls);
name = Symbols::Mint();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Bigint>();
object_store->set_bigint_class(cls);
name = Symbols::Bigint();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<Double>();
object_store->set_double_class(cls);
name = Symbols::Double();
RegisterPrivateClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
cls = Class::New<WeakProperty>();
object_store->set_weak_property_class(cls);
name = Symbols::_WeakProperty();
RegisterPrivateClass(cls, name, core_lib);
Library::InitScalarlistLibrary(isolate);
Library& scalarlist_lib = Library::Handle(Library::ScalarlistLibrary());
cls = Class::New<Int8Array>();
object_store->set_int8_array_class(cls);
name = Symbols::_Int8Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Uint8Array>();
object_store->set_uint8_array_class(cls);
name = Symbols::_Uint8Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Int16Array>();
object_store->set_int16_array_class(cls);
name = Symbols::_Int16Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Uint16Array>();
object_store->set_uint16_array_class(cls);
name = Symbols::_Uint16Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Int32Array>();
object_store->set_int32_array_class(cls);
name = Symbols::_Int32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Uint32Array>();
object_store->set_uint32_array_class(cls);
name = Symbols::_Uint32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Int64Array>();
object_store->set_int64_array_class(cls);
name = Symbols::_Int64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Uint64Array>();
object_store->set_uint64_array_class(cls);
name = Symbols::_Uint64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Float32Array>();
object_store->set_float32_array_class(cls);
name = Symbols::_Float32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<Float64Array>();
object_store->set_float64_array_class(cls);
name = Symbols::_Float64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalInt8Array>();
object_store->set_external_int8_array_class(cls);
name = Symbols::_ExternalInt8Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalUint8Array>();
object_store->set_external_uint8_array_class(cls);
name = Symbols::_ExternalUint8Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalInt16Array>();
object_store->set_external_int16_array_class(cls);
name = Symbols::_ExternalInt16Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalUint16Array>();
object_store->set_external_uint16_array_class(cls);
name = Symbols::_ExternalUint16Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalInt32Array>();
object_store->set_external_int32_array_class(cls);
name = Symbols::_ExternalInt32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalUint32Array>();
object_store->set_external_uint32_array_class(cls);
name = Symbols::_ExternalUint32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalInt64Array>();
object_store->set_external_int64_array_class(cls);
name = Symbols::_ExternalInt64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalUint64Array>();
object_store->set_external_uint64_array_class(cls);
name = Symbols::_ExternalUint64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalFloat32Array>();
object_store->set_external_float32_array_class(cls);
name = Symbols::_ExternalFloat32Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
cls = Class::New<ExternalFloat64Array>();
object_store->set_external_float64_array_class(cls);
name = Symbols::_ExternalFloat64Array();
RegisterPrivateClass(cls, name, scalarlist_lib);
// Set the super type of class Stacktrace to Object type so that the
// 'toString' method is implemented.
cls = object_store->stacktrace_class();
cls.set_super_type(type);
// Note: The abstract class Function is represented by VM class
// DartFunction, not VM class Function.
name = Symbols::Function();
cls = Class::New<DartFunction>();
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_function_type(type);
cls = Class::New<Number>();
name = Symbols::Number();
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_number_type(type);
name = Symbols::New("int");
cls = Class::New<Instance>(name, script, Scanner::kDummyTokenIndex);
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_int_type(type);
name = Symbols::New("double");
cls = Class::New<Instance>(name, script, Scanner::kDummyTokenIndex);
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_double_type(type);
name = Symbols::New("String");
cls = Class::New<Instance>(name, script, Scanner::kDummyTokenIndex);
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
type = Type::NewNonParameterizedType(cls);
object_store->set_string_type(type);
name = Symbols::New("List");
cls = Class::New<Instance>(name, script, Scanner::kDummyTokenIndex);
RegisterClass(cls, name, core_lib);
pending_classes.Add(cls, Heap::kOld);
object_store->set_list_class(cls);
cls = object_store->bool_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_bool_type(type);
cls = object_store->smi_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_smi_type(type);
cls = object_store->mint_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_mint_type(type);
// The classes 'Null' and 'void' are not registered in the class dictionary,
// because their names are reserved keywords. Their names are not heap
// allocated, because the classes reside in the VM isolate.
// The corresponding types are stored in the object store.
cls = null_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_null_type(type);
cls = void_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_void_type(type);
// The class 'dynamic' is registered in the class dictionary because its name
// is a built-in identifier, rather than a reserved keyword. Its name is not
// heap allocated, because the class resides in the VM isolate.
// The corresponding type, the "unknown type", is stored in the object store.
cls = dynamic_class();
type = Type::NewNonParameterizedType(cls);
object_store->set_dynamic_type(type);
// Allocate pre-initialized values.
Bool& bool_value = Bool::Handle();
bool_value = Bool::New(true);
object_store->set_true_value(bool_value);
bool_value = Bool::New(false);
object_store->set_false_value(bool_value);
// Setup some default native field classes which can be extended for
// specifying native fields in dart classes.
Library::InitNativeWrappersLibrary(isolate);
ASSERT(isolate->object_store()->native_wrappers_library() != Library::null());
// Finish the initialization by compiling the bootstrap scripts containing the
// base interfaces and the implementation of the internal classes.
Error& error = Error::Handle();
error = Bootstrap::Compile(core_lib, script);
if (!error.IsNull()) {
return error.raw();
}
Script& patch_script = Script::Handle(Bootstrap::LoadCoreScript(true));
error = core_lib.Patch(patch_script);
if (!error.IsNull()) {
return error.raw();
}
const Script& collection_script =
Script::Handle(Bootstrap::LoadCollectionScript(false));
const Library& collection_lib =
Library::Handle(Library::CollectionLibrary());
ASSERT(!collection_lib.IsNull());
error = Bootstrap::Compile(collection_lib, collection_script);
if (!error.IsNull()) {
return error.raw();
}
const Script& math_script = Script::Handle(Bootstrap::LoadMathScript(false));
const Library& math_lib = Library::Handle(Library::MathLibrary());
ASSERT(!math_lib.IsNull());
error = Bootstrap::Compile(math_lib, math_script);
if (!error.IsNull()) {
return error.raw();
}
patch_script = Bootstrap::LoadMathScript(true);
error = math_lib.Patch(patch_script);
if (!error.IsNull()) {
return error.raw();
}
const Script& isolate_script = Script::Handle(
Bootstrap::LoadIsolateScript(false));
Library::InitIsolateLibrary(isolate);
const Library& isolate_lib = Library::Handle(Library::IsolateLibrary());
ASSERT(!isolate_lib.IsNull());
error = Bootstrap::Compile(isolate_lib, isolate_script);
if (!error.IsNull()) {
return error.raw();
}
patch_script = Bootstrap::LoadIsolateScript(true);
error = isolate_lib.Patch(patch_script);
if (!error.IsNull()) {
return error.raw();
}
const Script& mirrors_script = Script::Handle(
Bootstrap::LoadMirrorsScript(false));
Library::InitMirrorsLibrary(isolate);
const Library& mirrors_lib = Library::Handle(Library::MirrorsLibrary());
ASSERT(!mirrors_lib.IsNull());
error = Bootstrap::Compile(mirrors_lib, mirrors_script);
if (!error.IsNull()) {
return error.raw();
}
patch_script = Bootstrap::LoadMirrorsScript(true);
error = mirrors_lib.Patch(patch_script);
if (!error.IsNull()) {
return error.raw();
}
const Script& scalarlist_script = Script::Handle(
Bootstrap::LoadScalarlistScript(false));
ASSERT(!scalarlist_lib.IsNull());
error = Bootstrap::Compile(scalarlist_lib, scalarlist_script);
if (!error.IsNull()) {
return error.raw();
}
patch_script = Bootstrap::LoadScalarlistScript(true);
error = scalarlist_lib.Patch(patch_script);
if (!error.IsNull()) {
return error.raw();
}
Bootstrap::SetupNativeResolver();
// Remove the Object superclass cycle by setting the super type to null (not
// to the type of null).
cls = object_store->object_class();
cls.set_super_type(Type::Handle());
ClassFinalizer::VerifyBootstrapClasses();
return Error::null();
}
void Object::InitFromSnapshot(Isolate* isolate) {
TIMERSCOPE(time_bootstrap);
ObjectStore* object_store = isolate->object_store();
Class& cls = Class::Handle();
// Set up empty classes in the object store, these will get
// initialized correctly when we read from the snapshot.
// This is done to allow bootstrapping of reading classes from the snapshot.
cls = Class::New<Instance>(kInstanceCid);
object_store->set_object_class(cls);
cls = Class::New<Type>();
object_store->set_type_class(cls);
cls = Class::New<TypeParameter>();
object_store->set_type_parameter_class(cls);
cls = Class::New<Array>();
object_store->set_array_class(cls);
cls = Class::New<ImmutableArray>();
object_store->set_immutable_array_class(cls);
cls = Class::New<GrowableObjectArray>();
object_store->set_growable_object_array_class(cls);
cls = Class::New<Int8Array>();
object_store->set_int8_array_class(cls);
cls = Class::New<Uint8Array>();
object_store->set_uint8_array_class(cls);
cls = Class::New<Int16Array>();
object_store->set_int16_array_class(cls);
cls = Class::New<Uint16Array>();
object_store->set_uint16_array_class(cls);
cls = Class::New<Int32Array>();
object_store->set_int32_array_class(cls);
cls = Class::New<Uint32Array>();
object_store->set_uint32_array_class(cls);
cls = Class::New<Int64Array>();
object_store->set_int64_array_class(cls);
cls = Class::New<Uint64Array>();
object_store->set_uint64_array_class(cls);
cls = Class::New<Float32Array>();
object_store->set_float32_array_class(cls);
cls = Class::New<Float64Array>();
object_store->set_float64_array_class(cls);
cls = Class::New<ExternalInt8Array>();
object_store->set_external_int8_array_class(cls);
cls = Class::New<ExternalUint8Array>();
object_store->set_external_uint8_array_class(cls);
cls = Class::New<ExternalInt16Array>();
object_store->set_external_int16_array_class(cls);
cls = Class::New<ExternalUint16Array>();
object_store->set_external_uint16_array_class(cls);
cls = Class::New<ExternalInt32Array>();
object_store->set_external_int32_array_class(cls);
cls = Class::New<ExternalUint32Array>();
object_store->set_external_uint32_array_class(cls);
cls = Class::New<ExternalInt64Array>();
object_store->set_external_int64_array_class(cls);
cls = Class::New<ExternalUint64Array>();
object_store->set_external_uint64_array_class(cls);
cls = Class::New<ExternalFloat32Array>();
object_store->set_external_float32_array_class(cls);
cls = Class::New<ExternalFloat64Array>();
object_store->set_external_float64_array_class(cls);
cls = Class::New<Integer>();
object_store->set_integer_implementation_class(cls);
cls = Class::New<Smi>();
object_store->set_smi_class(cls);
cls = Class::New<Mint>();
object_store->set_mint_class(cls);
cls = Class::New<Double>();
object_store->set_double_class(cls);
cls = Class::New<Bigint>();
object_store->set_bigint_class(cls);
cls = Class::NewStringClass(kOneByteStringCid);
object_store->set_one_byte_string_class(cls);
cls = Class::NewStringClass(kTwoByteStringCid);
object_store->set_two_byte_string_class(cls);
cls = Class::NewStringClass(kExternalOneByteStringCid);
object_store->set_external_one_byte_string_class(cls);
cls = Class::NewStringClass(kExternalTwoByteStringCid);
object_store->set_external_two_byte_string_class(cls);
cls = Class::New<Bool>();
object_store->set_bool_class(cls);
cls = Class::New<Stacktrace>();
object_store->set_stacktrace_class(cls);
cls = Class::New<JSRegExp>();
object_store->set_jsregexp_class(cls);
// Some classes are not stored in the object store. Yet we still need to
// create their Class object so that they get put into the class_table
// (as a side effect of Class::New()).
cls = Class::New<DartFunction>();
cls = Class::New<Number>();
cls = Class::New<WeakProperty>();
object_store->set_weak_property_class(cls);
// Allocate pre-initialized values.
Bool& bool_value = Bool::Handle();
bool_value = Bool::New(true);
object_store->set_true_value(bool_value);
bool_value = Bool::New(false);
object_store->set_false_value(bool_value);
}
void Object::Print() const {
OS::Print("%s\n", ToCString());
}
RawString* Object::DictionaryName() const {
return String::null();
}
void Object::InitializeObject(uword address, intptr_t class_id, intptr_t size) {
// TODO(iposva): Get a proper halt instruction from the assembler which
// would be needed here for code objects.
uword initial_value = reinterpret_cast<uword>(null_);
uword cur = address;
uword end = address + size;
while (cur < end) {
*reinterpret_cast<uword*>(cur) = initial_value;
cur += kWordSize;
}
uword tags = 0;
ASSERT(class_id != kIllegalCid);
tags = RawObject::ClassIdTag::update(class_id, tags);
tags = RawObject::SizeTag::update(size, tags);
reinterpret_cast<RawObject*>(address)->tags_ = tags;
}
RawObject* Object::Allocate(intptr_t cls_id,
intptr_t size,
Heap::Space space) {
ASSERT(Utils::IsAligned(size, kObjectAlignment));
Isolate* isolate = Isolate::Current();
Heap* heap = isolate->heap();
uword address = heap->Allocate(size, space);
if (address == 0) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
const Instance& exception =
Instance::Handle(isolate->object_store()->out_of_memory());
Exceptions::Throw(exception);
UNREACHABLE();
}
NoGCScope no_gc;
InitializeObject(address, cls_id, size);
RawObject* raw_obj = reinterpret_cast<RawObject*>(address + kHeapObjectTag);
ASSERT(cls_id == RawObject::ClassIdTag::decode(raw_obj->ptr()->tags_));
return raw_obj;
}
class StoreBufferObjectPointerVisitor : public ObjectPointerVisitor {
public:
explicit StoreBufferObjectPointerVisitor(Isolate* isolate) :
ObjectPointerVisitor(isolate) {
}
void VisitPointers(RawObject** first, RawObject** last) {
for (RawObject** curr = first; curr <= last; ++curr) {
if ((*curr)->IsNewObject()) {
uword ptr = reinterpret_cast<uword>(curr);
isolate()->store_buffer()->AddPointer(ptr);
}
}
}
private:
DISALLOW_COPY_AND_ASSIGN(StoreBufferObjectPointerVisitor);
};
RawObject* Object::Clone(const Object& src, Heap::Space space) {
const Class& cls = Class::Handle(src.clazz());
intptr_t size = src.raw()->Size();
RawObject* raw_obj = Object::Allocate(cls.id(), size, space);
NoGCScope no_gc;
memmove(raw_obj->ptr(), src.raw()->ptr(), size);
if (space == Heap::kOld) {
StoreBufferObjectPointerVisitor visitor(Isolate::Current());
raw_obj->VisitPointers(&visitor);
}
return raw_obj;
}
RawString* Class::Name() const {
ASSERT(raw_ptr()->name_ != String::null());
return raw_ptr()->name_;
}
RawString* Class::UserVisibleName() const {
if (FLAG_show_internal_names) {
return Name();
}
switch (id()) {
case kIntegerCid:
case kSmiCid:
case kMintCid:
case kBigintCid:
return Symbols::New("int");
case kDoubleCid:
return Symbols::New("double");
case kOneByteStringCid:
case kTwoByteStringCid:
case kExternalOneByteStringCid:
case kExternalTwoByteStringCid:
return Symbols::New("String");
case kArrayCid:
case kImmutableArrayCid:
case kGrowableObjectArrayCid:
return Symbols::New("List");
case kInt8ArrayCid:
case kExternalInt8ArrayCid:
return Symbols::New("Int8List");
case kUint8ArrayCid:
case kExternalUint8ArrayCid:
return Symbols::New("Uint8List");
case kInt16ArrayCid:
case kExternalInt16ArrayCid:
return Symbols::New("Int16List");
case kUint16ArrayCid:
case kExternalUint16ArrayCid:
return Symbols::New("Uint16List");
case kInt32ArrayCid:
case kExternalInt32ArrayCid:
return Symbols::New("Int32List");
case kUint32ArrayCid:
case kExternalUint32ArrayCid:
return Symbols::New("Uint32List");
case kInt64ArrayCid:
case kExternalInt64ArrayCid:
return Symbols::New("Int64List");
case kUint64ArrayCid:
case kExternalUint64ArrayCid:
return Symbols::New("Uint64List");
case kFloat32ArrayCid:
case kExternalFloat32ArrayCid:
return Symbols::New("Float32List");
case kFloat64ArrayCid:
case kExternalFloat64ArrayCid:
return Symbols::New("Float64List");
default:
if (!IsSignatureClass()) {
const String& name = String::Handle(Name());
return IdentifierPrettyName(name);
} else {
return Name();
}
}
UNREACHABLE();
}
RawType* Class::SignatureType() const {
ASSERT(IsSignatureClass());
const Function& function = Function::Handle(signature_function());
ASSERT(!function.IsNull());
if (function.signature_class() != raw()) {
// This class is a function type alias. Return the canonical signature type.
const Class& canonical_class = Class::Handle(function.signature_class());
return canonical_class.SignatureType();
}
// Return the first canonical signature type if already computed.
const Array& signature_types = Array::Handle(canonical_types());
// The canonical_types array is initialized to the empty array.
ASSERT(!signature_types.IsNull());
if (signature_types.Length() > 0) {
// At most one signature type per signature class.
ASSERT(signature_types.Length() == 1);
Type& signature_type = Type::Handle();
signature_type ^= signature_types.At(0);
ASSERT(!signature_type.IsNull());
return signature_type.raw();
}
// A signature class extends class Instance and is parameterized in the same
// way as the owner class of its non-static signature function.
// It is not type parameterized if its signature function is static.
// See Class::NewSignatureClass() for the setup of its type parameters.
// During type finalization, the type arguments of the super class of the
// owner class of its signature function will be prepended to the type
// argument vector. Therefore, we only need to set the type arguments
// matching the type parameters here.
const TypeArguments& signature_type_arguments =
TypeArguments::Handle(type_parameters());
const Type& signature_type = Type::Handle(
Type::New(*this, signature_type_arguments, token_pos()));
// Return the still unfinalized signature type.
ASSERT(!signature_type.IsFinalized());
return signature_type.raw();
}
template <class FakeObject>
RawClass* Class::New() {
ASSERT(Object::class_class() != Class::null());
Class& result = Class::Handle();
{
RawObject* raw = Object::Allocate(Class::kClassId,
Class::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
FakeObject fake;
result.set_handle_vtable(fake.vtable());
result.set_instance_size(FakeObject::InstanceSize());
result.set_next_field_offset(FakeObject::InstanceSize());
ASSERT((FakeObject::kClassId != kInstanceCid));
result.set_id(FakeObject::kClassId);
result.raw_ptr()->state_bits_ = 0;
// VM backed classes are almost ready: run checks and resolve class
// references, but do not recompute size.
result.set_is_prefinalized();
result.raw_ptr()->type_arguments_field_offset_in_words_ = kNoTypeArguments;
result.raw_ptr()->num_native_fields_ = 0;
result.raw_ptr()->token_pos_ = Scanner::kDummyTokenIndex;
result.InitEmptyFields();
Isolate::Current()->class_table()->Register(result);
return result.raw();
}
// Initialize class fields of type Array with empty array.
void Class::InitEmptyFields() {
if (Object::empty_array() == Array::null()) {
// The empty array has not been initialized yet.
return;
}
StorePointer(&raw_ptr()->interfaces_, Object::empty_array());
StorePointer(&raw_ptr()->constants_, Object::empty_array());
StorePointer(&raw_ptr()->canonical_types_, Object::empty_array());
StorePointer(&raw_ptr()->functions_, Object::empty_array());
StorePointer(&raw_ptr()->fields_, Object::empty_array());
}
bool Class::HasInstanceFields() const {
const Array& field_array = Array::Handle(fields());
Field& field = Field::Handle();
for (intptr_t i = 0; i < field_array.Length(); ++i) {
field ^= field_array.At(i);
if (!field.is_static()) {
return true;
}
}
return false;
}
void Class::SetFunctions(const Array& value) const {
ASSERT(!value.IsNull());
#if defined(DEBUG)
// Verify that all the functions in the array have this class as owner.
Function& func = Function::Handle();
intptr_t len = value.Length();
for (intptr_t i = 0; i < len; i++) {
func ^= value.At(i);
ASSERT(func.Owner() == raw());
}
#endif
StorePointer(&raw_ptr()->functions_, value.raw());
}
void Class::AddClosureFunction(const Function& function) const {
GrowableObjectArray& closures =
GrowableObjectArray::Handle(raw_ptr()->closure_functions_);
if (closures.IsNull()) {
closures = GrowableObjectArray::New(4);
StorePointer(&raw_ptr()->closure_functions_, closures.raw());
}
ASSERT(function.IsNonImplicitClosureFunction());
closures.Add(function);
}
// Lookup the innermost closure function that contains token at token_pos.
RawFunction* Class::LookupClosureFunction(intptr_t token_pos) const {
if (raw_ptr()->closure_functions_ == GrowableObjectArray::null()) {
return Function::null();
}
const GrowableObjectArray& closures =
GrowableObjectArray::Handle(raw_ptr()->closure_functions_);
Function& closure = Function::Handle();
intptr_t num_closures = closures.Length();
intptr_t best_fit_token_pos = -1;
intptr_t best_fit_index = -1;
for (intptr_t i = 0; i < num_closures; i++) {
closure ^= closures.At(i);
ASSERT(!closure.IsNull());
if ((closure.token_pos() <= token_pos) &&
(token_pos < closure.end_token_pos()) &&
(best_fit_token_pos < closure.token_pos())) {
best_fit_index = i;
best_fit_token_pos = closure.token_pos();
}
}
closure = Function::null();
if (best_fit_index >= 0) {
closure ^= closures.At(best_fit_index);
}
return closure.raw();
}
void Class::set_signature_function(const Function& value) const {
ASSERT(value.IsClosureFunction() || value.IsSignatureFunction());
StorePointer(&raw_ptr()->signature_function_, value.raw());
}
void Class::set_class_state(RawClass::ClassState state) const {
ASSERT((state == RawClass::kAllocated) ||
(state == RawClass::kPreFinalized) ||
(state == RawClass::kFinalized));
set_state_bits(StateBits::update(state, raw_ptr()->state_bits_));
}
void Class::set_state_bits(intptr_t bits) const {
raw_ptr()->state_bits_ = static_cast<uint8_t>(bits);
}
void Class::set_library(const Library& value) const {
StorePointer(&raw_ptr()->library_, value.raw());
}
void Class::set_type_parameters(const TypeArguments& value) const {
StorePointer(&raw_ptr()->type_parameters_, value.raw());
}
intptr_t Class::NumTypeParameters() const {
const TypeArguments& type_params = TypeArguments::Handle(type_parameters());
if (type_params.IsNull()) {
return 0;
} else {
return type_params.Length();
}
}
intptr_t Class::NumTypeArguments() const {
// To work properly, this call requires the super class of this class to be
// resolved, which is checked by the SuperClass() call.
Class& cls = Class::Handle(raw());
if (IsSignatureClass()) {
const Function& signature_fun = Function::Handle(signature_function());
if (!signature_fun.is_static() &&
!signature_fun.HasInstantiatedSignature()) {
cls = signature_fun.Owner();
}
}
intptr_t num_type_args = NumTypeParameters();
const Class& superclass = Class::Handle(cls.SuperClass());
// Object is its own super class during bootstrap.
if (!superclass.IsNull() && (superclass.raw() != raw())) {
num_type_args += superclass.NumTypeArguments();
}
return num_type_args;
}
bool Class::HasTypeArguments() const {
if (!IsSignatureClass() && (is_finalized() || is_prefinalized())) {
// More efficient than calling NumTypeArguments().
return type_arguments_field_offset() != kNoTypeArguments;
} else {
// No need to check NumTypeArguments() if class has type parameters.
return (NumTypeParameters() > 0) || (NumTypeArguments() > 0);
}
}
RawClass* Class::SuperClass() const {
const Type& sup_type = Type::Handle(super_type());
if (sup_type.IsNull()) {
return Class::null();
}
return sup_type.type_class();
}
void Class::set_super_type(const Type& value) const {
StorePointer(&raw_ptr()->super_type_, value.raw());
}
// Return a TypeParameter if the type_name is a type parameter of this class.
// Return null otherwise.
RawTypeParameter* Class::LookupTypeParameter(const String& type_name,
intptr_t token_pos) const {
ASSERT(!type_name.IsNull());
const TypeArguments& type_params = TypeArguments::Handle(type_parameters());
if (!type_params.IsNull()) {
intptr_t num_type_params = type_params.Length();
TypeParameter& type_param = TypeParameter::Handle();
String& type_param_name = String::Handle();
// TODO(regis): We do not copy the bound (= type_param.bound()), since
// we are not able to finalize the bounds of type parameter references
// without getting into cycles. Revisit.
const AbstractType& bound = AbstractType::Handle(
Isolate::Current()->object_store()->object_type());
for (intptr_t i = 0; i < num_type_params; i++) {
type_param ^= type_params.TypeAt(i);
type_param_name = type_param.name();
if (type_param_name.Equals(type_name)) {
intptr_t index = type_param.index();
// Create a non-finalized new TypeParameter with the given token_pos.
if (type_param.IsFinalized()) {
// The index was adjusted during finalization. Revert.
index -= NumTypeArguments() - num_type_params;
} else {
ASSERT(type_param.index() == i);
}
return TypeParameter::New(*this, index, type_name, bound, token_pos);
}
}
}
return TypeParameter::null();
}
void Class::CalculateFieldOffsets() const {
Array& flds = Array::Handle(fields());
const Class& super = Class::Handle(SuperClass());
intptr_t offset = 0;
intptr_t type_args_field_offset = kNoTypeArguments;
if (super.IsNull()) {
offset = sizeof(RawObject);
} else {
type_args_field_offset = super.type_arguments_field_offset();
offset = super.next_field_offset();
ASSERT(offset > 0);
// We should never call CalculateFieldOffsets for native wrapper
// classes, assert this.
ASSERT(num_native_fields() == 0);
set_num_native_fields(super.num_native_fields());
}
// If the super class is parameterized, use the same type_arguments field.
if (type_args_field_offset == kNoTypeArguments) {
const TypeArguments& type_params = TypeArguments::Handle(type_parameters());
if (!type_params.IsNull()) {
ASSERT(type_params.Length() > 0);
// The instance needs a type_arguments field.
type_args_field_offset = offset;
offset += kWordSize;
}
}
set_type_arguments_field_offset(type_args_field_offset);
ASSERT(offset != 0);
Field& field = Field::Handle();
intptr_t len = flds.Length();
for (intptr_t i = 0; i < len; i++) {
field ^= flds.At(i);
// Offset is computed only for instance fields.
if (!field.is_static()) {
ASSERT(field.Offset() == 0);
field.SetOffset(offset);
offset += kWordSize;
}
}
set_instance_size(RoundedAllocationSize(offset));
set_next_field_offset(offset);
}
void Class::Finalize() const {
ASSERT(!is_finalized());
// Prefinalized classes have a VM internal representation and no Dart fields.
// Their instance size is precomputed and field offsets are known.
if (!is_prefinalized()) {
// Compute offsets of instance fields and instance size.
CalculateFieldOffsets();
}
set_is_finalized();
}
static const char* FormatPatchError(const char* format, const Object& obj) {
const char* msg = obj.ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, msg) + 1;
char* result = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(result, len, format, msg);
return result;
}
// Apply the members from the patch class to the original class.
const char* Class::ApplyPatch(const Class& patch) const {
ASSERT(!is_finalized());
// Shared handles used during the iteration.
String& member_name = String::Handle();
const Script& patch_script = Script::Handle(patch.script());
const PatchClass& patch_class = PatchClass::Handle(
PatchClass::New(*this, patch_script));
Array& orig_list = Array::Handle(functions());
intptr_t orig_len = orig_list.Length();
Array& patch_list = Array::Handle(patch.functions());
intptr_t patch_len = patch_list.Length();
// TODO(iposva): Verify that only patching existing methods and adding only
// new private methods.
Function& func = Function::Handle();
Function& orig_func = Function::Handle();
const GrowableObjectArray& new_functions = GrowableObjectArray::Handle(
GrowableObjectArray::New(orig_len));
for (intptr_t i = 0; i < orig_len; i++) {
orig_func ^= orig_list.At(i);
member_name = orig_func.name();
func = patch.LookupFunction(member_name);
if (func.IsNull()) {
// Non-patched function is preserved, all patched functions are added in
// the loop below.
new_functions.Add(orig_func);
} else if (!func.HasCompatibleParametersWith(orig_func) &&
!(func.IsFactory() && orig_func.IsConstructor() &&
(func.num_fixed_parameters() + 1 ==
orig_func.num_fixed_parameters()))) {
return FormatPatchError("mismatched parameters: %s", member_name);
}
}
for (intptr_t i = 0; i < patch_len; i++) {
func ^= patch_list.At(i);
func.set_owner(patch_class);
new_functions.Add(func);
}
Array& new_list = Array::Handle(Array::MakeArray(new_functions));
SetFunctions(new_list);
// Merge the two list of fields. Raise an error when duplicates are found or
// when a public field is being added.
orig_list = fields();
orig_len = orig_list.Length();
patch_list = patch.fields();
patch_len = patch_list.Length();
Field& field = Field::Handle();
Field& orig_field = Field::Handle();
new_list = Array::New(patch_len + orig_len);
for (intptr_t i = 0; i < patch_len; i++) {
field ^= patch_list.At(i);
field.set_owner(*this);
member_name = field.name();
// TODO(iposva): Verify non-public fields only.
// Verify no duplicate additions.
orig_field = LookupField(member_name);
if (!orig_field.IsNull()) {
return FormatPatchError("duplicate field: %s", member_name);
}
new_list.SetAt(i, field);
}
for (intptr_t i = 0; i < orig_len; i++) {
field ^= orig_list.At(i);
new_list.SetAt(patch_len + i, field);
}
SetFields(new_list);
return NULL;
}
void Class::SetFields(const Array& value) const {
ASSERT(!value.IsNull());
#if defined(DEBUG)
// Verify that all the fields in the array have this class as owner.
Field& field = Field::Handle();
intptr_t len = value.Length();
for (intptr_t i = 0; i < len; i++) {
field ^= value.At(i);
ASSERT(field.owner() == raw());
}
#endif
// The value of static fields is already initialized to null.
StorePointer(&raw_ptr()->fields_, value.raw());
}
template <class FakeInstance>
RawClass* Class::New(intptr_t index) {
ASSERT(Object::class_class() != Class::null());
Class& result = Class::Handle();
{
RawObject* raw = Object::Allocate(Class::kClassId,
Class::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
FakeInstance fake;
ASSERT(fake.IsInstance());
result.set_handle_vtable(fake.vtable());
result.set_instance_size(FakeInstance::InstanceSize());
result.set_next_field_offset(FakeInstance::InstanceSize());
result.set_id(index);
result.raw_ptr()->state_bits_ = 0;
result.raw_ptr()->type_arguments_field_offset_in_words_ = kNoTypeArguments;
result.raw_ptr()->num_native_fields_ = 0;
result.raw_ptr()->token_pos_ = Scanner::kDummyTokenIndex;
result.InitEmptyFields();
Isolate::Current()->class_table()->Register(result);
return result.raw();
}
template <class FakeInstance>
RawClass* Class::New(const String& name,
const Script& script,
intptr_t token_pos) {
Class& result = Class::Handle(New<FakeInstance>(kIllegalCid));
result.set_name(name);
result.set_script(script);
result.set_token_pos(token_pos);
return result.raw();
}
RawClass* Class::New(const String& name,
const Script& script,
intptr_t token_pos) {
Class& result = Class::Handle(New<Instance>(name, script, token_pos));
return result.raw();
}
RawClass* Class::NewSignatureClass(const String& name,
const Function& signature_function,
const Script& script) {
ASSERT(!signature_function.IsNull());
const Class& owner_class = Class::Handle(signature_function.Owner());
ASSERT(!owner_class.IsNull());
TypeArguments& type_parameters = TypeArguments::Handle();
// A signature class extends class Instance and is parameterized in the same
// way as the owner class of its non-static signature function.
// It is not type parameterized if its signature function is static.
if (!signature_function.is_static() &&
(owner_class.NumTypeParameters() > 0) &&
!signature_function.HasInstantiatedSignature()) {
type_parameters = owner_class.type_parameters();
}
const intptr_t token_pos = signature_function.token_pos();
Class& result = Class::Handle(New<Instance>(name, script, token_pos));
const Type& super_type = Type::Handle(Type::ObjectType());
const Array& empty_array = Array::Handle(Object::empty_array());
ASSERT(!super_type.IsNull());
result.set_instance_size(Closure::InstanceSize());
result.set_next_field_offset(Closure::InstanceSize());
result.set_super_type(super_type);
result.set_signature_function(signature_function);
result.set_type_parameters(type_parameters);
result.SetFields(empty_array);
result.SetFunctions(empty_array);
result.set_type_arguments_field_offset(
Closure::type_arguments_offset());
// Implements interface "Function".
const Type& function_type = Type::Handle(Type::Function());
const Array& interfaces = Array::Handle(Array::New(1, Heap::kOld));
interfaces.SetAt(0, function_type);
result.set_interfaces(interfaces);
// Unless the signature function already has a signature class, create a
// canonical signature class by having the signature function point back to
// the signature class.
if (signature_function.signature_class() == Object::null()) {
signature_function.set_signature_class(result);
result.set_is_finalized();
} else {
// This new signature class is an alias.
ASSERT(!result.IsCanonicalSignatureClass());
// Do not yet mark it as finalized, so that the class finalizer can check it
// for illegal self references.
result.set_is_prefinalized();
}
// Instances of a signature class can only be closures.
ASSERT(result.instance_size() == Closure::InstanceSize());
// Cache the signature type as the first canonicalized type in result.
const Type& signature_type = Type::Handle(result.SignatureType());
ASSERT(!signature_type.IsFinalized());
const Array& new_canonical_types = Array::Handle(Array::New(1, Heap::kOld));
new_canonical_types.SetAt(0, signature_type);
result.set_canonical_types(new_canonical_types);
return result.raw();
}
RawClass* Class::NewNativeWrapper(const Library& library,
const String& name,
int field_count) {
Class& cls = Class::Handle(library.LookupClass(name));
if (cls.IsNull()) {
const Array& empty_array = Array::Handle(Object::empty_array());
cls = New<Instance>(name, Script::Handle(), Scanner::kDummyTokenIndex);
cls.SetFields(empty_array);
cls.SetFunctions(empty_array);
// Set super class to Object.
cls.set_super_type(Type::Handle(Type::ObjectType()));
// Compute instance size. First word contains a pointer to a properly
// sized typed array once the first native field has been set.
intptr_t instance_size = sizeof(RawObject) + kWordSize;
cls.set_instance_size(RoundedAllocationSize(instance_size));
cls.set_next_field_offset(instance_size);
cls.set_num_native_fields(field_count);
cls.set_is_finalized();
library.AddClass(cls);
return cls.raw();
} else {
return Class::null();
}
}
RawClass* Class::NewStringClass(intptr_t class_id) {
intptr_t instance_size;
if (class_id == kOneByteStringCid) {
instance_size = OneByteString::InstanceSize();
} else if (class_id == kTwoByteStringCid) {
instance_size = TwoByteString::InstanceSize();
} else if (class_id == kExternalOneByteStringCid) {
instance_size = ExternalOneByteString::InstanceSize();
} else {
ASSERT(class_id == kExternalTwoByteStringCid);
instance_size = ExternalTwoByteString::InstanceSize();
}
Class& result = Class::Handle(New<String>(class_id));
result.set_instance_size(instance_size);
result.set_next_field_offset(instance_size);
result.set_is_prefinalized();
return result.raw();
}
void Class::set_name(const String& value) const {
ASSERT(value.IsSymbol());
StorePointer(&raw_ptr()->name_, value.raw());
}
void Class::set_script(const Script& value) const {
StorePointer(&raw_ptr()->script_, value.raw());
}
void Class::set_token_pos(intptr_t token_pos) const {
ASSERT(token_pos >= 0);
raw_ptr()->token_pos_ = token_pos;
}
void Class::set_is_implemented() const {
set_state_bits(ImplementedBit::update(true, raw_ptr()->state_bits_));
}
void Class::set_is_abstract() const {
set_state_bits(AbstractBit::update(true, raw_ptr()->state_bits_));
}
void Class::set_is_const() const {
set_state_bits(ConstBit::update(true, raw_ptr()->state_bits_));
}
void Class::set_is_finalized() const {
ASSERT(!is_finalized());
set_state_bits(StateBits::update(RawClass::kFinalized,
raw_ptr()->state_bits_));
}
void Class::set_is_prefinalized() const {
ASSERT(!is_finalized());
set_state_bits(StateBits::update(RawClass::kPreFinalized,
raw_ptr()->state_bits_));
}
void Class::set_interfaces(const Array& value) const {
// Verification and resolving of interfaces occurs in finalizer.
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->interfaces_, value.raw());
}
void Class::AddDirectSubclass(const Class& subclass) const {
ASSERT(!subclass.IsNull());
ASSERT(subclass.SuperClass() == raw());
// Do not keep track of the direct subclasses of class Object.
ASSERT(!IsObjectClass());
GrowableObjectArray& direct_subclasses =
GrowableObjectArray::Handle(raw_ptr()->direct_subclasses_);
if (direct_subclasses.IsNull()) {
direct_subclasses = GrowableObjectArray::New(4, Heap::kOld);
StorePointer(&raw_ptr()->direct_subclasses_, direct_subclasses.raw());
}
#if defined(DEBUG)
// Verify that the same class is not added twice.
for (intptr_t i = 0; i < direct_subclasses.Length(); i++) {
ASSERT(direct_subclasses.At(i) != subclass.raw());
}
#endif
direct_subclasses.Add(subclass);
}
RawArray* Class::constants() const {
return raw_ptr()->constants_;
}
void Class::set_constants(const Array& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->constants_, value.raw());
}
RawArray* Class::canonical_types() const {
return raw_ptr()->canonical_types_;
}
void Class::set_canonical_types(const Array& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->canonical_types_, value.raw());
}
void Class::set_allocation_stub(const Code& value) const {
ASSERT(!value.IsNull());
ASSERT(raw_ptr()->allocation_stub_ == Code::null());
StorePointer(&raw_ptr()->allocation_stub_, value.raw());
}
bool Class::IsListClass() const {
return raw() == Isolate::Current()->object_store()->list_class();
}
bool Class::IsCanonicalSignatureClass() const {
const Function& function = Function::Handle(signature_function());
return (!function.IsNull() && (function.signature_class() == raw()));
}
// If test_kind == kIsSubtypeOf, checks if type S is a subtype of type T.
// If test_kind == kIsMoreSpecificThan, checks if S is more specific than T.
// Type S is specified by this class parameterized with 'type_arguments', and
// type T by class 'other' parameterized with 'other_type_arguments'.
// This class and class 'other' do not need to be finalized, however, they must
// be resolved as well as their interfaces.
bool Class::TypeTest(
TypeTestKind test_kind,
const AbstractTypeArguments& type_arguments,
const Class& other,
const AbstractTypeArguments& other_type_arguments,
Error* malformed_error) const {
ASSERT(!IsVoidClass());
// Check for DynamicType.
// Each occurrence of DynamicType in type T is interpreted as the dynamic
// type, a supertype of all types.
if (other.IsDynamicClass()) {
return true;
}
// In the case of a subtype test, each occurrence of DynamicType in type S is
// interpreted as the bottom type, a subtype of all types.
// However, DynamicType is not more specific than any type.
if (IsDynamicClass()) {
return test_kind == kIsSubtypeOf;
}
// Check for NullType, which is not a subtype of any type, but is more
// specific than any type.
if (IsNullClass()) {
// User code cannot refer to class Null, therefore, we can only encounter
// NullType here as the type of the null constant, which must be treated
// separately in 'instance of' checks. Therefore, the NullType can only
// be encountered here during optimizations in 'more specific than' tests.
ASSERT(test_kind == kIsMoreSpecificThan);
return true;
}
// Check for ObjectType. Any type that is not NullType or DynamicType (already
// checked above), is more specific than ObjectType.
if (other.IsObjectClass()) {
return true;
}
// Check for reflexivity.
if (raw() == other.raw()) {
const intptr_t len = NumTypeArguments();
if (len == 0) {
return true;
}
// Since we do not truncate the type argument vector of a subclass (see
// below), we only check a prefix of the proper length.
// Check for covariance.
if (other_type_arguments.IsNull() ||
other_type_arguments.IsRawInstantiatedRaw(len)) {
return true;
}
if (type_arguments.IsNull() ||
type_arguments.IsRawInstantiatedRaw(len)) {
return test_kind == kIsSubtypeOf;
}
return type_arguments.TypeTest(test_kind,
other_type_arguments,
len,
malformed_error);
}
// TODO(regis): Check if type S has a call() method of function type T.
// Check for two function types.
if (IsSignatureClass() && other.IsSignatureClass()) {
const Function& fun = Function::Handle(signature_function());
const Function& other_fun = Function::Handle(other.signature_function());
return fun.TypeTest(test_kind,
type_arguments,
other_fun,
other_type_arguments,
malformed_error);
}
// Check for 'direct super type' specified in the implements clause
// and check for transitivity at the same time.
Array& interfaces = Array::Handle(this->interfaces());
AbstractType& interface = AbstractType::Handle();
Class& interface_class = Class::Handle();
AbstractTypeArguments& interface_args = AbstractTypeArguments::Handle();
for (intptr_t i = 0; i < interfaces.Length(); i++) {
interface ^= interfaces.At(i);
interface_class = interface.type_class();
interface_args = interface.arguments();
if (!interface_args.IsNull() && !interface_args.IsInstantiated()) {
// This type class implements an interface that is parameterized with
// generic type(s), e.g. it implements List<T>.
// The uninstantiated type T must be instantiated using the type
// parameters of this type before performing the type test.
// The type arguments of this type that are referred to by the type
// parameters of the interface are at the end of the type vector,
// after the type arguments of the super type of this type.
// The index of the type parameters is adjusted upon finalization.
ASSERT(interface.IsFinalized());
interface_args = interface_args.InstantiateFrom(type_arguments);
// In checked mode, verify that the instantiated interface type
// arguments are within the bounds specified by the interface class.
// Note that the additional bounds check in checked mode may lead to a
// dynamic type error, but it will never change the result of the type
// check from true in production mode to false in checked mode.
if (FLAG_enable_type_checks && !interface_args.IsNull()) {
// Pass type_arguments as bounds instantiator.
if (!interface_args.IsWithinBoundsOf(interface_class,
type_arguments,
malformed_error)) {
continue;
}
}
}
if (interface_class.TypeTest(test_kind,
interface_args,
other,
other_type_arguments,
malformed_error)) {
return true;
}
}
const Class& super_class = Class::Handle(SuperClass());
if (super_class.IsNull()) {
return false;
}
// Instead of truncating the type argument vector to the length of the super
// type argument vector, we make sure that the code works with a vector that
// is longer than necessary.
return super_class.TypeTest(test_kind,
type_arguments,
other,
other_type_arguments,
malformed_error);
}
bool Class::IsTopLevel() const {
return String::Handle(Name()).Equals("::");
}
RawFunction* Class::LookupDynamicFunction(const String& name) const {
Function& function = Function::Handle(LookupFunction(name));
if (function.IsNull() || !function.IsDynamicFunction()) {
return Function::null();
}
return function.raw();
}
RawFunction* Class::LookupStaticFunction(const String& name) const {
Function& function = Function::Handle(LookupFunction(name));
if (function.IsNull() || !function.IsStaticFunction()) {
return Function::null();
}
return function.raw();
}
RawFunction* Class::LookupConstructor(const String& name) const {
Function& function = Function::Handle(LookupFunction(name));
if (function.IsNull() || !function.IsConstructor()) {
return Function::null();
}
ASSERT(!function.is_static());
return function.raw();
}
RawFunction* Class::LookupFactory(const String& name) const {
Function& function = Function::Handle(LookupFunction(name));
if (function.IsNull() || !function.IsFactory()) {
return Function::null();
}
ASSERT(function.is_static());
return function.raw();
}
static bool MatchesAccessorName(const String& name,
const char* prefix,
intptr_t prefix_length,
const String& accessor_name) {
intptr_t name_len = name.Length();
intptr_t accessor_name_len = accessor_name.Length();
if (name_len != (accessor_name_len + prefix_length)) {
return false;
}
for (intptr_t i = 0; i < prefix_length; i++) {
if (name.CharAt(i) != prefix[i]) {
return false;
}
}
for (intptr_t i = 0, j = prefix_length; i < accessor_name_len; i++, j++) {
if (name.CharAt(j) != accessor_name.CharAt(i)) {
return false;
}
}
return true;
}
RawFunction* Class::LookupFunction(const String& name) const {
Isolate* isolate = Isolate::Current();
ASSERT(name.IsOneByteString());
Array& funcs = Array::Handle(isolate, functions());
if (funcs.IsNull()) {
// This can occur, e.g., for Null classes.
return Function::null();
}
Function& function = Function::Handle(isolate, Function::null());
String& function_name = String::Handle(isolate, String::null());
intptr_t len = funcs.Length();
for (intptr_t i = 0; i < len; i++) {
function ^= funcs.At(i);
function_name ^= function.name();
if (OneByteString::EqualsIgnoringPrivateKey(function_name, name)) {
return function.raw();
}
}
// No function found.
return Function::null();
}
RawFunction* Class::LookupGetterFunction(const String& name) const {
return LookupAccessorFunction(kGetterPrefix, kGetterPrefixLength, name);
}
RawFunction* Class::LookupSetterFunction(const String& name) const {
return LookupAccessorFunction(kSetterPrefix, kSetterPrefixLength, name);
}
RawFunction* Class::LookupAccessorFunction(const char* prefix,
intptr_t prefix_length,
const String& name) const {
Isolate* isolate = Isolate::Current();
Array& funcs = Array::Handle(isolate, functions());
Function& function = Function::Handle(isolate, Function::null());
String& function_name = String::Handle(isolate, String::null());
intptr_t len = funcs.Length();
for (intptr_t i = 0; i < len; i++) {
function ^= funcs.At(i);
function_name ^= function.name();
if (MatchesAccessorName(function_name, prefix, prefix_length, name)) {
return function.raw();
}
}
// No function found.
return Function::null();
}
RawFunction* Class::LookupFunctionAtToken(intptr_t token_pos) const {
// TODO(hausner): we can shortcut the negative case if we knew the
// beginning and end token position of the class.
Function& func = Function::Handle();
func = LookupClosureFunction(token_pos);
if (!func.IsNull()) {
return func.raw();
}
Array& funcs = Array::Handle(functions());
intptr_t len = funcs.Length();
for (intptr_t i = 0; i < len; i++) {
func ^= funcs.At(i);
if ((func.token_pos() <= token_pos) &&
(token_pos < func.end_token_pos())) {
return func.raw();
}
}
// No function found.
return Function::null();
}
RawField* Class::LookupInstanceField(const String& name) const {
ASSERT(is_finalized());
const Field& field = Field::Handle(LookupField(name));
if (!field.IsNull()) {
if (field.is_static()) {
// Name matches but it is not of the correct kind, return NULL.
return Field::null();
}
return field.raw();
}
// No field found.
return Field::null();
}
RawField* Class::LookupStaticField(const String& name) const {
ASSERT(is_finalized());
const Field& field = Field::Handle(LookupField(name));
if (!field.IsNull()) {
if (!field.is_static()) {
// Name matches but it is not of the correct kind, return NULL.
return Field::null();
}
return field.raw();
}
// No field found.
return Field::null();
}
RawField* Class::LookupField(const String& name) const {
Isolate* isolate = Isolate::Current();
ASSERT(name.IsOneByteString());
const Array& flds = Array::Handle(isolate, fields());
Field& field = Field::Handle(isolate, Field::null());
String& field_name = String::Handle(isolate, String::null());
intptr_t len = flds.Length();
for (intptr_t i = 0; i < len; i++) {
field ^= flds.At(i);
field_name ^= field.name();
if (OneByteString::EqualsIgnoringPrivateKey(field_name, name)) {
return field.raw();
}
}
// No field found.
return Field::null();
}
RawLibraryPrefix* Class::LookupLibraryPrefix(const String& name) const {
Isolate* isolate = Isolate::Current();
const Library& lib = Library::Handle(isolate, library());
const Object& obj = Object::Handle(isolate, lib.LookupLocalObject(name));
if (!obj.IsNull() && obj.IsLibraryPrefix()) {
const LibraryPrefix& lib_prefix = LibraryPrefix::Cast(obj);
return lib_prefix.raw();
}
return LibraryPrefix::null();
}
const char* Class::ToCString() const {
const char* format = "%s Class: %s";
const Library& lib = Library::Handle(library());
const char* library_name = lib.IsNull() ? "" : lib.ToCString();
const char* class_name = String::Handle(Name()).ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, library_name, class_name) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, library_name, class_name);
return chars;
}
void Class::InsertCanonicalConstant(intptr_t index,
const Instance& constant) const {
// The constant needs to be added to the list. Grow the list if it is full.
Array& canonical_list = Array::Handle(constants());
const intptr_t list_len = canonical_list.Length();
if (index >= list_len) {
const intptr_t new_length = (list_len == 0) ? 4 : list_len + 4;
const Array& new_canonical_list =
Array::Handle(Array::Grow(canonical_list, new_length, Heap::kOld));
set_constants(new_canonical_list);
new_canonical_list.SetAt(index, constant);
} else {
canonical_list.SetAt(index, constant);
}
}
RawUnresolvedClass* UnresolvedClass::New(const LibraryPrefix& library_prefix,
const String& ident,
intptr_t token_pos) {
const UnresolvedClass& type = UnresolvedClass::Handle(UnresolvedClass::New());
type.set_library_prefix(library_prefix);
type.set_ident(ident);
type.set_token_pos(token_pos);
return type.raw();
}
RawUnresolvedClass* UnresolvedClass::New() {
ASSERT(Object::unresolved_class_class() != Class::null());
RawObject* raw = Object::Allocate(UnresolvedClass::kClassId,
UnresolvedClass::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawUnresolvedClass*>(raw);
}
void UnresolvedClass::set_token_pos(intptr_t token_pos) const {
ASSERT(token_pos >= 0);
raw_ptr()->token_pos_ = token_pos;
}
void UnresolvedClass::set_ident(const String& ident) const {
StorePointer(&raw_ptr()->ident_, ident.raw());
}
void UnresolvedClass::set_library_prefix(
const LibraryPrefix& library_prefix) const {
StorePointer(&raw_ptr()->library_prefix_, library_prefix.raw());
}
RawString* UnresolvedClass::Name() const {
if (library_prefix() != LibraryPrefix::null()) {
const LibraryPrefix& lib_prefix = LibraryPrefix::Handle(library_prefix());
String& name = String::Handle();
String& str = String::Handle();
name = lib_prefix.name(); // Qualifier.
str = Symbols::Dot();
name = String::Concat(name, str);
str = ident();
name = String::Concat(name, str);
return name.raw();
} else {
return ident();
}
}
const char* UnresolvedClass::ToCString() const {
const char* format = "unresolved class '%s'";
const char* cname = String::Handle(Name()).ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, cname) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, cname);
return chars;
}
intptr_t AbstractTypeArguments::Length() const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return -1;
}
RawAbstractType* AbstractTypeArguments::TypeAt(intptr_t index) const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return NULL;
}
void AbstractTypeArguments::SetTypeAt(intptr_t index,
const AbstractType& value) const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
}
bool AbstractTypeArguments::IsResolved() const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractTypeArguments::IsInstantiated() const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractTypeArguments::IsUninstantiatedIdentity() const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return false;
}
RawString* AbstractTypeArguments::SubvectorName(
intptr_t from_index,
intptr_t len,
NameVisibility name_visibility) const {
ASSERT(from_index + len <= Length());
String& name = String::Handle();
const intptr_t num_strings = 2*len + 1; // "<""T"", ""T"">".
const Array& strings = Array::Handle(Array::New(num_strings));
intptr_t s = 0;
strings.SetAt(s++, String::Handle(Symbols::New("<")));
const String& kCommaSpace = String::Handle(Symbols::New(", "));
AbstractType& type = AbstractType::Handle();
for (intptr_t i = 0; i < len; i++) {
type = TypeAt(from_index + i);
name = type.BuildName(name_visibility);
strings.SetAt(s++, name);
if (i < len - 1) {
strings.SetAt(s++, kCommaSpace);
}
}
strings.SetAt(s++, String::Handle(Symbols::New(">")));
ASSERT(s == num_strings);
name = String::ConcatAll(strings);
return Symbols::New(name);
}
bool AbstractTypeArguments::Equals(const AbstractTypeArguments& other) const {
ASSERT(!IsNull()); // Use AbstractTypeArguments::AreEqual().
if (this->raw() == other.raw()) {
return true;
}
if (other.IsNull()) {
return false;
}
intptr_t num_types = Length();
if (num_types != other.Length()) {
return false;
}
AbstractType& type = AbstractType::Handle();
AbstractType& other_type = AbstractType::Handle();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
other_type = other.TypeAt(i);
if (!type.Equals(other_type)) {
return false;
}
}
return true;
}
bool AbstractTypeArguments::AreEqual(
const AbstractTypeArguments& arguments,
const AbstractTypeArguments& other_arguments) {
if (arguments.raw() == other_arguments.raw()) {
return true;
}
if (arguments.IsNull()) {
return other_arguments.IsDynamicTypes(false, other_arguments.Length());
}
if (other_arguments.IsNull()) {
return arguments.IsDynamicTypes(false, arguments.Length());
}
return arguments.Equals(other_arguments);
}
RawAbstractTypeArguments* AbstractTypeArguments::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments) const {
// AbstractTypeArguments is an abstract class.
UNREACHABLE();
return NULL;
}
bool AbstractTypeArguments::IsDynamicTypes(bool raw_instantiated,
intptr_t len) const {
ASSERT(Length() >= len);
AbstractType& type = AbstractType::Handle();
Class& type_class = Class::Handle();
for (intptr_t i = 0; i < len; i++) {
type = TypeAt(i);
ASSERT(!type.IsNull());
if (!type.HasResolvedTypeClass()) {
if (raw_instantiated && type.IsTypeParameter()) {
// An uninstantiated type parameter is equivalent to dynamic.
continue;
}
ASSERT((!raw_instantiated && type.IsTypeParameter()) ||
type.IsMalformed());
return false;
}
type_class = type.type_class();
if (!type_class.IsDynamicClass()) {
return false;
}
}
return true;
}
static RawError* FormatError(const Error& prev_error,
const Script& script,
intptr_t token_pos,
const char* format, ...) {
va_list args;
va_start(args, format);
if (prev_error.IsNull()) {
return Parser::FormatError(script, token_pos, "Error", format, args);
} else {
return Parser::FormatErrorWithAppend(prev_error, script, token_pos,
"Error", format, args);
}
}
bool AbstractTypeArguments::IsWithinBoundsOf(
const Class& cls,
const AbstractTypeArguments& bounds_instantiator,
Error* malformed_error) const {
ASSERT(FLAG_enable_type_checks);
// This function may be called at compile time on (partially) uninstantiated
// type arguments and may return true, in which case a run time bounds check
// can be avoided.
ASSERT(Length() >= cls.NumTypeArguments());
const intptr_t num_type_params = cls.NumTypeParameters();
const intptr_t offset = cls.NumTypeArguments() - num_type_params;
AbstractType& this_type_arg = AbstractType::Handle();
AbstractType& cls_type_arg = AbstractType::Handle();
AbstractType& bound = AbstractType::Handle();
const TypeArguments& cls_type_params =
TypeArguments::Handle(cls.type_parameters());
ASSERT((cls_type_params.IsNull() && (num_type_params == 0)) ||
(cls_type_params.Length() == num_type_params));
for (intptr_t i = 0; i < num_type_params; i++) {
cls_type_arg = cls_type_params.TypeAt(i);
const TypeParameter& cls_type_param = TypeParameter::Cast(cls_type_arg);
bound = cls_type_param.bound();
if (!bound.IsDynamicType()) {
this_type_arg = TypeAt(offset + i);
Error& malformed_bound_error = Error::Handle();
if (bound.IsMalformed()) {
malformed_bound_error = bound.malformed_error();
} else if (!bound.IsInstantiated()) {
bound = bound.InstantiateFrom(bounds_instantiator);
}
if (!malformed_bound_error.IsNull() ||
!this_type_arg.IsSubtypeOf(bound, malformed_error)) {
// Ignore this bound error if another malformed error was already
// reported for this type test.
if ((malformed_error != NULL) && malformed_error->IsNull()) {
const String& type_arg_name =
String::Handle(this_type_arg.UserVisibleName());
const String& class_name = String::Handle(cls.Name());
const String& bound_name = String::Handle(bound.UserVisibleName());
const Script& script = Script::Handle(cls.script());
// Since the bound was canonicalized, its token index was lost,
// therefore, use the token index of the corresponding type parameter.
*malformed_error ^= FormatError(malformed_bound_error,
script, cls_type_param.token_pos(),
"type argument '%s' does not "
"extend bound '%s' of '%s'\n",
type_arg_name.ToCString(),
bound_name.ToCString(),
class_name.ToCString());
}
return false;
}
}
}
const Class& super_class = Class::Handle(cls.SuperClass());
if (!super_class.IsNull() &&
!IsWithinBoundsOf(super_class, bounds_instantiator, malformed_error)) {
return false;
}
return true;
}
bool AbstractTypeArguments::TypeTest(TypeTestKind test_kind,
const AbstractTypeArguments& other,
intptr_t len,
Error* malformed_error) const {
ASSERT(Length() >= len);
ASSERT(!other.IsNull());
ASSERT(other.Length() >= len);
AbstractType& type = AbstractType::Handle();
AbstractType& other_type = AbstractType::Handle();
for (intptr_t i = 0; i < len; i++) {
type = TypeAt(i);
ASSERT(!type.IsNull());
other_type = other.TypeAt(i);
ASSERT(!other_type.IsNull());
if (!type.TypeTest(test_kind, other_type, malformed_error)) {
return false;
}
}
return true;
}
const char* AbstractTypeArguments::ToCString() const {
// AbstractTypeArguments is an abstract class, valid only for representing
// null.
if (IsNull()) {
return "NULL AbstractTypeArguments";
}
UNREACHABLE();
return "AbstractTypeArguments";
}
intptr_t TypeArguments::Length() const {
ASSERT(!IsNull());
return Smi::Value(raw_ptr()->length_);
}
RawAbstractType* TypeArguments::TypeAt(intptr_t index) const {
return *TypeAddr(index);
}
void TypeArguments::SetTypeAt(intptr_t index, const AbstractType& value) const {
ASSERT(!IsCanonical());
StorePointer(TypeAddr(index), value.raw());
}
bool TypeArguments::IsResolved() const {
AbstractType& type = AbstractType::Handle();
intptr_t num_types = Length();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
if (!type.IsResolved()) {
return false;
}
}
return true;
}
bool TypeArguments::IsInstantiated() const {
AbstractType& type = AbstractType::Handle();
intptr_t num_types = Length();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
ASSERT(!type.IsNull());
if (!type.IsInstantiated()) {
return false;
}
}
return true;
}
bool TypeArguments::IsUninstantiatedIdentity() const {
ASSERT(!IsInstantiated());
AbstractType& type = AbstractType::Handle();
intptr_t num_types = Length();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
if (!type.IsTypeParameter()) {
return false;
}
const TypeParameter& type_param = TypeParameter::Cast(type);
if ((type_param.index() != i)) {
return false;
}
}
return true;
}
RawAbstractTypeArguments* TypeArguments::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments) const {
ASSERT(!IsInstantiated());
if (!instantiator_type_arguments.IsNull() &&
IsUninstantiatedIdentity() &&
(instantiator_type_arguments.Length() == Length())) {
return instantiator_type_arguments.raw();
}
const intptr_t num_types = Length();
TypeArguments& instantiated_array =
TypeArguments::Handle(TypeArguments::New(num_types, Heap::kNew));
AbstractType& type = AbstractType::Handle();
for (intptr_t i = 0; i < num_types; i++) {
type = TypeAt(i);
if (!type.IsInstantiated()) {
type = type.InstantiateFrom(instantiator_type_arguments);
}
instantiated_array.SetTypeAt(i, type);
}
return instantiated_array.raw();
}
RawTypeArguments* TypeArguments::New(intptr_t len, Heap::Space space) {
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in TypeArguments::New: invalid len %"Pd"\n", len);
}
TypeArguments& result = TypeArguments::Handle();
{
RawObject* raw = Object::Allocate(TypeArguments::kClassId,
TypeArguments::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
// Length must be set before we start storing into the array.
result.SetLength(len);
}
return result.raw();
}
RawAbstractType** TypeArguments::TypeAddr(intptr_t index) const {
// TODO(iposva): Determine if we should throw an exception here.
ASSERT((index >= 0) && (index < Length()));
return &raw_ptr()->types_[index];
}
void TypeArguments::SetLength(intptr_t value) const {
ASSERT(!IsCanonical());
// This is only safe because we create a new Smi, which does not cause
// heap allocation.
raw_ptr()->length_ = Smi::New(value);
}
RawAbstractTypeArguments* TypeArguments::Canonicalize() const {
if (IsNull() || IsCanonical()) {
ASSERT(IsOld());
return this->raw();
}
ObjectStore* object_store = Isolate::Current()->object_store();
// 'table' must be null terminated.
Array& table = Array::Handle(object_store->canonical_type_arguments());
ASSERT(table.Length() > 0);
intptr_t index = 0;
TypeArguments& result = TypeArguments::Handle();
result ^= table.At(index);
while (!result.IsNull()) {
if (this->Equals(result)) {
return result.raw();
}
result ^= table.At(++index);
}
// Not found. Add 'this' to table.
result ^= this->raw();
if (result.IsNew()) {
result ^= Object::Clone(result, Heap::kOld);
}
ASSERT(result.IsOld());
if (index == table.Length() - 1) {
table = Array::Grow(table, table.Length() + 4, Heap::kOld);
object_store->set_canonical_type_arguments(table);
}
table.SetAt(index, result);
result.SetCanonical();
return result.raw();
}
const char* TypeArguments::ToCString() const {
if (IsNull()) {
return "NULL TypeArguments";
}
const char* format = "%s [%s]";
const char* prev_cstr = "TypeArguments:";
for (int i = 0; i < Length(); i++) {
const AbstractType& type_at = AbstractType::Handle(TypeAt(i));
const char* type_cstr = type_at.IsNull() ? "null" : type_at.ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, prev_cstr, type_cstr) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, prev_cstr, type_cstr);
prev_cstr = chars;
}
return prev_cstr;
}
intptr_t InstantiatedTypeArguments::Length() const {
return AbstractTypeArguments::Handle(
uninstantiated_type_arguments()).Length();
}
RawAbstractType* InstantiatedTypeArguments::TypeAt(intptr_t index) const {
const AbstractType& type = AbstractType::Handle(
AbstractTypeArguments::Handle(
uninstantiated_type_arguments()).TypeAt(index));
if (!type.IsInstantiated()) {
const AbstractTypeArguments& instantiator_type_args =
AbstractTypeArguments::Handle(instantiator_type_arguments());
return type.InstantiateFrom(instantiator_type_args);
}
return type.raw();
}
void InstantiatedTypeArguments::SetTypeAt(intptr_t index,
const AbstractType& value) const {
// We only replace individual argument types during resolution at compile
// time, when no type parameters are instantiated yet.
UNREACHABLE();
}
void InstantiatedTypeArguments::set_uninstantiated_type_arguments(
const AbstractTypeArguments& value) const {
StorePointer(&raw_ptr()->uninstantiated_type_arguments_, value.raw());
}
void InstantiatedTypeArguments::set_instantiator_type_arguments(
const AbstractTypeArguments& value) const {
StorePointer(&raw_ptr()->instantiator_type_arguments_, value.raw());
}
RawInstantiatedTypeArguments* InstantiatedTypeArguments::New() {
ASSERT(Object::instantiated_type_arguments_class() != Class::null());
RawObject* raw = Object::Allocate(InstantiatedTypeArguments::kClassId,
InstantiatedTypeArguments::InstanceSize(),
Heap::kNew);
return reinterpret_cast<RawInstantiatedTypeArguments*>(raw);
}
RawInstantiatedTypeArguments* InstantiatedTypeArguments::New(
const AbstractTypeArguments& uninstantiated_type_arguments,
const AbstractTypeArguments& instantiator_type_arguments) {
const InstantiatedTypeArguments& result =
InstantiatedTypeArguments::Handle(InstantiatedTypeArguments::New());
result.set_uninstantiated_type_arguments(uninstantiated_type_arguments);
result.set_instantiator_type_arguments(instantiator_type_arguments);
return result.raw();
}
const char* InstantiatedTypeArguments::ToCString() const {
if (IsNull()) {
return "NULL InstantiatedTypeArguments";
}
const char* format = "InstantiatedTypeArguments: [%s] instantiator: [%s]";
const char* arg_cstr =
AbstractTypeArguments::Handle(
uninstantiated_type_arguments()).ToCString();
const char* instantiator_cstr =
AbstractTypeArguments::Handle(instantiator_type_arguments()).ToCString();
intptr_t len =
OS::SNPrint(NULL, 0, format, arg_cstr, instantiator_cstr) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, arg_cstr, instantiator_cstr);
return chars;
}
const char* PatchClass::ToCString() const {
const char* kFormat = "PatchClass for %s";
const Class& cls = Class::Handle(patched_class());
const char* cls_name = cls.ToCString();
intptr_t len = OS::SNPrint(NULL, 0, kFormat, cls_name) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, cls_name);
return chars;
}
RawPatchClass* PatchClass::New(const Class& patched_class,
const Script& script) {
const PatchClass& result = PatchClass::Handle(PatchClass::New());
result.set_patched_class(patched_class);
result.set_script(script);
return result.raw();
}
RawPatchClass* PatchClass::New() {
ASSERT(Object::patch_class_class() != Class::null());
RawObject* raw = Object::Allocate(PatchClass::kClassId,
PatchClass::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawPatchClass*>(raw);
}
void PatchClass::set_patched_class(const Class& value) const {
StorePointer(&raw_ptr()->patched_class_, value.raw());
}
void PatchClass::set_script(const Script& value) const {
StorePointer(&raw_ptr()->script_, value.raw());
}
void Function::SetCode(const Code& value) const {
StorePointer(&raw_ptr()->code_, value.raw());
ASSERT(Function::Handle(value.function()).IsNull() ||
(value.function() == this->raw()));
value.set_function(*this);
}
void Function::SwitchToUnoptimizedCode() const {
ASSERT(HasOptimizedCode());
const Code& current_code = Code::Handle(CurrentCode());
if (FLAG_trace_disabling_optimized_code) {
OS::Print("Disabling optimized code: '%s' entry: %#"Px"\n",
ToFullyQualifiedCString(),
current_code.EntryPoint());
}
// Patch entry of the optimized code.
CodePatcher::PatchEntry(current_code);
// Use previously compiled unoptimized code.
SetCode(Code::Handle(unoptimized_code()));
CodePatcher::RestoreEntry(Code::Handle(unoptimized_code()));
}
void Function::set_unoptimized_code(const Code& value) const {
StorePointer(&raw_ptr()->unoptimized_code_, value.raw());
}
RawContextScope* Function::context_scope() const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return ClosureData::Cast(obj).context_scope();
}
return ContextScope::null();
}
void Function::set_context_scope(const ContextScope& value) const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
ClosureData::Cast(obj).set_context_scope(value);
return;
}
UNREACHABLE();
}
RawInstance* Function::implicit_static_closure() const {
if (IsImplicitStaticClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return ClosureData::Cast(obj).implicit_static_closure();
}
return Instance::null();
}
void Function::set_implicit_static_closure(const Instance& closure) const {
if (IsImplicitStaticClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
ClosureData::Cast(obj).set_implicit_static_closure(closure);
return;
}
UNREACHABLE();
}
RawCode* Function::closure_allocation_stub() const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return ClosureData::Cast(obj).closure_allocation_stub();
}
return Code::null();
}
void Function::set_closure_allocation_stub(const Code& value) const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
ClosureData::Cast(obj).set_closure_allocation_stub(value);
return;
}
UNREACHABLE();
}
RawFunction* Function::parent_function() const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return ClosureData::Cast(obj).parent_function();
}
return Function::null();
}
void Function::set_parent_function(const Function& value) const {
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
ClosureData::Cast(obj).set_parent_function(value);
return;
}
UNREACHABLE();
}
RawFunction* Function::implicit_closure_function() const {
if (IsClosureFunction() || IsSignatureFunction()) {
return Function::null();
}
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(obj.IsNull() || obj.IsFunction());
return (obj.IsNull()) ? Function::null() : Function::Cast(obj).raw();
}
void Function::set_implicit_closure_function(const Function& value) const {
ASSERT(!IsClosureFunction() && !IsSignatureFunction());
set_data(value);
}
RawClass* Function::signature_class() const {
if (IsSignatureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(obj.IsNull() || obj.IsClass());
return (obj.IsNull()) ? Class::null() : Class::Cast(obj).raw();
}
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return ClosureData::Cast(obj).signature_class();
}
return Class::null();
}
void Function::set_signature_class(const Class& value) const {
if (IsSignatureFunction()) {
set_data(value);
return;
}
if (IsClosureFunction()) {
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
ClosureData::Cast(obj).set_signature_class(value);
return;
}
UNREACHABLE();
}
bool Function::IsRedirectingFactory() const {
if (!IsFactory() || (raw_ptr()->data_ == Object::null())) {
return false;
}
ASSERT(!IsClosureFunction()); // A factory cannot also be a closure.
return true;
}
RawType* Function::RedirectionType() const {
ASSERT(IsRedirectingFactory());
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return RedirectionData::Cast(obj).type();
}
void Function::SetRedirectionType(const Type& type) const {
ASSERT(IsFactory());
Object& obj = Object::Handle(raw_ptr()->data_);
if (obj.IsNull()) {
obj = RedirectionData::New();
set_data(obj);
}
RedirectionData::Cast(obj).set_type(type);
}
RawString* Function::RedirectionIdentifier() const {
ASSERT(IsRedirectingFactory());
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return RedirectionData::Cast(obj).identifier();
}
void Function::SetRedirectionIdentifier(const String& identifier) const {
ASSERT(IsFactory());
Object& obj = Object::Handle(raw_ptr()->data_);
if (obj.IsNull()) {
obj = RedirectionData::New();
set_data(obj);
}
RedirectionData::Cast(obj).set_identifier(identifier);
}
RawFunction* Function::RedirectionTarget() const {
ASSERT(IsRedirectingFactory());
const Object& obj = Object::Handle(raw_ptr()->data_);
ASSERT(!obj.IsNull());
return RedirectionData::Cast(obj).target();
}
void Function::SetRedirectionTarget(const Function& target) const {
ASSERT(IsFactory());
Object& obj = Object::Handle(raw_ptr()->data_);
if (obj.IsNull()) {
obj = RedirectionData::New();
set_data(obj);
}
RedirectionData::Cast(obj).set_target(target);
}
void Function::set_data(const Object& value) const {
StorePointer(&raw_ptr()->data_, value.raw());
}
bool Function::IsInFactoryScope() const {
if (!IsLocalFunction()) {
return IsFactory();
}
Function& outer_function = Function::Handle(parent_function());
while (outer_function.IsLocalFunction()) {
outer_function = outer_function.parent_function();
}
return outer_function.IsFactory();
}
void Function::set_name(const String& value) const {
ASSERT(value.IsSymbol());
StorePointer(&raw_ptr()->name_, value.raw());
}
void Function::set_owner(const Object& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->owner_, value.raw());
}
void Function::set_result_type(const AbstractType& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->result_type_, value.raw());
}
RawAbstractType* Function::ParameterTypeAt(intptr_t index) const {
const Array& parameter_types = Array::Handle(raw_ptr()->parameter_types_);
AbstractType& parameter_type = AbstractType::Handle();
parameter_type ^= parameter_types.At(index);
return parameter_type.raw();
}
void Function::SetParameterTypeAt(
intptr_t index, const AbstractType& value) const {
ASSERT(!value.IsNull());
const Array& parameter_types = Array::Handle(raw_ptr()->parameter_types_);
parameter_types.SetAt(index, value);
}
void Function::set_parameter_types(const Array& value) const {
StorePointer(&raw_ptr()->parameter_types_, value.raw());
}
RawString* Function::ParameterNameAt(intptr_t index) const {
const Array& parameter_names = Array::Handle(raw_ptr()->parameter_names_);
String& parameter_name = String::Handle();
parameter_name ^= parameter_names.At(index);
return parameter_name.raw();
}
void Function::SetParameterNameAt(intptr_t index, const String& value) const {
ASSERT(!value.IsNull() && value.IsSymbol());
const Array& parameter_names = Array::Handle(raw_ptr()->parameter_names_);
parameter_names.SetAt(index, value);
}
void Function::set_parameter_names(const Array& value) const {
StorePointer(&raw_ptr()->parameter_names_, value.raw());
}
void Function::set_kind(RawFunction::Kind value) const {
set_kind_tag(KindBits::update(value, raw_ptr()->kind_tag_));
}
void Function::set_intrinsic_kind(IntrinsicKind value) const {
set_kind_tag(IntrinsicKindBits::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_static(bool value) const {
set_kind_tag(StaticBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_const(bool value) const {
set_kind_tag(ConstBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_external(bool value) const {
set_kind_tag(ExternalBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_token_pos(intptr_t value) const {
ASSERT(value >= 0);
raw_ptr()->token_pos_ = value;
}
void Function::set_kind_tag(intptr_t value) const {
raw_ptr()->kind_tag_ = static_cast<uint16_t>(value);
}
void Function::set_num_fixed_parameters(intptr_t value) const {
ASSERT(value >= 0);
ASSERT(Utils::IsInt(16, value));
raw_ptr()->num_fixed_parameters_ = static_cast<int16_t>(value);
}
void Function::set_num_optional_parameters(intptr_t value) const {
// A positive value indicates positional params, a negative one named params.
ASSERT(Utils::IsInt(16, value));
raw_ptr()->num_optional_parameters_ = static_cast<int16_t>(value);
}
void Function::SetNumOptionalParameters(intptr_t num_optional_parameters,
bool are_optional_positional) const {
ASSERT(num_optional_parameters >= 0);
set_num_optional_parameters(are_optional_positional ?
num_optional_parameters :
-num_optional_parameters);
}
bool Function::is_optimizable() const {
return OptimizableBit::decode(raw_ptr()->kind_tag_) &&
(script() != Script::null()) &&
!is_native();
}
void Function::set_is_optimizable(bool value) const {
set_kind_tag(OptimizableBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_has_finally(bool value) const {
set_kind_tag(HasFinallyBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_native(bool value) const {
set_kind_tag(NativeBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_abstract(bool value) const {
set_kind_tag(AbstractBit::update(value, raw_ptr()->kind_tag_));
}
void Function::set_is_inlinable(bool value) const {
set_kind_tag(InlinableBit::update(value, raw_ptr()->kind_tag_));
}
bool Function::IsInlineable() const {
// '==' call is handled specially.
const String& equality_name = String::Handle(Symbols::EqualOperator());
return InlinableBit::decode(raw_ptr()->kind_tag_) &&
HasCode() &&
name() != equality_name.raw();
}
intptr_t Function::NumParameters() const {
return num_fixed_parameters() + NumOptionalParameters();
}
intptr_t Function::NumImplicitParameters() const {
if (kind() == RawFunction::kConstructor) {
if (is_static()) {
ASSERT(IsFactory());
return 1; // Type arguments.
} else {
ASSERT(IsConstructor());
return 2; // Instance, phase.
}
}
if ((kind() == RawFunction::kClosureFunction) ||
(kind() == RawFunction::kSignatureFunction)) {
return 1; // Closure object.
}
if (!is_static()) {
// Closure functions defined inside instance (i.e. non-static) functions are
// marked as non-static, but they do not have a receiver.
// Closures are handled above.
ASSERT((kind() != RawFunction::kClosureFunction) &&
(kind() != RawFunction::kSignatureFunction));
return 1; // Receiver.
}
return 0; // No implicit parameters.
}
bool Function::AreValidArgumentCounts(int num_arguments,
int num_named_arguments,
String* error_message) const {
if (num_named_arguments > NumOptionalNamedParameters()) {
if (error_message != NULL) {
const intptr_t kMessageBufferSize = 64;
char message_buffer[kMessageBufferSize];
OS::SNPrint(message_buffer,
kMessageBufferSize,
"%d named passed, at most %"Pd" expected",
num_named_arguments,
NumOptionalNamedParameters());
*error_message = String::New(message_buffer);
}
return false; // Too many named arguments.
}
const int num_pos_args = num_arguments - num_named_arguments;
const int num_opt_pos_params = NumOptionalPositionalParameters();
const int num_pos_params = num_fixed_parameters() + num_opt_pos_params;
if (num_pos_args > num_pos_params) {
if (error_message != NULL) {
const intptr_t kMessageBufferSize = 64;
char message_buffer[kMessageBufferSize];
// Hide implicit parameters to the user.
const intptr_t num_hidden_params = NumImplicitParameters();
OS::SNPrint(message_buffer,
kMessageBufferSize,
"%"Pd"%s passed, %s%"Pd" expected",
num_pos_args - num_hidden_params,
num_opt_pos_params > 0 ? " positional" : "",
num_opt_pos_params > 0 ? "at most " : "",
num_pos_params - num_hidden_params);
*error_message = String::New(message_buffer);
}
return false; // Too many fixed and/or positional arguments.
}
if (num_pos_args < num_fixed_parameters()) {
if (error_message != NULL) {
const intptr_t kMessageBufferSize = 64;
char message_buffer[kMessageBufferSize];
// Hide implicit parameters to the user.
const intptr_t num_hidden_params = NumImplicitParameters();
OS::SNPrint(message_buffer,
kMessageBufferSize,
"%"Pd"%s passed, %s%"Pd" expected",
num_pos_args - num_hidden_params,
num_opt_pos_params > 0 ? " positional" : "",
num_opt_pos_params > 0 ? "at least " : "",
num_fixed_parameters() - num_hidden_params);
*error_message = String::New(message_buffer);
}
return false; // Too few fixed and/or positional arguments.
}
return true;
}
bool Function::AreValidArguments(int num_arguments,
const Array& argument_names,
String* error_message) const {
const int num_named_arguments =
argument_names.IsNull() ? 0 : argument_names.Length();
if (!AreValidArgumentCounts(num_arguments,
num_named_arguments,
error_message)) {
return false;
}
// Verify that all argument names are valid parameter names.
String& argument_name = String::Handle();
String& parameter_name = String::Handle();
for (int i = 0; i < num_named_arguments; i++) {
argument_name ^= argument_names.At(i);
ASSERT(argument_name.IsSymbol());
bool found = false;
const int num_positional_args = num_arguments - num_named_arguments;
const int num_parameters = NumParameters();
for (int j = num_positional_args; !found && (j < num_parameters); j++) {
parameter_name ^= ParameterNameAt(j);
ASSERT(argument_name.IsSymbol());
if (argument_name.Equals(parameter_name)) {
found = true;
}
}
if (!found) {
if (error_message != NULL) {
const intptr_t kMessageBufferSize = 64;
char message_buffer[kMessageBufferSize];
OS::SNPrint(message_buffer,
kMessageBufferSize,
"no optional formal parameter named '%s'",
argument_name.ToCString());
*error_message = String::New(message_buffer);
}
return false;
}
}
return true;
}
// Helper allocating a C string buffer in the zone, printing the fully qualified
// name of a function in it, and replacing ':' by '_' to make sure the
// constructed name is a valid C++ identifier for debugging purpose.
// Set 'chars' to allocated buffer and return number of written characters.
static intptr_t ConstructFunctionFullyQualifiedCString(const Function& function,
char** chars,
intptr_t reserve_len) {
const char* name = String::Handle(function.name()).ToCString();
const char* function_format = (reserve_len == 0) ? "%s" : "%s_";
reserve_len += OS::SNPrint(NULL, 0, function_format, name);
const Function& parent = Function::Handle(function.parent_function());
intptr_t written = 0;
if (parent.IsNull()) {
const Class& function_class = Class::Handle(function.Owner());
ASSERT(!function_class.IsNull());
const char* class_name = String::Handle(function_class.Name()).ToCString();
ASSERT(class_name != NULL);
const Library& library = Library::Handle(function_class.library());
ASSERT(!library.IsNull());
const char* library_name = String::Handle(library.url()).ToCString();
ASSERT(library_name != NULL);
const char* lib_class_format =
(library_name[0] == '\0') ? "%s%s_" : "%s_%s_";
reserve_len +=
OS::SNPrint(NULL, 0, lib_class_format, library_name, class_name);
ASSERT(chars != NULL);
*chars = Isolate::Current()->current_zone()->Alloc<char>(reserve_len + 1);
written = OS::SNPrint(
*chars, reserve_len + 1, lib_class_format, library_name, class_name);
} else {
written = ConstructFunctionFullyQualifiedCString(parent,
chars,
reserve_len);
}
ASSERT(*chars != NULL);
char* next = *chars + written;
written += OS::SNPrint(next, reserve_len + 1, function_format, name);
// Replace ":" with "_".
while (true) {
next = strchr(next, ':');
if (next == NULL) break;
*next = '_';
}
return written;
}
const char* Function::ToFullyQualifiedCString() const {
char* chars = NULL;
ConstructFunctionFullyQualifiedCString(*this, &chars, 0);
return chars;
}
bool Function::HasCompatibleParametersWith(const Function& other) const {
const intptr_t num_fixed_params = num_fixed_parameters();
const intptr_t num_opt_pos_params = NumOptionalPositionalParameters();
const intptr_t num_opt_named_params = NumOptionalNamedParameters();
const intptr_t other_num_fixed_params = other.num_fixed_parameters();
const intptr_t other_num_opt_pos_params =
other.NumOptionalPositionalParameters();
const intptr_t other_num_opt_named_params =
other.NumOptionalNamedParameters();
// A generative constructor may be compared to a redirecting factory and be
// compatible although it has an additional phase parameter.
const intptr_t num_ignored_params =
(other.IsRedirectingFactory() && IsConstructor()) ? 1 : 0;
// The default values of optional parameters can differ.
if (((num_fixed_params - num_ignored_params) != other_num_fixed_params) ||
(num_opt_pos_params < other_num_opt_pos_params) ||
(num_opt_named_params < other_num_opt_named_params)) {
return false;
}
if (other_num_opt_named_params == 0) {
return true;
}
// Check that for each optional named parameter of the other function there
// exists an optional named parameter of this function with an identical
// name.
// Note that SetParameterNameAt() guarantees that names are symbols, so we
// can compare their raw pointers.
const int num_params = num_fixed_params + num_opt_named_params;
const int other_num_params =
other_num_fixed_params + other_num_opt_named_params;
bool found_param_name;
String& other_param_name = String::Handle();
for (intptr_t i = other_num_fixed_params; i < other_num_params; i++) {
other_param_name = other.ParameterNameAt(i);
found_param_name = false;
for (intptr_t j = num_fixed_params; j < num_params; j++) {
if (ParameterNameAt(j) == other_param_name.raw()) {
found_param_name = true;
break;
}
}
if (!found_param_name) {
return false;
}
}
return true;
}
// If test_kind == kIsSubtypeOf, checks if the type of the specified parameter
// of this function is a subtype or a supertype of the type of the specified
// parameter of the other function.
// If test_kind == kIsMoreSpecificThan, checks if the type of the specified
// parameter of this function is more specific than the type of the specified
// parameter of the other function.
// Note that we do not apply contravariance of parameter types, but covariance
// of both parameter types and result type.
bool Function::TestParameterType(
TypeTestKind test_kind,
intptr_t parameter_position,
intptr_t other_parameter_position,
const AbstractTypeArguments& type_arguments,
const Function& other,
const AbstractTypeArguments& other_type_arguments,
Error* malformed_error) const {
AbstractType& other_param_type =
AbstractType::Handle(other.ParameterTypeAt(other_parameter_position));
if (!other_param_type.IsInstantiated()) {
other_param_type = other_param_type.InstantiateFrom(other_type_arguments);
}
if (other_param_type.IsDynamicType()) {
return true;
}
AbstractType& param_type =
AbstractType::Handle(ParameterTypeAt(parameter_position));
if (!param_type.IsInstantiated()) {
param_type = param_type.InstantiateFrom(type_arguments);
}
if (param_type.IsDynamicType()) {
return test_kind == kIsSubtypeOf;
}
if (test_kind == kIsSubtypeOf) {
if (!param_type.IsSubtypeOf(other_param_type, malformed_error) &&
!other_param_type.IsSubtypeOf(param_type, malformed_error)) {
return false;
}
} else {
ASSERT(test_kind == kIsMoreSpecificThan);
if (!param_type.IsMoreSpecificThan(other_param_type, malformed_error)) {
return false;
}
}
return true;
}
bool Function::TypeTest(TypeTestKind test_kind,
const AbstractTypeArguments& type_arguments,
const Function& other,
const AbstractTypeArguments& other_type_arguments,
Error* malformed_error) const {
const intptr_t num_fixed_params = num_fixed_parameters();
const intptr_t num_opt_pos_params = NumOptionalPositionalParameters();
const intptr_t num_opt_named_params = NumOptionalNamedParameters();
const intptr_t other_num_fixed_params = other.num_fixed_parameters();
const intptr_t other_num_opt_pos_params =
other.NumOptionalPositionalParameters();
const intptr_t other_num_opt_named_params =
other.NumOptionalNamedParameters();
if ((num_fixed_params != other_num_fixed_params) ||
(num_opt_pos_params < other_num_opt_pos_params) ||
(num_opt_named_params < other_num_opt_named_params)) {
return false;
}
// Check the result type.
AbstractType& other_res_type = AbstractType::Handle(other.result_type());
if (!other_res_type.IsInstantiated()) {
other_res_type = other_res_type.InstantiateFrom(other_type_arguments);
}
if (!other_res_type.IsDynamicType() && !other_res_type.IsVoidType()) {
AbstractType& res_type = AbstractType::Handle(result_type());
if (!res_type.IsInstantiated()) {
res_type = res_type.InstantiateFrom(type_arguments);
}
if (res_type.IsVoidType()) {
return false;
}
if (test_kind == kIsSubtypeOf) {
if (!res_type.IsSubtypeOf(other_res_type, malformed_error) &&
!other_res_type.IsSubtypeOf(res_type, malformed_error)) {
return false;
}
} else {
ASSERT(test_kind == kIsMoreSpecificThan);
if (!res_type.IsMoreSpecificThan(other_res_type, malformed_error)) {
return false;
}
}
}
// Check the types of fixed and optional positional parameters.
for (intptr_t i = 0; i < num_fixed_params + other_num_opt_pos_params; i++) {
if (!TestParameterType(test_kind,
i, i, type_arguments, other, other_type_arguments,
malformed_error)) {
return false;
}
}
// Check the names and types of optional named parameters.
if (other_num_opt_named_params == 0) {
return true;
}
// Check that for each optional named parameter of type T of the other
// function type, there exists an optional named parameter of this function
// type with an identical name and with a type S that is a either a subtype
// or supertype of T (if test_kind == kIsSubtypeOf) or that is more specific
// than T (if test_kind == kIsMoreSpecificThan).
// Note that SetParameterNameAt() guarantees that names are symbols, so we
// can compare their raw pointers.
const int num_params = num_fixed_params + num_opt_named_params;
const int other_num_params =
other_num_fixed_params + other_num_opt_named_params;
bool found_param_name;
String& other_param_name = String::Handle();
for (intptr_t i = other_num_fixed_params; i < other_num_params; i++) {
other_param_name = other.ParameterNameAt(i);
ASSERT(other_param_name.IsSymbol());
found_param_name = false;
for (intptr_t j = num_fixed_params; j < num_params; j++) {
ASSERT(String::Handle(ParameterNameAt(j)).IsSymbol());
if (ParameterNameAt(j) == other_param_name.raw()) {
found_param_name = true;
if (!TestParameterType(test_kind,
j, i,
type_arguments, other, other_type_arguments,
malformed_error)) {
return false;
}
break;
}
}
if (!found_param_name) {
return false;
}
}
return true;
}
bool Function::IsImplicitClosureFunction() const {
if (!IsClosureFunction()) {
return false;
}
const Function& parent = Function::Handle(parent_function());
return (parent.implicit_closure_function() == raw());
}
RawFunction* Function::New() {
ASSERT(Object::function_class() != Class::null());
RawObject* raw = Object::Allocate(Function::kClassId,
Function::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawFunction*>(raw);
}
RawFunction* Function::New(const String& name,
RawFunction::Kind kind,
bool is_static,
bool is_const,
bool is_abstract,
bool is_external,
const Object& owner,
intptr_t token_pos) {
ASSERT(name.IsOneByteString());
ASSERT(!owner.IsNull());
const Function& result = Function::Handle(Function::New());
const Array& empty_array = Array::Handle(Object::empty_array());
result.set_parameter_types(empty_array);
result.set_parameter_names(empty_array);
result.set_name(name);
result.set_kind(kind);
result.set_is_static(is_static);
result.set_is_const(is_const);
result.set_is_abstract(is_abstract);
result.set_is_external(is_external);
result.set_intrinsic_kind(kUnknownIntrinsic);
result.set_owner(owner);
result.set_token_pos(token_pos);
result.set_end_token_pos(token_pos);
result.set_num_fixed_parameters(0);
result.set_num_optional_parameters(0);
result.set_usage_counter(0);
result.set_deoptimization_counter(0);
result.set_is_optimizable(true);
result.set_has_finally(false);
result.set_is_native(false);
result.set_is_inlinable(true);
if (kind == RawFunction::kClosureFunction) {
const ClosureData& data = ClosureData::Handle(ClosureData::New());
result.set_data(data);
}
return result.raw();
}
RawFunction* Function::NewClosureFunction(const String& name,
const Function& parent,
intptr_t token_pos) {
ASSERT(name.IsOneByteString());
ASSERT(!parent.IsNull());
// Use the owner defining the parent function and not the class containing it.
const Object& parent_owner = Object::Handle(parent.raw_ptr()->owner_);
ASSERT(!parent_owner.IsNull());
const Function& result = Function::Handle(
Function::New(name,
RawFunction::kClosureFunction,
/* is_static = */ parent.is_static(),
/* is_const = */ false,
/* is_abstract = */ false,
/* is_external = */ false,
parent_owner,
token_pos));
result.set_parent_function(parent);
return result.raw();
}
RawFunction* Function::ImplicitClosureFunction() const {
// Return the existing implicit closure function if any.
if (implicit_closure_function() != Function::null()) {
return implicit_closure_function();
}
ASSERT(!IsSignatureFunction() && !IsClosureFunction());
// Create closure function.
const String& closure_name = String::Handle(name());
const Function& closure_function = Function::Handle(
NewClosureFunction(closure_name, *this, token_pos()));
// Set closure function's context scope.
ContextScope& context_scope = ContextScope::Handle();
if (is_static()) {
context_scope ^= ContextScope::New(0);
} else {
context_scope ^= LocalScope::CreateImplicitClosureScope(*this);
}
closure_function.set_context_scope(context_scope);
// Set closure function's result type to this result type.
closure_function.set_result_type(AbstractType::Handle(result_type()));
// Set closure function's formal parameters to this formal parameters,
// removing the receiver if this is an instance method and adding the closure
// object as first parameter.
const int kClosure = 1;
const int has_receiver = is_static() ? 0 : 1;
const int num_fixed_params = kClosure - has_receiver + num_fixed_parameters();
const int num_opt_params = NumOptionalParameters();
const bool has_opt_pos_params = HasOptionalPositionalParameters();
const int num_params = num_fixed_params + num_opt_params;
closure_function.set_num_fixed_parameters(num_fixed_params);
closure_function.SetNumOptionalParameters(num_opt_params, has_opt_pos_params);
closure_function.set_parameter_types(Array::Handle(Array::New(num_params,
Heap::kOld)));
closure_function.set_parameter_names(Array::Handle(Array::New(num_params,
Heap::kOld)));
AbstractType& param_type = AbstractType::Handle();
String& param_name = String::Handle();
// Add implicit closure object parameter.
param_type = Type::DynamicType();
closure_function.SetParameterTypeAt(0, param_type);
param_name = Symbols::ClosureParameter();
closure_function.SetParameterNameAt(0, param_name);
for (int i = kClosure; i < num_params; i++) {
param_type = ParameterTypeAt(has_receiver - kClosure + i);
closure_function.SetParameterTypeAt(i, param_type);
param_name = ParameterNameAt(has_receiver - kClosure + i);
closure_function.SetParameterNameAt(i, param_name);
}
// Lookup or create a new signature class for the closure function in the
// library of the owner class.
const Class& owner_class = Class::Handle(Owner());
ASSERT(!owner_class.IsNull() && (Owner() == closure_function.Owner()));
const Library& library = Library::Handle(owner_class.library());
ASSERT(!library.IsNull());
const String& signature = String::Handle(closure_function.Signature());
Class& signature_class = Class::ZoneHandle(
library.LookupLocalClass(signature));
if (signature_class.IsNull()) {
const Script& script = Script::Handle(this->script());
signature_class = Class::NewSignatureClass(signature,
closure_function,
script);
library.AddClass(signature_class);
} else {
closure_function.set_signature_class(signature_class);
}
const Type& signature_type = Type::Handle(signature_class.SignatureType());
if (!signature_type.IsFinalized()) {
ClassFinalizer::FinalizeType(
signature_class, signature_type, ClassFinalizer::kCanonicalize);
}
ASSERT(closure_function.signature_class() == signature_class.raw());
set_implicit_closure_function(closure_function);
ASSERT(closure_function.IsImplicitClosureFunction());
return closure_function.raw();
}
RawString* Function::BuildSignature(
bool instantiate,
NameVisibility name_visibility,
const AbstractTypeArguments& instantiator) const {
const GrowableObjectArray& pieces =
GrowableObjectArray::Handle(GrowableObjectArray::New());
const String& kCommaSpace = String::Handle(Symbols::New(", "));
const String& kColonSpace = String::Handle(Symbols::New(": "));
const String& kLParen = String::Handle(Symbols::New("("));
const String& kRParenArrow = String::Handle(Symbols::New(") => "));
const String& kLBracket = String::Handle(Symbols::New("["));
const String& kRBracket = String::Handle(Symbols::New("]"));
const String& kLBrace = String::Handle(Symbols::New("{"));
const String& kRBrace = String::Handle(Symbols::New("}"));
String& name = String::Handle();
if (!instantiate && !is_static() && (name_visibility == kInternalName)) {
// Prefix the signature with its class and type parameters, if any (e.g.
// "Map<K, V>(K) => bool").
// The signature of static functions cannot be type parameterized.
const String& kSpaceExtendsSpace =
String::Handle(Symbols::New(" extends "));
const String& kLAngleBracket = String::Handle(Symbols::New("<"));
const String& kRAngleBracket = String::Handle(Symbols::New(">"));
const Class& function_class = Class::Handle(Owner());
ASSERT(!function_class.IsNull());
const TypeArguments& type_parameters = TypeArguments::Handle(
function_class.type_parameters());
if (!type_parameters.IsNull()) {
const String& function_class_name = String::Handle(function_class.Name());
pieces.Add(function_class_name);
intptr_t num_type_parameters = type_parameters.Length();
pieces.Add(kLAngleBracket);
TypeParameter& type_parameter = TypeParameter::Handle();
AbstractType& bound = AbstractType::Handle();
for (intptr_t i = 0; i < num_type_parameters; i++) {
type_parameter ^= type_parameters.TypeAt(i);
name = type_parameter.name();
pieces.Add(name);
bound = type_parameter.bound();
if (!bound.IsNull() && !bound.IsObjectType()) {
pieces.Add(kSpaceExtendsSpace);
name = bound.BuildName(name_visibility);
pieces.Add(name);
}
if (i < num_type_parameters - 1) {
pieces.Add(kCommaSpace);
}
}
pieces.Add(kRAngleBracket);
}
}
AbstractType& param_type = AbstractType::Handle();
const intptr_t num_params = NumParameters();
const intptr_t num_fixed_params = num_fixed_parameters();
const intptr_t num_opt_pos_params = NumOptionalPositionalParameters();
const intptr_t num_opt_named_params = NumOptionalNamedParameters();
const intptr_t num_opt_params = num_opt_pos_params + num_opt_named_params;
ASSERT((num_fixed_params + num_opt_params) == num_params);
pieces.Add(kLParen);
intptr_t i = 0;
if (name_visibility == kUserVisibleName) {
// Hide implicit parameters.
i = NumImplicitParameters();
}
while (i < num_fixed_params) {
param_type = ParameterTypeAt(i);
ASSERT(!param_type.IsNull());
if (instantiate && !param_type.IsInstantiated()) {
param_type = param_type.InstantiateFrom(instantiator);
}
name = param_type.BuildName(name_visibility);
pieces.Add(name);
if (i != (num_params - 1)) {
pieces.Add(kCommaSpace);
}
i++;
}
if (num_opt_params > 0) {
if (num_opt_pos_params > 0) {
pieces.Add(kLBracket);
} else {
pieces.Add(kLBrace);
}
for (intptr_t i = num_fixed_params; i < num_params; i++) {
// The parameter name of an optional positional parameter does not need
// to be part of the signature, since it is not used.
if (num_opt_named_params > 0) {
name = ParameterNameAt(i);
pieces.Add(name);
pieces.Add(kColonSpace);
}
param_type = ParameterTypeAt(i);
if (instantiate && !param_type.IsInstantiated()) {
param_type = param_type.InstantiateFrom(instantiator);
}
ASSERT(!param_type.IsNull());
name = param_type.BuildName(name_visibility);
pieces.Add(name);
if (i != (num_params - 1)) {
pieces.Add(kCommaSpace);
}
}
if (num_opt_pos_params > 0) {
pieces.Add(kRBracket);
} else {
pieces.Add(kRBrace);
}
}
pieces.Add(kRParenArrow);
AbstractType& res_type = AbstractType::Handle(result_type());
if (instantiate && !res_type.IsInstantiated()) {
res_type = res_type.InstantiateFrom(instantiator);
}
name = res_type.BuildName(name_visibility);
pieces.Add(name);
const Array& strings = Array::Handle(Array::MakeArray(pieces));
return Symbols::New(String::Handle(String::ConcatAll(strings)));
}
bool Function::HasInstantiatedSignature() const {
AbstractType& type = AbstractType::Handle(result_type());
if (!type.IsInstantiated()) {
return false;
}
const intptr_t num_parameters = NumParameters();
for (intptr_t i = 0; i < num_parameters; i++) {
type = ParameterTypeAt(i);
if (!type.IsInstantiated()) {
return false;
}
}
return true;
}
RawClass* Function::Owner() const {
const Object& obj = Object::Handle(raw_ptr()->owner_);
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
ASSERT(obj.IsPatchClass());
return PatchClass::Cast(obj).patched_class();
}
RawScript* Function::script() const {
const Object& obj = Object::Handle(raw_ptr()->owner_);
if (obj.IsClass()) {
return Class::Cast(obj).script();
}
ASSERT(obj.IsPatchClass());
return PatchClass::Cast(obj).script();
}
bool Function::HasOptimizedCode() const {
return HasCode() && Code::Handle(raw_ptr()->code_).is_optimized();
}
RawString* Function::UserVisibleName() const {
const String& str = String::Handle(name());
return IdentifierPrettyName(str);
}
RawString* Function::QualifiedUserVisibleName() const {
String& tmp = String::Handle();
String& suffix = String::Handle();
const Class& cls = Class::Handle(Owner());
if (IsClosureFunction()) {
if (IsLocalFunction()) {
const Function& parent = Function::Handle(parent_function());
tmp = parent.QualifiedUserVisibleName();
} else {
return UserVisibleName();
}
} else {
if (cls.IsTopLevel()) {
return UserVisibleName();
} else {
tmp = cls.UserVisibleName();
}
}
suffix = Symbols::Dot();
tmp = String::Concat(tmp, suffix);
suffix = UserVisibleName();
return String::Concat(tmp, suffix);
}
// Construct fingerprint from token stream. The token stream contains also
// arguments.
int32_t Function::SourceFingerprint() const {
uint32_t result = String::Handle(Signature()).Hash();
TokenStream::Iterator tokens_iterator(TokenStream::Handle(
Script::Handle(script()).tokens()), token_pos());
Object& obj = Object::Handle();
String& literal = String::Handle();
while (tokens_iterator.CurrentPosition() < end_token_pos()) {
uint32_t val = 0;
obj = tokens_iterator.CurrentToken();
if (obj.IsSmi()) {
val = Smi::Cast(obj).Value();
} else {
literal = tokens_iterator.MakeLiteralToken(obj);
val = literal.Hash();
}
result = 31 * result + val;
tokens_iterator.Advance();
}
result = result & ((static_cast<uint32_t>(1) << 31) - 1);
ASSERT(result <= static_cast<uint32_t>(kMaxInt32));
return result;
}
bool Function::CheckSourceFingerprint(intptr_t fp) const {
if (SourceFingerprint() != fp) {
OS::Print("FP mismatch while recognizing method %s:"
" expecting %"Pd" found %d\n",
ToFullyQualifiedCString(),
fp,
SourceFingerprint());
return false;
}
return true;
}
const char* Function::ToCString() const {
const char* static_str = is_static() ? " static" : "";
const char* abstract_str = is_abstract() ? " abstract" : "";
const char* kind_str = NULL;
const char* const_str = is_const() ? " const" : "";
switch (kind()) {
case RawFunction::kRegularFunction:
case RawFunction::kClosureFunction:
case RawFunction::kGetterFunction:
case RawFunction::kSetterFunction:
kind_str = "";
break;
case RawFunction::kSignatureFunction:
kind_str = " signature";
break;
case RawFunction::kConstructor:
kind_str = is_static() ? " factory" : " constructor";
break;
case RawFunction::kImplicitGetter:
kind_str = " getter";
break;
case RawFunction::kImplicitSetter:
kind_str = " setter";
break;
case RawFunction::kConstImplicitGetter:
kind_str = " const-getter";
break;
default:
UNREACHABLE();
}
const char* kFormat = "Function '%s':%s%s%s%s.";
const char* function_name = String::Handle(name()).ToCString();
intptr_t len = OS::SNPrint(NULL, 0, kFormat, function_name,
static_str, abstract_str, kind_str, const_str) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, function_name,
static_str, abstract_str, kind_str, const_str);
return chars;
}
void ClosureData::set_context_scope(const ContextScope& value) const {
StorePointer(&raw_ptr()->context_scope_, value.raw());
}
void ClosureData::set_implicit_static_closure(const Instance& closure) const {
ASSERT(!closure.IsNull());
ASSERT(raw_ptr()->closure_ == Instance::null());
StorePointer(&raw_ptr()->closure_, closure.raw());
}
void ClosureData::set_closure_allocation_stub(const Code& value) const {
ASSERT(!value.IsNull());
ASSERT(raw_ptr()->closure_allocation_stub_ == Code::null());
StorePointer(&raw_ptr()->closure_allocation_stub_, value.raw());
}
void ClosureData::set_parent_function(const Function& value) const {
StorePointer(&raw_ptr()->parent_function_, value.raw());
}
void ClosureData::set_signature_class(const Class& value) const {
StorePointer(&raw_ptr()->signature_class_, value.raw());
}
RawClosureData* ClosureData::New() {
ASSERT(Object::closure_data_class() != Class::null());
RawObject* raw = Object::Allocate(ClosureData::kClassId,
ClosureData::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawClosureData*>(raw);
}
const char* ClosureData::ToCString() const {
return "ClosureData class";
}
void RedirectionData::set_type(const Type& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->type_, value.raw());
}
void RedirectionData::set_identifier(const String& value) const {
StorePointer(&raw_ptr()->identifier_, value.raw());
}
void RedirectionData::set_target(const Function& value) const {
StorePointer(&raw_ptr()->target_, value.raw());
}
RawRedirectionData* RedirectionData::New() {
ASSERT(Object::redirection_data_class() != Class::null());
RawObject* raw = Object::Allocate(RedirectionData::kClassId,
RedirectionData::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawRedirectionData*>(raw);
}
const char* RedirectionData::ToCString() const {
return "RedirectionData class";
}
RawString* Field::GetterName(const String& field_name) {
String& str = String::Handle();
str = String::New(kGetterPrefix);
str = String::Concat(str, field_name);
return str.raw();
}
RawString* Field::GetterSymbol(const String& field_name) {
String& str = String::Handle();
str = Field::GetterName(field_name);
return Symbols::New(str);
}
RawString* Field::SetterName(const String& field_name) {
String& str = String::Handle();
str = String::New(kSetterPrefix);
str = String::Concat(str, field_name);
return str.raw();
}
RawString* Field::SetterSymbol(const String& field_name) {
String& str = String::Handle();
str = Field::SetterName(field_name);
return Symbols::New(str);
}
RawString* Field::NameFromGetter(const String& getter_name) {
String& str = String::Handle();
str = String::SubString(getter_name, strlen(kGetterPrefix));
return str.raw();
}
RawString* Field::NameFromSetter(const String& setter_name) {
String& str = String::Handle();
str = String::SubString(setter_name, strlen(kSetterPrefix));
return str.raw();
}
bool Field::IsGetterName(const String& function_name) {
return function_name.StartsWith(String::Handle(String::New(kGetterPrefix)));
}
bool Field::IsSetterName(const String& function_name) {
return function_name.StartsWith(String::Handle(String::New(kSetterPrefix)));
}
void Field::set_name(const String& value) const {
ASSERT(value.IsSymbol());
StorePointer(&raw_ptr()->name_, value.raw());
}
RawInstance* Field::value() const {
ASSERT(is_static()); // Valid only for static dart fields.
return raw_ptr()->value_;
}
void Field::set_value(const Instance& value) const {
ASSERT(is_static()); // Valid only for static dart fields.
StorePointer(&raw_ptr()->value_, value.raw());
}
void Field::set_type(const AbstractType& value) const {
ASSERT(!value.IsNull());
StorePointer(&raw_ptr()->type_, value.raw());
}
RawField* Field::New() {
ASSERT(Object::field_class() != Class::null());
RawObject* raw = Object::Allocate(Field::kClassId,
Field::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawField*>(raw);
}
RawField* Field::New(const String& name,
bool is_static,
bool is_final,
bool is_const,
const Class& owner,
intptr_t token_pos) {
ASSERT(name.IsOneByteString());
ASSERT(!owner.IsNull());
const Field& result = Field::Handle(Field::New());
result.set_name(name);
result.set_is_static(is_static);
if (is_static) {
result.set_value(Instance::Handle());
} else {
result.SetOffset(0);
}
result.set_is_final(is_final);
result.set_is_const(is_const);
result.set_owner(owner);
result.set_token_pos(token_pos);
result.set_has_initializer(false);
return result.raw();
}
RawString* Field::UserVisibleName() const {
const String& str = String::Handle(name());
return IdentifierPrettyName(str);
}
const char* Field::ToCString() const {
const char* kF0 = is_static() ? " static" : "";
const char* kF1 = is_final() ? " final" : "";
const char* kF2 = is_const() ? " const" : "";
const char* kFormat = "Field <%s.%s>:%s%s%s";
const char* field_name = String::Handle(name()).ToCString();
const Class& cls = Class::Handle(owner());
const char* cls_name = String::Handle(cls.Name()).ToCString();
intptr_t len =
OS::SNPrint(NULL, 0, kFormat, cls_name, field_name, kF0, kF1, kF2) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, cls_name, field_name, kF0, kF1, kF2);
return chars;
}
void LiteralToken::set_literal(const String& literal) const {
StorePointer(&raw_ptr()->literal_, literal.raw());
}
void LiteralToken::set_value(const Object& value) const {
StorePointer(&raw_ptr()->value_, value.raw());
}
RawLiteralToken* LiteralToken::New() {
ASSERT(Object::literal_token_class() != Class::null());
RawObject* raw = Object::Allocate(LiteralToken::kClassId,
LiteralToken::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawLiteralToken*>(raw);
}
RawLiteralToken* LiteralToken::New(Token::Kind kind, const String& literal) {
const LiteralToken& result = LiteralToken::Handle(LiteralToken::New());
result.set_kind(kind);
result.set_literal(literal);
if (kind == Token::kINTEGER) {
const Integer& value = Integer::Handle(Integer::NewCanonical(literal));
ASSERT(value.IsSmi() || value.IsOld());
result.set_value(value);
} else if (kind == Token::kDOUBLE) {
const Double& value = Double::Handle(Double::NewCanonical(literal));
result.set_value(value);
} else {
ASSERT(Token::NeedsLiteralToken(kind));
result.set_value(literal);
}
return result.raw();
}
const char* LiteralToken::ToCString() const {
const String& token = String::Handle(literal());
return token.ToCString();
}
RawArray* TokenStream::TokenObjects() const {
return raw_ptr()->token_objects_;
}
void TokenStream::SetTokenObjects(const Array& value) const {
StorePointer(&raw_ptr()->token_objects_, value.raw());
}
RawExternalUint8Array* TokenStream::GetStream() const {
return raw_ptr()->stream_;
}
void TokenStream::SetStream(const ExternalUint8Array& value) const {
StorePointer(&raw_ptr()->stream_, value.raw());
}
void TokenStream::DataFinalizer(void *peer) {
ASSERT(peer != NULL);
::free(peer);
}
RawString* TokenStream::PrivateKey() const {
return raw_ptr()->private_key_;
}
void TokenStream::SetPrivateKey(const String& value) const {
StorePointer(&raw_ptr()->private_key_, value.raw());
}
RawString* TokenStream::GenerateSource() const {
Iterator iterator(*this, 0);
const ExternalUint8Array& data = ExternalUint8Array::Handle(GetStream());
const GrowableObjectArray& literals =
GrowableObjectArray::Handle(GrowableObjectArray::New(data.Length()));
const String& private_key = String::Handle(PrivateKey());
intptr_t private_len = private_key.Length();
String& blank = String::Handle(String::New(" "));
String& newline = String::Handle(String::New("\n"));
String& two_newlines = String::Handle(String::New("\n\n"));
String& double_quotes = String::Handle(String::New("\""));
String& dollar = String::Handle(String::New("$"));
String& two_spaces = String::Handle(String::New(" "));
String& raw_string = String::Handle(String::New("r"));
Token::Kind curr = iterator.CurrentTokenKind();
Token::Kind prev = Token::kILLEGAL;
// Handles used in the loop.
Object& obj = Object::Handle();
String& literal = String::Handle();
// Current indentation level.
int indent = 0;
while (curr != Token::kEOS) {
// Remember current values for this token.
obj = iterator.CurrentToken();
literal = iterator.MakeLiteralToken(obj);
// Advance to be able to use next token kind.
iterator.Advance();
Token::Kind next = iterator.CurrentTokenKind();
// Handle the current token.
if (curr == Token::kSTRING) {
bool is_raw_string = false;
bool escape_characters = false;
for (intptr_t i = 0; i < literal.Length(); i++) {
if (IsSpecialCharacter(literal.CharAt(i))) {
escape_characters = true;
}
// TODO(4995): Temp solution for raw strings, this will break
// if we saw a string that is not a raw string but has back slashes
// in it.
if ((literal.CharAt(i) == '\\')) {
if ((next != Token::kINTERPOL_VAR) &&
(next != Token::kINTERPOL_START) &&
(prev != Token::kINTERPOL_VAR) &&
(prev != Token::kINTERPOL_END)) {
is_raw_string = true;
} else {
escape_characters = true;
}
}
}
if ((prev != Token::kINTERPOL_VAR) && (prev != Token::kINTERPOL_END)) {
if (is_raw_string) {
literals.Add(raw_string);
}
literals.Add(double_quotes);
}
if (escape_characters) {
literal = String::EscapeSpecialCharacters(literal, is_raw_string);
literals.Add(literal);
} else {
literals.Add(literal);
}
if ((next != Token::kINTERPOL_VAR) && (next != Token::kINTERPOL_START)) {
literals.Add(double_quotes);
}
} else if (curr == Token::kINTERPOL_VAR) {
literals.Add(dollar);
if (literal.CharAt(0) == Scanner::kPrivateIdentifierStart) {
literal = String::SubString(literal, 0, literal.Length() - private_len);
}
literals.Add(literal);
} else if (curr == Token::kIDENT) {
if (literal.CharAt(0) == Scanner::kPrivateIdentifierStart) {
literal = String::SubString(literal, 0, literal.Length() - private_len);
}
literals.Add(literal);
} else {
literals.Add(literal);
}
// Determine the separation text based on this current token.
const String* separator = NULL;
switch (curr) {
case Token::kLBRACE:
indent++;
separator = &newline;
break;
case Token::kRBRACE:
if (indent == 0) {
separator = &two_newlines;
} else {
separator = &newline;
}
break;
case Token::kSEMICOLON:
separator = &newline;
break;
case Token::kPERIOD:
case Token::kLPAREN:
case Token::kLBRACK:
case Token::kTIGHTADD:
case Token::kINTERPOL_VAR:
case Token::kINTERPOL_START:
case Token::kINTERPOL_END:
break;
default:
separator = &blank;
break;
}
// Determine whether the separation text needs to be updated based on the
// next token.
switch (next) {
case Token::kRBRACE:
indent--;
break;
case Token::kSEMICOLON:
case Token::kPERIOD:
case Token::kCOMMA:
case Token::kLPAREN:
case Token::kRPAREN:
case Token::kLBRACK:
case Token::kRBRACK:
case Token::kINTERPOL_VAR:
case Token::kINTERPOL_START:
case Token::kINTERPOL_END:
separator = NULL;
break;
case Token::kELSE:
separator = &blank;
default:
// Do nothing.
break;
}
// Update the few cases where both tokens need to be taken into account.
if (((curr == Token::kIF) || (curr == Token::kFOR)) &&
(next == Token::kLPAREN)) {
separator = &blank;
} else if ((curr == Token::kASSIGN) && (next == Token::kLPAREN)) {
separator = & blank;
} else if ((curr == Token::kLBRACE) && (next == Token::kRBRACE)) {
separator = NULL;
}
if (separator != NULL) {
literals.Add(*separator);
if (separator == &newline) {
for (int i = 0; i < indent; i++) {
literals.Add(two_spaces);
}
}
}
// Setup for next iteration.
prev = curr;
curr = next;
}
const Array& source = Array::Handle(Array::MakeArray(literals));
return String::ConcatAll(source);
}
intptr_t TokenStream::ComputeSourcePosition(intptr_t tok_pos) const {
Iterator iterator(*this, 0);
intptr_t src_pos = 0;
Token::Kind kind = iterator.CurrentTokenKind();
while (iterator.CurrentPosition() < tok_pos && kind != Token::kEOS) {
iterator.Advance();
kind = iterator.CurrentTokenKind();
src_pos += 1;
}
return src_pos;
}
intptr_t TokenStream::ComputeTokenPosition(intptr_t src_pos) const {
Iterator iterator(*this, 0);
intptr_t index = 0;
Token::Kind kind = iterator.CurrentTokenKind();
while (index < src_pos && kind != Token::kEOS) {
iterator.Advance();
kind = iterator.CurrentTokenKind();
index += 1;
}
return iterator.CurrentPosition();
}
RawTokenStream* TokenStream::New() {
ASSERT(Object::token_stream_class() != Class::null());
TokenStream& result = TokenStream::Handle();
{
RawObject* raw = Object::Allocate(TokenStream::kClassId,
TokenStream::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
return result.raw();
}
RawTokenStream* TokenStream::New(intptr_t len) {
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in TokenStream::New: invalid len %"Pd"\n", len);
}
uint8_t* data = reinterpret_cast<uint8_t*>(::malloc(len));
ASSERT(data != NULL);
const ExternalUint8Array& stream = ExternalUint8Array::Handle(
ExternalUint8Array::New(data, len, data, DataFinalizer, Heap::kOld));
const TokenStream& result = TokenStream::Handle(TokenStream::New());
result.SetStream(stream);
return result.raw();
}
// Helper class for creation of compressed token stream data.
class CompressedTokenStreamData : public ValueObject {
public:
static const intptr_t kIncrementSize = 16 * KB;
CompressedTokenStreamData() :
buffer_(NULL),
stream_(&buffer_, Reallocate, kIncrementSize),
token_objects_(GrowableObjectArray::Handle(
GrowableObjectArray::New(kInitialTokenCount, Heap::kOld))),
token_obj_(Object::Handle()),
literal_token_(LiteralToken::Handle()),
literal_str_(String::Handle()) {
const String& empty_literal = String::Handle();
token_objects_.Add(empty_literal);
}
~CompressedTokenStreamData() {
}
// Add an IDENT token into the stream and the token objects array.
void AddIdentToken(String* ident) {
if (ident != NULL) {
// If the IDENT token is already in the tokens object array use the
// same index instead of duplicating it.
intptr_t index = FindIdentIndex(ident);
if (index == -1) {
WriteIndex(token_objects_.Length());
ASSERT(ident != NULL);
token_objects_.Add(*ident);
} else {
WriteIndex(index);
}
} else {
WriteIndex(0);
}
}
// Add a LITERAL token into the stream and the token objects array.
void AddLiteralToken(Token::Kind kind, String* literal) {
if (literal != NULL) {
// If the literal token is already in the tokens object array use the
// same index instead of duplicating it.
intptr_t index = FindLiteralIndex(kind, literal);
if (index == -1) {
WriteIndex(token_objects_.Length());
ASSERT(literal != NULL);
literal_token_ = LiteralToken::New(kind, *literal);
token_objects_.Add(literal_token_);
} else {
WriteIndex(index);
}
} else {
WriteIndex(0);
}
}
// Add a simple token into the stream.
void AddSimpleToken(intptr_t kind) {
stream_.WriteUnsigned(kind);
}
// Return the compressed token stream.
uint8_t* GetStream() const { return buffer_; }
// Return the compressed token stream length.
intptr_t Length() const { return stream_.bytes_written(); }
// Return the token objects array.
const GrowableObjectArray& TokenObjects() const {
return token_objects_;
}
private:
intptr_t FindIdentIndex(String* ident) {
ASSERT(ident != NULL);
intptr_t hash_value = ident->Hash() % kTableSize;
GrowableArray<intptr_t>& value = ident_table_[hash_value];
for (intptr_t i = 0; i < value.length(); i++) {
intptr_t index = value[i];
token_obj_ = token_objects_.At(index);
if (token_obj_.IsString()) {
const String& ident_str = String::Cast(token_obj_);
if (ident->Equals(ident_str)) {
return index;
}
}
}
value.Add(token_objects_.Length());
return -1;
}
intptr_t FindLiteralIndex(Token::Kind kind, String* literal) {
ASSERT(literal != NULL);
intptr_t hash_value = literal->Hash() % kTableSize;
GrowableArray<intptr_t>& value = literal_table_[hash_value];
for (intptr_t i = 0; i < value.length(); i++) {
intptr_t index = value[i];
token_obj_ = token_objects_.At(index);
if (token_obj_.IsLiteralToken()) {
const LiteralToken& token = LiteralToken::Cast(token_obj_);
literal_str_ = token.literal();
if (kind == token.kind() && literal->Equals(literal_str_)) {
return index;
}
}
}
value.Add(token_objects_.Length());
return -1;
}
void WriteIndex(intptr_t value) {
stream_.WriteUnsigned(value + Token::kNumTokens);
}
static uint8_t* Reallocate(uint8_t* ptr,
intptr_t old_size,
intptr_t new_size) {
void* new_ptr = ::realloc(reinterpret_cast<void*>(ptr), new_size);
return reinterpret_cast<uint8_t*>(new_ptr);
}
static const int kInitialTokenCount = 32;
static const intptr_t kTableSize = 128;
uint8_t* buffer_;
WriteStream stream_;
GrowableArray<intptr_t> ident_table_[kTableSize];
GrowableArray<intptr_t> literal_table_[kTableSize];
const GrowableObjectArray& token_objects_;
Object& token_obj_;
LiteralToken& literal_token_;
String& literal_str_;
DISALLOW_COPY_AND_ASSIGN(CompressedTokenStreamData);
};
RawTokenStream* TokenStream::New(const Scanner::GrowableTokenStream& tokens,
const String& private_key) {
// Copy the relevant data out of the scanner into a compressed stream of
// tokens.
CompressedTokenStreamData data;
intptr_t len = tokens.length();
for (intptr_t i = 0; i < len; i++) {
Scanner::TokenDescriptor token = tokens[i];
if (token.kind == Token::kIDENT) { // Identifier token.
if (FLAG_compiler_stats) {
CompilerStats::num_ident_tokens_total += 1;
}
data.AddIdentToken(token.literal);
} else if (Token::NeedsLiteralToken(token.kind)) { // Literal token.
if (FLAG_compiler_stats) {
CompilerStats::num_literal_tokens_total += 1;
}
data.AddLiteralToken(token.kind, token.literal);
} else { // Keyword, pseudo keyword etc.
ASSERT(token.kind < Token::kNumTokens);
data.AddSimpleToken(token.kind);
}
}
if (FLAG_compiler_stats) {
CompilerStats::num_tokens_total += len;
}
data.AddSimpleToken(Token::kEOS); // End of stream.
// Create and setup the token stream object.
const ExternalUint8Array& stream = ExternalUint8Array::Handle(
ExternalUint8Array::New(data.GetStream(),
data.Length(),
data.GetStream(),
DataFinalizer,
Heap::kOld));
const TokenStream& result = TokenStream::Handle(New());
result.SetPrivateKey(private_key);
{
NoGCScope no_gc;
result.SetStream(stream);
const Array& tokens = Array::Handle(Array::MakeArray(data.TokenObjects()));
result.SetTokenObjects(tokens);
}
return result.raw();
}
const char* TokenStream::ToCString() const {
return "TokenStream";
}
TokenStream::Iterator::Iterator(const TokenStream& tokens, intptr_t token_pos)
: tokens_(tokens),
data_(ExternalUint8Array::Handle(tokens.GetStream())),
stream_(data_.ByteAddr(0), data_.Length()),
token_objects_(Array::Handle(tokens.TokenObjects())),
obj_(Object::Handle()),
cur_token_pos_(token_pos),
cur_token_kind_(Token::kILLEGAL),
cur_token_obj_index_(-1) {
SetCurrentPosition(token_pos);
}
bool TokenStream::Iterator::IsValid() const {
return !tokens_.IsNull();
}
Token::Kind TokenStream::Iterator::LookaheadTokenKind(intptr_t num_tokens) {
intptr_t saved_position = stream_.Position();
Token::Kind kind = Token::kILLEGAL;
intptr_t value = -1;
intptr_t count = 0;
while (count < num_tokens && value != Token::kEOS) {
value = ReadToken();
count += 1;
}
if (value < Token::kNumTokens) {
kind = static_cast<Token::Kind>(value);
} else {
value = value - Token::kNumTokens;
obj_ = token_objects_.At(value);
if (obj_.IsLiteralToken()) {
const LiteralToken& literal_token = LiteralToken::Cast(obj_);
kind = literal_token.kind();
} else {
ASSERT(obj_.IsString()); // Must be an identifier.
kind = Token::kIDENT;
}
}
stream_.SetPosition(saved_position);
return kind;
}
intptr_t TokenStream::Iterator::CurrentPosition() const {
return cur_token_pos_;
}
void TokenStream::Iterator::SetCurrentPosition(intptr_t value) {
stream_.SetPosition(value);
Advance();
}
void TokenStream::Iterator::Advance() {
cur_token_pos_ = stream_.Position();
intptr_t value = ReadToken();
if (value < Token::kNumTokens) {
cur_token_kind_ = static_cast<Token::Kind>(value);
cur_token_obj_index_ = -1;
return;
}
cur_token_obj_index_ = value - Token::kNumTokens;
obj_ = token_objects_.At(cur_token_obj_index_);
if (obj_.IsLiteralToken()) {
const LiteralToken& literal_token = LiteralToken::Cast(obj_);
cur_token_kind_ = literal_token.kind();
return;
}
ASSERT(obj_.IsString()); // Must be an identifier.
cur_token_kind_ = Token::kIDENT;
}
RawObject* TokenStream::Iterator::CurrentToken() const {
if (cur_token_obj_index_ != -1) {
return token_objects_.At(cur_token_obj_index_);
} else {
return Smi::New(cur_token_kind_);
}
}
RawString* TokenStream::Iterator::CurrentLiteral() const {
obj_ = CurrentToken();
return MakeLiteralToken(obj_);
}
RawString* TokenStream::Iterator::MakeLiteralToken(const Object& obj) const {
if (obj.IsString()) {
return reinterpret_cast<RawString*>(obj.raw());
} else if (obj.IsSmi()) {
Token::Kind kind = static_cast<Token::Kind>(
Smi::Value(reinterpret_cast<RawSmi*>(obj.raw())));
ASSERT(kind < Token::kNumTokens);
if (Token::IsPseudoKeyword(kind) || Token::IsKeyword(kind)) {
Isolate* isolate = Isolate::Current();
ObjectStore* object_store = isolate->object_store();
String& str = String::Handle(isolate, String::null());
const Array& symbols = Array::Handle(isolate,
object_store->keyword_symbols());
ASSERT(!symbols.IsNull());
str ^= symbols.At(kind - Token::kFirstKeyword);
ASSERT(!str.IsNull());
return str.raw();
}
return Symbols::New(Token::Str(kind));
} else {
ASSERT(obj.IsLiteralToken()); // Must be a literal token.
const LiteralToken& literal_token = LiteralToken::Cast(obj);
return literal_token.literal();
}
}
bool Script::HasSource() const {
return raw_ptr()->source_ != String::null();
}
RawString* Script::Source() const {
String& source = String::Handle(raw_ptr()->source_);
if (source.IsNull()) {
const TokenStream& token_stream = TokenStream::Handle(tokens());
return token_stream.GenerateSource();
} else {
return raw_ptr()->source_;
}
}
void Script::set_url(const String& value) const {
StorePointer(&raw_ptr()->url_, value.raw());
}
void Script::set_source(const String& value) const {
StorePointer(&raw_ptr()->source_, value.raw());
}
void Script::set_kind(RawScript::Kind value) const {
raw_ptr()->kind_ = value;
}
void Script::set_tokens(const TokenStream& value) const {
StorePointer(&raw_ptr()->tokens_, value.raw());
}
void Script::Tokenize(const String& private_key) const {
const TokenStream& tkns = TokenStream::Handle(tokens());
if (!tkns.IsNull()) {
// Already tokenized.
return;
}
// Get the source, scan and allocate the token stream.
TimerScope timer(FLAG_compiler_stats, &CompilerStats::scanner_timer);
const String& src = String::Handle(Source());
Scanner scanner(src, private_key);
set_tokens(TokenStream::Handle(TokenStream::New(scanner.GetStream(),
private_key)));
if (FLAG_compiler_stats) {
CompilerStats::src_length += src.Length();
}
}
void Script::GetTokenLocation(intptr_t token_pos,
intptr_t* line,
intptr_t* column) const {
const String& src = String::Handle(Source());
const String& dummy_key = String::Handle(Symbols::Empty());
const TokenStream& tkns = TokenStream::Handle(tokens());
intptr_t src_pos = tkns.ComputeSourcePosition(token_pos);
Scanner scanner(src, dummy_key);
scanner.ScanTo(src_pos);
*line = scanner.CurrentPosition().line;
*column = scanner.CurrentPosition().column;
}
void Script::TokenRangeAtLine(intptr_t line_number,
intptr_t* first_token_index,
intptr_t* last_token_index) const {
const String& src = String::Handle(Source());
const String& dummy_key = String::Handle(Symbols::Empty());
const TokenStream& tkns = TokenStream::Handle(tokens());
Scanner scanner(src, dummy_key);
scanner.TokenRangeAtLine(line_number, first_token_index, last_token_index);
if (*first_token_index >= 0) {
*first_token_index = tkns.ComputeTokenPosition(*first_token_index);
}
if (*last_token_index >= 0) {
*last_token_index = tkns.ComputeTokenPosition(*last_token_index);
}
}
RawString* Script::GetLine(intptr_t line_number) const {
const String& src = String::Handle(Source());
intptr_t current_line = 1;
intptr_t line_start = -1;
intptr_t last_char = -1;
for (intptr_t ix = 0;
(ix < src.Length()) && (current_line <= line_number);
ix++) {
if ((current_line == line_number) && (line_start < 0)) {
line_start = ix;
}
if (src.CharAt(ix) == '\n') {
current_line++;
} else if (src.CharAt(ix) == '\r') {
if ((ix + 1 != src.Length()) && (src.CharAt(ix + 1) != '\n')) {
current_line++;
}
} else {
last_char = ix;
}
}
// Guarantee that returned string is never NULL.
String& line = String::Handle(Symbols::Empty());
if (line_start >= 0) {
line = String::SubString(src, line_start, last_char - line_start + 1);
}
return line.raw();
}
RawString* Script::GetSnippet(intptr_t from_line,
intptr_t from_column,
intptr_t to_line,
intptr_t to_column) const {
const String& src = String::Handle(Source());
intptr_t length = src.Length();
intptr_t line = 1;
intptr_t column = 1;
intptr_t lookahead = 0;
intptr_t snippet_start = -1;
intptr_t snippet_end = -1;
char c = src.CharAt(lookahead);
while (lookahead != length) {
if (snippet_start == -1) {
if ((line == from_line) && (column == from_column)) {
snippet_start = lookahead;
}
} else if ((line == to_line) && (column == to_column)) {
snippet_end = lookahead;
break;
}
if (c == '\n') {
line++;
column = 0;
}
column++;
lookahead++;
if (lookahead != length) {
// Replace '\r' with '\n' and a sequence of '\r' '\n' with a single '\n'.
if (src.CharAt(lookahead) == '\r') {
c = '\n';
if (lookahead + 1 != length && src.CharAt(lookahead) == '\n') {
lookahead++;
}
} else {
c = src.CharAt(lookahead);
}
}
}
String& snippet = String::Handle();
if ((snippet_start != -1) && (snippet_end != -1)) {
snippet =
String::SubString(src, snippet_start, snippet_end - snippet_start);
}
return snippet.raw();
}
RawScript* Script::New() {
ASSERT(Object::script_class() != Class::null());
RawObject* raw = Object::Allocate(Script::kClassId,
Script::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawScript*>(raw);
}
RawScript* Script::New(const String& url,
const String& source,
RawScript::Kind kind) {
const Script& result = Script::Handle(Script::New());
result.set_url(String::Handle(Symbols::New(url)));
result.set_source(source);
result.set_kind(kind);
return result.raw();
}
const char* Script::ToCString() const {
return "Script";
}
DictionaryIterator::DictionaryIterator(const Library& library)
: array_(Array::Handle(library.dictionary())),
// Last element in array is a Smi.
size_(Array::Handle(library.dictionary()).Length() - 1),
next_ix_(0) {
MoveToNextObject();
}
RawObject* DictionaryIterator::GetNext() {
ASSERT(HasNext());
int ix = next_ix_++;
MoveToNextObject();
ASSERT(array_.At(ix) != Object::null());
return array_.At(ix);
}
void DictionaryIterator::MoveToNextObject() {
Object& obj = Object::Handle(array_.At(next_ix_));
while (obj.IsNull() && HasNext()) {
next_ix_++;
obj = array_.At(next_ix_);
}
}
ClassDictionaryIterator::ClassDictionaryIterator(const Library& library)
: DictionaryIterator(library) {
MoveToNextClass();
}
RawClass* ClassDictionaryIterator::GetNextClass() {
ASSERT(HasNext());
int ix = next_ix_++;
Object& obj = Object::Handle(array_.At(ix));
MoveToNextClass();
return Class::Cast(obj).raw();
}
void ClassDictionaryIterator::MoveToNextClass() {
Object& obj = Object::Handle(array_.At(next_ix_));
while (!obj.IsClass() && HasNext()) {
next_ix_++;
obj = array_.At(next_ix_);
}
}
LibraryPrefixIterator::LibraryPrefixIterator(const Library& library)
: DictionaryIterator(library) {
Advance();
}
RawLibraryPrefix* LibraryPrefixIterator::GetNext() {
ASSERT(HasNext());
int ix = next_ix_++;
Object& obj = Object::Handle(array_.At(ix));
Advance();
return LibraryPrefix::Cast(obj).raw();
}
void LibraryPrefixIterator::Advance() {
Object& obj = Object::Handle(array_.At(next_ix_));
while (!obj.IsLibraryPrefix() && HasNext()) {
next_ix_++;
obj = array_.At(next_ix_);
}
}
void Library::SetName(const String& name) const {
// Only set name once.
ASSERT(!Loaded());
ASSERT(name.IsSymbol());
StorePointer(&raw_ptr()->name_, name.raw());
}
void Library::SetLoadInProgress() const {
// Should not be already loaded.
ASSERT(raw_ptr()->load_state_ == RawLibrary::kAllocated);
raw_ptr()->load_state_ = RawLibrary::kLoadInProgress;
}
void Library::SetLoaded() const {
// Should not be already loaded or just allocated.
ASSERT(LoadInProgress());
raw_ptr()->load_state_ = RawLibrary::kLoaded;
}
void Library::SetLoadError() const {
// Should not be already loaded or just allocated.
ASSERT(LoadInProgress());
raw_ptr()->load_state_ = RawLibrary::kLoadError;
}
void Library::GrowDictionary(const Array& dict, intptr_t dict_size) const {
// TODO(iposva): Avoid exponential growth.
intptr_t new_dict_size = dict_size * 2;
const Array& new_dict =
Array::Handle(Array::New(new_dict_size + 1, Heap::kOld));
// Rehash all elements from the original dictionary
// to the newly allocated array.
Object& entry = Class::Handle();
String& entry_name = String::Handle();
Object& new_entry = Object::Handle();
for (intptr_t i = 0; i < dict_size; i++) {
entry = dict.At(i);
if (!entry.IsNull()) {
entry_name = entry.DictionaryName();
ASSERT(!entry_name.IsNull());
intptr_t hash = entry_name.Hash();
intptr_t index = hash % new_dict_size;
new_entry = new_dict.At(index);
while (!new_entry.IsNull()) {
index = (index + 1) % new_dict_size; // Move to next element.
new_entry = new_dict.At(index);
}
new_dict.SetAt(index, entry);
}
}
// Copy used count.
new_entry = dict.At(dict_size);
new_dict.SetAt(new_dict_size, new_entry);
// Remember the new dictionary now.
StorePointer(&raw_ptr()->dictionary_, new_dict.raw());
}
void Library::AddObject(const Object& obj, const String& name) const {
ASSERT(obj.IsClass() ||
obj.IsFunction() ||
obj.IsField() ||
obj.IsLibraryPrefix());
ASSERT(name.Equals(String::Handle(obj.DictionaryName())));
ASSERT(LookupLocalObject(name) == Object::null());
const Array& dict = Array::Handle(dictionary());
intptr_t dict_size = dict.Length() - 1;
intptr_t index = name.Hash() % dict_size;
Object& entry = Object::Handle();
entry = dict.At(index);
// An empty spot will be found because we keep the hash set at most 75% full.
while (!entry.IsNull()) {
index = (index + 1) % dict_size;
entry = dict.At(index);
}
// Insert the object at the empty slot.
dict.SetAt(index, obj);
Smi& used = Smi::Handle();
used ^= dict.At(dict_size);
intptr_t used_elements = used.Value() + 1; // One more element added.
used = Smi::New(used_elements);
dict.SetAt(dict_size, used); // Update used count.
// Rehash if symbol_table is 75% full.
if (used_elements > ((dict_size / 4) * 3)) {
GrowDictionary(dict, dict_size);
}
// Invalidate the cache of loaded scripts.
if (loaded_scripts() != Array::null()) {
StorePointer(&raw_ptr()->loaded_scripts_, Array::null());
}
}
// Lookup a name in the library's export namespace.
RawObject* Library::LookupExport(const String& name) const {
if (HasExports()) {
const Array& exports = Array::Handle(this->exports());
Namespace& ns = Namespace::Handle();
Object& obj = Object::Handle();
for (int i = 0; i < exports.Length(); i++) {
ns ^= exports.At(i);
obj = ns.Lookup(name);
if (!obj.IsNull()) {
return obj.raw();
}
}
}
return Object::null();
}
RawObject* Library::LookupEntry(const String& name, intptr_t *index) const {
Isolate* isolate = Isolate::Current();
const Array& dict = Array::Handle(isolate, dictionary());
intptr_t dict_size = dict.Length() - 1;
*index = name.Hash() % dict_size;
Object& entry = Object::Handle(isolate);
String& entry_name = String::Handle(isolate);
entry = dict.At(*index);
// Search the entry in the hash set.
while (!entry.IsNull()) {
entry_name = entry.DictionaryName();
ASSERT(!entry_name.IsNull());
if (entry_name.Equals(name)) {
return entry.raw();
}
*index = (*index + 1) % dict_size;
entry = dict.At(*index);
}
return Object::null();
}
void Library::ReplaceObject(const Object& obj, const String& name) const {
ASSERT(obj.IsClass() || obj.IsFunction() || obj.IsField());
ASSERT(LookupLocalObject(name) != Object::null());
intptr_t index;
LookupEntry(name, &index);
// The value is guaranteed to be found.
const Array& dict = Array::Handle(dictionary());
dict.SetAt(index, obj);
}
void Library::AddClass(const Class& cls) const {
AddObject(cls, String::Handle(cls.Name()));
// Link class to this library.
cls.set_library(*this);
}
RawArray* Library::LoadedScripts() const {
// We compute the list of loaded scripts lazily. The result is
// cached in loaded_scripts_.
if (loaded_scripts() == Array::null()) {
// Iterate over the library dictionary and collect all scripts.
const GrowableObjectArray& scripts =
GrowableObjectArray::Handle(GrowableObjectArray::New(8));
Object& entry = Object::Handle();
Class& cls = Class::Handle();
Script& owner_script = Script::Handle();
DictionaryIterator it(*this);
Script& script_obj = Script::Handle();
while (it.HasNext()) {
entry = it.GetNext();
if (entry.IsClass()) {
owner_script = Class::Cast(entry).script();
} else if (entry.IsFunction()) {
owner_script = Function::Cast(entry).script();
} else if (entry.IsField()) {
cls = Field::Cast(entry).owner();
owner_script = cls.script();
} else {
continue;
}
if (owner_script.IsNull()) {
continue;
}
bool is_unique = true;
for (int i = 0; i < scripts.Length(); i++) {
script_obj ^= scripts.At(i);
if (script_obj.raw() == owner_script.raw()) {
// We already have a reference to this script.
is_unique = false;
break;
}
}
if (is_unique) {
// Add script to the list of scripts.
scripts.Add(owner_script);
}
}
// Create the array of scripts and cache it in loaded_scripts_.
StorePointer(&raw_ptr()->loaded_scripts_, Array::MakeArray(scripts));
}
return loaded_scripts();
}
// TODO(hausner): we might want to add a script dictionary to the
// library class to make this lookup faster.
RawScript* Library::LookupScript(const String& url) const {
const Array& scripts = Array::Handle(LoadedScripts());
Script& script = Script::Handle();
String& script_url = String::Handle();
intptr_t num_scripts = scripts.Length();
for (int i = 0; i < num_scripts; i++) {
script ^= scripts.At(i);
script_url = script.url();
if (script_url.Equals(url)) {
return script.raw();
}
}
return Script::null();
}
RawFunction* Library::LookupFunctionInSource(const String& script_url,
intptr_t line_number) const {
Script& script = Script::Handle(LookupScript(script_url));
if (script.IsNull()) {
// The given script url is not loaded into this library.
return Function::null();
}
// Determine token position at given line number.
intptr_t first_token_pos, last_token_pos;
script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos);
if (first_token_pos < 0) {
// Script does not contain the given line number.
return Function::null();
}
return LookupFunctionInScript(script, first_token_pos);
}
RawFunction* Library::LookupFunctionInScript(const Script& script,
intptr_t token_pos) const {
Class& cls = Class::Handle();
Function& func = Function::Handle();
ClassDictionaryIterator it(*this);
while (it.HasNext()) {
cls = it.GetNextClass();
if (script.raw() == cls.script()) {
func = cls.LookupFunctionAtToken(token_pos);
if (!func.IsNull()) {
return func.raw();
}
}
}
// Look in anonymous classes for toplevel functions.
Array& anon_classes = Array::Handle(this->raw_ptr()->anonymous_classes_);
intptr_t num_anonymous = raw_ptr()->num_anonymous_;
for (int i = 0; i < num_anonymous; i++) {
cls ^= anon_classes.At(i);
ASSERT(!cls.IsNull());
if (script.raw() == cls.script()) {
func = cls.LookupFunctionAtToken(token_pos);
if (!func.IsNull()) {
return func.raw();
}
}
}
return Function::null();
}
RawObject* Library::LookupLocalObject(const String& name) const {
intptr_t index;
return LookupEntry(name, &index);
}
static bool ShouldBePrivate(const String& name) {
return
(name.Length() >= 1 &&
name.CharAt(0) == '_') ||
(name.Length() >= 5 &&
(name.CharAt(4) == '_' &&
(name.CharAt(0) == 'g' || name.CharAt(0) == 's') &&
name.CharAt(1) == 'e' &&
name.CharAt(2) == 't' &&
name.CharAt(3) == ':'));
}
RawField* Library::LookupFieldAllowPrivate(const String& name) const {
// First check if name is found in the local scope of the library.
Field& field = Field::Handle(LookupLocalField(name));
if (!field.IsNull()) {
return field.raw();
}
// Do not look up private names in imported libraries.
if (ShouldBePrivate(name)) {
return Field::null();
}
// Now check if name is found in any imported libs.
const Array& imports = Array::Handle(this->imports());
Namespace& import = Namespace::Handle();
Object& obj = Object::Handle();
for (intptr_t j = 0; j < this->num_imports(); j++) {
import ^= imports.At(j);
obj = import.Lookup(name);
if (!obj.IsNull() && obj.IsField()) {
field ^= obj.raw();
return field.raw();
}
}
return Field::null();
}
RawField* Library::LookupLocalField(const String& name) const {
Isolate* isolate = Isolate::Current();
Field& field = Field::Handle(isolate, Field::null());
Object& obj = Object::Handle(isolate, Object::null());
obj = LookupLocalObject(name);
if (obj.IsNull() && ShouldBePrivate(name)) {
String& private_name = String::Handle(isolate, PrivateName(name));
obj = LookupLocalObject(private_name);
}
if (!obj.IsNull()) {
if (obj.IsField()) {
field ^= obj.raw();
return field.raw();
}
}
// No field found.
return Field::null();
}
RawFunction* Library::LookupFunctionAllowPrivate(const String& name) const {
// First check if name is found in the local scope of the library.
Function& function = Function::Handle(LookupLocalFunction(name));
if (!function.IsNull()) {
return function.raw();
}
// Do not look up private names in imported libraries.
if (ShouldBePrivate(name)) {
return Function::null();
}
// Now check if name is found in any imported libs.
const Array& imports = Array::Handle(this->imports());
Namespace& import = Namespace::Handle();
Object& obj = Object::Handle();
for (intptr_t j = 0; j < this->num_imports(); j++) {
import ^= imports.At(j);
obj = import.Lookup(name);
if (!obj.IsNull() && obj.IsFunction()) {
function ^= obj.raw();
return function.raw();
}
}
return Function::null();
}
RawFunction* Library::LookupLocalFunction(const String& name) const {
Isolate* isolate = Isolate::Current();
Object& obj = Object::Handle(isolate, Object::null());
obj = LookupLocalObject(name);
if (obj.IsNull() && ShouldBePrivate(name)) {
String& private_name = String::Handle(isolate, PrivateName(name));
obj = LookupLocalObject(private_name);
}
if (obj.IsFunction()) {
return Function::Cast(obj).raw();
}
// No function found.
return Function::null();
}
RawObject* Library::LookupObject(const String& name) const {
// First check if name is found in the local scope of the library.
Object& obj = Object::Handle(LookupLocalObject(name));
if (!obj.IsNull()) {
return obj.raw();
}
// Now check if name is found in any imported libs.
const Array& imports = Array::Handle(this->imports());
Namespace& import = Namespace::Handle();
for (intptr_t j = 0; j < this->num_imports(); j++) {
import ^= imports.At(j);
obj = import.Lookup(name);
if (!obj.IsNull()) {
return obj.raw();
}
}
return Object::null();
}
RawClass* Library::LookupClass(const String& name) const {
Object& obj = Object::Handle(LookupObject(name));
if (!obj.IsNull() && obj.IsClass()) {
return Class::CheckedHandle(obj.raw()).raw();
}
return Class::null();
}
RawClass* Library::LookupLocalClass(const String& name) const {
Object& obj = Object::Handle(LookupLocalObject(name));
if (!obj.IsNull() && obj.IsClass()) {
return Class::CheckedHandle(obj.raw()).raw();
}
return Class::null();
}
RawClass* Library::LookupClassAllowPrivate(const String& name) const {
// See if the class is available in this library or in the top level
// scope of any imported library.
Isolate* isolate = Isolate::Current();
const Class& cls = Class::Handle(isolate, LookupClass(name));
if (!cls.IsNull()) {
return cls.raw();
}
// Now try to lookup the class using its private name, but only in
// this library (not in imported libraries).
if (ShouldBePrivate(name)) {
String& private_name = String::Handle(isolate, PrivateName(name));
const Object& obj = Object::Handle(LookupLocalObject(private_name));
if (obj.IsClass()) {
return Class::Cast(obj).raw();
}
}
return Class::null();
}
RawLibraryPrefix* Library::LookupLocalLibraryPrefix(const String& name) const {
const Object& obj = Object::Handle(LookupLocalObject(name));
if (obj.IsLibraryPrefix()) {
return LibraryPrefix::Cast(obj).raw();
}
return LibraryPrefix::null();
}
void Library::AddAnonymousClass(const Class& cls) const {
intptr_t num_anonymous = this->raw_ptr()->num_anonymous_;
Array& anon_array = Array::Handle(this->raw_ptr()->anonymous_classes_);
if (num_anonymous == anon_array.Length()) {
intptr_t new_len = (num_anonymous == 0) ? 4 : num_anonymous * 2;
anon_array = Array::Grow(anon_array, new_len);
StorePointer(&raw_ptr()->anonymous_classes_, anon_array.raw());
}
anon_array.SetAt(num_anonymous, cls);
num_anonymous++;
raw_ptr()->num_anonymous_ = num_anonymous;
}
RawLibrary* Library::ImportLibraryAt(intptr_t index) const {
Namespace& import = Namespace::Handle(ImportAt(index));
if (import.IsNull()) {
return Library::null();
}
return import.library();
}
RawNamespace* Library::ImportAt(intptr_t index) const {
if ((index < 0) || index >= num_imports()) {
return Namespace::null();
}
const Array& import_list = Array::Handle(imports());
Namespace& import = Namespace::Handle();
import ^= import_list.At(index);
return import.raw();
}
bool Library::ImportsCorelib() const {
Isolate* isolate = Isolate::Current();
Library& imported = Library::Handle(isolate);
intptr_t count = num_imports();
for (int i = 0; i < count; i++) {
imported = ImportLibraryAt(i);
if (imported.IsCoreLibrary()) {
return true;
}
}
LibraryPrefix& prefix = LibraryPrefix::Handle(isolate);
LibraryPrefixIterator it(*this);
while (it.HasNext()) {
prefix = it.GetNext();
count = prefix.num_imports();
for (int i = 0; i < count; i++) {
imported = prefix.GetLibrary(i);
if (imported.IsCoreLibrary()) {
return true;
}
}
}
return false;
}
void Library::AddImport(const Namespace& ns) const {
Array& imports = Array::Handle(this->imports());
intptr_t capacity = imports.Length();
if (num_imports() == capacity) {
capacity = capacity + kImportsCapacityIncrement;
imports = Array::Grow(imports, capacity);
StorePointer(&raw_ptr()->imports_, imports.raw());
}
intptr_t index = num_imports();
imports.SetAt(index, ns);
set_num_imports(index + 1);
}
// Convenience function to determine whether the export list is
// non-empty.
bool Library::HasExports() const {
return exports() != Object::empty_array();
}
// We add one namespace at a time to the exports array and don't
// pre-allocate any unused capacity. The assumption is that
// re-exports are quite rare.
void Library::AddExport(const Namespace& ns) const {
Array &exports = Array::Handle(this->exports());
intptr_t num_exports = exports.Length();
exports = Array::Grow(exports, num_exports + 1);
StorePointer(&raw_ptr()->exports_, exports.raw());
exports.SetAt(num_exports, ns);
}
void Library::InitClassDictionary() const {
// The last element of the dictionary specifies the number of in use slots.
// TODO(iposva): Find reasonable initial size.
const int kInitialElementCount = 16;
const Array& dictionary =
Array::Handle(Array::New(kInitialElementCount + 1, Heap::kOld));
dictionary.SetAt(kInitialElementCount, Smi::Handle(Smi::New(0)));
StorePointer(&raw_ptr()->dictionary_, dictionary.raw());
}
void Library::InitImportList() const {
const Array& imports =
Array::Handle(Array::New(kInitialImportsCapacity, Heap::kOld));
StorePointer(&raw_ptr()->imports_, imports.raw());
raw_ptr()->num_imports_ = 0;
}
RawLibrary* Library::New() {
ASSERT(Object::library_class() != Class::null());
RawObject* raw = Object::Allocate(Library::kClassId,
Library::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawLibrary*>(raw);
}
RawLibrary* Library::NewLibraryHelper(const String& url,
bool import_core_lib) {
const Library& result = Library::Handle(Library::New());
result.StorePointer(&result.raw_ptr()->name_, url.raw());
result.StorePointer(&result.raw_ptr()->url_, url.raw());
result.raw_ptr()->private_key_ = Scanner::AllocatePrivateKey(result);
result.raw_ptr()->dictionary_ = Object::empty_array();
result.raw_ptr()->anonymous_classes_ = Object::empty_array();
result.raw_ptr()->num_anonymous_ = 0;
result.raw_ptr()->imports_ = Object::empty_array();
result.raw_ptr()->exports_ = Object::empty_array();
result.raw_ptr()->loaded_scripts_ = Array::null();
result.set_native_entry_resolver(NULL);
result.raw_ptr()->corelib_imported_ = true;
result.set_debuggable(false);
result.raw_ptr()->load_state_ = RawLibrary::kAllocated;
result.raw_ptr()->index_ = -1;
result.InitClassDictionary();
result.InitImportList();
if (import_core_lib) {
const Library& core_lib = Library::Handle(Library::CoreLibrary());
ASSERT(!core_lib.IsNull());
const Namespace& ns = Namespace::Handle(
Namespace::New(core_lib, Array::Handle(), Array::Handle()));
result.AddImport(ns);
}
return result.raw();
}
RawLibrary* Library::New(const String& url) {
return NewLibraryHelper(url, false);
}
void Library::InitCoreLibrary(Isolate* isolate) {
const String& core_lib_url = String::Handle(Symbols::New("dart:core"));
const Library& core_lib =
Library::Handle(Library::NewLibraryHelper(core_lib_url, false));
core_lib.Register();
isolate->object_store()->set_core_library(core_lib);
Library::InitMathLibrary(isolate);
const Library& math_lib = Library::Handle(Library::MathLibrary());
const Namespace& math_ns = Namespace::Handle(
Namespace::New(math_lib, Array::Handle(), Array::Handle()));
Library::InitCollectionLibrary(isolate);
const Library& collection_lib =
Library::Handle(Library::CollectionLibrary());
const Namespace& collection_ns = Namespace::Handle(
Namespace::New(collection_lib, Array::Handle(), Array::Handle()));
core_lib.AddImport(math_ns);
core_lib.AddImport(collection_ns);
isolate->object_store()->set_root_library(Library::Handle());
// Hook up predefined classes without setting their library pointers. These
// classes are coming from the VM isolate, and are shared between multiple
// isolates so setting their library pointers would be wrong.
const Class& cls = Class::Handle(Object::dynamic_class());
core_lib.AddObject(cls, String::Handle(cls.Name()));
}
void Library::InitCollectionLibrary(Isolate* isolate) {
const String& url = String::Handle(Symbols::New("dart:collection"));
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
const Library& math_lib = Library::Handle(Library::MathLibrary());
const Namespace& math_ns = Namespace::Handle(
Namespace::New(math_lib, Array::Handle(), Array::Handle()));
lib.AddImport(math_ns);
isolate->object_store()->set_collection_library(lib);
}
void Library::InitMathLibrary(Isolate* isolate) {
const String& url = String::Handle(Symbols::New("dart:math"));
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
isolate->object_store()->set_math_library(lib);
}
void Library::InitIsolateLibrary(Isolate* isolate) {
const String& url = String::Handle(Symbols::New("dart:isolate"));
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
isolate->object_store()->set_isolate_library(lib);
}
void Library::InitMirrorsLibrary(Isolate* isolate) {
const String& url = String::Handle(Symbols::New("dart:mirrors"));
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
const Library& isolate_lib = Library::Handle(Library::IsolateLibrary());
const Namespace& isolate_ns = Namespace::Handle(
Namespace::New(isolate_lib, Array::Handle(), Array::Handle()));
lib.AddImport(isolate_ns);
const Library& wrappers_lib =
Library::Handle(Library::NativeWrappersLibrary());
const Namespace& wrappers_ns = Namespace::Handle(
Namespace::New(wrappers_lib, Array::Handle(), Array::Handle()));
lib.AddImport(wrappers_ns);
isolate->object_store()->set_mirrors_library(lib);
}
void Library::InitScalarlistLibrary(Isolate* isolate) {
const String& url = String::Handle(Symbols::New("dart:scalarlist"));
const Library& lib = Library::Handle(Library::NewLibraryHelper(url, true));
lib.Register();
const Library& collection_lib =
Library::Handle(Library::CollectionLibrary());
const Namespace& collection_ns = Namespace::Handle(
Namespace::New(collection_lib, Array::Handle(), Array::Handle()));
lib.AddImport(collection_ns);
isolate->object_store()->set_scalarlist_library(lib);
}
void Library::InitNativeWrappersLibrary(Isolate* isolate) {
static const int kNumNativeWrappersClasses = 4;
ASSERT(kNumNativeWrappersClasses > 0 && kNumNativeWrappersClasses < 10);
const String& native_flds_lib_url = String::Handle(
Symbols::New("dart:nativewrappers"));
const Library& native_flds_lib = Library::Handle(
Library::NewLibraryHelper(native_flds_lib_url, false));
native_flds_lib.Register();
isolate->object_store()->set_native_wrappers_library(native_flds_lib);
static const char* const kNativeWrappersClass = "NativeFieldWrapperClass";
static const int kNameLength = 25;
ASSERT(kNameLength == (strlen(kNativeWrappersClass) + 1 + 1));
char name_buffer[kNameLength];
String& cls_name = String::Handle();
for (int fld_cnt = 1; fld_cnt <= kNumNativeWrappersClasses; fld_cnt++) {
OS::SNPrint(name_buffer,
kNameLength,
"%s%d",
kNativeWrappersClass,
fld_cnt);
cls_name = Symbols::New(name_buffer);
Class::NewNativeWrapper(native_flds_lib, cls_name, fld_cnt);
}
}
RawLibrary* Library::LookupLibrary(const String &url) {
Isolate* isolate = Isolate::Current();
Library& lib = Library::Handle(isolate, Library::null());
String& lib_url = String::Handle(isolate, String::null());
GrowableObjectArray& libs = GrowableObjectArray::Handle(
isolate, isolate->object_store()->libraries());
for (int i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
lib_url = lib.url();
if (lib_url.Equals(url)) {
return lib.raw();
}
}
return Library::null();
}
RawError* Library::Patch(const Script& script) const {
ASSERT(script.kind() == RawScript::kPatchTag);
return Compiler::Compile(*this, script);
}
bool Library::IsKeyUsed(intptr_t key) {
intptr_t lib_key;
const GrowableObjectArray& libs = GrowableObjectArray::Handle(
Isolate::Current()->object_store()->libraries());
Library& lib = Library::Handle();
String& lib_url = String::Handle();
for (int i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
lib_url ^= lib.url();
lib_key = lib_url.Hash();
if (lib_key == key) {
return true;
}
}
return false;
}
RawString* Library::PrivateName(const String& name) const {
ASSERT(ShouldBePrivate(name));
// ASSERT(strchr(name, '@') == NULL);
String& str = String::Handle();
str ^= name.raw();
str = String::Concat(str, String::Handle(this->private_key()));
str = Symbols::New(str);
return str.raw();
}
RawLibrary* Library::GetLibrary(intptr_t index) {
Isolate* isolate = Isolate::Current();
const GrowableObjectArray& libs =
GrowableObjectArray::Handle(isolate->object_store()->libraries());
ASSERT(!libs.IsNull());
if ((0 <= index) && (index < libs.Length())) {
Library& lib = Library::Handle();
lib ^= libs.At(index);
return lib.raw();
}
return Library::null();
}
void Library::Register() const {
ASSERT(Library::LookupLibrary(String::Handle(url())) == Library::null());
ObjectStore* object_store = Isolate::Current()->object_store();
GrowableObjectArray& libs =
GrowableObjectArray::Handle(object_store->libraries());
ASSERT(!libs.IsNull());
set_index(libs.Length());
libs.Add(*this);
}
RawLibrary* Library::CoreLibrary() {
return Isolate::Current()->object_store()->core_library();
}
RawLibrary* Library::CollectionLibrary() {
return Isolate::Current()->object_store()->collection_library();
}
RawLibrary* Library::MathLibrary() {
return Isolate::Current()->object_store()->math_library();
}
RawLibrary* Library::IsolateLibrary() {
return Isolate::Current()->object_store()->isolate_library();
}
RawLibrary* Library::MirrorsLibrary() {
return Isolate::Current()->object_store()->mirrors_library();
}
RawLibrary* Library::ScalarlistLibrary() {
return Isolate::Current()->object_store()->scalarlist_library();
}
RawLibrary* Library::NativeWrappersLibrary() {
return Isolate::Current()->object_store()->native_wrappers_library();
}
const char* Library::ToCString() const {
const char* kFormat = "Library:'%s'";
const String& name = String::Handle(url());
intptr_t len = OS::SNPrint(NULL, 0, kFormat, name.ToCString()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, name.ToCString());
return chars;
}
RawLibrary* LibraryPrefix::GetLibrary(int index) const {
if ((index >= 0) || (index < num_imports())) {
const Array& imports = Array::Handle(this->imports());
const Namespace& import = Namespace::CheckedHandle(imports.At(index));
return import.library();
}
return Library::null();
}
bool LibraryPrefix::ContainsLibrary(const Library& library) const {
intptr_t num_current_imports = num_imports();
if (num_current_imports > 0) {
Library& lib = Library::Handle();
const String& url = String::Handle(library.url());
String& lib_url = String::Handle();
for (intptr_t i = 0; i < num_current_imports; i++) {
lib = GetLibrary(i);
ASSERT(!lib.IsNull());
lib_url = lib.url();
if (url.Equals(lib_url)) {
return true;
}
}
}
return false;
}
void LibraryPrefix::AddImport(const Namespace& import) const {
intptr_t num_current_imports = num_imports();
// The library needs to be added to the list.
Array& imports = Array::Handle(this->imports());
const intptr_t length = (imports.IsNull()) ? 0 : imports.Length();
// Grow the list if it is full.
if (num_current_imports >= length) {
const intptr_t new_length = length + kIncrementSize;
imports = Array::Grow(imports, new_length, Heap::kOld);
set_imports(imports);
}
imports.SetAt(num_current_imports, import);
set_num_imports(num_current_imports + 1);
}
RawClass* LibraryPrefix::LookupLocalClass(const String& class_name) const {
Array& imports = Array::Handle(this->imports());
Object& obj = Object::Handle();
Namespace& import = Namespace::Handle();
for (intptr_t i = 0; i < num_imports(); i++) {
import ^= imports.At(i);
obj = import.Lookup(class_name);
if (!obj.IsNull() && obj.IsClass()) {
// TODO(hausner):
return Class::Cast(obj).raw();
}
}
return Class::null();
}
RawLibraryPrefix* LibraryPrefix::New() {
ASSERT(Object::library_prefix_class() != Class::null());
RawObject* raw = Object::Allocate(LibraryPrefix::kClassId,
LibraryPrefix::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawLibraryPrefix*>(raw);
}
RawLibraryPrefix* LibraryPrefix::New(const String& name,
const Namespace& import) {
const LibraryPrefix& result = LibraryPrefix::Handle(LibraryPrefix::New());
result.set_name(name);
result.set_num_imports(0);
result.set_imports(Array::Handle(Array::New(kInitialSize)));
result.AddImport(import);
return result.raw();
}
void LibraryPrefix::set_name(const String& value) const {
ASSERT(value.IsSymbol());
StorePointer(&raw_ptr()->name_, value.raw());
}
void LibraryPrefix::set_imports(const Array& value) const {
StorePointer(&raw_ptr()->imports_, value.raw());
}
void LibraryPrefix::set_num_imports(intptr_t value) const {
raw_ptr()->num_imports_ = value;
}
const char* LibraryPrefix::ToCString() const {
const char* kFormat = "LibraryPrefix:'%s'";
const String& prefix = String::Handle(name());
intptr_t len = OS::SNPrint(NULL, 0, kFormat, prefix.ToCString()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, prefix.ToCString());
return chars;
}
const char* Namespace::ToCString() const {
const char* kFormat = "Namespace for library '%s'";
const Library& lib = Library::Handle(library());
intptr_t len = OS::SNPrint(NULL, 0, kFormat, lib.ToCString()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, lib.ToCString());
return chars;
}
bool Namespace::HidesName(const String& name) const {
// Check whether the name is in the list of explicitly hidden names.
if (hide_names() != Array::null()) {
const Array& names = Array::Handle(hide_names());
String& hidden = String::Handle();
intptr_t num_names = names.Length();
for (intptr_t i = 0; i < num_names; i++) {
hidden ^= names.At(i);
if (name.Equals(hidden)) {
return true;
}
}
}
// The name is not explicitly hidden. Now check whether it is in the
// list of explicitly visible names, if there is one.
if (show_names() != Array::null()) {
const Array& names = Array::Handle(show_names());
String& shown = String::Handle();
intptr_t num_names = names.Length();
for (intptr_t i = 0; i < num_names; i++) {
shown ^= names.At(i);
if (name.Equals(shown)) {
return false;
}
}
// There is a list of visible names. The name we're looking for is not
// contained in the list, so it is hidden.
return true;
}
// The name is not filtered out.
return false;
}
RawObject* Namespace::Lookup(const String& name) const {
const Library& lib = Library::Handle(library());
intptr_t ignore = 0;
// Lookup the name in the library's symbols.
Object& obj = Object::Handle(lib.LookupEntry(name, &ignore));
if (obj.IsNull()) {
// Lookup in the re-exported symbols.
obj = lib.LookupExport(name);
}
if (obj.IsNull() || HidesName(name)) {
return Object::null();
}
return obj.raw();
}
RawNamespace* Namespace::New() {
ASSERT(Object::namespace_class() != Class::null());
RawObject* raw = Object::Allocate(Namespace::kClassId,
Namespace::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawNamespace*>(raw);
}
RawNamespace* Namespace::New(const Library& library,
const Array& show_names,
const Array& hide_names) {
ASSERT(show_names.IsNull() || (show_names.Length() > 0));
ASSERT(hide_names.IsNull() || (hide_names.Length() > 0));
const Namespace& result = Namespace::Handle(Namespace::New());
result.StorePointer(&result.raw_ptr()->library_, library.raw());
result.StorePointer(&result.raw_ptr()->show_names_, show_names.raw());
result.StorePointer(&result.raw_ptr()->hide_names_, hide_names.raw());
return result.raw();
}
RawError* Library::CompileAll() {
Error& error = Error::Handle();
const GrowableObjectArray& libs = GrowableObjectArray::Handle(
Isolate::Current()->object_store()->libraries());
Library& lib = Library::Handle();
Class& cls = Class::Handle();
for (int i = 0; i < libs.Length(); i++) {
lib ^= libs.At(i);
ClassDictionaryIterator it(lib);
while (it.HasNext()) {
cls ^= it.GetNextClass();
error = Compiler::CompileAllFunctions(cls);
if (!error.IsNull()) {
return error.raw();
}
}
Array& anon_classes = Array::Handle(lib.raw_ptr()->anonymous_classes_);
for (int i = 0; i < lib.raw_ptr()->num_anonymous_; i++) {
cls ^= anon_classes.At(i);
error = Compiler::CompileAllFunctions(cls);
if (!error.IsNull()) {
return error.raw();
}
}
}
return error.raw();
}
RawInstructions* Instructions::New(intptr_t size) {
ASSERT(Object::instructions_class() != Class::null());
if (size < 0 || size > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in Instructions::New: invalid size %"Pd"\n", size);
}
Instructions& result = Instructions::Handle();
{
uword aligned_size = Instructions::InstanceSize(size);
RawObject* raw = Object::Allocate(Instructions::kClassId,
aligned_size,
Heap::kCode);
NoGCScope no_gc;
result ^= raw;
result.set_size(size);
}
return result.raw();
}
const char* Instructions::ToCString() const {
return "Instructions";
}
intptr_t PcDescriptors::Length() const {
return Smi::Value(raw_ptr()->length_);
}
void PcDescriptors::SetLength(intptr_t value) const {
// This is only safe because we create a new Smi, which does not cause
// heap allocation.
raw_ptr()->length_ = Smi::New(value);
}
uword PcDescriptors::PC(intptr_t index) const {
return static_cast<uword>(*(EntryAddr(index, kPcEntry)));
}
void PcDescriptors::SetPC(intptr_t index, uword value) const {
*(EntryAddr(index, kPcEntry)) = static_cast<intptr_t>(value);
}
PcDescriptors::Kind PcDescriptors::DescriptorKind(intptr_t index) const {
return static_cast<PcDescriptors::Kind>(*(EntryAddr(index, kKindEntry)));
}
void PcDescriptors::SetKind(intptr_t index, PcDescriptors::Kind value) const {
*(EntryAddr(index, kKindEntry)) = value;
}
intptr_t PcDescriptors::DeoptId(intptr_t index) const {
return *(EntryAddr(index, kDeoptIdEntry));
}
void PcDescriptors::SetDeoptId(intptr_t index, intptr_t value) const {
*(EntryAddr(index, kDeoptIdEntry)) = value;
}
intptr_t PcDescriptors::TokenPos(intptr_t index) const {
return *(EntryAddr(index, kTokenPosEntry));
}
void PcDescriptors::SetTokenPos(intptr_t index, intptr_t value) const {
*(EntryAddr(index, kTokenPosEntry)) = value;
}
intptr_t PcDescriptors::TryIndex(intptr_t index) const {
return *(EntryAddr(index, kTryIndexEntry));
}
void PcDescriptors::SetTryIndex(intptr_t index, intptr_t value) const {
*(EntryAddr(index, kTryIndexEntry)) = value;
}
RawPcDescriptors* PcDescriptors::New(intptr_t num_descriptors) {
ASSERT(Object::pc_descriptors_class() != Class::null());
if (num_descriptors < 0 || num_descriptors > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in PcDescriptors::New: "
"invalid num_descriptors %"Pd"\n", num_descriptors);
}
PcDescriptors& result = PcDescriptors::Handle();
{
uword size = PcDescriptors::InstanceSize(num_descriptors);
RawObject* raw = Object::Allocate(PcDescriptors::kClassId,
size,
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.SetLength(num_descriptors);
}
return result.raw();
}
const char* PcDescriptors::KindAsStr(intptr_t index) const {
switch (DescriptorKind(index)) {
case PcDescriptors::kDeoptBefore: return "deopt-before ";
case PcDescriptors::kDeoptAfter: return "deopt-after ";
case PcDescriptors::kPatchCode: return "patch ";
case PcDescriptors::kLazyDeoptJump: return "lazy-deopt ";
case PcDescriptors::kIcCall: return "ic-call ";
case PcDescriptors::kFuncCall: return "fn-call ";
case PcDescriptors::kReturn: return "return ";
case PcDescriptors::kOther: return "other ";
}
UNREACHABLE();
return "";
}
void PcDescriptors::PrintHeaderString() {
// 4 bits per hex digit + 2 for "0x".
const int addr_width = (kBitsPerWord / 4) + 2;
// "*" in a printf format specifier tells it to read the field width from
// the printf argument list.
OS::Print("%-*s\tkind \tdeopt-id\ttok-ix\ttry-ix\n",
addr_width, "pc");
}
const char* PcDescriptors::ToCString() const {
if (Length() == 0) {
return "No pc descriptors\n";
}
// 4 bits per hex digit.
const int addr_width = kBitsPerWord / 4;
// "*" in a printf format specifier tells it to read the field width from
// the printf argument list.
const char* kFormat =
"%#-*"Px"\t%s\t%"Pd"\t\t%"Pd"\t%"Pd"\n";
// First compute the buffer size required.
intptr_t len = 1; // Trailing '\0'.
for (intptr_t i = 0; i < Length(); i++) {
len += OS::SNPrint(NULL, 0, kFormat, addr_width,
PC(i),
KindAsStr(i),
DeoptId(i),
TokenPos(i),
TryIndex(i));
}
// Allocate the buffer.
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(len);
// Layout the fields in the buffer.
intptr_t index = 0;
for (intptr_t i = 0; i < Length(); i++) {
index += OS::SNPrint((buffer + index), (len - index), kFormat, addr_width,
PC(i),
KindAsStr(i),
DeoptId(i),
TokenPos(i),
TryIndex(i));
}
return buffer;
}
// Verify assumptions (in debug mode only).
// - No two deopt descriptors have the same deoptimization id.
// - No two ic-call descriptors have the same deoptimization id (type feedback).
// A function without unique ids is marked as non-optimizable (e.g., because of
// finally blocks).
void PcDescriptors::Verify(const Function& function) const {
#if defined(DEBUG)
// TODO(srdjan): Implement a more efficient way to check, currently drop
// the check for too large number of descriptors.
if (Length() > 3000) {
if (FLAG_trace_compiler) {
OS::Print("Not checking pc decriptors, length %"Pd"\n", Length());
}
return;
}
// Only check ids for unoptimized code that is optimizable.
if (!function.is_optimizable()) return;
for (intptr_t i = 0; i < Length(); i++) {
PcDescriptors::Kind kind = DescriptorKind(i);
// 'deopt_id' is set for kDeopt and kIcCall and must be unique for one kind.
intptr_t deopt_id = Isolate::kNoDeoptId;
if ((DescriptorKind(i) == PcDescriptors::kDeoptBefore) ||
(DescriptorKind(i) == PcDescriptors::kIcCall)) {
deopt_id = DeoptId(i);
}
for (intptr_t k = i + 1; k < Length(); k++) {
if (kind == DescriptorKind(k)) {
if (deopt_id != Isolate::kNoDeoptId) {
ASSERT(DeoptId(k) != deopt_id);
}
}
}
}
#endif // DEBUG
}
uword PcDescriptors::GetPcForKind(Kind kind) const {
for (intptr_t i = 0; i < Length(); i++) {
if (DescriptorKind(i) == kind) {
return PC(i);
}
}
return 0;
}
void Stackmap::SetCode(const dart::Code& code) const {
StorePointer(&raw_ptr()->code_, code.raw());
}
bool Stackmap::GetBit(intptr_t bit_index) const {
ASSERT(InRange(bit_index));
int byte_index = bit_index >> kBitsPerByteLog2;
int bit_remainder = bit_index & (kBitsPerByte - 1);
uint8_t byte_mask = 1U << bit_remainder;
uint8_t byte = raw_ptr()->data_[byte_index];
return (byte & byte_mask);
}
void Stackmap::SetBit(intptr_t bit_index, bool value) const {
ASSERT(InRange(bit_index));
int byte_index = bit_index >> kBitsPerByteLog2;
int bit_remainder = bit_index & (kBitsPerByte - 1);
uint8_t byte_mask = 1U << bit_remainder;
uint8_t* byte_addr = &(raw_ptr()->data_[byte_index]);
if (value) {
*byte_addr |= byte_mask;
} else {
*byte_addr &= ~byte_mask;
}
}
RawStackmap* Stackmap::New(intptr_t pc_offset,
BitmapBuilder* bmap,
intptr_t register_bit_count) {
ASSERT(Object::stackmap_class() != Class::null());
ASSERT(bmap != NULL);
Stackmap& result = Stackmap::Handle();
// Guard against integer overflow of the instance size computation.
intptr_t length = bmap->Length();
intptr_t payload_size =
Utils::RoundUp(length, kBitsPerByte) / kBitsPerByte;
if ((payload_size < 0) ||
(payload_size >
(kSmiMax - static_cast<intptr_t>(sizeof(RawStackmap))))) {
// This should be caught before we reach here.
FATAL1("Fatal error in Stackmap::New: invalid length %"Pd"\n",
length);
}
{
// Stackmap data objects are associated with a code object, allocate them
// in old generation.
RawObject* raw = Object::Allocate(Stackmap::kClassId,
Stackmap::InstanceSize(length),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.SetLength(length);
}
// When constructing a stackmap we store the pc offset in the stackmap's
// PC. StackmapTableBuilder::FinalizeStackmaps will replace it with the pc
// address.
ASSERT(pc_offset >= 0);
result.SetPC(pc_offset);
for (intptr_t i = 0; i < length; ++i) {
result.SetBit(i, bmap->Get(i));
}
result.SetRegisterBitCount(register_bit_count);
return result.raw();
}
const char* Stackmap::ToCString() const {
if (IsNull()) {
return "{null}";
} else {
const char* kFormat = "%#"Px": ";
intptr_t fixed_length = OS::SNPrint(NULL, 0, kFormat, PC()) + 1;
Isolate* isolate = Isolate::Current();
// Guard against integer overflow in the computation of alloc_size.
//
// TODO(kmillikin): We could just truncate the string if someone
// tries to print a 2 billion plus entry stackmap.
if (Length() > (kIntptrMax - fixed_length)) {
FATAL1("Length() is unexpectedly large (%"Pd")", Length());
}
intptr_t alloc_size = fixed_length + Length();
char* chars = isolate->current_zone()->Alloc<char>(alloc_size);
intptr_t index = OS::SNPrint(chars, alloc_size, kFormat, PC());
for (intptr_t i = 0; i < Length(); i++) {
chars[index++] = IsObject(i) ? '1' : '0';
}
chars[index] = '\0';
return chars;
}
}
RawString* LocalVarDescriptors::GetName(intptr_t var_index) const {
ASSERT(var_index < Length());
const Array& names = Array::Handle(raw_ptr()->names_);
ASSERT(Length() == names.Length());
const String& name = String::CheckedHandle(names.At(var_index));
return name.raw();
}
void LocalVarDescriptors::SetVar(intptr_t var_index,
const String& name,
RawLocalVarDescriptors::VarInfo* info) const {
ASSERT(var_index < Length());
const Array& names = Array::Handle(raw_ptr()->names_);
ASSERT(Length() == names.Length());
names.SetAt(var_index, name);
raw_ptr()->data_[var_index] = *info;
}
void LocalVarDescriptors::GetInfo(intptr_t var_index,
RawLocalVarDescriptors::VarInfo* info) const {
ASSERT(var_index < Length());
*info = raw_ptr()->data_[var_index];
}
const char* LocalVarDescriptors::ToCString() const {
UNIMPLEMENTED();
return "LocalVarDescriptors";
}
RawLocalVarDescriptors* LocalVarDescriptors::New(intptr_t num_variables) {
ASSERT(Object::var_descriptors_class() != Class::null());
if (num_variables < 0 || num_variables > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in LocalVarDescriptors::New: "
"invalid num_variables %"Pd"\n", num_variables);
}
LocalVarDescriptors& result = LocalVarDescriptors::Handle();
{
uword size = LocalVarDescriptors::InstanceSize(num_variables);
RawObject* raw = Object::Allocate(LocalVarDescriptors::kClassId,
size,
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.raw_ptr()->length_ = num_variables;
}
const Array& names = Array::Handle(Array::New(num_variables, Heap::kOld));
result.raw_ptr()->names_ = names.raw();
return result.raw();
}
intptr_t LocalVarDescriptors::Length() const {
return raw_ptr()->length_;
}
intptr_t ExceptionHandlers::Length() const {
return Smi::Value(raw_ptr()->length_);
}
void ExceptionHandlers::SetLength(intptr_t value) const {
// This is only safe because we create a new Smi, which does not cause
// heap allocation.
raw_ptr()->length_ = Smi::New(value);
}
intptr_t ExceptionHandlers::TryIndex(intptr_t index) const {
return *(EntryAddr(index, kTryIndexEntry));
}
void ExceptionHandlers::SetTryIndex(intptr_t index, intptr_t value) const {
*(EntryAddr(index, kTryIndexEntry)) = value;
}
intptr_t ExceptionHandlers::HandlerPC(intptr_t index) const {
return *(EntryAddr(index, kHandlerPcEntry));
}
void ExceptionHandlers::SetHandlerPC(intptr_t index,
intptr_t value) const {
*(EntryAddr(index, kHandlerPcEntry)) = value;
}
RawExceptionHandlers* ExceptionHandlers::New(intptr_t num_handlers) {
ASSERT(Object::exception_handlers_class() != Class::null());
if (num_handlers < 0 || num_handlers > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ExceptionHandlers::New: "
"invalid num_handlers %"Pd"\n",
num_handlers);
}
ExceptionHandlers& result = ExceptionHandlers::Handle();
{
uword size = ExceptionHandlers::InstanceSize(num_handlers);
RawObject* raw = Object::Allocate(ExceptionHandlers::kClassId,
size,
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.SetLength(num_handlers);
}
return result.raw();
}
const char* ExceptionHandlers::ToCString() const {
if (Length() == 0) {
return "No exception handlers\n";
}
// First compute the buffer size required.
const char* kFormat = "%"Pd" => %#"Px"\n";
intptr_t len = 1; // Trailing '\0'.
for (intptr_t i = 0; i < Length(); i++) {
len += OS::SNPrint(NULL, 0, kFormat, TryIndex(i), HandlerPC(i));
}
// Allocate the buffer.
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(len);
// Layout the fields in the buffer.
intptr_t index = 0;
for (intptr_t i = 0; i < Length(); i++) {
index += OS::SNPrint((buffer + index),
(len - index),
kFormat,
TryIndex(i),
HandlerPC(i));
}
return buffer;
}
intptr_t DeoptInfo::Length() const {
return Smi::Value(raw_ptr()->length_);
}
intptr_t DeoptInfo::FromIndex(intptr_t index) const {
return *(EntryAddr(index, kFromIndex));
}
intptr_t DeoptInfo::Instruction(intptr_t index) const {
return *(EntryAddr(index, kInstruction));
}
intptr_t DeoptInfo::TranslationLength() const {
intptr_t length = Length();
if (Instruction(length - 1) != DeoptInstr::kSuffix) return length;
// If the last command is a suffix, add in the length of the suffix and
// do not count the suffix command as a translation command.
intptr_t ignored = 0;
intptr_t suffix_length =
DeoptInstr::DecodeSuffix(FromIndex(length - 1), &ignored);
return length + suffix_length - 1;
}
void DeoptInfo::ToInstructions(const Array& table,
GrowableArray<DeoptInstr*>* instructions) const {
ASSERT(instructions->is_empty());
Smi& offset = Smi::Handle();
DeoptInfo& info = DeoptInfo::Handle(raw());
Smi& reason = Smi::Handle();
intptr_t index = 0;
intptr_t length = TranslationLength();
while (index < length) {
intptr_t instruction = info.Instruction(index);
intptr_t from_index = info.FromIndex(index);
if (instruction == DeoptInstr::kSuffix) {
// Suffix instructions cause us to 'jump' to another translation,
// changing info, length and index.
intptr_t info_number = 0;
intptr_t suffix_length =
DeoptInstr::DecodeSuffix(from_index, &info_number);
DeoptTable::GetEntry(table, info_number, &offset, &info, &reason);
length = info.TranslationLength();
index = length - suffix_length;
} else {
instructions->Add(DeoptInstr::Create(instruction, from_index));
++index;
}
}
}
const char* DeoptInfo::ToCString() const {
if (Length() == 0) {
return "No DeoptInfo";
}
// Convert to DeoptInstr.
GrowableArray<DeoptInstr*> deopt_instrs(Length());
for (intptr_t i = 0; i < Length(); i++) {
deopt_instrs.Add(DeoptInstr::Create(Instruction(i), FromIndex(i)));
}
// Compute the buffer size required.
intptr_t len = 1; // Trailing '\0'.
for (intptr_t i = 0; i < Length(); i++) {
len += OS::SNPrint(NULL, 0, "[%s]", deopt_instrs[i]->ToCString());
}
// Allocate the buffer.
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(len);
// Layout the fields in the buffer.
intptr_t index = 0;
for (intptr_t i = 0; i < Length(); i++) {
index += OS::SNPrint((buffer + index),
(len - index),
"[%s]",
deopt_instrs[i]->ToCString());
}
return buffer;
}
RawDeoptInfo* DeoptInfo::New(intptr_t num_commands) {
ASSERT(Object::deopt_info_class() != Class::null());
DeoptInfo& result = DeoptInfo::Handle();
{
uword size = DeoptInfo::InstanceSize(num_commands);
RawObject* raw = Object::Allocate(DeoptInfo::kClassId,
size,
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.SetLength(num_commands);
}
return result.raw();
}
void DeoptInfo::SetLength(intptr_t value) const {
// This is only safe because we create a new Smi, which does not cause
// heap allocation.
raw_ptr()->length_ = Smi::New(value);
}
void DeoptInfo::SetAt(intptr_t index,
intptr_t instr_kind,
intptr_t from_index) const {
*(EntryAddr(index, kInstruction)) = instr_kind;
*(EntryAddr(index, kFromIndex)) = from_index;
}
Code::Comments& Code::Comments::New(intptr_t count) {
Comments* comments;
if (count < 0 || count > (kIntptrMax / kNumberOfEntries)) {
// This should be caught before we reach here.
FATAL1("Fatal error in Code::Comments::New: invalid count %"Pd"\n", count);
}
if (count == 0) {
comments = new Comments(Object::empty_array());
} else {
comments = new Comments(Array::New(count * kNumberOfEntries));
}
return *comments;
}
intptr_t Code::Comments::Length() const {
if (comments_.IsNull()) {
return 0;
}
return comments_.Length() / kNumberOfEntries;
}
intptr_t Code::Comments::PCOffsetAt(intptr_t idx) const {
return Smi::CheckedHandle(
comments_.At(idx * kNumberOfEntries + kPCOffsetEntry)).Value();
}
void Code::Comments::SetPCOffsetAt(intptr_t idx, intptr_t pc) {
comments_.SetAt(idx * kNumberOfEntries + kPCOffsetEntry,
Smi::Handle(Smi::New(pc)));
}
const String& Code::Comments::CommentAt(intptr_t idx) const {
return String::CheckedHandle(
comments_.At(idx * kNumberOfEntries + kCommentEntry));
}
void Code::Comments::SetCommentAt(intptr_t idx, const String& comment) {
comments_.SetAt(idx * kNumberOfEntries + kCommentEntry, comment);
}
Code::Comments::Comments(RawArray* comments)
: comments_(Array::Handle(comments)) {
}
void Code::set_stackmaps(const Array& maps) const {
StorePointer(&raw_ptr()->stackmaps_, maps.raw());
}
void Code::set_deopt_info_array(const Array& array) const {
StorePointer(&raw_ptr()->deopt_info_array_, array.raw());
}
void Code::set_object_table(const Array& array) const {
StorePointer(&raw_ptr()->object_table_, array.raw());
}
void Code::set_static_calls_target_table(const Array& value) const {
StorePointer(&raw_ptr()->static_calls_target_table_, value.raw());
}
RawFunction* Code::GetStaticCallTargetFunctionAt(uword pc) const {
RawObject* raw_code_offset =
reinterpret_cast<RawObject*>(Smi::New(pc - EntryPoint()));
const Array& array =
Array::Handle(raw_ptr()->static_calls_target_table_);
for (intptr_t i = 0; i < array.Length(); i += kSCallTableEntryLength) {
if (array.At(i) == raw_code_offset) {
Function& function = Function::Handle();
function ^= array.At(i + kSCallTableFunctionEntry);
return function.raw();
}
}
return Function::null();
}
void Code::SetStaticCallTargetCodeAt(uword pc, const Code& code) const {
RawObject* raw_code_offset =
reinterpret_cast<RawObject*>(Smi::New(pc - EntryPoint()));
const Array& array =
Array::Handle(raw_ptr()->static_calls_target_table_);
for (intptr_t i = 0; i < array.Length(); i += kSCallTableEntryLength) {
if (array.At(i) == raw_code_offset) {
ASSERT(code.IsNull() ||
(code.function() == array.At(i + kSCallTableFunctionEntry)));
array.SetAt(i + kSCallTableCodeEntry, code);
return;
}
}
UNREACHABLE();
}
const Code::Comments& Code::comments() const {
Comments* comments = new Code::Comments(raw_ptr()->comments_);
return *comments;
}
void Code::set_comments(const Code::Comments& comments) const {
StorePointer(&raw_ptr()->comments_, comments.comments_.raw());
}
RawCode* Code::New(intptr_t pointer_offsets_length) {
if (pointer_offsets_length < 0 || pointer_offsets_length > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in Code::New: invalid pointer_offsets_length %"Pd"\n",
pointer_offsets_length);
}
ASSERT(Object::code_class() != Class::null());
Code& result = Code::Handle();
{
uword size = Code::InstanceSize(pointer_offsets_length);
RawObject* raw = Object::Allocate(Code::kClassId, size, Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.set_pointer_offsets_length(pointer_offsets_length);
result.set_is_optimized(false);
result.set_is_alive(true);
result.set_comments(Comments::New(0));
}
return result.raw();
}
RawCode* Code::FinalizeCode(const char* name,
Assembler* assembler,
bool optimized) {
ASSERT(assembler != NULL);
// Allocate the Instructions object.
Instructions& instrs =
Instructions::ZoneHandle(Instructions::New(assembler->CodeSize()));
// Copy the instructions into the instruction area and apply all fixups.
// Embedded pointers are still in handles at this point.
MemoryRegion region(reinterpret_cast<void*>(instrs.EntryPoint()),
instrs.size());
assembler->FinalizeInstructions(region);
CodeObservers::NotifyAll(name,
instrs.EntryPoint(),
assembler->prologue_offset(),
instrs.size(),
optimized);
const ZoneGrowableArray<int>& pointer_offsets =
assembler->GetPointerOffsets();
// Allocate the code object.
Code& code = Code::ZoneHandle(Code::New(pointer_offsets.length()));
{
NoGCScope no_gc;
// Set pointer offsets list in Code object and resolve all handles in
// the instruction stream to raw objects.
ASSERT(code.pointer_offsets_length() == pointer_offsets.length());
for (int i = 0; i < pointer_offsets.length(); i++) {
int offset_in_instrs = pointer_offsets[i];
code.SetPointerOffsetAt(i, offset_in_instrs);
const Object* object = region.Load<const Object*>(offset_in_instrs);
region.Store<RawObject*>(offset_in_instrs, object->raw());
}
// Hook up Code and Instruction objects.
instrs.set_code(code.raw());
code.set_instructions(instrs.raw());
}
return code.raw();
}
RawCode* Code::FinalizeCode(const Function& function,
Assembler* assembler,
bool optimized) {
// Calling ToFullyQualifiedCString is very expensive, try to avoid it.
if (CodeObservers::AreActive()) {
return FinalizeCode(function.ToFullyQualifiedCString(),
assembler,
optimized);
} else {
return FinalizeCode("", assembler);
}
}
// Check if object matches find condition.
bool Code::FindRawCodeVisitor::FindObject(RawObject* obj) {
return RawInstructions::ContainsPC(obj, pc_);
}
RawCode* Code::LookupCode(uword pc) {
Isolate* isolate = Isolate::Current();
NoGCScope no_gc;
FindRawCodeVisitor visitor(pc);
RawInstructions* instr;
instr = isolate->heap()->FindObjectInCodeSpace(&visitor);
if (instr != Instructions::null()) {
return instr->ptr()->code_;
}
return Code::null();
}
intptr_t Code::GetTokenIndexOfPC(uword pc) const {
intptr_t token_pos = -1;
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
for (intptr_t i = 0; i < descriptors.Length(); i++) {
if (descriptors.PC(i) == pc) {
token_pos = descriptors.TokenPos(i);
break;
}
}
return token_pos;
}
uword Code::GetPcForDeoptId(intptr_t deopt_id, PcDescriptors::Kind kind) const {
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
for (intptr_t i = 0; i < descriptors.Length(); i++) {
if ((descriptors.DeoptId(i) == deopt_id) &&
(descriptors.DescriptorKind(i) == kind)) {
uword pc = descriptors.PC(i);
ASSERT((EntryPoint() < pc) && (pc < (EntryPoint() + Size())));
return pc;
}
}
return 0;
}
uword Code::GetDeoptBeforePcAtDeoptId(intptr_t deopt_id) const {
ASSERT(!is_optimized());
return GetPcForDeoptId(deopt_id, PcDescriptors::kDeoptBefore);
}
uword Code::GetDeoptAfterPcAtDeoptId(intptr_t deopt_id) const {
ASSERT(!is_optimized());
return GetPcForDeoptId(deopt_id, PcDescriptors::kDeoptAfter);
}
const char* Code::ToCString() const {
const char* kFormat = "Code entry:%p";
intptr_t len = OS::SNPrint(NULL, 0, kFormat, EntryPoint()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, EntryPoint());
return chars;
}
uword Code::GetPatchCodePc() const {
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
return descriptors.GetPcForKind(PcDescriptors::kPatchCode);
}
uword Code::GetLazyDeoptPc() const {
const PcDescriptors& descriptors = PcDescriptors::Handle(pc_descriptors());
return descriptors.GetPcForKind(PcDescriptors::kLazyDeoptJump);
}
bool Code::ObjectExistsInArea(intptr_t start_offset,
intptr_t end_offset) const {
for (intptr_t i = 0; i < this->pointer_offsets_length(); i++) {
const intptr_t offset = this->GetPointerOffsetAt(i);
if ((start_offset <= offset) && (offset < end_offset)) {
return false;
}
}
return true;
}
intptr_t Code::ExtractIcDataArraysAtCalls(
GrowableArray<intptr_t>* node_ids,
const GrowableObjectArray& ic_data_objs) const {
ASSERT(node_ids != NULL);
ASSERT(!ic_data_objs.IsNull());
const PcDescriptors& descriptors =
PcDescriptors::Handle(this->pc_descriptors());
ICData& ic_data_obj = ICData::Handle();
intptr_t max_id = -1;
for (intptr_t i = 0; i < descriptors.Length(); i++) {
if (descriptors.DescriptorKind(i) == PcDescriptors::kIcCall) {
intptr_t deopt_id = descriptors.DeoptId(i);
if (deopt_id > max_id) {
max_id = deopt_id;
}
node_ids->Add(deopt_id);
CodePatcher::GetInstanceCallAt(descriptors.PC(i), &ic_data_obj, NULL);
ic_data_objs.Add(ic_data_obj);
}
}
return max_id;
}
RawArray* Code::ExtractTypeFeedbackArray() const {
ASSERT(!IsNull() && !is_optimized());
GrowableArray<intptr_t> deopt_ids;
const GrowableObjectArray& ic_data_objs =
GrowableObjectArray::Handle(GrowableObjectArray::New());
const intptr_t max_id =
ExtractIcDataArraysAtCalls(&deopt_ids, ic_data_objs);
const Array& result = Array::Handle(Array::New(max_id + 1));
for (intptr_t i = 0; i < deopt_ids.length(); i++) {
intptr_t result_index = deopt_ids[i];
ASSERT(result.At(result_index) == Object::null());
result.SetAt(result_index, Object::Handle(ic_data_objs.At(i)));
}
return result.raw();
}
void Code::ExtractUncalledStaticCallDeoptIds(
GrowableArray<intptr_t>* deopt_ids) const {
ASSERT(!IsNull() && !is_optimized());
ASSERT(deopt_ids != NULL);
deopt_ids->Clear();
const PcDescriptors& descriptors =
PcDescriptors::Handle(this->pc_descriptors());
for (intptr_t i = 0; i < descriptors.Length(); i++) {
if (descriptors.DescriptorKind(i) == PcDescriptors::kFuncCall) {
// Static call.
const uword target_addr =
CodePatcher::GetStaticCallTargetAt(descriptors.PC(i));
if (target_addr == StubCode::CallStaticFunctionEntryPoint()) {
deopt_ids->Add(descriptors.DeoptId(i));
}
}
}
}
RawStackmap* Code::GetStackmap(uword pc, Array* maps, Stackmap* map) const {
// This code is used during iterating frames during a GC and hence it
// should not in turn start a GC.
NoGCScope no_gc;
if (stackmaps() == Array::null()) {
// No stack maps are present in the code object which means this
// frame relies on tagged pointers.
return Stackmap::null();
}
// A stack map is present in the code object, use the stack map to visit
// frame slots which are marked as having objects.
*maps = stackmaps();
*map = Stackmap::null();
for (intptr_t i = 0; i < maps->Length(); i++) {
*map ^= maps->At(i);
ASSERT(!map->IsNull());
if (map->PC() == pc) {
return map->raw(); // We found a stack map for this frame.
}
}
// If the code has stackmaps, it must have them for all safepoints.
UNREACHABLE();
return Stackmap::null();
}
RawContext* Context::New(intptr_t num_variables, Heap::Space space) {
ASSERT(num_variables >= 0);
ASSERT(Object::context_class() != Class::null());
if (num_variables < 0 || num_variables > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in Context::New: invalid num_variables %"Pd"\n",
num_variables);
}
Context& result = Context::Handle();
{
RawObject* raw = Object::Allocate(Context::kClassId,
Context::InstanceSize(num_variables),
space);
NoGCScope no_gc;
result ^= raw;
result.set_num_variables(num_variables);
}
result.set_isolate(Isolate::Current());
return result.raw();
}
const char* Context::ToCString() const {
return "Context";
}
RawContextScope* ContextScope::New(intptr_t num_variables) {
ASSERT(Object::context_scope_class() != Class::null());
if (num_variables < 0 || num_variables > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ContextScope::New: invalid num_variables %"Pd"\n",
num_variables);
}
intptr_t size = ContextScope::InstanceSize(num_variables);
ContextScope& result = ContextScope::Handle();
{
RawObject* raw = Object::Allocate(ContextScope::kClassId,
size,
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
result.set_num_variables(num_variables);
}
return result.raw();
}
intptr_t ContextScope::TokenIndexAt(intptr_t scope_index) const {
return Smi::Value(VariableDescAddr(scope_index)->token_pos);
}
void ContextScope::SetTokenIndexAt(intptr_t scope_index,
intptr_t token_pos) const {
VariableDescAddr(scope_index)->token_pos = Smi::New(token_pos);
}
RawString* ContextScope::NameAt(intptr_t scope_index) const {
return VariableDescAddr(scope_index)->name;
}
void ContextScope::SetNameAt(intptr_t scope_index, const String& name) const {
StorePointer(&(VariableDescAddr(scope_index)->name), name.raw());
}
bool ContextScope::IsFinalAt(intptr_t scope_index) const {
return Bool::Handle(VariableDescAddr(scope_index)->is_final).value();
}
void ContextScope::SetIsFinalAt(intptr_t scope_index, bool is_final) const {
VariableDescAddr(scope_index)->is_final = Bool::Get(is_final);
}
bool ContextScope::IsConstAt(intptr_t scope_index) const {
return Bool::Handle(VariableDescAddr(scope_index)->is_const).value();
}
void ContextScope::SetIsConstAt(intptr_t scope_index, bool is_const) const {
VariableDescAddr(scope_index)->is_const = Bool::Get(is_const);
}
RawAbstractType* ContextScope::TypeAt(intptr_t scope_index) const {
ASSERT(!IsConstAt(scope_index));
return VariableDescAddr(scope_index)->type;
}
void ContextScope::SetTypeAt(
intptr_t scope_index, const AbstractType& type) const {
StorePointer(&(VariableDescAddr(scope_index)->type), type.raw());
}
RawInstance* ContextScope::ConstValueAt(intptr_t scope_index) const {
ASSERT(IsConstAt(scope_index));
return VariableDescAddr(scope_index)->value;
}
void ContextScope::SetConstValueAt(
intptr_t scope_index, const Instance& value) const {
ASSERT(IsConstAt(scope_index));
StorePointer(&(VariableDescAddr(scope_index)->value), value.raw());
}
intptr_t ContextScope::ContextIndexAt(intptr_t scope_index) const {
return Smi::Value(VariableDescAddr(scope_index)->context_index);
}
void ContextScope::SetContextIndexAt(intptr_t scope_index,
intptr_t context_index) const {
VariableDescAddr(scope_index)->context_index = Smi::New(context_index);
}
intptr_t ContextScope::ContextLevelAt(intptr_t scope_index) const {
return Smi::Value(VariableDescAddr(scope_index)->context_level);
}
void ContextScope::SetContextLevelAt(intptr_t scope_index,
intptr_t context_level) const {
VariableDescAddr(scope_index)->context_level = Smi::New(context_level);
}
const char* ContextScope::ToCString() const {
return "ContextScope";
}
const char* ICData::ToCString() const {
const char* kFormat = "ICData target:'%s' num-checks: %"Pd"";
const String& name = String::Handle(target_name());
const intptr_t num = NumberOfChecks();
intptr_t len = OS::SNPrint(NULL, 0, kFormat, name.ToCString(), num) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, name.ToCString(), num);
return chars;
}
void ICData::set_function(const Function& value) const {
StorePointer(&raw_ptr()->function_, value.raw());
}
void ICData::set_target_name(const String& value) const {
StorePointer(&raw_ptr()->target_name_, value.raw());
}
void ICData::set_deopt_id(intptr_t value) const {
raw_ptr()->deopt_id_ = value;
}
void ICData::set_num_args_tested(intptr_t value) const {
raw_ptr()->num_args_tested_ = value;
}
void ICData::set_ic_data(const Array& value) const {
StorePointer(&raw_ptr()->ic_data_, value.raw());
}
void ICData::set_deopt_reason(intptr_t deopt_reason) const {
raw_ptr()->deopt_reason_ = deopt_reason;
}
void ICData::set_is_closure_call(bool value) const {
raw_ptr()->is_closure_call_ = value ? 1 : 0;
}
intptr_t ICData::TestEntryLengthFor(intptr_t num_args) {
return num_args + 1 /* target function*/ + 1 /* frequency */;
}
intptr_t ICData::TestEntryLength() const {
return TestEntryLengthFor(num_args_tested());
}
intptr_t ICData::NumberOfChecks() const {
// Do not count the sentinel;
return (Array::Handle(ic_data()).Length() / TestEntryLength()) - 1;
}
void ICData::WriteSentinel() const {
const Smi& sentinel_value = Smi::Handle(Smi::New(kIllegalCid));
const Array& data = Array::Handle(ic_data());
for (intptr_t i = 1; i <= TestEntryLength(); i++) {
data.SetAt(data.Length() - i, sentinel_value);
}
}
#if defined(DEBUG)
// Used in asserts to verify that a check is not added twice.
bool ICData::HasCheck(const GrowableArray<intptr_t>& cids) const {
const intptr_t len = NumberOfChecks();
for (intptr_t i = 0; i < len; i++) {
GrowableArray<intptr_t> class_ids;
Function& target = Function::Handle();
GetCheckAt(i, &class_ids, &target);
bool matches = true;
for (intptr_t k = 0; k < class_ids.length(); k++) {
if (class_ids[k] != cids[k]) {
matches = false;
break;
}
}
if (matches) {
return true;
}
}
return false;
}
#endif // DEBUG
void ICData::AddCheck(const GrowableArray<intptr_t>& class_ids,
const Function& target) const {
DEBUG_ASSERT(!HasCheck(class_ids));
ASSERT(num_args_tested() > 1); // Otherwise use 'AddReceiverCheck'.
ASSERT(class_ids.length() == num_args_tested());
const intptr_t old_num = NumberOfChecks();
Array& data = Array::Handle(ic_data());
const intptr_t new_len = data.Length() + TestEntryLength();
data = Array::Grow(data, new_len, Heap::kOld);
set_ic_data(data);
WriteSentinel();
intptr_t data_pos = old_num * TestEntryLength();
for (intptr_t i = 0; i < class_ids.length(); i++) {
// kIllegalCid is used as terminating value, do not add it.
ASSERT(class_ids[i] != kIllegalCid);
data.SetAt(data_pos++, Smi::Handle(Smi::New(class_ids[i])));
}
ASSERT(!target.IsNull());
data.SetAt(data_pos++, target);
data.SetAt(data_pos, Smi::Handle(Smi::New(1)));
}
void ICData::AddReceiverCheck(intptr_t receiver_class_id,
const Function& target) const {
#if defined(DEBUG)
GrowableArray<intptr_t> class_ids(1);
class_ids.Add(receiver_class_id);
ASSERT(!HasCheck(class_ids));
#endif // DEBUG
ASSERT(num_args_tested() == 1); // Otherwise use 'AddCheck'.
ASSERT(receiver_class_id != kIllegalCid);
ASSERT(!target.IsNull());
const intptr_t old_num = NumberOfChecks();
Array& data = Array::Handle(ic_data());
const intptr_t new_len = data.Length() + TestEntryLength();
data = Array::Grow(data, new_len, Heap::kOld);
set_ic_data(data);
WriteSentinel();
intptr_t data_pos = old_num * TestEntryLength();
if ((receiver_class_id == kSmiCid) && (data_pos > 0)) {
ASSERT(GetReceiverClassIdAt(0) != kSmiCid);
// Move class occupying position 0 to the data_pos.
for (intptr_t i = 0; i < TestEntryLength(); i++) {
data.SetAt(data_pos + i, Object::Handle(data.At(i)));
}
// Insert kSmiCid in position 0.
data_pos = 0;
}
data.SetAt(data_pos, Smi::Handle(Smi::New(receiver_class_id)));
data.SetAt(data_pos + 1, target);
data.SetAt(data_pos + 2, Smi::Handle(Smi::New(1)));
}
void ICData::GetCheckAt(intptr_t index,
GrowableArray<intptr_t>* class_ids,
Function* target) const {
ASSERT(index < NumberOfChecks());
ASSERT(class_ids != NULL);
ASSERT(target != NULL);
class_ids->Clear();
const Array& data = Array::Handle(ic_data());
intptr_t data_pos = index * TestEntryLength();
Smi& smi = Smi::Handle();
for (intptr_t i = 0; i < num_args_tested(); i++) {
smi ^= data.At(data_pos++);
class_ids->Add(smi.Value());
}
(*target) ^= data.At(data_pos++);
}
void ICData::GetOneClassCheckAt(intptr_t index,
intptr_t* class_id,
Function* target) const {
ASSERT(class_id != NULL);
ASSERT(target != NULL);
ASSERT(num_args_tested() == 1);
const Array& data = Array::Handle(ic_data());
intptr_t data_pos = index * TestEntryLength();
Smi& smi = Smi::Handle();
smi ^= data.At(data_pos);
*class_id = smi.Value();
*target ^= data.At(data_pos + 1);
}
intptr_t ICData::GetClassIdAt(intptr_t index, intptr_t arg_nr) const {
GrowableArray<intptr_t> class_ids;
Function& target = Function::Handle();
GetCheckAt(index, &class_ids, &target);
return class_ids[arg_nr];
}
intptr_t ICData::GetReceiverClassIdAt(intptr_t index) const {
ASSERT(index < NumberOfChecks());
const Array& data = Array::Handle(ic_data());
const intptr_t data_pos = index * TestEntryLength();
Smi& smi = Smi::Handle();
smi ^= data.At(data_pos);
return smi.Value();
}
RawFunction* ICData::GetTargetAt(intptr_t index) const {
const Array& data = Array::Handle(ic_data());
const intptr_t data_pos = index * TestEntryLength() + num_args_tested();
ASSERT(Object::Handle(data.At(data_pos)).IsFunction());
return reinterpret_cast<RawFunction*>(data.At(data_pos));
}
intptr_t ICData::GetCountAt(intptr_t index) const {
const Array& data = Array::Handle(ic_data());
const intptr_t data_pos = index * TestEntryLength() +
CountIndexFor(num_args_tested());
Smi& smi = Smi::Handle();
smi ^= data.At(data_pos);
return smi.Value();
}
RawFunction* ICData::GetTargetForReceiverClassId(intptr_t class_id) const {
const intptr_t len = NumberOfChecks();
for (intptr_t i = 0; i < len; i++) {
if (GetReceiverClassIdAt(i) == class_id) {
return GetTargetAt(i);
}
}
return Function::null();
}
RawICData* ICData::AsUnaryClassChecksForArgNr(intptr_t arg_nr) const {
ASSERT(!IsNull());
ASSERT(num_args_tested() > arg_nr);
if ((arg_nr == 0) && (num_args_tested() == 1)) {
// Frequent case.
return raw();
}
const intptr_t kNumArgsTested = 1;
ICData& result = ICData::Handle(ICData::New(
Function::Handle(function()),
String::Handle(target_name()),
deopt_id(),
kNumArgsTested));
const intptr_t len = NumberOfChecks();
for (intptr_t i = 0; i < len; i++) {
const intptr_t class_id = GetClassIdAt(i, arg_nr);
intptr_t duplicate_class_id = -1;
const intptr_t result_len = result.NumberOfChecks();
for (intptr_t k = 0; k < result_len; k++) {
if (class_id == result.GetReceiverClassIdAt(k)) {
duplicate_class_id = k;
break;
}
}
if (duplicate_class_id >= 0) {
// This check is valid only when checking the receiver.
ASSERT((arg_nr != 0) ||
(result.GetTargetAt(duplicate_class_id) == GetTargetAt(i)));
} else {
// This will make sure that Smi is first if it exists.
result.AddReceiverCheck(class_id,
Function::Handle(GetTargetAt(i)));
}
}
return result.raw();
}
bool ICData::AllTargetsHaveSameOwner(intptr_t owner_cid) const {
if (NumberOfChecks() == 0) return false;
Class& cls = Class::Handle();
const intptr_t len = NumberOfChecks();
for (intptr_t i = 0; i < len; i++) {
cls = Function::Handle(GetTargetAt(i)).Owner();
if (cls.id() != owner_cid) {
return false;
}
}
return true;
}
bool ICData::AllReceiversAreNumbers() const {
if (NumberOfChecks() == 0) return false;
Class& cls = Class::Handle();
const intptr_t len = NumberOfChecks();
for (intptr_t i = 0; i < len; i++) {
cls = Function::Handle(GetTargetAt(i)).Owner();
const intptr_t cid = cls.id();
if ((cid != kSmiCid) &&
(cid != kMintCid) &&
(cid != kBigintCid) &&
(cid != kDoubleCid)) {
return false;
}
}
return true;
}
// Returns true if all targets are the same.
// TODO(srdjan): if targets are native use their C_function to compare.
bool ICData::HasOneTarget() const {
ASSERT(NumberOfChecks() > 0);
const Function& first_target = Function::Handle(GetTargetAt(0));
const intptr_t len = NumberOfChecks();
for (intptr_t i = 1; i < len; i++) {
if (GetTargetAt(i) != first_target.raw()) {
return false;
}
}
return true;
}
RawICData* ICData::New(const Function& function,
const String& target_name,
intptr_t deopt_id,
intptr_t num_args_tested) {
ASSERT(Object::icdata_class() != Class::null());
ASSERT(num_args_tested > 0);
ICData& result = ICData::Handle();
{
// IC data objects are long living objects, allocate them in old generation.
RawObject* raw = Object::Allocate(ICData::kClassId,
ICData::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
result.set_function(function);
result.set_target_name(target_name);
result.set_deopt_id(deopt_id);
result.set_num_args_tested(num_args_tested);
result.set_deopt_reason(kDeoptUnknown);
result.set_is_closure_call(false);
// Number of array elements in one test entry.
intptr_t len = result.TestEntryLength();
// IC data array must be null terminated (sentinel entry).
const Array& ic_data = Array::Handle(Array::New(len, Heap::kOld));
result.set_ic_data(ic_data);
result.WriteSentinel();
return result.raw();
}
RawSubtypeTestCache* SubtypeTestCache::New() {
ASSERT(Object::subtypetestcache_class() != Class::null());
SubtypeTestCache& result = SubtypeTestCache::Handle();
{
// SubtypeTestCache objects are long living objects, allocate them in the
// old generation.
RawObject* raw = Object::Allocate(SubtypeTestCache::kClassId,
SubtypeTestCache::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
const Array& cache = Array::Handle(Array::New(kTestEntryLength));
result.set_cache(cache);
return result.raw();
}
void SubtypeTestCache::set_cache(const Array& value) const {
StorePointer(&raw_ptr()->cache_, value.raw());
}
intptr_t SubtypeTestCache::NumberOfChecks() const {
// Do not count the sentinel;
return (Array::Handle(cache()).Length() / kTestEntryLength) - 1;
}
void SubtypeTestCache::AddCheck(
intptr_t instance_class_id,
const AbstractTypeArguments& instance_type_arguments,
const AbstractTypeArguments& instantiator_type_arguments,
const Bool& test_result) const {
intptr_t old_num = NumberOfChecks();
Array& data = Array::Handle(cache());
intptr_t new_len = data.Length() + kTestEntryLength;
data = Array::Grow(data, new_len);
set_cache(data);
intptr_t data_pos = old_num * kTestEntryLength;
data.SetAt(data_pos + kInstanceClassId,
Smi::Handle(Smi::New(instance_class_id)));
data.SetAt(data_pos + kInstanceTypeArguments, instance_type_arguments);
data.SetAt(data_pos + kInstantiatorTypeArguments,
instantiator_type_arguments);
data.SetAt(data_pos + kTestResult, test_result);
}
void SubtypeTestCache::GetCheck(
intptr_t ix,
intptr_t* instance_class_id,
AbstractTypeArguments* instance_type_arguments,
AbstractTypeArguments* instantiator_type_arguments,
Bool* test_result) const {
Array& data = Array::Handle(cache());
intptr_t data_pos = ix * kTestEntryLength;
Smi& instance_class_id_handle = Smi::Handle();
instance_class_id_handle ^= data.At(data_pos + kInstanceClassId);
*instance_class_id = instance_class_id_handle.Value();
*instance_type_arguments ^= data.At(data_pos + kInstanceTypeArguments);
*instantiator_type_arguments ^=
data.At(data_pos + kInstantiatorTypeArguments);
*test_result ^= data.At(data_pos + kTestResult);
}
const char* SubtypeTestCache::ToCString() const {
return "SubtypeTestCache";
}
const char* Error::ToErrorCString() const {
UNREACHABLE();
return "Internal Error";
}
const char* Error::ToCString() const {
// Error is an abstract class. We should never reach here.
UNREACHABLE();
return "Error";
}
RawApiError* ApiError::New() {
ASSERT(Object::api_error_class() != Class::null());
RawObject* raw = Object::Allocate(ApiError::kClassId,
ApiError::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawApiError*>(raw);
}
RawApiError* ApiError::New(const String& message, Heap::Space space) {
ASSERT(Object::api_error_class() != Class::null());
ApiError& result = ApiError::Handle();
{
RawObject* raw = Object::Allocate(ApiError::kClassId,
ApiError::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_message(message);
return result.raw();
}
void ApiError::set_message(const String& message) const {
StorePointer(&raw_ptr()->message_, message.raw());
}
const char* ApiError::ToErrorCString() const {
const String& msg_str = String::Handle(message());
return msg_str.ToCString();
}
const char* ApiError::ToCString() const {
return "ApiError";
}
RawLanguageError* LanguageError::New() {
ASSERT(Object::language_error_class() != Class::null());
RawObject* raw = Object::Allocate(LanguageError::kClassId,
LanguageError::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawLanguageError*>(raw);
}
RawLanguageError* LanguageError::New(const String& message, Heap::Space space) {
ASSERT(Object::language_error_class() != Class::null());
LanguageError& result = LanguageError::Handle();
{
RawObject* raw = Object::Allocate(LanguageError::kClassId,
LanguageError::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_message(message);
return result.raw();
}
void LanguageError::set_message(const String& message) const {
StorePointer(&raw_ptr()->message_, message.raw());
}
const char* LanguageError::ToErrorCString() const {
const String& msg_str = String::Handle(message());
return msg_str.ToCString();
}
const char* LanguageError::ToCString() const {
return "LanguageError";
}
RawUnhandledException* UnhandledException::New(const Instance& exception,
const Instance& stacktrace,
Heap::Space space) {
ASSERT(Object::unhandled_exception_class() != Class::null());
UnhandledException& result = UnhandledException::Handle();
{
RawObject* raw = Object::Allocate(UnhandledException::kClassId,
UnhandledException::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_exception(exception);
result.set_stacktrace(stacktrace);
return result.raw();
}
void UnhandledException::set_exception(const Instance& exception) const {
StorePointer(&raw_ptr()->exception_, exception.raw());
}
void UnhandledException::set_stacktrace(const Instance& stacktrace) const {
StorePointer(&raw_ptr()->stacktrace_, stacktrace.raw());
}
const char* UnhandledException::ToErrorCString() const {
Isolate* isolate = Isolate::Current();
HANDLESCOPE(isolate);
Object& strtmp = Object::Handle();
const Instance& exc = Instance::Handle(exception());
strtmp = DartLibraryCalls::ToString(exc);
const char* exc_str =
"<Received error while converting exception to string>";
if (!strtmp.IsError()) {
exc_str = strtmp.ToCString();
}
const Instance& stack = Instance::Handle(stacktrace());
strtmp = DartLibraryCalls::ToString(stack);
const char* stack_str =
"<Received error while converting stack trace to string>";
if (!strtmp.IsError()) {
stack_str = strtmp.ToCString();
}
const char* format = "Unhandled exception:\n%s\n%s";
int len = (strlen(exc_str) + strlen(stack_str) + strlen(format)
- 4 // Two '%s'
+ 1); // '\0'
char* chars = isolate->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, exc_str, stack_str);
return chars;
}
const char* UnhandledException::ToCString() const {
return "UnhandledException";
}
RawUnwindError* UnwindError::New(const String& message, Heap::Space space) {
ASSERT(Object::unwind_error_class() != Class::null());
UnwindError& result = UnwindError::Handle();
{
RawObject* raw = Object::Allocate(UnwindError::kClassId,
UnwindError::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_message(message);
return result.raw();
}
void UnwindError::set_message(const String& message) const {
StorePointer(&raw_ptr()->message_, message.raw());
}
const char* UnwindError::ToErrorCString() const {
const String& msg_str = String::Handle(message());
return msg_str.ToCString();
}
const char* UnwindError::ToCString() const {
return "UnwindError";
}
bool Instance::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
return true; // "===".
}
if (other.IsNull() || (this->clazz() != other.clazz())) {
return false;
}
{
NoGCScope no_gc;
// Raw bits compare.
const intptr_t instance_size = Class::Handle(this->clazz()).instance_size();
ASSERT(instance_size != 0);
uword this_addr = reinterpret_cast<uword>(this->raw_ptr());
uword other_addr = reinterpret_cast<uword>(other.raw_ptr());
for (intptr_t offset = sizeof(RawObject);
offset < instance_size;
offset += kWordSize) {
if ((*reinterpret_cast<RawObject**>(this_addr + offset)) !=
(*reinterpret_cast<RawObject**>(other_addr + offset))) {
return false;
}
}
}
return true;
}
RawInstance* Instance::Canonicalize() const {
ASSERT(!IsNull());
if (this->IsCanonical()) {
return this->raw();
}
Instance& result = Instance::Handle();
const Class& cls = Class::Handle(this->clazz());
Array& constants = Array::Handle(cls.constants());
const intptr_t constants_len = constants.Length();
// Linear search to see whether this value is already present in the
// list of canonicalized constants.
intptr_t index = 0;
while (index < constants_len) {
result ^= constants.At(index);
if (result.IsNull()) {
break;
}
if (this->Equals(result)) {
return result.raw();
}
index++;
}
// The value needs to be added to the list. Grow the list if
// it is full.
result ^= this->raw();
if (result.IsNew()) {
// Create a canonical object in old space.
result ^= Object::Clone(result, Heap::kOld);
}
ASSERT(result.IsOld());
cls.InsertCanonicalConstant(index, result);
result.SetCanonical();
return result.raw();
}
RawType* Instance::GetType() const {
if (IsNull()) {
return Type::NullType();
}
const Class& cls = Class::Handle(clazz());
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle();
if (cls.HasTypeArguments()) {
type_arguments = GetTypeArguments();
}
const Type& type = Type::Handle(
Type::New(cls, type_arguments, Scanner::kDummyTokenIndex));
type.set_is_finalized_instantiated();
return type.raw();
}
RawAbstractTypeArguments* Instance::GetTypeArguments() const {
const Class& cls = Class::Handle(clazz());
intptr_t field_offset = cls.type_arguments_field_offset();
ASSERT(field_offset != Class::kNoTypeArguments);
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle();
type_arguments ^= *FieldAddrAtOffset(field_offset);
return type_arguments.raw();
}
void Instance::SetTypeArguments(const AbstractTypeArguments& value) const {
const Class& cls = Class::Handle(clazz());
intptr_t field_offset = cls.type_arguments_field_offset();
ASSERT(field_offset != Class::kNoTypeArguments);
SetFieldAtOffset(field_offset, value);
}
bool Instance::IsInstanceOf(const AbstractType& other,
const AbstractTypeArguments& other_instantiator,
Error* malformed_error) const {
ASSERT(other.IsFinalized());
ASSERT(!other.IsDynamicType());
ASSERT(!other.IsMalformed());
const Class& cls = Class::Handle(clazz());
if (cls.IsNullClass()) {
if (!IsNull()) {
// We can only encounter Object::sentinel() or
// Object::transition_sentinel() if type checks were not eliminated at
// compile time. Both sentinels are instances of the Null class, but they
// are not the Object::null() instance.
ASSERT((raw() == Object::transition_sentinel()) ||
(raw() == Object::sentinel()));
ASSERT(!FLAG_eliminate_type_checks);
return true; // We are doing an instance of test as part of a type check.
}
// The null instance can be returned from a void function.
if (other.IsVoidType()) {
return true;
}
// Otherwise, null is only an instance of Object and of dynamic.
// It is not necessary to fully instantiate the other type for this test.
Class& other_class = Class::Handle();
if (other.IsTypeParameter()) {
if (other_instantiator.IsNull()) {
return true; // Other type is uninstantiated, i.e. dynamic.
}
const TypeParameter& other_type_param = TypeParameter::Cast(other);
const AbstractType& instantiated_other = AbstractType::Handle(
other_instantiator.TypeAt(other_type_param.index()));
ASSERT(instantiated_other.IsInstantiated());
other_class = instantiated_other.type_class();
} else {
other_class = other.type_class();
}
return other_class.IsObjectClass() || other_class.IsDynamicClass();
}
if (other.IsVoidType()) {
return false;
}
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle();
const intptr_t num_type_arguments = cls.NumTypeArguments();
if (num_type_arguments > 0) {
type_arguments = GetTypeArguments();
if (!type_arguments.IsNull() && !type_arguments.IsCanonical()) {
type_arguments = type_arguments.Canonicalize();
SetTypeArguments(type_arguments);
}
// Verify that the number of type arguments in the instance matches the
// number of type arguments expected by the instance class.
// A discrepancy is allowed for closures, which borrow the type argument
// vector of their instantiator, which may be of a super class of the class
// defining the closure. Truncating the vector to the correct length on
// instantiation is unnecessary. The vector may therefore be longer.
ASSERT(type_arguments.IsNull() ||
(type_arguments.Length() == num_type_arguments) ||
(cls.IsSignatureClass() &&
(type_arguments.Length() > num_type_arguments)));
}
Class& other_class = Class::Handle();
AbstractTypeArguments& other_type_arguments = AbstractTypeArguments::Handle();
// In case 'other' is not instantiated, we could simply call
// other.InstantiateFrom(other_instantiator), however, we can save the
// allocation of a new AbstractType by inlining the code.
if (other.IsTypeParameter()) {
if (other_instantiator.IsNull()) {
// An uninstantiated type parameter is equivalent to dynamic.
return true;
}
const TypeParameter& other_type_param = TypeParameter::Cast(other);
AbstractType& instantiated_other = AbstractType::Handle(
other_instantiator.TypeAt(other_type_param.index()));
if (instantiated_other.IsDynamicType() ||
instantiated_other.IsTypeParameter()) {
return true;
}
other_class = instantiated_other.type_class();
other_type_arguments = instantiated_other.arguments();
} else {
other_class = other.type_class();
other_type_arguments = other.arguments();
if (!other_type_arguments.IsNull() &&
!other_type_arguments.IsInstantiated()) {
other_type_arguments =
other_type_arguments.InstantiateFrom(other_instantiator);
}
}
return cls.IsSubtypeOf(type_arguments, other_class, other_type_arguments,
malformed_error);
}
void Instance::SetNativeField(int index, intptr_t value) const {
ASSERT(IsValidNativeIndex(index));
Object& native_fields = Object::Handle(*NativeFieldsAddr());
if (native_fields.IsNull()) {
// Allocate backing storage for the native fields.
const Class& cls = Class::Handle(clazz());
int num_native_fields = cls.num_native_fields();
native_fields = IntPtrArray::New(num_native_fields);
StorePointer(NativeFieldsAddr(), native_fields.raw());
}
IntPtrArray::Cast(native_fields).SetAt(index, value);
}
bool Instance::IsClosure() const {
const Class& cls = Class::Handle(clazz());
return cls.IsSignatureClass();
}
RawInstance* Instance::New(const Class& cls, Heap::Space space) {
Instance& result = Instance::Handle();
{
intptr_t instance_size = cls.instance_size();
ASSERT(instance_size > 0);
RawObject* raw = Object::Allocate(cls.id(), instance_size, space);
NoGCScope no_gc;
result ^= raw;
}
return result.raw();
}
bool Instance::IsValidFieldOffset(int offset) const {
const Class& cls = Class::Handle(clazz());
return (offset >= 0 && offset <= (cls.instance_size() - kWordSize));
}
const char* Instance::ToCString() const {
if (IsNull()) {
return "null";
} else if (raw() == Object::sentinel()) {
return "sentinel";
} else if (raw() == Object::transition_sentinel()) {
return "transition_sentinel";
} else if (Isolate::Current()->no_gc_scope_depth() > 0) {
// Can occur when running disassembler.
return "Instance";
} else {
if (IsClosure()) {
return Closure::ToCString(*this);
}
const char* kFormat = "Instance of '%s'";
const Class& cls = Class::Handle(clazz());
AbstractTypeArguments& type_arguments = AbstractTypeArguments::Handle();
const intptr_t num_type_arguments = cls.NumTypeArguments();
if (num_type_arguments > 0) {
type_arguments = GetTypeArguments();
}
const Type& type =
Type::Handle(Type::New(cls, type_arguments, Scanner::kDummyTokenIndex));
const String& type_name = String::Handle(type.Name());
// Calculate the size of the string.
intptr_t len = OS::SNPrint(NULL, 0, kFormat, type_name.ToCString()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, type_name.ToCString());
return chars;
}
}
bool AbstractType::IsResolved() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractType::HasResolvedTypeClass() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
RawClass* AbstractType::type_class() const {
// AbstractType is an abstract class.
UNREACHABLE();
return Class::null();
}
RawUnresolvedClass* AbstractType::unresolved_class() const {
// AbstractType is an abstract class.
UNREACHABLE();
return UnresolvedClass::null();
}
RawAbstractTypeArguments* AbstractType::arguments() const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
}
intptr_t AbstractType::token_pos() const {
// AbstractType is an abstract class.
UNREACHABLE();
return -1;
}
bool AbstractType::IsInstantiated() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractType::IsFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractType::IsBeingFinalized() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
bool AbstractType::IsMalformed() const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
RawError* AbstractType::malformed_error() const {
// AbstractType is an abstract class.
UNREACHABLE();
return Error::null();
}
void AbstractType::set_malformed_error(const Error& value) const {
// AbstractType is an abstract class.
UNREACHABLE();
}
bool AbstractType::Equals(const Instance& other) const {
// AbstractType is an abstract class.
UNREACHABLE();
return false;
}
RawAbstractType* AbstractType::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments) const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
}
RawAbstractType* AbstractType::Canonicalize() const {
// AbstractType is an abstract class.
UNREACHABLE();
return NULL;
}
RawString* AbstractType::BuildName(NameVisibility name_visibility) const {
if (IsTypeParameter()) {
return TypeParameter::Cast(*this).name();
}
// If the type is still being finalized, we may be reporting an error about
// a malformed type, so proceed with caution.
const AbstractTypeArguments& args =
AbstractTypeArguments::Handle(arguments());
const intptr_t num_args = args.IsNull() ? 0 : args.Length();
String& class_name = String::Handle();
intptr_t first_type_param_index;
intptr_t num_type_params; // Number of type parameters to print.
if (HasResolvedTypeClass()) {
const Class& cls = Class::Handle(type_class());
num_type_params = cls.NumTypeParameters(); // Do not print the full vector.
if (name_visibility == kInternalName) {
class_name = cls.Name();
} else {
ASSERT(name_visibility == kUserVisibleName);
// Map internal types to their corresponding public interfaces.
class_name = cls.UserVisibleName();
}
if (num_type_params > num_args) {
first_type_param_index = 0;
if (!IsFinalized() || IsBeingFinalized() || IsMalformed()) {
// Most probably a malformed type. Do not fill up with "dynamic",
// but use actual vector.
num_type_params = num_args;
} else {
ASSERT(num_args == 0); // Type is raw.
// No need to fill up with "dynamic".
num_type_params = 0;
}
} else {
first_type_param_index = num_args - num_type_params;
}
if (cls.IsSignatureClass()) {
// We may be reporting an error about a malformed function type. In that
// case, avoid instantiating the signature, since it may lead to cycles.
if (!IsFinalized() || IsBeingFinalized() || IsMalformed()) {
return class_name.raw();
}
// In order to avoid cycles, print the name of a typedef (non-canonical
// signature class) as a regular, possibly parameterized, class.
if (cls.IsCanonicalSignatureClass()) {
const Function& signature_function = Function::Handle(
cls.signature_function());
// Signature classes have no super type.
ASSERT(first_type_param_index == 0);
return signature_function.InstantiatedSignatureFrom(args,
name_visibility);
}
}
} else {
const UnresolvedClass& cls = UnresolvedClass::Handle(unresolved_class());
class_name = cls.Name();
num_type_params = num_args;
first_type_param_index = 0;
}
String& type_name = String::Handle();
if (num_type_params == 0) {
type_name = class_name.raw();
} else {
const String& args_name = String::Handle(
args.SubvectorName(first_type_param_index,
num_type_params,
name_visibility));
type_name = String::Concat(class_name, args_name);
}
// The name is only used for type checking and debugging purposes.
// Unless profiling data shows otherwise, it is not worth caching the name in
// the type.
return Symbols::New(type_name);
}
RawString* AbstractType::ClassName() const {
if (HasResolvedTypeClass()) {
return Class::Handle(type_class()).Name();
} else {
return UnresolvedClass::Handle(unresolved_class()).Name();
}
}
bool AbstractType::IsBoolType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::BoolType()).type_class());
}
bool AbstractType::IsIntType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::IntType()).type_class());
}
bool AbstractType::IsDoubleType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::Double()).type_class());
}
bool AbstractType::IsNumberType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::Number()).type_class());
}
bool AbstractType::IsStringType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::StringType()).type_class());
}
bool AbstractType::IsFunctionType() const {
return HasResolvedTypeClass() &&
(type_class() == Type::Handle(Type::Function()).type_class());
}
bool AbstractType::TypeTest(TypeTestKind test_kind,
const AbstractType& other,
Error* malformed_error) const {
ASSERT(IsFinalized());
ASSERT(other.IsFinalized());
// In case the type checked in a type test is malformed, the code generator
// may compile a throw instead of a run time call performing the type check.
// However, in checked mode, a function type may include malformed result type
// and/or malformed parameter types, which will then be encountered here at
// run time.
if (IsMalformed()) {
ASSERT(FLAG_enable_type_checks);
if ((malformed_error != NULL) && malformed_error->IsNull()) {
*malformed_error = this->malformed_error();
}
return false;
}
if (other.IsMalformed()) {
ASSERT(FLAG_enable_type_checks);
if ((malformed_error != NULL) && malformed_error->IsNull()) {
*malformed_error = other.malformed_error();
}
return false;
}
// AbstractType parameters cannot be handled by Class::TypeTest().
// When comparing two uninstantiated function types, one returning type
// parameter K, the other returning type parameter V, we cannot assume that K
// is a subtype of V, or vice versa. We only return true if K == V, i.e. if
// they have the same index (both are finalized, so their indices are
// comparable).
// The same rule applies When checking the upper bound of a still
// uninstantiated type at compile time. Returning false will defer the test
// to run time. But there are cases where it can be decided at compile time.
// For example, with class A<K, V extends K>, new A<T, T> called from within
// a class B<T> will never require a run time bounds check, even it T is
// uninstantiated at compile time.
if (IsTypeParameter()) {
const TypeParameter& type_param = TypeParameter::Cast(*this);
if (other.IsTypeParameter()) {
const TypeParameter& other_type_param = TypeParameter::Cast(other);
return type_param.index() == other_type_param.index();
} else if (FLAG_enable_type_checks) {
// In checked mode, if the upper bound of this type is more specific than
// the other type, then this type is more specific than the other type.
const AbstractType& type_param_bound =
AbstractType::Handle(type_param.bound());
if (type_param_bound.IsMoreSpecificThan(other, malformed_error)) {
return true;
}
}
return false;
}
if (other.IsTypeParameter()) {
return false;
}
const Class& cls = Class::Handle(type_class());
return cls.TypeTest(test_kind,
AbstractTypeArguments::Handle(arguments()),
Class::Handle(other.type_class()),
AbstractTypeArguments::Handle(other.arguments()),
malformed_error);
}
const char* AbstractType::ToCString() const {
// AbstractType is an abstract class.
UNREACHABLE();
return "AbstractType";
}
RawType* Type::NullType() {
return Isolate::Current()->object_store()->null_type();
}
RawType* Type::DynamicType() {
return Isolate::Current()->object_store()->dynamic_type();
}
RawType* Type::VoidType() {
return Isolate::Current()->object_store()->void_type();
}
RawType* Type::ObjectType() {
return Isolate::Current()->object_store()->object_type();
}
RawType* Type::BoolType() {
return Isolate::Current()->object_store()->bool_type();
}
RawType* Type::IntType() {
return Isolate::Current()->object_store()->int_type();
}
RawType* Type::SmiType() {
return Isolate::Current()->object_store()->smi_type();
}
RawType* Type::MintType() {
return Isolate::Current()->object_store()->mint_type();
}
RawType* Type::Double() {
return Isolate::Current()->object_store()->double_type();
}
RawType* Type::Number() {
return Isolate::Current()->object_store()->number_type();
}
RawType* Type::StringType() {
return Isolate::Current()->object_store()->string_type();
}
RawType* Type::ArrayType() {
return Isolate::Current()->object_store()->array_type();
}
RawType* Type::Function() {
return Isolate::Current()->object_store()->function_type();
}
RawType* Type::NewNonParameterizedType(const Class& type_class) {
ASSERT(!type_class.HasTypeArguments());
const TypeArguments& no_type_arguments = TypeArguments::Handle();
Type& type = Type::Handle();
type ^= Type::New(Object::Handle(type_class.raw()),
no_type_arguments,
Scanner::kDummyTokenIndex);
type.set_is_finalized_instantiated();
type ^= type.Canonicalize();
return type.raw();
}
void Type::set_is_finalized_instantiated() const {
ASSERT(!IsFinalized());
set_type_state(RawType::kFinalizedInstantiated);
}
void Type::set_is_finalized_uninstantiated() const {
ASSERT(!IsFinalized());
set_type_state(RawType::kFinalizedUninstantiated);
}
void Type::set_is_being_finalized() const {
ASSERT(!IsFinalized() && !IsBeingFinalized());
set_type_state(RawType::kBeingFinalized);
}
bool Type::IsMalformed() const {
return raw_ptr()->malformed_error_ != Error::null();
}
void Type::set_malformed_error(const Error& value) const {
StorePointer(&raw_ptr()->malformed_error_, value.raw());
}
RawError* Type::malformed_error() const {
ASSERT(IsMalformed());
return raw_ptr()->malformed_error_;
}
bool Type::IsResolved() const {
if (IsFinalized()) {
return true;
}
if (!HasResolvedTypeClass()) {
return false;
}
const AbstractTypeArguments& args =
AbstractTypeArguments::Handle(arguments());
return args.IsNull() || args.IsResolved();
}
bool Type::HasResolvedTypeClass() const {
const Object& type_class = Object::Handle(raw_ptr()->type_class_);
return !type_class.IsNull() && type_class.IsClass();
}
RawClass* Type::type_class() const {
ASSERT(HasResolvedTypeClass());
Class& type_class = Class::Handle();
type_class ^= raw_ptr()->type_class_;
return type_class.raw();
}
RawUnresolvedClass* Type::unresolved_class() const {
ASSERT(!HasResolvedTypeClass());
UnresolvedClass& unresolved_class = UnresolvedClass::Handle();
unresolved_class ^= raw_ptr()->type_class_;
ASSERT(!unresolved_class.IsNull());
return unresolved_class.raw();
}
RawString* Type::TypeClassName() const {
if (HasResolvedTypeClass()) {
const Class& cls = Class::Handle(type_class());
return cls.Name();
} else {
const UnresolvedClass& cls = UnresolvedClass::Handle(unresolved_class());
return cls.Name();
}
}
RawAbstractTypeArguments* Type::arguments() const {
return raw_ptr()->arguments_;
}
bool Type::IsInstantiated() const {
if (raw_ptr()->type_state_ == RawType::kFinalizedInstantiated) {
return true;
}
if (raw_ptr()->type_state_ == RawType::kFinalizedUninstantiated) {
return false;
}
const AbstractTypeArguments& args =
AbstractTypeArguments::Handle(arguments());
return args.IsNull() || args.IsInstantiated();
}
RawAbstractType* Type::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments) const {
ASSERT(IsFinalized());
ASSERT(!IsInstantiated());
AbstractTypeArguments& type_arguments =
AbstractTypeArguments::Handle(arguments());
type_arguments = type_arguments.InstantiateFrom(instantiator_type_arguments);
const Class& cls = Class::Handle(type_class());
ASSERT(cls.is_finalized());
Type& instantiated_type = Type::Handle(
Type::New(cls, type_arguments, token_pos()));
ASSERT(type_arguments.IsNull() ||
(type_arguments.Length() == cls.NumTypeArguments()));
instantiated_type.set_is_finalized_instantiated();
return instantiated_type.raw();
}
bool Type::Equals(const Instance& other) const {
if (raw() == other.raw()) {
return true;
}
if (!other.IsType()) {
return false;
}
const AbstractType& other_type = AbstractType::Cast(other);
ASSERT(IsFinalized() && other_type.IsFinalized());
if (IsMalformed() || other_type.IsMalformed()) {
return false;
}
if (type_class() != other_type.type_class()) {
return false;
}
return AbstractTypeArguments::AreEqual(
AbstractTypeArguments::Handle(arguments()),
AbstractTypeArguments::Handle(other_type.arguments()));
}
RawAbstractType* Type::Canonicalize() const {
ASSERT(IsFinalized());
if (IsCanonical() || IsMalformed()) {
ASSERT(IsMalformed() || AbstractTypeArguments::Handle(arguments()).IsOld());
return this->raw();
}
const Class& cls = Class::Handle(type_class());
Array& canonical_types = Array::Handle(cls.canonical_types());
if (canonical_types.IsNull()) {
// Types defined in the VM isolate are canonicalized via the object store.
return this->raw();
}
const intptr_t canonical_types_len = canonical_types.Length();
// Linear search to see whether this type is already present in the
// list of canonicalized types.
// TODO(asiva): Try to re-factor this lookup code to make sharing
// easy between the 4 versions of this loop.
Type& type = Type::Handle();
intptr_t index = 0;
while (index < canonical_types_len) {
type ^= canonical_types.At(index);
if (type.IsNull()) {
break;
}
if (!type.IsFinalized()) {
ASSERT((index == 0) && cls.IsSignatureClass());
index++;
continue;
}
if (this->Equals(type)) {
return type.raw();
}
index++;
}
// Canonicalize the type arguments.
AbstractTypeArguments& type_args = AbstractTypeArguments::Handle(arguments());
type_args = type_args.Canonicalize();
set_arguments(type_args);
// The type needs to be added to the list. Grow the list if it is full.
if (index == canonical_types_len) {
const intptr_t kLengthIncrement = 2; // Raw and parameterized.
const intptr_t new_length = canonical_types.Length() + kLengthIncrement;
const Array& new_canonical_types =
Array::Handle(Array::Grow(canonical_types, new_length, Heap::kOld));
cls.set_canonical_types(new_canonical_types);
new_canonical_types.SetAt(index, *this);
} else {
canonical_types.SetAt(index, *this);
}
ASSERT(IsOld());
SetCanonical();
return this->raw();
}
void Type::set_type_class(const Object& value) const {
ASSERT(!value.IsNull() && (value.IsClass() || value.IsUnresolvedClass()));
StorePointer(&raw_ptr()->type_class_, value.raw());
}
void Type::set_arguments(const AbstractTypeArguments& value) const {
StorePointer(&raw_ptr()->arguments_, value.raw());
}
RawType* Type::New(Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->type_class() != Class::null());
RawObject* raw = Object::Allocate(Type::kClassId,
Type::InstanceSize(),
space);
return reinterpret_cast<RawType*>(raw);
}
RawType* Type::New(const Object& clazz,
const AbstractTypeArguments& arguments,
intptr_t token_pos,
Heap::Space space) {
const Type& result = Type::Handle(Type::New(space));
result.set_type_class(clazz);
result.set_arguments(arguments);
result.set_token_pos(token_pos);
result.raw_ptr()->type_state_ = RawType::kAllocated;
return result.raw();
}
void Type::set_token_pos(intptr_t token_pos) const {
ASSERT(token_pos >= 0);
raw_ptr()->token_pos_ = token_pos;
}
void Type::set_type_state(int8_t state) const {
ASSERT((state == RawType::kAllocated) ||
(state == RawType::kBeingFinalized) ||
(state == RawType::kFinalizedInstantiated) ||
(state == RawType::kFinalizedUninstantiated));
raw_ptr()->type_state_ = state;
}
const char* Type::ToCString() const {
if (IsResolved()) {
const AbstractTypeArguments& type_arguments =
AbstractTypeArguments::Handle(arguments());
const char* class_name;
if (HasResolvedTypeClass()) {
class_name = String::Handle(
Class::Handle(type_class()).Name()).ToCString();
} else {
class_name = UnresolvedClass::Handle(unresolved_class()).ToCString();
}
if (type_arguments.IsNull()) {
const char* format = "Type: class '%s'";
intptr_t len = OS::SNPrint(NULL, 0, format, class_name) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, class_name);
return chars;
} else {
const char* format = "Type: class '%s', args:[%s]";
const char* args_cstr =
AbstractTypeArguments::Handle(arguments()).ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, class_name, args_cstr) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, class_name, args_cstr);
return chars;
}
} else {
return "Unresolved Type";
}
}
void TypeParameter::set_is_finalized() const {
ASSERT(!IsFinalized());
set_type_state(RawTypeParameter::kFinalizedUninstantiated);
}
bool TypeParameter::Equals(const Instance& other) const {
if (raw() == other.raw()) {
return true;
}
if (!other.IsTypeParameter()) {
return false;
}
const TypeParameter& other_type_param = TypeParameter::Cast(other);
if (IsFinalized() != other_type_param.IsFinalized()) {
return false;
}
if (parameterized_class() != other_type_param.parameterized_class()) {
return false;
}
if (index() != other_type_param.index()) {
return false;
}
const String& type_param_name = String::Handle(name());
const String& other_type_param_name = String::Handle(other_type_param.name());
return type_param_name.Equals(other_type_param_name);
}
void TypeParameter::set_parameterized_class(const Class& value) const {
// Set value may be null.
StorePointer(&raw_ptr()->parameterized_class_, value.raw());
}
void TypeParameter::set_index(intptr_t value) const {
ASSERT(value >= 0);
raw_ptr()->index_ = value;
}
void TypeParameter::set_name(const String& value) const {
ASSERT(value.IsSymbol());
StorePointer(&raw_ptr()->name_, value.raw());
}
void TypeParameter::set_bound(const AbstractType& value) const {
StorePointer(&raw_ptr()->bound_, value.raw());
}
RawAbstractType* TypeParameter::InstantiateFrom(
const AbstractTypeArguments& instantiator_type_arguments) const {
ASSERT(IsFinalized());
if (instantiator_type_arguments.IsNull()) {
return Type::DynamicType();
}
return instantiator_type_arguments.TypeAt(index());
}
RawTypeParameter* TypeParameter::New() {
ASSERT(Isolate::Current()->object_store()->type_parameter_class() !=
Class::null());
RawObject* raw = Object::Allocate(TypeParameter::kClassId,
TypeParameter::InstanceSize(),
Heap::kOld);
return reinterpret_cast<RawTypeParameter*>(raw);
}
RawTypeParameter* TypeParameter::New(const Class& parameterized_class,
intptr_t index,
const String& name,
const AbstractType& bound,
intptr_t token_pos) {
const TypeParameter& result = TypeParameter::Handle(TypeParameter::New());
result.set_parameterized_class(parameterized_class);
result.set_index(index);
result.set_name(name);
result.set_bound(bound);
result.set_token_pos(token_pos);
result.raw_ptr()->type_state_ = RawTypeParameter::kAllocated;
return result.raw();
}
void TypeParameter::set_token_pos(intptr_t token_pos) const {
ASSERT(token_pos >= 0);
raw_ptr()->token_pos_ = token_pos;
}
void TypeParameter::set_type_state(int8_t state) const {
ASSERT((state == RawTypeParameter::kAllocated) ||
(state == RawTypeParameter::kBeingFinalized) ||
(state == RawTypeParameter::kFinalizedUninstantiated));
raw_ptr()->type_state_ = state;
}
const char* TypeParameter::ToCString() const {
const char* format = "TypeParameter: name %s; index: %d; class: %s";
const char* name_cstr = String::Handle(Name()).ToCString();
const Class& cls = Class::Handle(parameterized_class());
const char* cls_cstr =
cls.IsNull() ? " null" : String::Handle(cls.Name()).ToCString();
intptr_t len = OS::SNPrint(NULL, 0, format, name_cstr, index(), cls_cstr) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, name_cstr, index(), cls_cstr);
return chars;
}
const char* Number::ToCString() const {
// Number is an interface. No instances of Number should exist.
UNREACHABLE();
return "Number";
}
const char* Integer::ToCString() const {
// Integer is an interface. No instances of Integer should exist.
UNREACHABLE();
return "Integer";
}
RawInteger* Integer::New(const String& str, Heap::Space space) {
// We are not supposed to have integers represented as two byte strings.
ASSERT(str.IsOneByteString());
int64_t value;
if (!OS::StringToInt64(str.ToCString(), &value)) {
const Bigint& big = Bigint::Handle(Bigint::New(str, space));
ASSERT(!BigintOperations::FitsIntoSmi(big));
ASSERT(!BigintOperations::FitsIntoMint(big));
return big.raw();
}
return Integer::New(value, space);
}
RawInteger* Integer::NewCanonical(const String& str) {
// We are not supposed to have integers represented as two byte strings.
ASSERT(str.IsOneByteString());
int64_t value;
if (!OS::StringToInt64(str.ToCString(), &value)) {
const Bigint& big = Bigint::Handle(Bigint::NewCanonical(str));
ASSERT(!BigintOperations::FitsIntoSmi(big));
ASSERT(!BigintOperations::FitsIntoMint(big));
return big.raw();
}
if ((value <= Smi::kMaxValue) && (value >= Smi::kMinValue)) {
return Smi::New(value);
}
return Mint::NewCanonical(value);
}
RawInteger* Integer::New(int64_t value, Heap::Space space) {
if ((value <= Smi::kMaxValue) && (value >= Smi::kMinValue)) {
return Smi::New(value);
}
return Mint::New(value, space);
}
double Integer::AsDoubleValue() const {
UNIMPLEMENTED();
return 0.0;
}
int64_t Integer::AsInt64Value() const {
UNIMPLEMENTED();
return 0;
}
int Integer::CompareWith(const Integer& other) const {
UNIMPLEMENTED();
return 0;
}
RawInteger* Integer::AsValidInteger() const {
if (IsSmi()) return raw();
if (IsMint()) {
Mint& mint = Mint::Handle();
mint ^= raw();
if (Smi::IsValid64(mint.value())) {
return Smi::New(mint.value());
} else {
return raw();
}
}
ASSERT(IsBigint());
Bigint& big_value = Bigint::Handle();
big_value ^= raw();
if (BigintOperations::FitsIntoSmi(big_value)) {
return BigintOperations::ToSmi(big_value);
} else if (BigintOperations::FitsIntoMint(big_value)) {
return Mint::New(BigintOperations::ToMint(big_value));
} else {
return big_value.raw();
}
}
RawInteger* Integer::ArithmeticOp(Token::Kind operation,
const Integer& other) const {
// In 32-bit mode, the result of any operation between two Smis will fit in a
// 32-bit signed result, except the product of two Smis, which will be 64-bit.
// In 64-bit mode, the result of any operation between two Smis will fit in a
// 64-bit signed result, except the product of two Smis (unless the Smis are
// 32-bit or less).
if (IsSmi() && other.IsSmi()) {
Smi& left_smi = Smi::Handle();
Smi& right_smi = Smi::Handle();
left_smi ^= raw();
right_smi ^= other.raw();
const intptr_t left_value = left_smi.Value();
const intptr_t right_value = right_smi.Value();
switch (operation) {
case Token::kADD:
return Integer::New(left_value + right_value);
case Token::kSUB:
return Integer::New(left_value - right_value);
case Token::kMUL: {
if (Smi::kBits < 32) {
// In 32-bit mode, the product of two Smis fits in a 64-bit result.
return Integer::New(static_cast<int64_t>(left_value) *
static_cast<int64_t>(right_value));
} else {
// In 64-bit mode, the product of two 32-bit signed integers fits in a
// 64-bit result.
ASSERT(sizeof(intptr_t) == sizeof(int64_t));
if (Utils::IsInt(32, left_value) && Utils::IsInt(32, right_value)) {
return Integer::New(left_value * right_value);
}
}
// Perform a Bigint multiplication below.
break;
}
case Token::kTRUNCDIV:
return Integer::New(left_value / right_value);
case Token::kMOD: {
const intptr_t remainder = left_value % right_value;
if (remainder < 0) {
if (right_value < 0) {
return Integer::New(remainder - right_value);
} else {
return Integer::New(remainder + right_value);
}
}
return Integer::New(remainder);
}
default:
UNIMPLEMENTED();
}
}
// In 32-bit mode, the result of any operation between two 63-bit signed
// integers (or 32-bit for multiplication) will fit in a 64-bit signed result.
// In 64-bit mode, 63-bit signed integers are Smis, already processed above.
if ((Smi::kBits < 32) && !IsBigint() && !other.IsBigint()) {
const int64_t left_value = AsInt64Value();
if (Utils::IsInt(63, left_value)) {
const int64_t right_value = other.AsInt64Value();
if (Utils::IsInt(63, right_value)) {
switch (operation) {
case Token::kADD:
return Integer::New(left_value + right_value);
case Token::kSUB:
return Integer::New(left_value - right_value);
case Token::kMUL: {
if (Utils::IsInt(32, left_value) && Utils::IsInt(32, right_value)) {
return Integer::New(left_value * right_value);
}
// Perform a Bigint multiplication below.
break;
}
case Token::kTRUNCDIV:
return Integer::New(left_value / right_value);
case Token::kMOD: {
const int64_t remainder = left_value % right_value;
if (remainder < 0) {
if (right_value < 0) {
return Integer::New(remainder - right_value);
} else {
return Integer::New(remainder + right_value);
}
}
return Integer::New(remainder);
}
default:
UNIMPLEMENTED();
}
}
}
}
const Bigint& left_big = Bigint::Handle(AsBigint());
const Bigint& right_big = Bigint::Handle(other.AsBigint());
const Bigint& result =
Bigint::Handle(left_big.ArithmeticOp(operation, right_big));
return Integer::Handle(result.AsValidInteger()).raw();
}
static bool Are64bitOperands(const Integer& op1, const Integer& op2) {
return !op1.IsBigint() && !op2.IsBigint();
}
RawInteger* Integer::BitOp(Token::Kind kind, const Integer& other) const {
if (IsSmi() && other.IsSmi()) {
Smi& op1 = Smi::Handle();
Smi& op2 = Smi::Handle();
op1 ^= raw();
op2 ^= other.raw();
intptr_t result = 0;
switch (kind) {
case Token::kBIT_AND:
result = op1.Value() & op2.Value();
break;
case Token::kBIT_OR:
result = op1.Value() | op2.Value();
break;
case Token::kBIT_XOR:
result = op1.Value() ^ op2.Value();
break;
default:
UNIMPLEMENTED();
}
ASSERT(Smi::IsValid(result));
return Smi::New(result);
} else if (Are64bitOperands(*this, other)) {
int64_t a = AsInt64Value();
int64_t b = other.AsInt64Value();
switch (kind) {
case Token::kBIT_AND:
return Integer::New(a & b);
case Token::kBIT_OR:
return Integer::New(a | b);
case Token::kBIT_XOR:
return Integer::New(a ^ b);
default:
UNIMPLEMENTED();
}
} else {
Bigint& op1 = Bigint::Handle(AsBigint());
Bigint& op2 = Bigint::Handle(other.AsBigint());
switch (kind) {
case Token::kBIT_AND:
return BigintOperations::BitAnd(op1, op2);
case Token::kBIT_OR:
return BigintOperations::BitOr(op1, op2);
case Token::kBIT_XOR:
return BigintOperations::BitXor(op1, op2);
default:
UNIMPLEMENTED();
}
}
return Integer::null();
}
// TODO(srdjan): Clarify handling of negative right operand in a shift op.
RawInteger* Smi::ShiftOp(Token::Kind kind, const Smi& other) const {
intptr_t result = 0;
const intptr_t left_value = Value();
const intptr_t right_value = other.Value();
ASSERT(right_value >= 0);
switch (kind) {
case Token::kSHL: {
if ((left_value == 0) || (right_value == 0)) {
return raw();
}
{ // Check for overflow.
int cnt = Utils::HighestBit(left_value);
if ((cnt + right_value) >= Smi::kBits) {
if ((cnt + right_value) >= Mint::kBits) {
return BigintOperations::ShiftLeft(
Bigint::Handle(AsBigint()), right_value);
} else {
int64_t left_64 = left_value;
return Integer::New(left_64 << right_value);
}
}
}
result = left_value << right_value;
break;
}
case Token::kSHR: {
const intptr_t shift_amount =
(right_value >= kBitsPerWord) ? (kBitsPerWord - 1) : right_value;
result = left_value >> shift_amount;
break;
}
default:
UNIMPLEMENTED();
}
ASSERT(Smi::IsValid(result));
return Smi::New(result);
}
bool Smi::Equals(const Instance& other) const {
if (other.IsNull() || !other.IsSmi()) {
return false;
}
return (this->Value() == Smi::Cast(other).Value());
}
double Smi::AsDoubleValue() const {
return static_cast<double>(this->Value());
}
int64_t Smi::AsInt64Value() const {
return this->Value();
}
static bool FitsIntoSmi(const Integer& integer) {
if (integer.IsSmi()) {
return true;
}
if (integer.IsMint()) {
int64_t mint_value = integer.AsInt64Value();
return Smi::IsValid64(mint_value);
}
if (integer.IsBigint()) {
return BigintOperations::FitsIntoSmi(Bigint::Cast(integer));
}
UNREACHABLE();
return false;
}
int Smi::CompareWith(const Integer& other) const {
if (other.IsSmi()) {
const Smi& other_smi = Smi::Cast(other);
if (this->Value() < other_smi.Value()) {
return -1;
} else if (this->Value() > other_smi.Value()) {
return 1;
} else {
return 0;
}
}
ASSERT(!FitsIntoSmi(other));
if (other.IsMint() || other.IsBigint()) {
if (this->IsNegative() == other.IsNegative()) {
return this->IsNegative() ? 1 : -1;
}
return this->IsNegative() ? -1 : 1;
}
UNREACHABLE();
return 0;
}
const char* Smi::ToCString() const {
const char* kFormat = "%ld";
// Calculate the size of the string.
intptr_t len = OS::SNPrint(NULL, 0, kFormat, Value()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, Value());
return chars;
}
RawClass* Smi::Class() {
return Isolate::Current()->object_store()->smi_class();
}
void Mint::set_value(int64_t value) const {
raw_ptr()->value_ = value;
}
RawMint* Mint::New(int64_t val, Heap::Space space) {
// Do not allocate a Mint if Smi would do.
ASSERT(!Smi::IsValid64(val));
ASSERT(Isolate::Current()->object_store()->mint_class() != Class::null());
Mint& result = Mint::Handle();
{
RawObject* raw = Object::Allocate(Mint::kClassId,
Mint::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_value(val);
return result.raw();
}
RawMint* Mint::NewCanonical(int64_t value) {
// Do not allocate a Mint if Smi would do.
ASSERT(!Smi::IsValid64(value));
const Class& cls =
Class::Handle(Isolate::Current()->object_store()->mint_class());
const Array& constants = Array::Handle(cls.constants());
const intptr_t constants_len = constants.Length();
// Linear search to see whether this value is already present in the
// list of canonicalized constants.
Mint& canonical_value = Mint::Handle();
intptr_t index = 0;
while (index < constants_len) {
canonical_value ^= constants.At(index);
if (canonical_value.IsNull()) {
break;
}
if (canonical_value.value() == value) {
return canonical_value.raw();
}
index++;
}
// The value needs to be added to the constants list. Grow the list if
// it is full.
canonical_value = Mint::New(value, Heap::kOld);
cls.InsertCanonicalConstant(index, canonical_value);
canonical_value.SetCanonical();
return canonical_value.raw();
}
bool Mint::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
// Both handles point to the same raw instance.
return true;
}
if (!other.IsMint() || other.IsNull()) {
return false;
}
return value() == Mint::Cast(other).value();
}
double Mint::AsDoubleValue() const {
return static_cast<double>(this->value());
}
int64_t Mint::AsInt64Value() const {
return this->value();
}
int Mint::CompareWith(const Integer& other) const {
ASSERT(!FitsIntoSmi(*this));
if (other.IsMint() || other.IsSmi()) {
int64_t a = AsInt64Value();
int64_t b = other.AsInt64Value();
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}
if (other.IsBigint()) {
ASSERT(!BigintOperations::FitsIntoMint(Bigint::Cast(other)));
if (this->IsNegative() == other.IsNegative()) {
return this->IsNegative() ? 1 : -1;
}
return this->IsNegative() ? -1 : 1;
}
UNREACHABLE();
return 0;
}
const char* Mint::ToCString() const {
const char* kFormat = "%lld";
// Calculate the size of the string.
intptr_t len = OS::SNPrint(NULL, 0, kFormat, value()) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, kFormat, value());
return chars;
}
void Double::set_value(double value) const {
raw_ptr()->value_ = value;
}
bool Double::EqualsToDouble(double value) const {
intptr_t value_offset = Double::value_offset();
void* this_addr = reinterpret_cast<void*>(
reinterpret_cast<uword>(this->raw_ptr()) + value_offset);
void* other_addr = reinterpret_cast<void*>(&value);
return (memcmp(this_addr, other_addr, sizeof(value)) == 0);
}
bool Double::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
return true; // "===".
}
if (other.IsNull() || !other.IsDouble()) {
return false;
}
return EqualsToDouble(Double::Cast(other).value());
}
RawDouble* Double::New(double d, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->double_class() != Class::null());
Double& result = Double::Handle();
{
RawObject* raw = Object::Allocate(Double::kClassId,
Double::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
result.set_value(d);
return result.raw();
}
static bool IsWhiteSpace(char ch) {
return ch == '\0' || ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t';
}
static bool StringToDouble(const String& str, double* double_value) {
ASSERT(double_value != NULL);
// TODO(regis): For now, we use strtod to convert a string to double.
const char* nptr = str.ToCString();
char* endptr = NULL;
*double_value = strtod(nptr, &endptr);
// We do not treat overflow or underflow as an error and therefore do not
// check errno for ERANGE.
if (!IsWhiteSpace(*endptr)) {
return false;
}
return true;
}
RawDouble* Double::New(const String& str, Heap::Space space) {
double double_value;
if (!StringToDouble(str, &double_value)) {
return Double::Handle().raw();
}
return New(double_value, space);
}
RawDouble* Double::NewCanonical(double value) {
const Class& cls =
Class::Handle(Isolate::Current()->object_store()->double_class());
const Array& constants = Array::Handle(cls.constants());
const intptr_t constants_len = constants.Length();
// Linear search to see whether this value is already present in the
// list of canonicalized constants.
Double& canonical_value = Double::Handle();
intptr_t index = 0;
while (index < constants_len) {
canonical_value ^= constants.At(index);
if (canonical_value.IsNull()) {
break;
}
if (canonical_value.EqualsToDouble(value)) {
return canonical_value.raw();
}
index++;
}
// The value needs to be added to the constants list. Grow the list if
// it is full.
canonical_value = Double::New(value, Heap::kOld);
cls.InsertCanonicalConstant(index, canonical_value);
canonical_value.SetCanonical();
return canonical_value.raw();
}
RawDouble* Double::NewCanonical(const String& str) {
double double_value;
if (!StringToDouble(str, &double_value)) {
return Double::Handle().raw();
}
return NewCanonical(double_value);
}
const char* Double::ToCString() const {
if (isnan(value())) {
return "NaN";
}
if (isinf(value())) {
return value() < 0 ? "-Infinity" : "Infinity";
}
const int kBufferSize = 128;
char* buffer = Isolate::Current()->current_zone()->Alloc<char>(kBufferSize);
buffer[kBufferSize - 1] = '\0';
DoubleToCString(value(), buffer, kBufferSize);
return buffer;
}
RawBigint* Integer::AsBigint() const {
ASSERT(!IsNull());
if (IsSmi()) {
Smi& smi = Smi::Handle();
smi ^= raw();
return BigintOperations::NewFromSmi(smi);
} else if (IsMint()) {
Mint& mint = Mint::Handle();
mint ^= raw();
return BigintOperations::NewFromInt64(mint.value());
} else {
ASSERT(IsBigint());
Bigint& big = Bigint::Handle();
big ^= raw();
ASSERT(!BigintOperations::FitsIntoSmi(big));
return big.raw();
}
}
RawBigint* Bigint::ArithmeticOp(Token::Kind operation,
const Bigint& other) const {
switch (operation) {
case Token::kADD:
return BigintOperations::Add(*this, other);
case Token::kSUB:
return BigintOperations::Subtract(*this, other);
case Token::kMUL:
return BigintOperations::Multiply(*this, other);
case Token::kTRUNCDIV:
return BigintOperations::Divide(*this, other);
case Token::kMOD:
return BigintOperations::Modulo(*this, other);
default:
UNIMPLEMENTED();
return Bigint::null();
}
}
bool Bigint::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
// Both handles point to the same raw instance.
return true;
}
if (!other.IsBigint() || other.IsNull()) {
return false;
}
const Bigint& other_bgi = Bigint::Cast(other);
if (this->IsNegative() != other_bgi.IsNegative()) {
return false;
}
intptr_t len = this->Length();
if (len != other_bgi.Length()) {
return false;
}
for (intptr_t i = 0; i < len; i++) {
if (this->GetChunkAt(i) != other_bgi.GetChunkAt(i)) {
return false;
}
}
return true;
}
RawBigint* Bigint::New(const String& str, Heap::Space space) {
const Bigint& result = Bigint::Handle(
BigintOperations::NewFromCString(str.ToCString(), space));
ASSERT(!BigintOperations::FitsIntoMint(result));
return result.raw();
}
RawBigint* Bigint::NewCanonical(const String& str) {
const Bigint& value = Bigint::Handle(
BigintOperations::NewFromCString(str.ToCString(), Heap::kOld));
ASSERT(!BigintOperations::FitsIntoMint(value));
const Class& cls =
Class::Handle(Isolate::Current()->object_store()->bigint_class());
const Array& constants = Array::Handle(cls.constants());
const intptr_t constants_len = constants.Length();
// Linear search to see whether this value is already present in the
// list of canonicalized constants.
Bigint& canonical_value = Bigint::Handle();
intptr_t index = 0;
while (index < constants_len) {
canonical_value ^= constants.At(index);
if (canonical_value.IsNull()) {
break;
}
if (canonical_value.Equals(value)) {
return canonical_value.raw();
}
index++;
}
// The value needs to be added to the constants list. Grow the list if
// it is full.
cls.InsertCanonicalConstant(index, value);
value.SetCanonical();
return value.raw();
}
double Bigint::AsDoubleValue() const {
return Double::Handle(BigintOperations::ToDouble(*this)).value();
}
int64_t Bigint::AsInt64Value() const {
if (!BigintOperations::FitsIntoMint(*this)) {
UNREACHABLE();
}
return BigintOperations::ToMint(*this);
}
// For positive values: Smi < Mint < Bigint.
int Bigint::CompareWith(const Integer& other) const {
ASSERT(!FitsIntoSmi(*this));
ASSERT(!BigintOperations::FitsIntoMint(*this));
if (other.IsBigint()) {
return BigintOperations::Compare(*this, Bigint::Cast(other));
}
if (this->IsNegative() == other.IsNegative()) {
return this->IsNegative() ? -1 : 1;
}
return this->IsNegative() ? -1 : 1;
}
RawBigint* Bigint::Allocate(intptr_t length, Heap::Space space) {
if (length < 0 || length > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in Bigint::Allocate: invalid length %"Pd"\n", length);
}
ASSERT(Isolate::Current()->object_store()->bigint_class() != Class::null());
Bigint& result = Bigint::Handle();
{
RawObject* raw = Object::Allocate(Bigint::kClassId,
Bigint::InstanceSize(length),
space);
NoGCScope no_gc;
result ^= raw;
result.raw_ptr()->allocated_length_ = length; // Chunk length allocated.
result.raw_ptr()->signed_length_ = length; // Chunk length in use.
}
return result.raw();
}
static uword BigintAllocator(intptr_t size) {
Zone* zone = Isolate::Current()->current_zone();
return zone->AllocUnsafe(size);
}
const char* Bigint::ToCString() const {
return BigintOperations::ToDecimalCString(*this, &BigintAllocator);
}
class StringHasher : ValueObject {
public:
StringHasher() : hash_(0) {}
void Add(int32_t ch) {
hash_ += ch;
hash_ += hash_ << 10;
hash_ ^= hash_ >> 6;
}
// Return a non-zero hash of at most 'bits' bits.
intptr_t Finalize(int bits) {
ASSERT(1 <= bits && bits <= (kBitsPerWord - 1));
hash_ += hash_ << 3;
hash_ ^= hash_ >> 11;
hash_ += hash_ << 15;
hash_ = hash_ & ((static_cast<intptr_t>(1) << bits) - 1);
ASSERT(hash_ <= static_cast<uint32_t>(kMaxInt32));
return hash_ == 0 ? 1 : hash_;
}
private:
uint32_t hash_;
};
intptr_t String::Hash() const {
intptr_t result = Smi::Value(raw_ptr()->hash_);
if (result != 0) {
return result;
}
result = String::Hash(*this, 0, this->Length());
this->SetHash(result);
return result;
}
intptr_t String::Hash(const String& str, intptr_t begin_index, intptr_t len) {
ASSERT(begin_index >= 0);
ASSERT(len >= 0);
ASSERT((begin_index + len) <= str.Length());
StringHasher hasher;
CodePointIterator it(str, begin_index, len);
while (it.Next()) {
hasher.Add(it.Current());
}
return hasher.Finalize(String::kHashBits);
}
template<typename T>
static intptr_t HashImpl(const T* characters, intptr_t len) {
ASSERT(len >= 0);
StringHasher hasher;
for (intptr_t i = 0; i < len; i++) {
hasher.Add(characters[i]);
}
return hasher.Finalize(String::kHashBits);
}
intptr_t String::Hash(const uint8_t* characters, intptr_t len) {
return HashImpl(characters, len);
}
intptr_t String::Hash(const uint16_t* characters, intptr_t len) {
StringHasher hasher;
intptr_t i = 0;
while (i < len) {
hasher.Add(Utf16::Next(characters, &i, len));
}
return hasher.Finalize(String::kHashBits);
}
intptr_t String::Hash(const int32_t* characters, intptr_t len) {
return HashImpl(characters, len);
}
int32_t String::CharAt(intptr_t index) const {
intptr_t class_id = raw()->GetClassId();
ASSERT(RawObject::IsStringClassId(class_id));
NoGCScope no_gc;
if (class_id == kOneByteStringCid) {
return *OneByteString::CharAddr(*this, index);
}
if (class_id == kTwoByteStringCid) {
return *TwoByteString::CharAddr(*this, index);
}
if (class_id == kExternalOneByteStringCid) {
return *ExternalOneByteString::CharAddr(*this, index);
}
ASSERT(class_id == kExternalTwoByteStringCid);
return *ExternalTwoByteString::CharAddr(*this, index);
}
intptr_t String::CharSize() const {
intptr_t class_id = raw()->GetClassId();
if (class_id == kOneByteStringCid || class_id == kExternalOneByteStringCid) {
return kOneByteChar;
}
ASSERT(class_id == kTwoByteStringCid ||
class_id == kExternalTwoByteStringCid);
return kTwoByteChar;
}
void* String::GetPeer() const {
intptr_t class_id = raw()->GetClassId();
if (class_id == kExternalOneByteStringCid) {
return ExternalOneByteString::GetPeer(*this);
}
ASSERT(class_id == kExternalTwoByteStringCid);
return ExternalTwoByteString::GetPeer(*this);
}
bool String::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
// Both handles point to the same raw instance.
return true;
}
if (!other.IsString() || other.IsNull()) {
return false;
}
const String& other_string = String::Cast(other);
if (this->HasHash() && other_string.HasHash() &&
(this->Hash() != other_string.Hash())) {
return false; // Both sides have a hash code and it does not match.
}
return Equals(other_string, 0, other_string.Length());
}
bool String::Equals(const char* cstr) const {
ASSERT(cstr != NULL);
CodePointIterator it(*this);
intptr_t len = strlen(cstr);
while (it.Next()) {
if (*cstr == '\0') {
// Lengths don't match.
return false;
}
int32_t ch;
intptr_t consumed = Utf8::Decode(reinterpret_cast<const uint8_t*>(cstr),
len,
&ch);
if (consumed == 0 || it.Current() != ch) {
return false;
}
cstr += consumed;
len -= consumed;
}
return *cstr == '\0';
}
bool String::Equals(const uint8_t* latin1_array, intptr_t len) const {
if (len != this->Length()) {
// Lengths don't match.
return false;
}
for (intptr_t i = 0; i < len; i++) {
if (this->CharAt(i) != latin1_array[i]) {
return false;
}
}
return true;
}
bool String::Equals(const uint16_t* utf16_array, intptr_t len) const {
if (len != this->Length()) {
// Lengths don't match.
return false;
}
for (intptr_t i = 0; i < len; i++) {
if (this->CharAt(i) != utf16_array[i]) {
return false;
}
}
return true;
}
bool String::Equals(const int32_t* utf32_array, intptr_t len) const {
CodePointIterator it(*this);
intptr_t i = 0;
while (it.Next()) {
if (it.Current() != static_cast<int32_t>(utf32_array[i])) {
return false;
}
++i;
}
if (i != len) {
return false;
}
return true;
}
intptr_t String::CompareTo(const String& other) const {
const intptr_t this_len = this->Length();
const intptr_t other_len = other.IsNull() ? 0 : other.Length();
const intptr_t len = (this_len < other_len) ? this_len : other_len;
for (intptr_t i = 0; i < len; i++) {
int32_t this_code_point = this->CharAt(i);
int32_t other_code_point = other.CharAt(i);
if (this_code_point < other_code_point) {
return -1;
}
if (this_code_point > other_code_point) {
return 1;
}
}
if (this_len < other_len) return -1;
if (this_len > other_len) return 1;
return 0;
}
bool String::StartsWith(const String& other) const {
if (other.IsNull() || (other.Length() > this->Length())) {
return false;
}
intptr_t slen = other.Length();
for (int i = 0; i < slen; i++) {
if (this->CharAt(i) != other.CharAt(i)) {
return false;
}
}
return true;
}
RawInstance* String::Canonicalize() const {
if (IsCanonical()) {
return this->raw();
}
return Symbols::New(*this);
}
RawString* String::New(const char* cstr, Heap::Space space) {
ASSERT(cstr != NULL);
intptr_t array_len = strlen(cstr);
const uint8_t* utf8_array = reinterpret_cast<const uint8_t*>(cstr);
return String::FromUTF8(utf8_array, array_len, space);
}
RawString* String::FromUTF8(const uint8_t* utf8_array,
intptr_t array_len,
Heap::Space space) {
Utf8::Type type;
intptr_t len = Utf8::CodeUnitCount(utf8_array, array_len, &type);
if (type == Utf8::kLatin1) {
const String& strobj = String::Handle(OneByteString::New(len, space));
if (len > 0) {
NoGCScope no_gc;
Utf8::DecodeToLatin1(utf8_array, array_len,
OneByteString::CharAddr(strobj, 0), len);
}
return strobj.raw();
}
ASSERT((type == Utf8::kBMP) || (type == Utf8::kSupplementary));
const String& strobj = String::Handle(TwoByteString::New(len, space));
NoGCScope no_gc;
Utf8::DecodeToUTF16(utf8_array, array_len,
TwoByteString::CharAddr(strobj, 0), len);
return strobj.raw();
}
RawString* String::New(const uint8_t* latin1_array,
intptr_t array_len,
Heap::Space space) {
return OneByteString::New(latin1_array, array_len, space);
}
RawString* String::New(const uint16_t* utf16_array,
intptr_t array_len,
Heap::Space space) {
bool is_one_byte_string = true;
for (intptr_t i = 0; i < array_len; ++i) {
if (!Utf::IsLatin1(utf16_array[i])) {
is_one_byte_string = false;
break;
}
}
if (is_one_byte_string) {
return OneByteString::New(utf16_array, array_len, space);
}
return TwoByteString::New(utf16_array, array_len, space);
}
RawString* String::New(const int32_t* utf32_array,
intptr_t array_len,
Heap::Space space) {
bool is_one_byte_string = true;
intptr_t utf16_len = array_len;
for (intptr_t i = 0; i < array_len; ++i) {
if (!Utf::IsLatin1(utf32_array[i])) {
is_one_byte_string = false;
if (Utf::IsSupplementary(utf32_array[i])) {
utf16_len += 1;
}
}
}
if (is_one_byte_string) {
return OneByteString::New(utf32_array, array_len, space);
}
return TwoByteString::New(utf16_len, utf32_array, array_len, space);
}
RawString* String::New(const String& str, Heap::Space space) {
// Currently this just creates a copy of the string in the correct space.
// Once we have external string support, this will also create a heap copy of
// the string if necessary. Some optimizations are possible, such as not
// copying internal strings into the same space.
intptr_t len = str.Length();
String& result = String::Handle();
intptr_t char_size = str.CharSize();
if (char_size == kOneByteChar) {
result ^= OneByteString::New(len, space);
} else {
ASSERT(char_size == kTwoByteChar);
result ^= TwoByteString::New(len, space);
}
String::Copy(result, 0, str, 0, len);
return result.raw();
}
RawString* String::NewExternal(const uint8_t* characters,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
return ExternalOneByteString::New(characters, len, peer, callback, space);
}
RawString* String::NewExternal(const uint16_t* characters,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
return ExternalTwoByteString::New(characters, len, peer, callback, space);
}
void String::Copy(const String& dst, intptr_t dst_offset,
const uint8_t* characters,
intptr_t len) {
ASSERT(dst_offset >= 0);
ASSERT(len >= 0);
ASSERT(len <= (dst.Length() - dst_offset));
if (dst.IsOneByteString()) {
NoGCScope no_gc;
if (len > 0) {
memmove(OneByteString::CharAddr(dst, dst_offset),
characters,
len);
}
} else if (dst.IsTwoByteString()) {
for (intptr_t i = 0; i < len; ++i) {
*TwoByteString::CharAddr(dst, i + dst_offset) = characters[i];
}
}
}
void String::Copy(const String& dst, intptr_t dst_offset,
const uint16_t* utf16_array,
intptr_t array_len) {
ASSERT(dst_offset >= 0);
ASSERT(array_len >= 0);
ASSERT(array_len <= (dst.Length() - dst_offset));
if (dst.IsOneByteString()) {
NoGCScope no_gc;
for (intptr_t i = 0; i < array_len; ++i) {
ASSERT(Utf::IsLatin1(utf16_array[i]));
*OneByteString::CharAddr(dst, i + dst_offset) = utf16_array[i];
}
} else {
ASSERT(dst.IsTwoByteString());
NoGCScope no_gc;
if (array_len > 0) {
memmove(TwoByteString::CharAddr(dst, dst_offset),
utf16_array,
array_len * 2);
}
}
}
void String::Copy(const String& dst, intptr_t dst_offset,
const String& src, intptr_t src_offset,
intptr_t len) {
ASSERT(dst_offset >= 0);
ASSERT(src_offset >= 0);
ASSERT(len >= 0);
ASSERT(len <= (dst.Length() - dst_offset));
ASSERT(len <= (src.Length() - src_offset));
if (len > 0) {
intptr_t char_size = src.CharSize();
if (char_size == kOneByteChar) {
if (src.IsOneByteString()) {
NoGCScope no_gc;
String::Copy(dst,
dst_offset,
OneByteString::CharAddr(src, src_offset),
len);
} else {
ASSERT(src.IsExternalOneByteString());
NoGCScope no_gc;
String::Copy(dst,
dst_offset,
ExternalOneByteString::CharAddr(src, src_offset),
len);
}
} else {
ASSERT(char_size == kTwoByteChar);
if (src.IsTwoByteString()) {
NoGCScope no_gc;
String::Copy(dst,
dst_offset,
TwoByteString::CharAddr(src, src_offset),
len);
} else {
ASSERT(src.IsExternalTwoByteString());
NoGCScope no_gc;
String::Copy(dst,
dst_offset,
ExternalTwoByteString::CharAddr(src, src_offset),
len);
}
}
}
}
RawString* String::EscapeSpecialCharacters(const String& str, bool raw_str) {
if (str.IsOneByteString()) {
return OneByteString::EscapeSpecialCharacters(str, raw_str);
}
ASSERT(str.IsTwoByteString());
return TwoByteString::EscapeSpecialCharacters(str, raw_str);
}
RawString* String::NewFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
RawString* result = NewFormattedV(format, args);
NoGCScope no_gc;
va_end(args);
return result;
}
RawString* String::NewFormattedV(const char* format, va_list args) {
va_list args_copy;
va_copy(args_copy, args);
intptr_t len = OS::VSNPrint(NULL, 0, format, args_copy);
va_end(args_copy);
Zone* zone = Isolate::Current()->current_zone();
char* buffer = zone->Alloc<char>(len + 1);
OS::VSNPrint(buffer, (len + 1), format, args);
return String::New(buffer);
}
RawString* String::Concat(const String& str1,
const String& str2,
Heap::Space space) {
ASSERT(!str1.IsNull() && !str2.IsNull());
intptr_t char_size = Utils::Maximum(str1.CharSize(), str2.CharSize());
if (char_size == kTwoByteChar) {
return TwoByteString::Concat(str1, str2, space);
}
return OneByteString::Concat(str1, str2, space);
}
RawString* String::ConcatAll(const Array& strings,
Heap::Space space) {
ASSERT(!strings.IsNull());
intptr_t result_len = 0;
intptr_t strings_len = strings.Length();
String& str = String::Handle();
intptr_t char_size = kOneByteChar;
for (intptr_t i = 0; i < strings_len; i++) {
str ^= strings.At(i);
result_len += str.Length();
char_size = Utils::Maximum(char_size, str.CharSize());
}
if (char_size == kOneByteChar) {
return OneByteString::ConcatAll(strings, result_len, space);
}
ASSERT(char_size == kTwoByteChar);
return TwoByteString::ConcatAll(strings, result_len, space);
}
RawString* String::SubString(const String& str,
intptr_t begin_index,
Heap::Space space) {
ASSERT(!str.IsNull());
if (begin_index >= str.Length()) {
return String::null();
}
return String::SubString(str, begin_index, (str.Length() - begin_index));
}
RawString* String::SubString(const String& str,
intptr_t begin_index,
intptr_t length,
Heap::Space space) {
ASSERT(!str.IsNull());
ASSERT(begin_index >= 0);
ASSERT(length >= 0);
if (begin_index <= str.Length() && length == 0) {
return Symbols::Empty();
}
if (begin_index > str.Length()) {
return String::null();
}
String& result = String::Handle();
bool is_one_byte_string = true;
intptr_t char_size = str.CharSize();
if (char_size == kTwoByteChar) {
for (intptr_t i = begin_index; i < begin_index + length; ++i) {
if (!Utf::IsLatin1(str.CharAt(i))) {
is_one_byte_string = false;
break;
}
}
}
if (is_one_byte_string) {
result ^= OneByteString::New(length, space);
} else {
result ^= TwoByteString::New(length, space);
}
String::Copy(result, 0, str, begin_index, length);
return result.raw();
}
const char* String::ToCString() const {
intptr_t len = Utf8::Length(*this);
Zone* zone = Isolate::Current()->current_zone();
uint8_t* result = zone->Alloc<uint8_t>(len + 1);
ToUTF8(result, len);
result[len] = 0;
return reinterpret_cast<const char*>(result);
}
void String::ToUTF8(uint8_t* utf8_array, intptr_t array_len) const {
ASSERT(array_len >= Utf8::Length(*this));
Utf8::Encode(*this, reinterpret_cast<char*>(utf8_array), array_len);
}
static void AddFinalizer(const Object& referent,
void* peer,
Dart_WeakPersistentHandleFinalizer callback) {
ASSERT(callback != NULL);
ApiState* state = Isolate::Current()->api_state();
ASSERT(state != NULL);
FinalizablePersistentHandle* weak_ref =
state->weak_persistent_handles().AllocateHandle();
weak_ref->set_raw(referent);
weak_ref->set_peer(peer);
weak_ref->set_callback(callback);
}
RawString* String::MakeExternal(void* array,
intptr_t length,
void* peer,
Dart_PeerFinalizer cback) const {
NoGCScope no_gc;
ASSERT(array != NULL);
intptr_t str_length = this->Length();
ASSERT(length >= (str_length * this->CharSize()));
intptr_t class_id = raw()->GetClassId();
intptr_t used_size = 0;
intptr_t original_size = 0;
uword tags = raw_ptr()->tags_;
ASSERT(!IsCanonical());
if (class_id == kOneByteStringCid) {
used_size = ExternalOneByteString::InstanceSize();
original_size = OneByteString::InstanceSize(str_length);
ASSERT(original_size >= used_size);
// Copy the data into the external array.
if (str_length > 0) {
memmove(array, OneByteString::CharAddr(*this, 0), str_length);
}
// Update the class information of the object.
const intptr_t class_id = kExternalOneByteStringCid;
tags = RawObject::SizeTag::update(used_size, tags);
tags = RawObject::ClassIdTag::update(class_id, tags);
raw_ptr()->tags_ = tags;
const String& result = String::Handle(this->raw());
ExternalStringData<uint8_t>* ext_data = new ExternalStringData<uint8_t>(
reinterpret_cast<const uint8_t*>(array), peer, cback);
result.SetLength(str_length);
result.SetHash(0);
ExternalOneByteString::SetExternalData(result, ext_data);
AddFinalizer(result, ext_data, ExternalOneByteString::Finalize);
} else {
ASSERT(class_id == kTwoByteStringCid);
used_size = ExternalTwoByteString::InstanceSize();
original_size = TwoByteString::InstanceSize(str_length);
ASSERT(original_size >= used_size);
// Copy the data into the external array.
if (str_length > 0) {
memmove(array,
TwoByteString::CharAddr(*this, 0),
(str_length * kTwoByteChar));
}
// Update the class information of the object.
const intptr_t class_id = kExternalTwoByteStringCid;
tags = RawObject::SizeTag::update(used_size, tags);
tags = RawObject::ClassIdTag::update(class_id, tags);
raw_ptr()->tags_ = tags;
const String& result = String::Handle(this->raw());
ExternalStringData<uint16_t>* ext_data = new ExternalStringData<uint16_t>(
reinterpret_cast<const uint16_t*>(array), peer, cback);
result.SetLength(str_length);
result.SetHash(0);
ExternalTwoByteString::SetExternalData(result, ext_data);
AddFinalizer(result, ext_data, ExternalTwoByteString::Finalize);
}
// If there is any left over space fill it with either an Array object or
// just a plain object (depending on the amount of left over space) so
// that it can be traversed over successfully during garbage collection.
Object::MakeUnusedSpaceTraversable(*this, original_size, used_size);
return this->raw();
}
RawString* String::Transform(int32_t (*mapping)(int32_t ch),
const String& str,
Heap::Space space) {
ASSERT(!str.IsNull());
bool has_mapping = false;
int32_t dst_max = 0;
CodePointIterator it(str);
while (it.Next()) {
int32_t src = it.Current();
int32_t dst = mapping(src);
if (src != dst) {
has_mapping = true;
}
dst_max = Utils::Maximum(dst_max, dst);
}
if (!has_mapping) {
return str.raw();
}
if (Utf::IsLatin1(dst_max)) {
return OneByteString::Transform(mapping, str, space);
}
ASSERT(Utf::IsBmp(dst_max) || Utf::IsSupplementary(dst_max));
return TwoByteString::Transform(mapping, str, space);
}
RawString* String::ToUpperCase(const String& str, Heap::Space space) {
// TODO(cshapiro): create a fast-path for OneByteString instances.
return Transform(CaseMapping::ToUpper, str, space);
}
RawString* String::ToLowerCase(const String& str, Heap::Space space) {
// TODO(cshapiro): create a fast-path for OneByteString instances.
return Transform(CaseMapping::ToLower, str, space);
}
bool String::CodePointIterator::Next() {
ASSERT(index_ >= -1);
intptr_t length = Utf16::Length(ch_);
if (index_ < (end_ - length)) {
index_ += length;
ch_ = str_.CharAt(index_);
if (Utf16::IsLeadSurrogate(ch_) && (index_ < (end_ - 1))) {
int32_t ch2 = str_.CharAt(index_ + 1);
if (Utf16::IsTrailSurrogate(ch2)) {
ch_ = Utf16::Decode(ch_, ch2);
}
}
return true;
}
index_ = end_;
return false;
}
RawOneByteString* OneByteString::EscapeSpecialCharacters(const String& str,
bool raw_str) {
intptr_t len = str.Length();
if (len > 0) {
intptr_t num_escapes = 0;
intptr_t index = 0;
for (intptr_t i = 0; i < len; i++) {
if (IsSpecialCharacter(*CharAddr(str, i)) ||
(!raw_str && (*CharAddr(str, i) == '\\'))) {
num_escapes += 1;
}
}
const String& dststr = String::Handle(
OneByteString::New(len + num_escapes, Heap::kNew));
for (intptr_t i = 0; i < len; i++) {
if (IsSpecialCharacter(*CharAddr(str, i))) {
*(CharAddr(dststr, index)) = '\\';
*(CharAddr(dststr, index + 1)) = SpecialCharacter(*CharAddr(str, i));
index += 2;
} else if (!raw_str && (*CharAddr(str, i) == '\\')) {
*(CharAddr(dststr, index)) = '\\';
*(CharAddr(dststr, index + 1)) = '\\';
index += 2;
} else {
*(CharAddr(dststr, index)) = *CharAddr(str, i);
index += 1;
}
}
return OneByteString::raw(dststr);
}
return OneByteString::null();
}
// Check to see if 'str1' matches 'str2' as is or
// once the private key separator is stripped from str2.
//
// Things are made more complicated by the fact that constructors are
// added *after* the private suffix, so "foo@123.named" should match
// "foo.named".
//
// Also, the private suffix can occur more than once in the name, as in:
//
// _ReceivePortImpl@6be832b._internal@6be832b
//
bool OneByteString::EqualsIgnoringPrivateKey(const String& str1,
const String& str2) {
ASSERT(str2.IsOneByteString());
if (str1.raw() == str2.raw()) {
return true; // Both handles point to the same raw instance.
}
NoGCScope no_gc;
intptr_t len = str1.Length();
intptr_t str2_len = str2.Length();
if (len == str2_len) {
for (intptr_t i = 0; i < len; i++) {
if (*CharAddr(str1, i) != *CharAddr(str2, i)) {
return false;
}
}
return true;
}
if (len < str2_len) {
return false; // No way they can match.
}
intptr_t pos = 0;
intptr_t str2_pos = 0;
while (pos < len) {
int32_t ch = *CharAddr(str1, pos);
pos++;
if (ch == Scanner::kPrivateKeySeparator) {
// Consume a private key separator.
while ((pos < len) && (*CharAddr(str1, pos) != '.')) {
pos++;
}
// Resume matching characters.
continue;
}
if ((str2_pos == str2_len) || (ch != *CharAddr(str2, str2_pos))) {
return false;
}
str2_pos++;
}
// We have reached the end of mangled_name string.
ASSERT(pos == len);
return (str2_pos == str2_len);
}
RawOneByteString* OneByteString::New(intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current() == Dart::vm_isolate() ||
Isolate::Current()->object_store()->one_byte_string_class() !=
Class::null());
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in OneByteString::New: invalid len %"Pd"\n", len);
}
String& result = String::Handle();
{
RawObject* raw = Object::Allocate(OneByteString::kClassId,
OneByteString::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
result.SetHash(0);
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::New(const uint8_t* characters,
intptr_t len,
Heap::Space space) {
const String& result = String::Handle(OneByteString::New(len, space));
if (len > 0) {
NoGCScope no_gc;
memmove(CharAddr(result, 0), characters, len);
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::New(const uint16_t* characters,
intptr_t len,
Heap::Space space) {
const String& result =String::Handle(OneByteString::New(len, space));
for (intptr_t i = 0; i < len; ++i) {
ASSERT(Utf::IsLatin1(characters[i]));
*CharAddr(result, i) = characters[i];
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::New(const int32_t* characters,
intptr_t len,
Heap::Space space) {
const String& result = String::Handle(OneByteString::New(len, space));
for (intptr_t i = 0; i < len; ++i) {
ASSERT(Utf::IsLatin1(characters[i]));
*CharAddr(result, i) = characters[i];
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::New(const String& str,
Heap::Space space) {
intptr_t len = str.Length();
const String& result = String::Handle(OneByteString::New(len, space));
String::Copy(result, 0, str, 0, len);
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::New(const String& other_one_byte_string,
intptr_t other_start_index,
intptr_t other_len,
Heap::Space space) {
const String& result = String::Handle(OneByteString::New(other_len, space));
ASSERT(other_one_byte_string.IsOneByteString());
if (other_len > 0) {
NoGCScope no_gc;
memmove(OneByteString::CharAddr(result, 0),
OneByteString::CharAddr(other_one_byte_string, other_start_index),
other_len);
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::Concat(const String& str1,
const String& str2,
Heap::Space space) {
intptr_t len1 = str1.Length();
intptr_t len2 = str2.Length();
intptr_t len = len1 + len2;
const String& result = String::Handle(OneByteString::New(len, space));
String::Copy(result, 0, str1, 0, len1);
String::Copy(result, len1, str2, 0, len2);
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::ConcatAll(const Array& strings,
intptr_t len,
Heap::Space space) {
const String& result = String::Handle(OneByteString::New(len, space));
String& str = String::Handle();
intptr_t strings_len = strings.Length();
intptr_t pos = 0;
for (intptr_t i = 0; i < strings_len; i++) {
str ^= strings.At(i);
intptr_t str_len = str.Length();
String::Copy(result, pos, str, 0, str_len);
pos += str_len;
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::Transform(int32_t (*mapping)(int32_t ch),
const String& str,
Heap::Space space) {
ASSERT(!str.IsNull());
intptr_t len = str.Length();
const String& result = String::Handle(OneByteString::New(len, space));
for (intptr_t i = 0; i < len; ++i) {
int32_t ch = mapping(str.CharAt(i));
ASSERT(Utf::IsLatin1(ch));
*CharAddr(result, i) = ch;
}
return OneByteString::raw(result);
}
RawOneByteString* OneByteString::SubStringUnchecked(const String& str,
intptr_t begin_index,
intptr_t length,
Heap::Space space) {
ASSERT(!str.IsNull() && str.IsOneByteString());
ASSERT(begin_index >= 0);
ASSERT(length >= 0);
if (begin_index <= str.Length() && length == 0) {
return OneByteString::raw(String::Handle(Symbols::Empty()));
}
ASSERT(begin_index < str.Length());
RawOneByteString* result = OneByteString::New(length, space);
NoGCScope no_gc;
if (length > 0) {
uint8_t* dest = &result->ptr()->data_[0];
uint8_t* src = &raw_ptr(str)->data_[begin_index];
memmove(dest, src, length);
}
return result;
}
RawTwoByteString* TwoByteString::EscapeSpecialCharacters(const String& str,
bool raw_str) {
intptr_t len = str.Length();
if (len > 0) {
intptr_t num_escapes = 0;
intptr_t index = 0;
for (intptr_t i = 0; i < len; i++) {
if (IsSpecialCharacter(*CharAddr(str, i)) ||
(!raw_str && (*CharAddr(str, i) == '\\'))) {
num_escapes += 1;
}
}
const String& dststr = String::Handle(
TwoByteString::New(len + num_escapes, Heap::kNew));
for (intptr_t i = 0; i < len; i++) {
if (IsSpecialCharacter(*CharAddr(str, i))) {
*(CharAddr(dststr, index)) = '\\';
*(CharAddr(dststr, index + 1)) = SpecialCharacter(*CharAddr(str, i));
index += 2;
} else if (!raw_str && (*CharAddr(str, i) == '\\')) {
*(CharAddr(dststr, index)) = '\\';
*(CharAddr(dststr, index + 1)) = '\\';
index += 2;
} else {
*(CharAddr(dststr, index)) = *CharAddr(str, i);
index += 1;
}
}
return TwoByteString::raw(dststr);
}
return TwoByteString::null();
}
RawTwoByteString* TwoByteString::New(intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->two_byte_string_class());
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in TwoByteString::New: invalid len %"Pd"\n", len);
}
String& result = String::Handle();
{
RawObject* raw = Object::Allocate(TwoByteString::kClassId,
TwoByteString::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
result.SetHash(0);
}
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::New(const uint16_t* utf16_array,
intptr_t array_len,
Heap::Space space) {
ASSERT(array_len > 0);
const String& result = String::Handle(TwoByteString::New(array_len, space));
{
NoGCScope no_gc;
memmove(CharAddr(result, 0), utf16_array, (array_len * 2));
}
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::New(intptr_t utf16_len,
const int32_t* utf32_array,
intptr_t array_len,
Heap::Space space) {
ASSERT((array_len > 0) && (utf16_len >= array_len));
const String& result = String::Handle(TwoByteString::New(utf16_len, space));
{
NoGCScope no_gc;
intptr_t j = 0;
for (intptr_t i = 0; i < array_len; ++i) {
if (Utf::IsSupplementary(utf32_array[i])) {
ASSERT(j < (utf16_len - 1));
Utf16::Encode(utf32_array[i], CharAddr(result, j));
j += 2;
} else {
ASSERT(j < utf16_len);
*CharAddr(result, j) = utf32_array[i];
j += 1;
}
}
}
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::New(const String& str,
Heap::Space space) {
intptr_t len = str.Length();
const String& result = String::Handle(TwoByteString::New(len, space));
String::Copy(result, 0, str, 0, len);
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::Concat(const String& str1,
const String& str2,
Heap::Space space) {
intptr_t len1 = str1.Length();
intptr_t len2 = str2.Length();
intptr_t len = len1 + len2;
const String& result = String::Handle(TwoByteString::New(len, space));
String::Copy(result, 0, str1, 0, len1);
String::Copy(result, len1, str2, 0, len2);
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::ConcatAll(const Array& strings,
intptr_t len,
Heap::Space space) {
const String& result = String::Handle(TwoByteString::New(len, space));
String& str = String::Handle();
intptr_t strings_len = strings.Length();
intptr_t pos = 0;
for (intptr_t i = 0; i < strings_len; i++) {
str ^= strings.At(i);
intptr_t str_len = str.Length();
String::Copy(result, pos, str, 0, str_len);
pos += str_len;
}
return TwoByteString::raw(result);
}
RawTwoByteString* TwoByteString::Transform(int32_t (*mapping)(int32_t ch),
const String& str,
Heap::Space space) {
ASSERT(!str.IsNull());
intptr_t len = str.Length();
const String& result = String::Handle(TwoByteString::New(len, space));
String::CodePointIterator it(str);
intptr_t i = 0;
while (it.Next()) {
int32_t src = it.Current();
int32_t dst = mapping(src);
ASSERT(dst >= 0 && dst <= 0x10FFFF);
intptr_t len = Utf16::Length(dst);
if (len == 1) {
*CharAddr(result, i) = dst;
} else {
ASSERT(len == 2);
Utf16::Encode(dst, CharAddr(result, i));
}
i += len;
}
return TwoByteString::raw(result);
}
RawExternalOneByteString* ExternalOneByteString::New(
const uint8_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->
external_one_byte_string_class() != Class::null());
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ExternalOneByteString::New: invalid len %"Pd"\n",
len);
}
String& result = String::Handle();
ExternalStringData<uint8_t>* external_data =
new ExternalStringData<uint8_t>(data, peer, callback);
{
RawObject* raw = Object::Allocate(ExternalOneByteString::kClassId,
ExternalOneByteString::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
result.SetHash(0);
SetExternalData(result, external_data);
}
AddFinalizer(result, external_data, ExternalOneByteString::Finalize);
return ExternalOneByteString::raw(result);
}
static void DeleteWeakPersistentHandle(Dart_Handle handle) {
ApiState* state = Isolate::Current()->api_state();
ASSERT(state != NULL);
FinalizablePersistentHandle* weak_ref =
reinterpret_cast<FinalizablePersistentHandle*>(handle);
ASSERT(state->IsValidWeakPersistentHandle(handle));
state->weak_persistent_handles().FreeHandle(weak_ref);
}
void ExternalOneByteString::Finalize(Dart_Handle handle, void* peer) {
delete reinterpret_cast<ExternalStringData<uint8_t>*>(peer);
DeleteWeakPersistentHandle(handle);
}
RawExternalTwoByteString* ExternalTwoByteString::New(
const uint16_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_two_byte_string_class() !=
Class::null());
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ExternalTwoByteString::New: invalid len %"Pd"\n",
len);
}
String& result = String::Handle();
ExternalStringData<uint16_t>* external_data =
new ExternalStringData<uint16_t>(data, peer, callback);
{
RawObject* raw = Object::Allocate(ExternalTwoByteString::kClassId,
ExternalTwoByteString::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
result.SetHash(0);
SetExternalData(result, external_data);
}
AddFinalizer(result, external_data, ExternalTwoByteString::Finalize);
return ExternalTwoByteString::raw(result);
}
void ExternalTwoByteString::Finalize(Dart_Handle handle, void* peer) {
delete reinterpret_cast<ExternalStringData<uint16_t>*>(peer);
DeleteWeakPersistentHandle(handle);
}
RawBool* Bool::True() {
return Isolate::Current()->object_store()->true_value();
}
RawBool* Bool::False() {
return Isolate::Current()->object_store()->false_value();
}
RawBool* Bool::New(bool value) {
ASSERT(Isolate::Current()->object_store()->bool_class() != Class::null());
Bool& result = Bool::Handle();
{
// Since the two boolean instances are singletons we allocate them straight
// in the old generation.
RawObject* raw = Object::Allocate(Bool::kClassId,
Bool::InstanceSize(),
Heap::kOld);
NoGCScope no_gc;
result ^= raw;
}
result.set_value(value);
return result.raw();
}
const char* Bool::ToCString() const {
return value() ? "true" : "false";
}
bool Array::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
// Both handles point to the same raw instance.
return true;
}
if (!other.IsArray() || other.IsNull()) {
return false;
}
// Must have the same type arguments.
if (!AbstractTypeArguments::AreEqual(
AbstractTypeArguments::Handle(GetTypeArguments()),
AbstractTypeArguments::Handle(other.GetTypeArguments()))) {
return false;
}
const Array& other_arr = Array::Cast(other);
intptr_t len = this->Length();
if (len != other_arr.Length()) {
return false;
}
for (intptr_t i = 0; i < len; i++) {
if (this->At(i) != other_arr.At(i)) {
return false;
}
}
return true;
}
RawArray* Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->array_class() != Class::null());
return New(kClassId, len, space);
}
RawArray* Array::New(intptr_t class_id, intptr_t len, Heap::Space space) {
if (len < 0 || len > Array::kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in Array::New: invalid len %"Pd"\n", len);
}
Array& result = Array::Handle();
{
RawObject* raw = Object::Allocate(class_id,
Array::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
}
return result.raw();
}
void Array::MakeImmutable() const {
NoGCScope no_gc;
uword tags = raw_ptr()->tags_;
tags = RawObject::ClassIdTag::update(kImmutableArrayCid, tags);
raw_ptr()->tags_ = tags;
}
const char* Array::ToCString() const {
return "Array";
}
RawArray* Array::Grow(const Array& source, int new_length, Heap::Space space) {
const Array& result = Array::Handle(Array::New(new_length, space));
intptr_t len = 0;
if (!source.IsNull()) {
len = source.Length();
result.SetTypeArguments(
AbstractTypeArguments::Handle(source.GetTypeArguments()));
}
ASSERT(new_length >= len); // Cannot copy 'source' into new array.
ASSERT(new_length != len); // Unnecessary copying of array.
Object& obj = Object::Handle();
for (int i = 0; i < len; i++) {
obj = source.At(i);
result.SetAt(i, obj);
}
return result.raw();
}
RawArray* Array::MakeArray(const GrowableObjectArray& growable_array) {
intptr_t used_len = growable_array.Length();
intptr_t capacity_len = growable_array.Capacity();
Isolate* isolate = Isolate::Current();
const Array& array = Array::Handle(isolate, growable_array.data());
const Array& new_array = Array::Handle(isolate, Object::empty_array());
intptr_t capacity_size = Array::InstanceSize(capacity_len);
intptr_t used_size = Array::InstanceSize(used_len);
NoGCScope no_gc;
// Update the size in the header field and length of the array object.
uword tags = array.raw_ptr()->tags_;
ASSERT(kArrayCid == RawObject::ClassIdTag::decode(tags));
tags = RawObject::SizeTag::update(used_size, tags);
array.raw_ptr()->tags_ = tags;
array.SetLength(used_len);
// Null the GrowableObjectArray, we are removing it's backing array.
growable_array.SetLength(0);
growable_array.SetData(new_array);
// If there is any left over space fill it with either an Array object or
// just a plain object (depending on the amount of left over space) so
// that it can be traversed over successfully during garbage collection.
Object::MakeUnusedSpaceTraversable(array, capacity_size, used_size);
return array.raw();
}
RawImmutableArray* ImmutableArray::New(intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->immutable_array_class() !=
Class::null());
return reinterpret_cast<RawImmutableArray*>(Array::New(kClassId, len, space));
}
const char* ImmutableArray::ToCString() const {
return "ImmutableArray";
}
void GrowableObjectArray::Add(const Object& value, Heap::Space space) const {
Add(Isolate::Current(), value, space);
}
void GrowableObjectArray::Add(Isolate* isolate,
const Object& value,
Heap::Space space) const {
ASSERT(!IsNull());
Array& contents = Array::Handle(isolate, data());
if (Length() == Capacity()) {
// TODO(Issue 2500): Need a better growth strategy.
intptr_t new_capacity = (Capacity() == 0) ? 4 : Capacity() * 2;
if (new_capacity <= Capacity()) {
// Use the preallocated out of memory exception to avoid calling
// into dart code or allocating any code.
const Instance& exception =
Instance::Handle(isolate->object_store()->out_of_memory());
Exceptions::Throw(exception);
UNREACHABLE();
}
Grow(new_capacity, space);
contents = data();
}
ASSERT(Length() < Capacity());
intptr_t index = Length();
SetLength(index + 1);
contents.SetAt(index, value);
}
void GrowableObjectArray::Grow(intptr_t new_capacity, Heap::Space space) const {
ASSERT(new_capacity > Capacity());
const Array& contents = Array::Handle(data());
const Array& new_contents =
Array::Handle(Array::Grow(contents, new_capacity, space));
StorePointer(&(raw_ptr()->data_), new_contents.raw());
ASSERT(AbstractTypeArguments::AreEqual(
AbstractTypeArguments::Handle(new_contents.GetTypeArguments()),
AbstractTypeArguments::Handle(raw_ptr()->type_arguments_)));
}
RawObject* GrowableObjectArray::RemoveLast() const {
ASSERT(!IsNull());
ASSERT(Length() > 0);
intptr_t index = Length() - 1;
const Array& contents = Array::Handle(data());
const Object& obj = Object::Handle(contents.At(index));
contents.SetAt(index, Object::Handle());
SetLength(index);
return obj.raw();
}
bool GrowableObjectArray::Equals(const Instance& other) const {
// If both handles point to the same raw instance they are equal.
if (this->raw() == other.raw()) {
return true;
}
// Other instance must be non null and a GrowableObjectArray.
if (!other.IsGrowableObjectArray() || other.IsNull()) {
return false;
}
const GrowableObjectArray& other_arr = GrowableObjectArray::Cast(other);
// The capacity and length of both objects must be equal.
if (Capacity() != other_arr.Capacity() || Length() != other_arr.Length()) {
return false;
}
// Both must have the same type arguments.
if (!AbstractTypeArguments::AreEqual(
AbstractTypeArguments::Handle(GetTypeArguments()),
AbstractTypeArguments::Handle(other.GetTypeArguments()))) {
return false;
}
// The data part in both arrays must be identical.
const Array& contents = Array::Handle(data());
const Array& other_contents = Array::Handle(other_arr.data());
for (intptr_t i = 0; i < Length(); i++) {
if (contents.At(i) != other_contents.At(i)) {
return false;
}
}
return true;
}
RawGrowableObjectArray* GrowableObjectArray::New(intptr_t capacity,
Heap::Space space) {
const Array& data = Array::Handle(Array::New(capacity, space));
return New(data, space);
}
RawGrowableObjectArray* GrowableObjectArray::New(const Array& array,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->growable_object_array_class()
!= Class::null());
GrowableObjectArray& result = GrowableObjectArray::Handle();
{
RawObject* raw = Object::Allocate(GrowableObjectArray::kClassId,
GrowableObjectArray::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(0);
result.SetData(array);
}
return result.raw();
}
const char* GrowableObjectArray::ToCString() const {
return "GrowableObjectArray";
}
void ByteArray::Copy(void* dst,
const ByteArray& src,
intptr_t src_offset,
intptr_t length) {
ASSERT(Utils::RangeCheck(src_offset, length, src.ByteLength()));
{
NoGCScope no_gc;
if (length > 0) {
memmove(dst, src.ByteAddr(src_offset), length);
}
}
}
void ByteArray::Copy(const ByteArray& dst,
intptr_t dst_offset,
const void* src,
intptr_t length) {
ASSERT(Utils::RangeCheck(dst_offset, length, dst.ByteLength()));
{
NoGCScope no_gc;
if (length > 0) {
memmove(dst.ByteAddr(dst_offset), src, length);
}
}
}
void ByteArray::Copy(const ByteArray& dst,
intptr_t dst_offset,
const ByteArray& src,
intptr_t src_offset,
intptr_t length) {
ASSERT(Utils::RangeCheck(src_offset, length, src.ByteLength()));
ASSERT(Utils::RangeCheck(dst_offset, length, dst.ByteLength()));
{
NoGCScope no_gc;
if (length > 0) {
memmove(dst.ByteAddr(dst_offset), src.ByteAddr(src_offset), length);
}
}
}
template<typename T>
static void ExternalByteArrayFinalize(Dart_Handle handle, void* peer) {
delete reinterpret_cast<ExternalByteArrayData<T>*>(peer);
DeleteWeakPersistentHandle(handle);
}
template<typename HandleT, typename RawT, typename ElementT>
RawT* ByteArray::NewExternalImpl(intptr_t class_id,
ElementT* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
if (len < 0 || len > HandleT::kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ByteArray::NewExternalImpl: invalid len %"Pd"\n",
len);
}
HandleT& result = HandleT::Handle();
ExternalByteArrayData<ElementT>* external_data =
new ExternalByteArrayData<ElementT>(data, peer, callback);
{
RawObject* raw = Object::Allocate(class_id, HandleT::InstanceSize(), space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
result.SetExternalData(external_data);
}
AddFinalizer(result, external_data, ExternalByteArrayFinalize<ElementT>);
return result.raw();
}
intptr_t ByteArray::ByteLength() const {
// ByteArray is an abstract class.
UNREACHABLE();
return 0;
}
uint8_t* ByteArray::ByteAddr(intptr_t byte_offset) const {
// ByteArray is an abstract class.
UNREACHABLE();
return NULL;
}
const char* ByteArray::ToCString() const {
// ByteArray is an abstract class.
UNREACHABLE();
return "ByteArray";
}
template<typename HandleT, typename RawT>
RawT* ByteArray::NewImpl(intptr_t class_id, intptr_t len, Heap::Space space) {
if (len < 0 || len > HandleT::kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ByteArray::NewImpl: invalid len %"Pd"\n", len);
}
HandleT& result = HandleT::Handle();
{
RawObject* raw = Object::Allocate(class_id,
HandleT::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
if (len > 0) {
memset(result.ByteAddr(0), 0, result.ByteLength());
}
}
return result.raw();
}
template<typename HandleT, typename RawT, typename ElementT>
RawT* ByteArray::NewImpl(intptr_t class_id,
const ElementT* data,
intptr_t len,
Heap::Space space) {
if (len < 0 || len > HandleT::kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in ByteArray::NewImpl: invalid len %"Pd"\n", len);
}
HandleT& result = HandleT::Handle();
{
RawObject* raw = Object::Allocate(class_id,
HandleT::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.SetLength(len);
if (len > 0) {
memmove(result.ByteAddr(0), data, result.ByteLength());
}
}
return result.raw();
}
RawInt8Array* Int8Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int8_array_class() !=
Class::null());
return NewImpl<Int8Array, RawInt8Array>(kClassId, len, space);
}
RawInt8Array* Int8Array::New(const int8_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int8_array_class() !=
Class::null());
return NewImpl<Int8Array, RawInt8Array>(kClassId, data, len, space);
}
const char* Int8Array::ToCString() const {
return "_Int8Array";
}
RawUint8Array* Uint8Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint8_array_class() !=
Class::null());
return NewImpl<Uint8Array, RawUint8Array>(kClassId, len, space);
}
RawUint8Array* Uint8Array::New(const uint8_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint8_array_class() !=
Class::null());
return NewImpl<Uint8Array, RawUint8Array>(kClassId, data, len, space);
}
const char* Uint8Array::ToCString() const {
return "_Uint8Array";
}
RawInt16Array* Int16Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int16_array_class() !=
Class::null());
return NewImpl<Int16Array, RawInt16Array>(kClassId, len, space);
}
RawInt16Array* Int16Array::New(const int16_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int16_array_class() !=
Class::null());
return NewImpl<Int16Array, RawInt16Array>(kClassId, data, len, space);
}
const char* Int16Array::ToCString() const {
return "_Int16Array";
}
RawUint16Array* Uint16Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint16_array_class() !=
Class::null());
return NewImpl<Uint16Array, RawUint16Array>(kClassId, len, space);
}
RawUint16Array* Uint16Array::New(const uint16_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint16_array_class() !=
Class::null());
return NewImpl<Uint16Array, RawUint16Array>(kClassId, data, len, space);
}
const char* Uint16Array::ToCString() const {
return "_Uint16Array";
}
RawInt32Array* Int32Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int32_array_class() !=
Class::null());
return NewImpl<Int32Array, RawInt32Array>(kClassId, len, space);
}
RawInt32Array* Int32Array::New(const int32_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int32_array_class() !=
Class::null());
return NewImpl<Int32Array, RawInt32Array>(kClassId, data, len, space);
}
const char* Int32Array::ToCString() const {
return "_Int32Array";
}
RawUint32Array* Uint32Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint32_array_class() !=
Class::null());
return NewImpl<Uint32Array, RawUint32Array>(kClassId, len, space);
}
RawUint32Array* Uint32Array::New(const uint32_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint32_array_class() !=
Class::null());
return NewImpl<Uint32Array, RawUint32Array>(kClassId, data, len, space);
}
const char* Uint32Array::ToCString() const {
return "_Uint32Array";
}
RawInt64Array* Int64Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int64_array_class() !=
Class::null());
return NewImpl<Int64Array, RawInt64Array>(kClassId, len, space);
}
RawInt64Array* Int64Array::New(const int64_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->int64_array_class() !=
Class::null());
return NewImpl<Int64Array, RawInt64Array>(kClassId, data, len, space);
}
const char* Int64Array::ToCString() const {
return "_Int64Array";
}
RawUint64Array* Uint64Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint64_array_class() !=
Class::null());
return NewImpl<Uint64Array, RawUint64Array>(kClassId, len, space);
}
RawUint64Array* Uint64Array::New(const uint64_t* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->uint64_array_class() !=
Class::null());
return NewImpl<Uint64Array, RawUint64Array>(kClassId, data, len, space);
}
const char* Uint64Array::ToCString() const {
return "_Uint64Array";
}
RawFloat32Array* Float32Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->float32_array_class() !=
Class::null());
return NewImpl<Float32Array, RawFloat32Array>(kClassId, len, space);
}
RawFloat32Array* Float32Array::New(const float* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->float32_array_class() !=
Class::null());
return NewImpl<Float32Array, RawFloat32Array>(kClassId, data, len, space);
}
const char* Float32Array::ToCString() const {
return "_Float32Array";
}
RawFloat64Array* Float64Array::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->float64_array_class() !=
Class::null());
return NewImpl<Float64Array, RawFloat64Array>(kClassId, len, space);
}
RawFloat64Array* Float64Array::New(const double* data,
intptr_t len,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->float64_array_class() !=
Class::null());
return NewImpl<Float64Array, RawFloat64Array>(kClassId, data, len, space);
}
const char* Float64Array::ToCString() const {
return "_Float64Array";
}
RawExternalInt8Array* ExternalInt8Array::New(int8_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_int8_array_class() !=
Class::null());
return NewExternalImpl<ExternalInt8Array, RawExternalInt8Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalInt8Array::ToCString() const {
return "_ExternalInt8Array";
}
RawExternalUint8Array* ExternalUint8Array::New(uint8_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_uint8_array_class() !=
Class::null());
return NewExternalImpl<ExternalUint8Array, RawExternalUint8Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalUint8Array::ToCString() const {
return "_ExternalUint8Array";
}
RawExternalInt16Array* ExternalInt16Array::New(int16_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_int16_array_class() !=
Class::null());
return NewExternalImpl<ExternalInt16Array, RawExternalInt16Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalInt16Array::ToCString() const {
return "_ExternalInt16Array";
}
RawExternalUint16Array* ExternalUint16Array::New(uint16_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_uint16_array_class() !=
Class::null());
return NewExternalImpl<ExternalUint16Array, RawExternalUint16Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalUint16Array::ToCString() const {
return "_ExternalUint16Array";
}
RawExternalInt32Array* ExternalInt32Array::New(int32_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_int32_array_class() !=
Class::null());
return NewExternalImpl<ExternalInt32Array, RawExternalInt32Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalInt32Array::ToCString() const {
return "_ExternalInt32Array";
}
RawExternalUint32Array* ExternalUint32Array::New(uint32_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_uint32_array_class() !=
Class::null());
return NewExternalImpl<ExternalUint32Array, RawExternalUint32Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalUint32Array::ToCString() const {
return "_ExternalUint32Array";
}
RawExternalInt64Array* ExternalInt64Array::New(int64_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_int64_array_class() !=
Class::null());
return NewExternalImpl<ExternalInt64Array, RawExternalInt64Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalInt64Array::ToCString() const {
return "_ExternalInt64Array";
}
RawExternalUint64Array* ExternalUint64Array::New(uint64_t* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_uint64_array_class() !=
Class::null());
return NewExternalImpl<ExternalUint64Array, RawExternalUint64Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalUint64Array::ToCString() const {
return "_ExternalUint64Array";
}
RawExternalFloat32Array* ExternalFloat32Array::New(float* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_float32_array_class() !=
Class::null());
return NewExternalImpl<ExternalFloat32Array, RawExternalFloat32Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalFloat32Array::ToCString() const {
return "_ExternalFloat32Array";
}
RawExternalFloat64Array* ExternalFloat64Array::New(double* data,
intptr_t len,
void* peer,
Dart_PeerFinalizer callback,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->external_float64_array_class() !=
Class::null());
return NewExternalImpl<ExternalFloat64Array, RawExternalFloat64Array>(
kClassId, data, len, peer, callback, space);
}
const char* ExternalFloat64Array::ToCString() const {
return "_ExternalFloat64Array";
}
const char* Closure::ToCString(const Instance& closure) {
const Function& fun = Function::Handle(Closure::function(closure));
const bool is_implicit_closure = fun.IsImplicitClosureFunction();
const char* fun_sig = String::Handle(fun.Signature()).ToCString();
const char* from = is_implicit_closure ? " from " : "";
const char* fun_desc = is_implicit_closure ? fun.ToCString() : "";
const char* format = "Closure: %s%s%s";
intptr_t len = OS::SNPrint(NULL, 0, format, fun_sig, from, fun_desc) + 1;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
OS::SNPrint(chars, len, format, fun_sig, from, fun_desc);
return chars;
}
RawInstance* Closure::New(const Function& function,
const Context& context,
Heap::Space space) {
Isolate* isolate = Isolate::Current();
ASSERT(context.isolate() == isolate);
const Class& cls = Class::Handle(function.signature_class());
ASSERT(cls.instance_size() == Closure::InstanceSize());
Instance& result = Instance::Handle();
{
RawObject* raw = Object::Allocate(cls.id(), Closure::InstanceSize(), space);
NoGCScope no_gc;
result ^= raw;
}
Closure::set_function(result, function);
Closure::set_context(result, context);
return result.raw();
}
const char* DartFunction::ToCString() const {
return "Function type class";
}
intptr_t Stacktrace::Length() const {
const Array& code_array = Array::Handle(raw_ptr()->code_array_);
return code_array.Length();
}
RawFunction* Stacktrace::FunctionAtFrame(intptr_t frame_index) const {
const Array& function_array = Array::Handle(raw_ptr()->function_array_);
return reinterpret_cast<RawFunction*>(function_array.At(frame_index));
}
RawCode* Stacktrace::CodeAtFrame(intptr_t frame_index) const {
const Array& code_array = Array::Handle(raw_ptr()->code_array_);
return reinterpret_cast<RawCode*>(code_array.At(frame_index));
}
RawSmi* Stacktrace::PcOffsetAtFrame(intptr_t frame_index) const {
const Array& pc_offset_array = Array::Handle(raw_ptr()->pc_offset_array_);
return reinterpret_cast<RawSmi*>(pc_offset_array.At(frame_index));
}
void Stacktrace::set_function_array(const Array& function_array) const {
StorePointer(&raw_ptr()->function_array_, function_array.raw());
}
void Stacktrace::set_code_array(const Array& code_array) const {
StorePointer(&raw_ptr()->code_array_, code_array.raw());
}
void Stacktrace::set_pc_offset_array(const Array& pc_offset_array) const {
StorePointer(&raw_ptr()->pc_offset_array_, pc_offset_array.raw());
}
RawStacktrace* Stacktrace::New(const GrowableObjectArray& func_list,
const GrowableObjectArray& code_list,
const GrowableObjectArray& pc_offset_list,
Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->stacktrace_class() !=
Class::null());
Stacktrace& result = Stacktrace::Handle();
{
RawObject* raw = Object::Allocate(Stacktrace::kClassId,
Stacktrace::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
// Create arrays for the function, code and pc_offset triplet for each frame.
const Array& function_array = Array::Handle(Array::MakeArray(func_list));
const Array& code_array = Array::Handle(Array::MakeArray(code_list));
const Array& pc_offset_array =
Array::Handle(Array::MakeArray(pc_offset_list));
result.set_function_array(function_array);
result.set_code_array(code_array);
result.set_pc_offset_array(pc_offset_array);
return result.raw();
}
void Stacktrace::Append(const GrowableObjectArray& func_list,
const GrowableObjectArray& code_list,
const GrowableObjectArray& pc_offset_list) const {
intptr_t old_length = Length();
intptr_t new_length = old_length + pc_offset_list.Length();
ASSERT(pc_offset_list.Length() == func_list.Length());
ASSERT(pc_offset_list.Length() == code_list.Length());
// Grow the arrays for function, code and pc_offset triplet to accommodate
// the new stack frames.
Array& function_array = Array::Handle(raw_ptr()->function_array_);
Array& code_array = Array::Handle(raw_ptr()->code_array_);
Array& pc_offset_array = Array::Handle(raw_ptr()->pc_offset_array_);
function_array = Array::Grow(function_array, new_length);
code_array = Array::Grow(code_array, new_length);
pc_offset_array = Array::Grow(pc_offset_array, new_length);
set_function_array(function_array);
set_code_array(code_array);
set_pc_offset_array(pc_offset_array);
// Now append the new function and code list to the existing arrays.
intptr_t j = 0;
Object& obj = Object::Handle();
for (intptr_t i = old_length; i < new_length; i++, j++) {
obj = func_list.At(j);
function_array.SetAt(i, obj);
obj = code_list.At(j);
code_array.SetAt(i, obj);
obj = pc_offset_list.At(j);
pc_offset_array.SetAt(i, obj);
}
}
const char* Stacktrace::ToCString() const {
Function& function = Function::Handle();
Code& code = Code::Handle();
Script& script = Script::Handle();
String& function_name = String::Handle();
String& url = String::Handle();
// Iterate through the stack frames and create C string description
// for each frame.
intptr_t total_len = 0;
const char* kFormat = "#%-6d %s (%s:%d:%d)\n";
GrowableArray<char*> frame_strings;
for (intptr_t i = 0; i < Length(); i++) {
function = FunctionAtFrame(i);
code = CodeAtFrame(i);
uword pc = code.EntryPoint() + Smi::Value(PcOffsetAtFrame(i));
intptr_t token_pos = code.GetTokenIndexOfPC(pc);
script = function.script();
function_name = function.QualifiedUserVisibleName();
url = script.url();
intptr_t line = -1;
intptr_t column = -1;
if (token_pos >= 0) {
script.GetTokenLocation(token_pos, &line, &column);
}
intptr_t len = OS::SNPrint(NULL, 0, kFormat,
i,
function_name.ToCString(),
url.ToCString(),
line, column);
total_len += len;
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
OS::SNPrint(chars, (len + 1), kFormat,
i,
function_name.ToCString(),
url.ToCString(),
line, column);
frame_strings.Add(chars);
}
// Now concatentate the frame descriptions into a single C string.
char* chars = Isolate::Current()->current_zone()->Alloc<char>(total_len + 1);
intptr_t index = 0;
for (intptr_t i = 0; i < frame_strings.length(); i++) {
index += OS::SNPrint((chars + index),
(total_len + 1 - index),
"%s",
frame_strings[i]);
}
return chars;
}
void JSRegExp::set_pattern(const String& pattern) const {
StorePointer(&raw_ptr()->pattern_, pattern.raw());
}
void JSRegExp::set_num_bracket_expressions(intptr_t value) const {
raw_ptr()->num_bracket_expressions_ = Smi::New(value);
}
RawJSRegExp* JSRegExp::New(intptr_t len, Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->jsregexp_class() !=
Class::null());
if (len < 0 || len > kMaxElements) {
// This should be caught before we reach here.
FATAL1("Fatal error in JSRegexp::New: invalid len %"Pd"\n", len);
}
JSRegExp& result = JSRegExp::Handle();
{
RawObject* raw = Object::Allocate(JSRegExp::kClassId,
JSRegExp::InstanceSize(len),
space);
NoGCScope no_gc;
result ^= raw;
result.set_type(kUnitialized);
result.set_flags(0);
result.SetLength(len);
}
return result.raw();
}
void* JSRegExp::GetDataStartAddress() const {
intptr_t addr = reinterpret_cast<intptr_t>(raw_ptr());
return reinterpret_cast<void*>(addr + sizeof(RawJSRegExp));
}
RawJSRegExp* JSRegExp::FromDataStartAddress(void* data) {
JSRegExp& regexp = JSRegExp::Handle();
intptr_t addr = reinterpret_cast<intptr_t>(data) - sizeof(RawJSRegExp);
regexp ^= RawObject::FromAddr(addr);
return regexp.raw();
}
const char* JSRegExp::Flags() const {
switch (raw_ptr()->flags_) {
case kGlobal | kIgnoreCase | kMultiLine :
case kIgnoreCase | kMultiLine :
return "im";
case kGlobal | kIgnoreCase :
case kIgnoreCase:
return "i";
case kGlobal | kMultiLine :
case kMultiLine:
return "m";
default:
break;
}
return "";
}
bool JSRegExp::Equals(const Instance& other) const {
if (this->raw() == other.raw()) {
return true; // "===".
}
if (other.IsNull() || !other.IsJSRegExp()) {
return false;
}
const JSRegExp& other_js = JSRegExp::Cast(other);
// Match the pattern.
const String& str1 = String::Handle(pattern());
const String& str2 = String::Handle(other_js.pattern());
if (!str1.Equals(str2)) {
return false;
}
// Match the flags.
if ((is_global() != other_js.is_global()) ||
(is_ignore_case() != other_js.is_ignore_case()) ||
(is_multi_line() != other_js.is_multi_line())) {
return false;
}
return true;
}
const char* JSRegExp::ToCString() const {
const String& str = String::Handle(pattern());
const char* format = "JSRegExp: pattern=%s flags=%s";
intptr_t len = OS::SNPrint(NULL, 0, format, str.ToCString(), Flags());
char* chars = Isolate::Current()->current_zone()->Alloc<char>(len + 1);
OS::SNPrint(chars, (len + 1), format, str.ToCString(), Flags());
return chars;
}
RawWeakProperty* WeakProperty::New(Heap::Space space) {
ASSERT(Isolate::Current()->object_store()->weak_property_class()
!= Class::null());
WeakProperty& result = WeakProperty::Handle();
{
RawObject* raw = Object::Allocate(WeakProperty::kClassId,
WeakProperty::InstanceSize(),
space);
NoGCScope no_gc;
result ^= raw;
}
return result.raw();
}
const char* WeakProperty::ToCString() const {
return "_WeakProperty";
}
} // namespace dart