| // 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/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_FLAG(bool, trace_intrinsified_natives, false, |
| "Report if any of the intrinsified natives are called"); |
| |
| // Smi natives. |
| |
| // Returns false if integer is in wrong representation, e.g., as is a Bigint |
| // when it could have been a Smi. |
| static bool CheckInteger(const Integer& i) { |
| if (i.IsBigint()) { |
| const Bigint& bigint = Bigint::Cast(i); |
| return !bigint.FitsIntoSmi() && !bigint.FitsIntoInt64(); |
| } |
| if (i.IsMint()) { |
| const Mint& mint = Mint::Cast(i); |
| return !Smi::IsValid(mint.value()); |
| } |
| return true; |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_bitAndFromInteger, 2) { |
| const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right)); |
| ASSERT(CheckInteger(left)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_bitAndFromInteger %s & %s\n", |
| right.ToCString(), left.ToCString()); |
| } |
| const Integer& result = Integer::Handle(left.BitOp(Token::kBIT_AND, right)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_bitOrFromInteger, 2) { |
| const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right)); |
| ASSERT(CheckInteger(left)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_bitOrFromInteger %s | %s\n", |
| left.ToCString(), right.ToCString()); |
| } |
| const Integer& result = Integer::Handle(left.BitOp(Token::kBIT_OR, right)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_bitXorFromInteger, 2) { |
| const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right)); |
| ASSERT(CheckInteger(left)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_bitXorFromInteger %s ^ %s\n", |
| left.ToCString(), right.ToCString()); |
| } |
| const Integer& result = Integer::Handle(left.BitOp(Token::kBIT_XOR, right)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_addFromInteger, 2) { |
| const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right_int)); |
| ASSERT(CheckInteger(left_int)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_addFromInteger %s + %s\n", |
| left_int.ToCString(), right_int.ToCString()); |
| } |
| const Integer& result = |
| Integer::Handle(left_int.ArithmeticOp(Token::kADD, right_int)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_subFromInteger, 2) { |
| const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right_int)); |
| ASSERT(CheckInteger(left_int)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_subFromInteger %s - %s\n", |
| left_int.ToCString(), right_int.ToCString()); |
| } |
| const Integer& result = |
| Integer::Handle(left_int.ArithmeticOp(Token::kSUB, right_int)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_mulFromInteger, 2) { |
| const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right_int)); |
| ASSERT(CheckInteger(left_int)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_mulFromInteger %s * %s\n", |
| left_int.ToCString(), right_int.ToCString()); |
| } |
| const Integer& result = |
| Integer::Handle(left_int.ArithmeticOp(Token::kMUL, right_int)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_truncDivFromInteger, 2) { |
| const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right_int)); |
| ASSERT(CheckInteger(left_int)); |
| ASSERT(!right_int.IsZero()); |
| const Integer& result = |
| Integer::Handle(left_int.ArithmeticOp(Token::kTRUNCDIV, right_int)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_moduloFromInteger, 2) { |
| const Integer& right_int = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left_int, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right_int)); |
| ASSERT(CheckInteger(right_int)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_moduloFromInteger %s mod %s\n", |
| left_int.ToCString(), right_int.ToCString()); |
| } |
| if (right_int.IsZero()) { |
| // Should have been caught before calling into runtime. |
| UNIMPLEMENTED(); |
| } |
| const Integer& result = |
| Integer::Handle(left_int.ArithmeticOp(Token::kMOD, right_int)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_greaterThanFromInteger, 2) { |
| const Integer& right = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, left, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(right)); |
| ASSERT(CheckInteger(left)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_greaterThanFromInteger %s > %s\n", |
| left.ToCString(), right.ToCString()); |
| } |
| return Bool::Get(left.CompareWith(right) == 1).raw(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_equalToInteger, 2) { |
| const Integer& left = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, right, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(left)); |
| ASSERT(CheckInteger(right)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Integer_equalToInteger %s == %s\n", |
| left.ToCString(), right.ToCString()); |
| } |
| return Bool::Get(left.CompareWith(right) == 0).raw(); |
| } |
| |
| |
| static RawInteger* ParseInteger(const String& value) { |
| // Used by both Integer_parse and Integer_fromEnvironment. |
| if (value.IsOneByteString()) { |
| // Quick conversion for unpadded integers in strings. |
| const intptr_t len = value.Length(); |
| if (len > 0) { |
| const char* cstr = value.ToCString(); |
| ASSERT(cstr != NULL); |
| char* p_end = NULL; |
| const int64_t int_value = strtoll(cstr, &p_end, 10); |
| if (p_end == (cstr + len)) { |
| if ((int_value != LLONG_MIN) && (int_value != LLONG_MAX)) { |
| return Integer::New(int_value); |
| } |
| } |
| } |
| } |
| |
| Scanner scanner(value, Symbols::Empty()); |
| const Scanner::GrowableTokenStream& tokens = scanner.GetStream(); |
| const String* int_string; |
| bool is_positive; |
| if (Scanner::IsValidLiteral(tokens, |
| Token::kINTEGER, |
| &is_positive, |
| &int_string)) { |
| if (is_positive) { |
| return Integer::New(*int_string); |
| } |
| String& temp = String::Handle(); |
| temp = String::Concat(Symbols::Dash(), *int_string); |
| return Integer::New(temp); |
| } |
| |
| return Integer::null(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_parse, 1) { |
| GET_NON_NULL_NATIVE_ARGUMENT(String, value, arguments->NativeArgAt(0)); |
| return ParseInteger(value); |
| } |
| |
| |
| 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. |
| const String& env_value = |
| String::Handle(Api::CallEnvironmentCallback(isolate, name)); |
| if (!env_value.IsNull()) { |
| const Integer& result = Integer::Handle(ParseInteger(env_value)); |
| if (!result.IsNull()) { |
| if (result.IsSmi()) { |
| return result.raw(); |
| } |
| return result.CheckAndCanonicalize(NULL); |
| } |
| } |
| return default_value.raw(); |
| } |
| |
| |
| // Passing true for 'silent' prevents throwing JavascriptIntegerOverflow. |
| static RawInteger* ShiftOperationHelper(Token::Kind kind, |
| const Integer& value, |
| const Smi& amount, |
| const bool silent = false) { |
| if (amount.Value() < 0) { |
| Exceptions::ThrowArgumentError(amount); |
| } |
| if (value.IsSmi()) { |
| const Smi& smi_value = Smi::Cast(value); |
| return smi_value.ShiftOp(kind, amount, silent); |
| } |
| if (value.IsMint()) { |
| const int64_t mint_value = value.AsInt64Value(); |
| const int count = Utils::HighestBit(mint_value); |
| intptr_t shift_count = amount.Value(); |
| if (kind == Token::kSHR) { |
| shift_count = -shift_count; |
| } |
| if ((count + shift_count) < Mint::kBits) { |
| switch (kind) { |
| case Token::kSHL: |
| return Integer::New(mint_value << shift_count, Heap::kNew, silent); |
| case Token::kSHR: |
| return Integer::New(mint_value >> -shift_count, Heap::kNew, silent); |
| default: |
| UNIMPLEMENTED(); |
| } |
| } else { |
| // Overflow in shift, use Bigints |
| return Integer::null(); |
| } |
| } else { |
| ASSERT(value.IsBigint()); |
| } |
| return Integer::null(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Integer_leftShiftWithMask32, 3) { |
| const Integer& value = Integer::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, shift_count, arguments->NativeArgAt(1)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, mask, arguments->NativeArgAt(2)); |
| ASSERT(CheckInteger(value)); |
| ASSERT(CheckInteger(shift_count)); |
| ASSERT(CheckInteger(mask)); |
| if (!shift_count.IsSmi()) { |
| // Shift count is too large.. |
| const Instance& exception = |
| Instance::Handle(isolate->object_store()->out_of_memory()); |
| Exceptions::Throw(isolate, exception); |
| } |
| const Smi& smi_shift_count = Smi::Cast(shift_count); |
| const Integer& shift_result = Integer::Handle( |
| ShiftOperationHelper(Token::kSHL, value, smi_shift_count, true)); |
| const Integer& result = |
| Integer::Handle(shift_result.BitOp(Token::kBIT_AND, mask)); |
| return result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Smi_shrFromInt, 2) { |
| const Smi& amount = Smi::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(amount)); |
| ASSERT(CheckInteger(value)); |
| const Integer& result = Integer::Handle( |
| ShiftOperationHelper(Token::kSHR, value, amount)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| |
| DEFINE_NATIVE_ENTRY(Smi_shlFromInt, 2) { |
| const Smi& amount = Smi::CheckedHandle(arguments->NativeArgAt(0)); |
| GET_NON_NULL_NATIVE_ARGUMENT(Integer, value, arguments->NativeArgAt(1)); |
| ASSERT(CheckInteger(amount)); |
| ASSERT(CheckInteger(value)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Smi_shlFromInt: %s << %s\n", |
| value.ToCString(), amount.ToCString()); |
| } |
| const Integer& result = Integer::Handle( |
| ShiftOperationHelper(Token::kSHL, value, amount)); |
| // A null result indicates that a bigint operation is required. |
| return result.IsNull() ? result.raw() : result.AsValidInteger(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Smi_bitNegate, 1) { |
| const Smi& operand = Smi::CheckedHandle(arguments->NativeArgAt(0)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Smi_bitNegate: %s\n", operand.ToCString()); |
| } |
| intptr_t result = ~operand.Value(); |
| ASSERT(Smi::IsValid(result)); |
| return Smi::New(result); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Smi_bitLength, 1) { |
| const Smi& operand = Smi::CheckedHandle(arguments->NativeArgAt(0)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Smi_bitLength: %s\n", operand.ToCString()); |
| } |
| int64_t value = operand.AsInt64Value(); |
| intptr_t result = Utils::BitLength(value); |
| ASSERT(Smi::IsValid(result)); |
| return Smi::New(result); |
| } |
| |
| |
| // Mint natives. |
| |
| DEFINE_NATIVE_ENTRY(Mint_bitNegate, 1) { |
| const Mint& operand = Mint::CheckedHandle(arguments->NativeArgAt(0)); |
| ASSERT(CheckInteger(operand)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Mint_bitNegate: %s\n", operand.ToCString()); |
| } |
| int64_t result = ~operand.value(); |
| return Integer::New(result); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Mint_bitLength, 1) { |
| const Mint& operand = Mint::CheckedHandle(arguments->NativeArgAt(0)); |
| ASSERT(CheckInteger(operand)); |
| if (FLAG_trace_intrinsified_natives) { |
| OS::Print("Mint_bitLength: %s\n", operand.ToCString()); |
| } |
| int64_t value = operand.AsInt64Value(); |
| intptr_t result = Utils::BitLength(value); |
| ASSERT(Smi::IsValid(result)); |
| return Smi::New(result); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Mint_shlFromInt, 2) { |
| // 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(isolate, exception); |
| UNREACHABLE(); |
| return 0; |
| } |
| |
| |
| // Bigint natives. |
| |
| DEFINE_NATIVE_ENTRY(Bigint_getNeg, 1) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| return bigint.neg(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_setNeg, 2) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| const Bool& neg = Bool::CheckedHandle(arguments->NativeArgAt(1)); |
| bigint.set_neg(neg); |
| return Object::null(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_getUsed, 1) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| return bigint.used(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_setUsed, 2) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| const Smi& used = Smi::CheckedHandle(arguments->NativeArgAt(1)); |
| bigint.set_used(used); |
| return Object::null(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_getDigits, 1) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| return bigint.digits(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_setDigits, 2) { |
| const Bigint& bigint = Bigint::CheckedHandle(arguments->NativeArgAt(0)); |
| const TypedData& digits = TypedData::CheckedHandle(arguments->NativeArgAt(1)); |
| bigint.set_digits(digits); |
| return Object::null(); |
| } |
| |
| |
| DEFINE_NATIVE_ENTRY(Bigint_allocate, 1) { |
| // Argument is null type arguments, since class Bigint is not parameterized. |
| return Bigint::New(); |
| } |
| |
| } // namespace dart |