1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/builtins/builtins_symbol.h" 17 18#include "ecmascript/ecma_string_table.h" 19#include "ecmascript/global_env.h" 20#include "ecmascript/js_primitive_ref.h" 21#include "ecmascript/symbol_table.h" 22 23namespace panda::ecmascript::builtins { 24// prototype 25// 19.4.3.1 26// constructor 27JSTaggedValue BuiltinsSymbol::SymbolConstructor(EcmaRuntimeCallInfo *argv) 28{ 29 ASSERT(argv); 30 BUILTINS_API_TRACE(argv->GetThread(), Symbol, Constructor); 31 JSThread *thread = argv->GetThread(); 32 [[maybe_unused]] EcmaHandleScope handleScope(thread); 33 // 1.If NewTarget is not undefined, throw a TypeError exception. 34 JSHandle<JSTaggedValue> key = GetCallArg(argv, 0); 35 JSHandle<JSTaggedValue> newTarget = GetNewTarget(argv); 36 if (!newTarget->IsUndefined()) { 37 THROW_TYPE_ERROR_AND_RETURN(thread, "SymbolConstructor: NewTarget is not undefined", 38 JSTaggedValue::Exception()); 39 } 40 // 2.If description is undefined, let descString be undefined. 41 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 42 if (key->IsUndefined()) { 43 JSHandle<JSSymbol> jsSymbol = factory->NewJSSymbol(); 44 return jsSymbol.GetTaggedValue(); 45 } 46 // 3.Else, let descString be ToString(description). 47 JSHandle<JSTaggedValue> descHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key)); 48 // 4.ReturnIfAbrupt(descString). 49 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 50 // 5.Return a new unique Symbol value whose [[Description]] value is descString. 51 JSHandle<JSSymbol> jsSymbol = factory->NewPublicSymbol(descHandle); 52 return jsSymbol.GetTaggedValue(); 53} 54 55// 19.4.3.2 Symbol.prototype.toString() 56JSTaggedValue BuiltinsSymbol::ToString(EcmaRuntimeCallInfo *argv) 57{ 58 ASSERT(argv); 59 BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToString); 60 JSThread *thread = argv->GetThread(); 61 [[maybe_unused]] EcmaHandleScope handleScope(thread); 62 // Let s be the this value. 63 JSHandle<JSTaggedValue> valueHandle = GetThis(argv); 64 // 1.If value is a Symbol, return value. 65 if (valueHandle->IsSymbol()) { 66 // Return SymbolDescriptiveString(sym). 67 return SymbolDescriptiveString(thread, valueHandle.GetTaggedValue()); 68 } 69 70 // 2.If value is an Object and value has a [[SymbolData]] internal slot, then 71 if (valueHandle->IsJSPrimitiveRef()) { 72 // Let sym be the value of s's [[SymbolData]] internal slot. 73 JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); 74 if (primitive.IsSymbol()) { 75 return SymbolDescriptiveString(thread, primitive); 76 } 77 } 78 79 // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. 80 THROW_TYPE_ERROR_AND_RETURN(thread, "ToString: no [[SymbolData]]", JSTaggedValue::Exception()); 81} 82 83JSTaggedValue BuiltinsSymbol::SymbolDescriptiveString(JSThread *thread, JSTaggedValue sym) 84{ 85 BUILTINS_API_TRACE(thread, Symbol, SymbolDescriptiveString); 86 // Assert: Type(sym) is Symbol. 87 ASSERT(sym.IsSymbol()); 88 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 89 90 // Let desc be sym’s [[Description]] value. 91 auto symbolObject = reinterpret_cast<JSSymbol *>(sym.GetTaggedObject()); 92 JSHandle<JSTaggedValue> descHandle(thread, symbolObject->GetDescription()); 93 94 // If desc is undefined, let desc be the empty string. 95 JSHandle<SingleCharTable> singleCharTable(thread, thread->GetSingleCharTable()); 96 auto constants = thread->GlobalConstants(); 97 if (descHandle->IsUndefined()) { 98 JSHandle<EcmaString> leftHandle = JSHandle<EcmaString>::Cast(constants->GetHandledSymbolLeftParentheses()); 99 JSHandle<EcmaString> rightHandle(thread, singleCharTable->GetStringFromSingleCharTable(')')); 100 JSHandle<EcmaString> str = factory->ConcatFromString(leftHandle, rightHandle); 101 return str.GetTaggedValue(); 102 } 103 // Assert: Type(desc) is String. 104 ASSERT(descHandle->IsString()); 105 // Return the result of concatenating the strings "Symbol(", desc, and ")". 106 JSHandle<EcmaString> leftHandle = JSHandle<EcmaString>::Cast(constants->GetHandledSymbolLeftParentheses()); 107 JSHandle<EcmaString> rightHandle(thread, singleCharTable->GetStringFromSingleCharTable(')')); 108 JSHandle<EcmaString> stringLeft = 109 factory->ConcatFromString(leftHandle, JSTaggedValue::ToString(thread, descHandle)); 110 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 111 JSHandle<EcmaString> str = factory->ConcatFromString(stringLeft, rightHandle); 112 return str.GetTaggedValue(); 113} 114 115// 19.4.3.3 116JSTaggedValue BuiltinsSymbol::ValueOf(EcmaRuntimeCallInfo *argv) 117{ 118 ASSERT(argv); 119 BUILTINS_API_TRACE(argv->GetThread(), Symbol, ValueOf); 120 JSThread *thread = argv->GetThread(); 121 [[maybe_unused]] EcmaHandleScope handleScope(thread); 122 // Let s be the this value. 123 JSHandle<JSTaggedValue> valueHandle = GetThis(argv); 124 // 1.If value is a Symbol, return value. 125 if (valueHandle->IsSymbol()) { 126 return valueHandle.GetTaggedValue(); 127 } 128 129 // 2.If value is an Object and value has a [[SymbolData]] internal slot, then 130 if (valueHandle->IsJSPrimitiveRef()) { 131 // Let sym be the value of s's [[SymbolData]] internal slot. 132 JSTaggedValue primitive = JSPrimitiveRef::Cast(valueHandle->GetTaggedObject())->GetValue(); 133 if (primitive.IsSymbol()) { 134 return primitive; 135 } 136 } 137 138 // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. 139 THROW_TYPE_ERROR_AND_RETURN(thread, "ValueOf: no [[SymbolData]]", JSTaggedValue::Exception()); 140} 141 142// 19.4.2.1 Symbol.for (key) 143JSTaggedValue BuiltinsSymbol::For(EcmaRuntimeCallInfo *argv) 144{ 145 ASSERT(argv); 146 BUILTINS_API_TRACE(argv->GetThread(), Symbol, For); 147 JSThread *thread = argv->GetThread(); 148 [[maybe_unused]] EcmaHandleScope handleScope(thread); 149 // 1.Let stringKey be ToString(key). 150 JSHandle<JSTaggedValue> key = BuiltinsSymbol::GetCallArg(argv, 0); 151 JSHandle<JSTaggedValue> stringHandle = JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread, key)); 152 // 2.ReturnIfAbrupt 153 RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::Exception()); 154 155 // 3.For each element e of the GlobalSymbolRegistry List, 156 // If SameValue(e.[[key]], stringKey) is true, return e.[[symbol]]. 157 // 4.Assert: GlobalSymbolRegistry does not currently contain an entry for stringKey. 158 // 5.Let newSymbol be a new unique Symbol value whose [[Description]] value is stringKey. 159 // 6.Append the record { [[key]]: stringKey, [[symbol]]: newSymbol } to the GlobalSymbolRegistry List. 160 // 7.Return newSymbol. 161 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 162 JSHandle<JSSymbol> symbol = factory->NewSymbolWithTable(stringHandle); 163 return symbol.GetTaggedValue(); 164} 165 166// 19.4.2.5 Symbol.keyFor (sym) 167JSTaggedValue BuiltinsSymbol::KeyFor(EcmaRuntimeCallInfo *argv) 168{ 169 ASSERT(argv); 170 BUILTINS_API_TRACE(argv->GetThread(), Symbol, KeyFor); 171 JSThread *thread = argv->GetThread(); 172 [[maybe_unused]] EcmaHandleScope handleScope(thread); 173 // 1.If Type(sym) is not Symbol, throw a TypeError exception. 174 JSHandle<JSTaggedValue> sym = BuiltinsSymbol::GetCallArg(argv, 0); 175 if (!sym->IsSymbol()) { 176 // return typeError 177 THROW_TYPE_ERROR_AND_RETURN(thread, "KeyFor: sym is not Symbol", JSTaggedValue::Exception()); 178 } 179 // 2.For each element e of the GlobalSymbolRegistry List, 180 // If SameValue(e.[[symbol]], sym) is true, return e.[[key]]. 181 // 3.Assert: GlobalSymbolRegistry does not currently contain an entry for sym. 182 // 4.Return undefined. 183 JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv(); 184 auto *table = env->GetRegisterSymbols().GetObject<SymbolTable>(); 185 JSTaggedValue key = table->FindSymbol(sym.GetTaggedValue()); 186 if (key.IsUndefined()) { 187 return JSTaggedValue::Undefined(); 188 } 189 return JSTaggedValue::ToString(thread, JSHandle<JSTaggedValue>(thread, key)).GetTaggedValue(); 190} 191 192// 19.4.3.4 Symbol.prototype [ @@toPrimitive ] ( hint ) 193JSTaggedValue BuiltinsSymbol::ToPrimitive(EcmaRuntimeCallInfo *argv) 194{ 195 ASSERT(argv); 196 BUILTINS_API_TRACE(argv->GetThread(), Symbol, ToPrimitive); 197 JSThread *thread = argv->GetThread(); 198 [[maybe_unused]] EcmaHandleScope handleScope(thread); 199 // Let s be the this value. 200 JSHandle<JSTaggedValue> sym = GetThis(argv); 201 // 1.If value is a Symbol, return value. 202 if (sym->IsSymbol()) { 203 return sym.GetTaggedValue(); 204 } 205 206 // 2.If value is an Object and value has a [[SymbolData]] internal slot, then 207 if (sym->IsJSPrimitiveRef()) { 208 // Let sym be the value of s's [[SymbolData]] internal slot. 209 JSTaggedValue primitive = JSPrimitiveRef::Cast(sym->GetTaggedObject())->GetValue(); 210 if (primitive.IsSymbol()) { 211 return primitive; 212 } 213 } 214 215 // 3.If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. 216 THROW_TYPE_ERROR_AND_RETURN(thread, "ToPrimitive: s is not Object", JSTaggedValue::Exception()); 217} 218JSTaggedValue BuiltinsSymbol::DescriptionGetter(EcmaRuntimeCallInfo *argv) 219{ 220 ASSERT(argv); 221 BUILTINS_API_TRACE(argv->GetThread(), Symbol, DescriptionGetter); 222 // 1.Let s be the this value. 223 JSThread *thread = argv->GetThread(); 224 [[maybe_unused]] EcmaHandleScope handleScope(thread); 225 JSHandle<JSTaggedValue> s = GetThis(argv); 226 // 2.Let sym be ? thisSymbolValue(s). 227 // 3.Return sym.[[Description]]. 228 return ThisSymbolValue(thread, s); 229} 230 231JSTaggedValue BuiltinsSymbol::ThisSymbolValue(JSThread *thread, const JSHandle<JSTaggedValue> &value) 232{ 233 BUILTINS_API_TRACE(thread, Symbol, ThisSymbolValue); 234 if (value->IsSymbol()) { 235 JSTaggedValue desValue = JSSymbol::Cast(value->GetTaggedObject())->GetDescription(); 236 return desValue; 237 } 238 239 // If s does not have a [[SymbolData]] internal slot, throw a TypeError exception. 240 if (value->IsJSPrimitiveRef()) { 241 JSTaggedValue primitive = JSPrimitiveRef::Cast(value->GetTaggedObject())->GetValue(); 242 if (primitive.IsSymbol()) { 243 // Return the value of s's [[SymbolData]] internal slot. 244 JSTaggedValue primitiveDesValue = JSSymbol::Cast(primitive.GetTaggedObject())->GetDescription(); 245 return primitiveDesValue; 246 } 247 } 248 THROW_TYPE_ERROR_AND_RETURN(thread, "can not convert to Symbol", JSTaggedValue::Exception()); 249} 250} // namespace panda::ecmascript::builtins 251