1// Copyright 2017 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#ifndef V8_INTL_SUPPORT 6#error Internationalization is expected to be enabled. 7#endif // V8_INTL_SUPPORT 8 9#include "src/builtins/builtins-iterator-gen.h" 10#include "src/builtins/builtins-utils-gen.h" 11#include "src/codegen/code-stub-assembler.h" 12#include "src/objects/js-list-format-inl.h" 13#include "src/objects/js-list-format.h" 14#include "src/objects/objects-inl.h" 15#include "src/objects/objects.h" 16 17namespace v8 { 18namespace internal { 19 20class IntlBuiltinsAssembler : public CodeStubAssembler { 21 public: 22 explicit IntlBuiltinsAssembler(compiler::CodeAssemblerState* state) 23 : CodeStubAssembler(state) {} 24 25 void ListFormatCommon(TNode<Context> context, TNode<Int32T> argc, 26 Runtime::FunctionId format_func_id, 27 const char* method_name); 28 29 TNode<JSArray> AllocateEmptyJSArray(TNode<Context> context); 30 31 TNode<IntPtrT> PointerToSeqStringData(TNode<String> seq_string) { 32 CSA_DCHECK(this, 33 IsSequentialStringInstanceType(LoadInstanceType(seq_string))); 34 STATIC_ASSERT(SeqOneByteString::kHeaderSize == 35 SeqTwoByteString::kHeaderSize); 36 return IntPtrAdd( 37 BitcastTaggedToWord(seq_string), 38 IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag)); 39 } 40}; 41 42TF_BUILTIN(StringToLowerCaseIntl, IntlBuiltinsAssembler) { 43 const auto string = Parameter<String>(Descriptor::kString); 44 45 Label call_c(this), return_string(this), runtime(this, Label::kDeferred); 46 47 // Early exit on empty strings. 48 const TNode<Uint32T> length = LoadStringLengthAsWord32(string); 49 GotoIf(Word32Equal(length, Uint32Constant(0)), &return_string); 50 51 // Unpack strings if possible, and bail to runtime unless we get a one-byte 52 // flat string. 53 ToDirectStringAssembler to_direct( 54 state(), string, ToDirectStringAssembler::kDontUnpackSlicedStrings); 55 to_direct.TryToDirect(&runtime); 56 57 const TNode<Int32T> instance_type = to_direct.instance_type(); 58 CSA_DCHECK(this, 59 Word32BinaryNot(IsIndirectStringInstanceType(instance_type))); 60 GotoIfNot(IsOneByteStringInstanceType(instance_type), &runtime); 61 62 // For short strings, do the conversion in CSA through the lookup table. 63 64 const TNode<String> dst = AllocateSeqOneByteString(length); 65 66 const int kMaxShortStringLength = 24; // Determined empirically. 67 GotoIf(Uint32GreaterThan(length, Uint32Constant(kMaxShortStringLength)), 68 &call_c); 69 70 { 71 const TNode<IntPtrT> dst_ptr = PointerToSeqStringData(dst); 72 TVARIABLE(IntPtrT, var_cursor, IntPtrConstant(0)); 73 74 const TNode<IntPtrT> start_address = 75 ReinterpretCast<IntPtrT>(to_direct.PointerToData(&call_c)); 76 const TNode<IntPtrT> end_address = 77 Signed(IntPtrAdd(start_address, ChangeUint32ToWord(length))); 78 79 const TNode<ExternalReference> to_lower_table_addr = 80 ExternalConstant(ExternalReference::intl_to_latin1_lower_table()); 81 82 TVARIABLE(Word32T, var_did_change, Int32Constant(0)); 83 84 VariableList push_vars({&var_cursor, &var_did_change}, zone()); 85 BuildFastLoop<IntPtrT>( 86 push_vars, start_address, end_address, 87 [&](TNode<IntPtrT> current) { 88 TNode<Uint8T> c = Load<Uint8T>(current); 89 TNode<Uint8T> lower = 90 Load<Uint8T>(to_lower_table_addr, ChangeInt32ToIntPtr(c)); 91 StoreNoWriteBarrier(MachineRepresentation::kWord8, dst_ptr, 92 var_cursor.value(), lower); 93 94 var_did_change = 95 Word32Or(Word32NotEqual(c, lower), var_did_change.value()); 96 97 Increment(&var_cursor); 98 }, 99 kCharSize, IndexAdvanceMode::kPost); 100 101 // Return the original string if it remained unchanged in order to preserve 102 // e.g. internalization and private symbols (such as the preserved object 103 // hash) on the source string. 104 GotoIfNot(var_did_change.value(), &return_string); 105 106 Return(dst); 107 } 108 109 // Call into C for case conversion. The signature is: 110 // String ConvertOneByteToLower(String src, String dst); 111 BIND(&call_c); 112 { 113 const TNode<String> src = to_direct.string(); 114 115 const TNode<ExternalReference> function_addr = 116 ExternalConstant(ExternalReference::intl_convert_one_byte_to_lower()); 117 118 MachineType type_tagged = MachineType::AnyTagged(); 119 120 const TNode<String> result = CAST(CallCFunction( 121 function_addr, type_tagged, std::make_pair(type_tagged, src), 122 std::make_pair(type_tagged, dst))); 123 124 Return(result); 125 } 126 127 BIND(&return_string); 128 Return(string); 129 130 BIND(&runtime); 131 { 132 const TNode<Object> result = CallRuntime(Runtime::kStringToLowerCaseIntl, 133 NoContextConstant(), string); 134 Return(result); 135 } 136} 137 138TF_BUILTIN(StringPrototypeToLowerCaseIntl, IntlBuiltinsAssembler) { 139 auto maybe_string = Parameter<Object>(Descriptor::kReceiver); 140 auto context = Parameter<Context>(Descriptor::kContext); 141 142 TNode<String> string = 143 ToThisString(context, maybe_string, "String.prototype.toLowerCase"); 144 145 Return(CallBuiltin(Builtin::kStringToLowerCaseIntl, context, string)); 146} 147 148void IntlBuiltinsAssembler::ListFormatCommon(TNode<Context> context, 149 TNode<Int32T> argc, 150 Runtime::FunctionId format_func_id, 151 const char* method_name) { 152 CodeStubArguments args(this, argc); 153 154 // Label has_list(this); 155 // 1. Let lf be this value. 156 // 2. If Type(lf) is not Object, throw a TypeError exception. 157 TNode<Object> receiver = args.GetReceiver(); 158 159 // 3. If lf does not have an [[InitializedListFormat]] internal slot, throw a 160 // TypeError exception. 161 ThrowIfNotInstanceType(context, receiver, JS_LIST_FORMAT_TYPE, method_name); 162 TNode<JSListFormat> list_format = CAST(receiver); 163 164 TNode<Object> list = args.GetOptionalArgumentValue(0); 165 { 166 // 4. Let stringList be ? StringListFromIterable(list). 167 TNode<Object> string_list = 168 CallBuiltin(Builtin::kStringListFromIterable, context, list); 169 170 // 6. Return ? FormatList(lf, stringList). 171 args.PopAndReturn( 172 CallRuntime(format_func_id, context, list_format, string_list)); 173 } 174} 175 176TNode<JSArray> IntlBuiltinsAssembler::AllocateEmptyJSArray( 177 TNode<Context> context) { 178 return CodeStubAssembler::AllocateJSArray( 179 PACKED_ELEMENTS, 180 LoadJSArrayElementsMap(PACKED_ELEMENTS, LoadNativeContext(context)), 181 IntPtrConstant(0), SmiConstant(0)); 182} 183 184TF_BUILTIN(ListFormatPrototypeFormat, IntlBuiltinsAssembler) { 185 ListFormatCommon( 186 Parameter<Context>(Descriptor::kContext), 187 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount), 188 Runtime::kFormatList, "Intl.ListFormat.prototype.format"); 189} 190 191TF_BUILTIN(ListFormatPrototypeFormatToParts, IntlBuiltinsAssembler) { 192 ListFormatCommon( 193 Parameter<Context>(Descriptor::kContext), 194 UncheckedParameter<Int32T>(Descriptor::kJSActualArgumentsCount), 195 Runtime::kFormatListToParts, "Intl.ListFormat.prototype.formatToParts"); 196} 197 198} // namespace internal 199} // namespace v8 200