From 36a67fa046803bf0d8b83ae604e8b6a69675320c Mon Sep 17 00:00:00 2001 From: "sgjesse@google.com" Date: Thu, 31 Oct 2013 05:46:57 +0000 Subject: [PATCH] Implement fromEnvironment on bool, int and String This implements const constructor fromEnvironment on bool, int and String. The VM have the added -Dname=value option to define the value for the properties. All values are provided by using the -D - nothing is read from the environment. If the resulting value is null or - in the case of int.fromEnvironment - not a number an ArgumentError is thrown. This CL does not have any implementation for dart2js. This is a continuation of the change https://chromiumcodereview.appspot.com/24975002 by iposva@ BUG= R=iposva@google.com Review URL: https://codereview.chromium.org//50983002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@29642 260f80e4-7a28-3924-810f-c04153c831b5 --- runtime/bin/main.cc | 92 +++++++++++++++++++ runtime/include/dart_api.h | 28 +++++- runtime/lib/bool.cc | 46 ++++++++++ runtime/lib/bool_patch.dart | 4 + runtime/lib/corelib_sources.gypi | 1 + runtime/lib/integers.cc | 58 ++++++++++++ runtime/lib/integers_patch.dart | 4 + runtime/lib/string.cc | 29 ++++++ runtime/lib/string_patch.dart | 4 + runtime/platform/hashmap.h | 21 +++++ runtime/vm/bootstrap_natives.h | 3 + runtime/vm/dart_api_impl.cc | 11 +++ runtime/vm/isolate.cc | 1 + runtime/vm/isolate.h | 8 ++ runtime/vm/parser.cc | 48 +++++++--- sdk/lib/_internal/lib/core_patch.dart | 17 ++++ sdk/lib/core/bool.dart | 9 +- sdk/lib/core/int.dart | 8 ++ sdk/lib/core/string.dart | 7 ++ .../corelib/bool_from_environment2_test.dart | 11 +++ ...l_from_environment_default_value_test.dart | 12 +++ tests/corelib/bool_from_environment_test.dart | 11 +++ tests/corelib/corelib.status | 27 ++++++ tests/corelib/int_from_environment2_test.dart | 12 +++ tests/corelib/int_from_environment3_test.dart | 11 +++ ...t_from_environment_default_value_test.dart | 10 ++ tests/corelib/int_from_environment_test.dart | 13 +++ .../string_from_environment2_test.dart | 12 +++ .../string_from_environment3_test.dart | 11 +++ ...string_from_environment_default_value.dart | 11 +++ .../corelib/string_from_environment_test.dart | 13 +++ 31 files changed, 533 insertions(+), 20 deletions(-) create mode 100644 runtime/lib/bool.cc create mode 100644 tests/corelib/bool_from_environment2_test.dart create mode 100644 tests/corelib/bool_from_environment_default_value_test.dart create mode 100644 tests/corelib/bool_from_environment_test.dart create mode 100644 tests/corelib/int_from_environment2_test.dart create mode 100644 tests/corelib/int_from_environment3_test.dart create mode 100644 tests/corelib/int_from_environment_default_value_test.dart create mode 100644 tests/corelib/int_from_environment_test.dart create mode 100644 tests/corelib/string_from_environment2_test.dart create mode 100644 tests/corelib/string_from_environment3_test.dart create mode 100644 tests/corelib/string_from_environment_default_value.dart create mode 100644 tests/corelib/string_from_environment_test.dart diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc index 6dbdd62f978..fae077d8172 100644 --- a/runtime/bin/main.cc +++ b/runtime/bin/main.cc @@ -22,6 +22,7 @@ #include "bin/process.h" #include "bin/vmservice_impl.h" #include "platform/globals.h" +#include "platform/hashmap.h" namespace dart { namespace bin { @@ -72,6 +73,8 @@ static bool start_vm_service = false; static int vm_service_server_port = -1; static const int DEFAULT_VM_SERVICE_SERVER_PORT = 8181; +// The environment provided through the command line using -D options. +static dart::HashMap* environment = NULL; static bool IsValidFlag(const char* name, const char* prefix, @@ -132,6 +135,48 @@ static bool ProcessPackageRootOption(const char* arg) { } +static void* GetHashmapKeyFromString(char* key) { + return reinterpret_cast(key); +} + +static bool ProcessEnvironmentOption(const char* arg) { + ASSERT(arg != NULL); + if (*arg == '\0') { + // Ignore empty -D option. + Log::PrintErr("No arguments given to -D option\n"); + return true; + } + if (environment == NULL) { + environment = new HashMap(&HashMap::SameStringValue, 4); + } + // Split the name=value part of the -Dname=value argument. + char* name; + char* value = NULL; + const char* equals_pos = strchr(arg, '='); + if (equals_pos == NULL) { + // No equal sign (name without value) currently not supported. + Log::PrintErr("No value given to -D option\n"); + return false; + } else { + int name_len = equals_pos - arg; + if (name_len == 0) { + Log::PrintErr("No name given to -D option\n"); + return false; + } + // Split name=value into name and value. + name = reinterpret_cast(malloc(name_len + 1)); + strncpy(name, arg, name_len); + name[name_len] = '\0'; + value = strdup(equals_pos + 1); + } + HashMap::Entry* entry = environment->Lookup( + GetHashmapKeyFromString(name), HashMap::StringHash(name), true); + ASSERT(entry != NULL); // Lookup adds an entry if key not found. + entry->value = value; + return true; +} + + static bool ProcessCompileAllOption(const char* arg) { ASSERT(arg != NULL); if (*arg != '\0') { @@ -246,6 +291,7 @@ static struct { { "--verbose", ProcessVerboseOption }, { "-v", ProcessVerboseOption }, { "--package-root=", ProcessPackageRootOption }, + { "-D", ProcessEnvironmentOption }, // VM specific options to the standalone dart program. { "--break-at=", ProcessBreakpointOption }, { "--compile_all", ProcessCompileAllOption }, @@ -402,6 +448,38 @@ static Dart_Handle CreateRuntimeOptions(CommandLineOptions* options) { } \ +static Dart_Handle EnvironmentCallback(Dart_Handle name) { + uint8_t* utf8_array; + intptr_t utf8_len; + Dart_Handle result = Dart_Null(); + Dart_Handle handle = Dart_StringToUTF8(name, &utf8_array, &utf8_len); + if (Dart_IsError(handle)) { + handle = Dart_ThrowException( + DartUtils::NewDartArgumentError(Dart_GetError(handle))); + } else { + char* name_chars = reinterpret_cast(malloc(utf8_len + 1)); + memmove(name_chars, utf8_array, utf8_len); + name_chars[utf8_len] = '\0'; + const char* value = NULL; + if (environment != NULL) { + HashMap::Entry* entry = environment->Lookup( + GetHashmapKeyFromString(name_chars), + HashMap::StringHash(name_chars), + false); + if (entry != NULL) { + value = reinterpret_cast(entry->value); + } + } + if (value != NULL) { + result = Dart_NewStringFromUTF8(reinterpret_cast(value), + strlen(value)); + } + free(name_chars); + } + return result; +} + + // Returns true on success, false on failure. static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri, const char* main, @@ -426,6 +504,9 @@ static Dart_Isolate CreateIsolateAndSetupHelper(const char* script_uri, Dart_Handle result = Dart_SetLibraryTagHandler(DartUtils::LibraryTagHandler); CHECK_RESULT(result); + result = Dart_SetEnvironmentCallback(EnvironmentCallback); + CHECK_RESULT(result); + // Load the specified application script into the newly created isolate. // Prepare builtin and its dependent libraries for use to resolve URIs. @@ -906,6 +987,17 @@ int main(int argc, char** argv) { for (int i = 0; i < argc; i++) free(argv[i]); } + // Free environment if any. + if (environment != NULL) { + for (HashMap::Entry* p = environment->Start(); + p != NULL; + p = environment->Next(p)) { + free(p->key); + free(p->value); + } + free(environment); + } + return Process::GlobalExitCode(); } diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h index 3cc6fb5ea0e..6fe83feb160 100755 --- a/runtime/include/dart_api.h +++ b/runtime/include/dart_api.h @@ -1477,7 +1477,7 @@ DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str, /** * Gets the data corresponding to the string object. This function returns * the data only for Latin-1 (ISO-8859-1) string objects. For all other - * string objects it return and error. + * string objects it returns an error. * * \param str A string. * \param latin1_array An array allocated by the caller, used to return @@ -2106,6 +2106,32 @@ typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name, /* TODO(turnidge): Consider renaming to NativeFunctionResolver or * NativeResolver. */ +/* + * =========== + * Environment + * =========== + */ + +/** + * An environment lookup callback function. + * + * \param name The name of the value to lookup in the environment. + * + * \return A valid handle to a string if the name exists in the + * current environment or Dart_Null() if not. + */ +typedef Dart_Handle (*Dart_EnvironmentCallback)(Dart_Handle name); + +/** + * Sets the environment callback for the current isolate. This + * callback is used to lookup environment values by name in the + * current environment. This enables the embedder to supply values for + * the const constructors bool.fromEnvironment, int.fromEnvironment + * and String.fromEnvironment. + */ +DART_EXPORT Dart_Handle Dart_SetEnvironmentCallback( + Dart_EnvironmentCallback callback); + /** * Sets the callback used to resolve native functions for a library. * diff --git a/runtime/lib/bool.cc b/runtime/lib/bool.cc new file mode 100644 index 00000000000..ae4af27f930 --- /dev/null +++ b/runtime/lib/bool.cc @@ -0,0 +1,46 @@ +// 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/bootstrap_natives.h" + +#include "include/dart_api.h" +#include "vm/bigint_operations.h" +#include "vm/dart_entry.h" +#include "vm/dart_api_impl.h" +#include "vm/exceptions.h" +#include "vm/isolate.h" +#include "vm/native_entry.h" +#include "vm/object.h" +#include "vm/object_store.h" +#include "vm/symbols.h" + +namespace dart { + +DEFINE_NATIVE_ENTRY(Bool_fromEnvironment, 3) { + GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1)); + GET_NATIVE_ARGUMENT(Bool, default_value, arguments->NativeArgAt(2)); + // Call the embedder to supply us with the environment. + Dart_EnvironmentCallback callback = isolate->environment_callback(); + if (callback != NULL) { + Dart_Handle result = callback(Api::NewHandle(isolate, name.raw())); + if (Dart_IsError(result)) { + const Object& error = + Object::Handle(isolate, Api::UnwrapHandle(result)); + Exceptions::ThrowArgumentError( + String::Handle( + String::New(Error::Cast(error).ToErrorCString()))); + } else if (Dart_IsString(result)) { + const char *chars; + Dart_StringToCString(result, &chars); + return (strcmp("true", chars) == 0) + ? Bool::True().raw() : Bool::False().raw(); + } else if (!Dart_IsNull(result)) { + Exceptions::ThrowArgumentError( + String::Handle(String::New("Illegal environment value"))); + } + } + return default_value.raw(); +} + +} // namespace dart diff --git a/runtime/lib/bool_patch.dart b/runtime/lib/bool_patch.dart index e6463feab13..cd6b7408a0c 100644 --- a/runtime/lib/bool_patch.dart +++ b/runtime/lib/bool_patch.dart @@ -6,6 +6,10 @@ patch class bool { + /* patch */ const factory bool.fromEnvironment(String name, + {bool defaultValue}) + native "Bool_fromEnvironment"; + int get _identityHashCode { return this ? 1231 : 1237; } diff --git a/runtime/lib/corelib_sources.gypi b/runtime/lib/corelib_sources.gypi index 95a846d5299..dde67213ed9 100644 --- a/runtime/lib/corelib_sources.gypi +++ b/runtime/lib/corelib_sources.gypi @@ -8,6 +8,7 @@ 'sources': [ 'core_patch.dart', # The above file needs to be first as it imports required libraries. + 'bool.cc', 'bool_patch.dart', 'date.cc', 'date_patch.dart', diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc index 8df13969a51..1d93b40e1bf 100644 --- a/runtime/lib/integers.cc +++ b/runtime/lib/integers.cc @@ -4,9 +4,12 @@ #include "vm/bootstrap_natives.h" +#include "include/dart_api.h" #include "vm/bigint_operations.h" #include "vm/dart_entry.h" +#include "vm/dart_api_impl.h" #include "vm/exceptions.h" +#include "vm/isolate.h" #include "vm/native_entry.h" #include "vm/object.h" #include "vm/object_store.h" @@ -226,6 +229,61 @@ DEFINE_NATIVE_ENTRY(Integer_parse, 1) { } +DEFINE_NATIVE_ENTRY(Integer_fromEnvironment, 3) { + GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1)); + GET_NATIVE_ARGUMENT(Integer, default_value, arguments->NativeArgAt(2)); + // Call the embedder to supply us with the environment. + Dart_EnvironmentCallback callback = isolate->environment_callback(); + if (callback != NULL) { + Dart_Handle result = callback(Api::NewHandle(isolate, name.raw())); + if (Dart_IsError(result)) { + const Object& error = + Object::Handle(isolate, Api::UnwrapHandle(result)); + Exceptions::ThrowArgumentError( + String::Handle( + String::New(Error::Cast(error).ToErrorCString()))); + } else if (Dart_IsString(result)) { + uint8_t* digits; + intptr_t digits_len; + Dart_StringToUTF8(result, &digits, &digits_len); + if (digits_len > 0) { + // Check for valid integer literal before constructing integer object. + // Skip leading minus if present. + if (digits[0] == '-') { + digits++; + digits_len--; + } + // Check remaining string for decimal or hex-decimal literal. + bool is_number = true; + if (digits_len > 2 && + digits[0] == '0' && + (digits[1] == 'x' || digits[1] == 'X')) { + for (int i = 2; i < digits_len && is_number; i++) { + is_number = ('0' <= digits[i] && digits[i] <= '9') || + ('A' <= digits[i] && digits[i] <= 'F') || + ('a' <= digits[i] && digits[i] <= 'f'); + } + } else { + for (int i = 0; i < digits_len && is_number; i++) { + is_number = '0' <= digits[i] && digits[i] <= '9'; + } + } + if (digits_len > 0 && is_number) { + const Object& value = + Object::Handle(isolate, Api::UnwrapHandle(result)); + ASSERT(value.IsString()); + return Integer::NewCanonical(String::Cast(value)); + } + } + } else if (!Dart_IsNull(result)) { + Exceptions::ThrowArgumentError( + String::Handle(String::New("Illegal environment value"))); + } + } + return default_value.raw(); +} + + // Passing true for 'silent' prevents throwing JavascriptIntegerOverflow. static RawInteger* ShiftOperationHelper(Token::Kind kind, const Integer& value, diff --git a/runtime/lib/integers_patch.dart b/runtime/lib/integers_patch.dart index fab603f03ec..36c4d8fc812 100644 --- a/runtime/lib/integers_patch.dart +++ b/runtime/lib/integers_patch.dart @@ -87,6 +87,10 @@ patch class int { return _slowParse(source, radix, onError); } + /* patch */ const factory int.fromEnvironment(String name, + {int defaultValue}) + native "Integer_fromEnvironment"; + static int _slowParse(String source, int radix, int onError(String str)) { if (source is! String) throw new ArgumentError(source); if (radix is! int) throw new ArgumentError("Radix is not an integer"); diff --git a/runtime/lib/string.cc b/runtime/lib/string.cc index a6103c06268..ba42a57339a 100644 --- a/runtime/lib/string.cc +++ b/runtime/lib/string.cc @@ -4,7 +4,10 @@ #include "vm/bootstrap_natives.h" +#include "include/dart_api.h" #include "vm/exceptions.h" +#include "vm/dart_api_impl.h" +#include "vm/isolate.h" #include "vm/native_entry.h" #include "vm/object.h" #include "vm/symbols.h" @@ -12,6 +15,32 @@ namespace dart { +DEFINE_NATIVE_ENTRY(String_fromEnvironment, 3) { + GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1)); + GET_NATIVE_ARGUMENT(String, default_value, arguments->NativeArgAt(2)); + // Call the embedder to supply us with the environment. + Dart_EnvironmentCallback callback = isolate->environment_callback(); + if (callback != NULL) { + Dart_Handle result = callback(Api::NewHandle(isolate, name.raw())); + if (Dart_IsError(result)) { + const Object& error = + Object::Handle(isolate, Api::UnwrapHandle(result)); + Exceptions::ThrowArgumentError( + String::Handle( + String::New(Error::Cast(error).ToErrorCString()))); + } else if (Dart_IsString(result)) { + const Object& value = + Object::Handle(isolate, Api::UnwrapHandle(result)); + return Symbols::New(String::Cast(value)); + } else if (!Dart_IsNull(result)) { + Exceptions::ThrowArgumentError( + String::Handle(String::New("Illegal environment value"))); + } + } + return default_value.raw(); +} + + DEFINE_NATIVE_ENTRY(StringBase_createFromCodePoints, 1) { GET_NON_NULL_NATIVE_ARGUMENT(Instance, list, arguments->NativeArgAt(0)); if (!list.IsGrowableObjectArray() && !list.IsArray()) { diff --git a/runtime/lib/string_patch.dart b/runtime/lib/string_patch.dart index 4ffb8db6dac..aeb2150cd4a 100644 --- a/runtime/lib/string_patch.dart +++ b/runtime/lib/string_patch.dart @@ -6,6 +6,10 @@ patch class String { /* patch */ factory String.fromCharCodes(Iterable charCodes) { return _StringBase.createFromCharCodes(charCodes); } + + /* patch */ const factory String.fromEnvironment(String name, + {String defaultValue}) + native "String_fromEnvironment"; } diff --git a/runtime/platform/hashmap.h b/runtime/platform/hashmap.h index 01fad72157f..d5a02930f45 100644 --- a/runtime/platform/hashmap.h +++ b/runtime/platform/hashmap.h @@ -17,6 +17,27 @@ class HashMap { return key1 == key2; } + static uint32_t StringHash(char* key) { + uint32_t hash_ = 0; + if (key == NULL) return hash_; + int len = strlen(key); + for (int i = 0; i < len; i++) { + hash_ += key[i]; + hash_ += hash_ << 10; + hash_ ^= hash_ >> 6; + } + hash_ += hash_ << 3; + hash_ ^= hash_ >> 11; + hash_ += hash_ << 15; + return hash_ == 0 ? 1 : hash_; + } + + static bool SameStringValue(void* key1, void* key2) { + return strcmp(reinterpret_cast(key1), + reinterpret_cast(key2)) == 0; + } + + // initial_capacity is the size of the initial hash map; // it must be a power of 2 (and thus must not be 0). HashMap(MatchFun match, uint32_t initial_capacity); diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h index e4553e300f3..fa0b058c364 100644 --- a/runtime/vm/bootstrap_natives.h +++ b/runtime/vm/bootstrap_natives.h @@ -37,8 +37,10 @@ namespace dart { V(Integer_moduloFromInteger, 2) \ V(Integer_greaterThanFromInteger, 2) \ V(Integer_equalToInteger, 2) \ + V(Integer_fromEnvironment, 3) \ V(Integer_parse, 1) \ V(Integer_leftShiftWithMask32, 3) \ + V(Bool_fromEnvironment, 3) \ V(RawReceivePortImpl_factory, 1) \ V(RawReceivePortImpl_closeInternal, 1) \ V(SendPortImpl_sendInternal_, 3) \ @@ -100,6 +102,7 @@ namespace dart { V(String_charAt, 2) \ V(String_codeUnitAt, 2) \ V(String_concat, 2) \ + V(String_fromEnvironment, 3) \ V(String_toLowerCase, 1) \ V(String_toUpperCase, 1) \ V(String_concatRange, 3) \ diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc index 32fc7bfa6c8..b3b9ce43b49 100644 --- a/runtime/vm/dart_api_impl.cc +++ b/runtime/vm/dart_api_impl.cc @@ -3910,6 +3910,17 @@ DART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args, } +// --- Environment --- +DART_EXPORT Dart_Handle Dart_SetEnvironmentCallback( + Dart_EnvironmentCallback callback) { + Isolate* isolate = Isolate::Current(); + CHECK_ISOLATE(isolate); + isolate->set_environment_callback(callback); + return Api::Success(); +} + + +// --- Scripts and Libraries --- DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args, bool retval) { TRACE_API_CALL(CURRENT_FUNC); diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index cecc0888084..49eb2082fc2 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -284,6 +284,7 @@ Isolate::Isolate() top_context_(Context::null()), top_exit_frame_info_(0), init_callback_data_(NULL), + environment_callback_(NULL), library_tag_handler_(NULL), api_state_(NULL), stub_code_(NULL), diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index f42247e3482..bd3d0be33bc 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -174,6 +174,13 @@ class Isolate : public BaseIsolate { return init_callback_data_; } + Dart_EnvironmentCallback environment_callback() const { + return environment_callback_; + } + void set_environment_callback(Dart_EnvironmentCallback value) { + environment_callback_ = value; + } + Dart_LibraryTagHandler library_tag_handler() const { return library_tag_handler_; } @@ -393,6 +400,7 @@ class Isolate : public BaseIsolate { RawContext* top_context_; uword top_exit_frame_info_; void* init_callback_data_; + Dart_EnvironmentCallback environment_callback_; Dart_LibraryTagHandler library_tag_handler_; ApiState* api_state_; StubCode* stub_code_; diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc index 85a7253c851..5f277902398 100644 --- a/runtime/vm/parser.cc +++ b/runtime/vm/parser.cc @@ -3104,9 +3104,13 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { ErrorMsg(method->name_pos, "external method '%s' may not have a function body", method->name->ToCString()); - } else if (method->IsFactoryOrConstructor() && method->has_const) { + } else if (method->IsConstructor() && method->has_const) { ErrorMsg(method->name_pos, - "const constructor or factory '%s' may not have a function body", + "const constructor '%s' may not have a function body", + method->name->ToCString()); + } else if (method->IsFactory() && method->has_const) { + ErrorMsg(method->name_pos, + "const factory '%s' may not have a function body", method->name->ToCString()); } if (method->redirect_name != NULL) { @@ -3128,9 +3132,9 @@ void Parser::ParseMethodOrConstructor(ClassDesc* members, MemberDesc* method) { ErrorMsg(method->name_pos, "abstract method '%s' may not have a function body", method->name->ToCString()); - } else if (method->IsFactoryOrConstructor() && method->has_const) { + } else if (method->IsConstructor() && method->has_const) { ErrorMsg(method->name_pos, - "const constructor or factory '%s' may not be native", + "const constructor '%s' may not be native", method->name->ToCString()); } if (method->redirect_name != NULL) { @@ -8706,21 +8710,28 @@ RawObject* Parser::EvaluateConstConstructorCall( const AbstractTypeArguments& type_arguments, const Function& constructor, ArgumentListNode* arguments) { - const int kNumExtraArgs = 2; // implicit rcvr and construction phase args. + // Factories have one extra argument: the type arguments. + // Constructors have 2 extra arguments: rcvr and construction phase. + const int kNumExtraArgs = constructor.IsFactory() ? 1 : 2; const int num_arguments = arguments->length() + kNumExtraArgs; const Array& arg_values = Array::Handle(Array::New(num_arguments)); Instance& instance = Instance::Handle(); - ASSERT(!constructor.IsFactory()); - instance = Instance::New(type_class, Heap::kOld); - if (!type_arguments.IsNull()) { - if (!type_arguments.IsInstantiated()) { - ErrorMsg("type must be constant in const constructor"); + if (!constructor.IsFactory()) { + instance = Instance::New(type_class, Heap::kOld); + if (!type_arguments.IsNull()) { + if (!type_arguments.IsInstantiated()) { + ErrorMsg("type must be constant in const constructor"); + } + instance.SetTypeArguments( + AbstractTypeArguments::Handle(type_arguments.Canonicalize())); } - instance.SetTypeArguments( - AbstractTypeArguments::Handle(type_arguments.Canonicalize())); + arg_values.SetAt(0, instance); + arg_values.SetAt(1, Smi::Handle(Smi::New(Function::kCtorPhaseAll))); + } else { + // Prepend type_arguments to list of arguments to factory. + ASSERT(type_arguments.IsZoneHandle()); + arg_values.SetAt(0, type_arguments); } - arg_values.SetAt(0, instance); - arg_values.SetAt(1, Smi::Handle(Smi::New(Function::kCtorPhaseAll))); for (int i = 0; i < arguments->length(); i++) { AstNode* arg = arguments->NodeAt(i); // Arguments have been evaluated to a literal value already. @@ -8747,6 +8758,10 @@ RawObject* Parser::EvaluateConstConstructorCall( return Object::null(); } } else { + if (constructor.IsFactory()) { + // The factory method returns the allocated object. + instance ^= result.raw(); + } return TryCanonicalize(instance, TokenPos()); } } @@ -9890,7 +9905,10 @@ AstNode* Parser::ParseNewOperator(Token::Kind op_kind) { new_pos, "error while evaluating const constructor"); } else { - const Instance& const_instance = Instance::Cast(constructor_result); + // Const constructors can return null in the case where a const native + // factory returns a null value. Thus we cannot use a Instance::Cast here. + Instance& const_instance = Instance::Handle(); + const_instance ^= constructor_result.raw(); new_object = new LiteralNode(new_pos, Instance::ZoneHandle(const_instance.raw())); if (!type_bound.IsNull()) { diff --git a/sdk/lib/_internal/lib/core_patch.dart b/sdk/lib/_internal/lib/core_patch.dart index cb844e0410f..8e5bec02c81 100644 --- a/sdk/lib/_internal/lib/core_patch.dart +++ b/sdk/lib/_internal/lib/core_patch.dart @@ -102,6 +102,11 @@ patch class int { int onError(String source) }) { return Primitives.parseInt(source, radix, onError); } + + patch factory int.fromEnvironment(String name, {int defaultValue}) { + throw new UnsupportedError( + 'int.fromEnvironement can only be used as a const constructor'); + } } patch class double { @@ -219,6 +224,18 @@ patch class String { } return Primitives.stringFromCharCodes(charCodes); } + + patch factory String.fromEnvironment(String name, {String defaultValue}) { + throw new UnsupportedError( + 'String.fromEnvironement can only be used as a const constructor'); + } +} + +patch class bool { + patch factory bool.fromEnvironment(String name, {bool defaultValue}) { + throw new UnsupportedError( + 'bool.fromEnvironement can only be used as a const constructor'); + } } patch class RegExp { diff --git a/sdk/lib/core/bool.dart b/sdk/lib/core/bool.dart index 6c7797fc4cb..c4348b71173 100644 --- a/sdk/lib/core/bool.dart +++ b/sdk/lib/core/bool.dart @@ -12,10 +12,11 @@ part of dart.core; * bool. */ class bool { - factory bool._uninstantiable() { - throw new UnsupportedError( - "class bool cannot be instantiated"); - } + /** + * Returns the boolean for the given environment variable [name] or + * [defaultValue] if [name] is not present. + */ + external const factory bool.fromEnvironment(String name, {bool defaultValue}); /** * Returns [:"true":] if the receiver is [:true:], or [:"false":] if the diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart index a5235bbd66b..1c17a317b39 100644 --- a/sdk/lib/core/int.dart +++ b/sdk/lib/core/int.dart @@ -252,4 +252,12 @@ abstract class int extends num { external static int parse(String source, { int radix, int onError(String source) }); + + /** + * Returns the integer value for the given environment variable + * [name] or [defaultValue] if [name] is not present. If the value + * of the environment variable is not a valid integer literal a + * [FormatException] is thrown. + */ + external const factory int.fromEnvironment(String name, {int defaultValue}); } diff --git a/sdk/lib/core/string.dart b/sdk/lib/core/string.dart index 488078d803a..8da434e7d52 100644 --- a/sdk/lib/core/string.dart +++ b/sdk/lib/core/string.dart @@ -124,6 +124,13 @@ abstract class String implements Comparable, Pattern { return new String.fromCharCodes(charCodes); } + /** + * Returns the string for the given environment variable [name] or + * [defaultValue] if [name] is not present. + */ + external const factory String.fromEnvironment(String name, + {String defaultValue}); + /** * Gets the character (as a single-code-unit [String]) at the given [index]. * diff --git a/tests/corelib/bool_from_environment2_test.dart b/tests/corelib/bool_from_environment2_test.dart new file mode 100644 index 00000000000..0a9bff7933e --- /dev/null +++ b/tests/corelib/bool_from_environment2_test.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, 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. + +main() { + const bool.fromEnvironment('NOT_FOUND', defaultValue: ''); /// 01: compile-time error + const bool.fromEnvironment('NOT_FOUND', defaultValue: 1); /// 02: compile-time error + const bool.fromEnvironment(null); /// 03: compile-time error + const bool.fromEnvironment(1); /// 04: compile-time error + const bool.fromEnvironment([]); /// 05: compile-time error +} diff --git a/tests/corelib/bool_from_environment_default_value_test.dart b/tests/corelib/bool_from_environment_default_value_test.dart new file mode 100644 index 00000000000..82acf265a02 --- /dev/null +++ b/tests/corelib/bool_from_environment_default_value_test.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2013, 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. + +import "package:expect/expect.dart"; + +main() { + Expect.isNull(const bool.fromEnvironment('NOT_FOUND')); + Expect.isTrue(const bool.fromEnvironment('NOT_FOUND', defaultValue: true)); + Expect.isFalse(const bool.fromEnvironment('NOT_FOUND', defaultValue: false)); + Expect.isNull(const bool.fromEnvironment('NOT_FOUND', defaultValue: null)); +} diff --git a/tests/corelib/bool_from_environment_test.dart b/tests/corelib/bool_from_environment_test.dart new file mode 100644 index 00000000000..66351e458f6 --- /dev/null +++ b/tests/corelib/bool_from_environment_test.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, 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. +// SharedOptions=-Da=true -Db=false + +import "package:expect/expect.dart"; + +main() { + Expect.isTrue(const bool.fromEnvironment('a')); + Expect.isFalse(const bool.fromEnvironment('b')); +} diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status index 9e5e67343b7..8f88ac0e6fc 100644 --- a/tests/corelib/corelib.status +++ b/tests/corelib/corelib.status @@ -2,6 +2,33 @@ # 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. +# Skip these tests until XXX.fromEnvironment in dart2js lands. +[ $compiler == dart2js || $compiler == dart2dart] +bool_from_environment2_test: Skip +bool_from_environment_default_value_test: Skip +bool_from_environment_test: Skip +int_from_environment2_test: Skip +int_from_environment3_test: Skip +int_from_environment_default_value_test: Skip +int_from_environment_test: Skip +string_from_environment2_test: Skip +string_from_environment3_test: Skip +string_from_environment_default_value: Skip +string_from_environment_test: Skip + +[ $compiler == none && $runtime == drt ] +bool_from_environment2_test: Skip +bool_from_environment_default_value_test: Skip +bool_from_environment_test: Skip +int_from_environment2_test: Skip +int_from_environment3_test: Skip +int_from_environment_default_value_test: Skip +int_from_environment_test: Skip +string_from_environment2_test: Skip +string_from_environment3_test: Skip +string_from_environment_default_value: Skip +string_from_environment_test: Skip + [ $compiler == none ] unicode_test: Fail # Bug 6706 compare_to2_test: Fail # Bug 4018 diff --git a/tests/corelib/int_from_environment2_test.dart b/tests/corelib/int_from_environment2_test.dart new file mode 100644 index 00000000000..8b145269219 --- /dev/null +++ b/tests/corelib/int_from_environment2_test.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2013, 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. +// SharedOptions=-Da=x -Db=- -Dc=0xg + +import "package:expect/expect.dart"; + +main() { + Expect.isNull(const int.fromEnvironment('a')); + Expect.isNull(const int.fromEnvironment('b')); + Expect.isNull(const int.fromEnvironment('c')); +} diff --git a/tests/corelib/int_from_environment3_test.dart b/tests/corelib/int_from_environment3_test.dart new file mode 100644 index 00000000000..707add88181 --- /dev/null +++ b/tests/corelib/int_from_environment3_test.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, 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. + +main() { + const int.fromEnvironment('NOT_FOUND', defaultValue: ''); /// 01: compile-time error + const int.fromEnvironment('NOT_FOUND', defaultValue: true); /// 02: compile-time error + const int.fromEnvironment(null); /// 03: compile-time error + const int.fromEnvironment(1); /// 04: compile-time error + const int.fromEnvironment([]); /// 05: compile-time error +} diff --git a/tests/corelib/int_from_environment_default_value_test.dart b/tests/corelib/int_from_environment_default_value_test.dart new file mode 100644 index 00000000000..bb318d00c5a --- /dev/null +++ b/tests/corelib/int_from_environment_default_value_test.dart @@ -0,0 +1,10 @@ +// Copyright (c) 2013, 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. + +import "package:expect/expect.dart"; + +main() { + Expect.isNull(const int.fromEnvironment('NOT_FOUND')); + Expect.equals(12345, const int.fromEnvironment('NOT_FOUND', defaultValue: 12345)); +} diff --git a/tests/corelib/int_from_environment_test.dart b/tests/corelib/int_from_environment_test.dart new file mode 100644 index 00000000000..f842fddb4c3 --- /dev/null +++ b/tests/corelib/int_from_environment_test.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2013, 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. +// SharedOptions=-Da=1 -Db=-12 -Dc=0x123 -Dd=-0x1234 + +import "package:expect/expect.dart"; + +main() { + Expect.equals(1, const int.fromEnvironment('a')); + Expect.equals(-12, const int.fromEnvironment('b')); + Expect.equals(0x123, const int.fromEnvironment('c')); + Expect.equals(-0x1234, const int.fromEnvironment('d')); +} diff --git a/tests/corelib/string_from_environment2_test.dart b/tests/corelib/string_from_environment2_test.dart new file mode 100644 index 00000000000..b6d81b816d7 --- /dev/null +++ b/tests/corelib/string_from_environment2_test.dart @@ -0,0 +1,12 @@ +// Copyright (c) 2013, 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. +// SharedOptions=-Da=a -Da=bb -Db=bb -Dc=ccc -Da=ccc -Db=ccc + +import "package:expect/expect.dart"; + +main() { + Expect.equals('ccc', const String.fromEnvironment('a')); + Expect.equals('ccc', const String.fromEnvironment('b')); + Expect.equals('ccc', const String.fromEnvironment('c')); +} diff --git a/tests/corelib/string_from_environment3_test.dart b/tests/corelib/string_from_environment3_test.dart new file mode 100644 index 00000000000..589cca2d52f --- /dev/null +++ b/tests/corelib/string_from_environment3_test.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, 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. + +main() { + const String.fromEnvironment('NOT_FOUND', defaultValue: 1); /// 01: compile-time error + const String.fromEnvironment('NOT_FOUND', defaultValue: true); /// 02: compile-time error + const String.fromEnvironment(null); /// 03: compile-time error + const String.fromEnvironment(1); /// 04: compile-time error + const String.fromEnvironment([]); /// 05: compile-time error +} diff --git a/tests/corelib/string_from_environment_default_value.dart b/tests/corelib/string_from_environment_default_value.dart new file mode 100644 index 00000000000..3d236e7ea4e --- /dev/null +++ b/tests/corelib/string_from_environment_default_value.dart @@ -0,0 +1,11 @@ +// Copyright (c) 2013, 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. + +import "package:expect/expect.dart"; + +main() { + Expect.isNull(const String.fromEnvironment('NOT_FOUND')); + Expect.equals('x', + const String.fromEnvironment('NOT_FOUND', defaultValue: 'x')); +} diff --git a/tests/corelib/string_from_environment_test.dart b/tests/corelib/string_from_environment_test.dart new file mode 100644 index 00000000000..2134e44da1f --- /dev/null +++ b/tests/corelib/string_from_environment_test.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2013, 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. +// SharedOptions=-Da=a -Db=bb -Dc=ccc -Dd= + +import "package:expect/expect.dart"; + +main() { + Expect.equals('a', const String.fromEnvironment('a')); + Expect.equals('bb', const String.fromEnvironment('b')); + Expect.equals('ccc', const String.fromEnvironment('c')); + Expect.equals('', const String.fromEnvironment('d')); +}