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_function.h"
17
18 #include "ecmascript/ecma_vm.h"
19 #include "ecmascript/global_env.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/jspandafile/js_pandafile_manager.h"
22 #include "ecmascript/js_arguments.h"
23 #include "ecmascript/js_stable_array.h"
24 #include "ecmascript/tagged_array-inl.h"
25
26 namespace panda::ecmascript::builtins {
27 // ecma 19.2.1 Function (p1, p2, ... , pn, body)
FunctionConstructor(EcmaRuntimeCallInfo * argv)28 JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv)
29 {
30 // not support
31 JSThread *thread = argv->GetThread();
32 BUILTINS_API_TRACE(thread, Function, Constructor);
33 [[maybe_unused]] EcmaHandleScope handleScope(thread);
34 THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().",
35 JSTaggedValue::Exception());
36 }
37
38 // ecma 19.2.3 The Function prototype object is itself a built-in function object.
39 // When invoked, it accepts any arguments and returns undefined.
FunctionPrototypeInvokeSelf(EcmaRuntimeCallInfo * argv)40 JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv)
41 {
42 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeInvokeSelf);
43 return JSTaggedValue::Undefined();
44 }
45 namespace {
MakeArgListWithHole(JSThread * thread,TaggedArray * argv,size_t length)46 static size_t MakeArgListWithHole(JSThread *thread, TaggedArray *argv, size_t length)
47 {
48 if (length > argv->GetLength()) {
49 length = argv->GetLength();
50 }
51 for (size_t index = 0; index < length; ++index) {
52 JSTaggedValue value = argv->Get(thread, index);
53 if (value.IsHole()) {
54 argv->Set(thread, index, JSTaggedValue::Undefined());
55 }
56 }
57 return length;
58 }
59
BuildArgumentsListFast(JSThread * thread,const JSHandle<JSTaggedValue> & arrayObj)60 static std::pair<TaggedArray*, size_t> BuildArgumentsListFast(JSThread *thread,
61 const JSHandle<JSTaggedValue> &arrayObj)
62 {
63 if (!arrayObj->HasStableElements(thread)) {
64 return std::make_pair(nullptr, 0);
65 }
66 if (arrayObj->IsStableJSArguments(thread)) {
67 JSHandle<JSArguments> argList = JSHandle<JSArguments>::Cast(arrayObj);
68 TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
69 auto env = thread->GetEcmaVM()->GetGlobalEnv();
70 if (argList->GetClass() != env->GetArgumentsClass().GetObject<JSHClass>()) {
71 return std::make_pair(nullptr, 0);
72 }
73 auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX);
74 if (!result.IsInt()) {
75 return std::make_pair(nullptr, 0);
76 }
77 size_t length = static_cast<size_t>(result.GetInt());
78 size_t res = MakeArgListWithHole(thread, elements, length);
79 return std::make_pair(elements, res);
80 } else if (arrayObj->IsStableJSArray(thread)) {
81 JSHandle<JSArray> argList = JSHandle<JSArray>::Cast(arrayObj);
82 TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
83 size_t length = argList->GetArrayLength();
84 if (elements->GetLength() == 0 && length != 0) {
85 JSHandle<TaggedArray> array =
86 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined());
87 return std::make_pair(*array, length);
88 }
89 size_t res = MakeArgListWithHole(thread, elements, length);
90 return std::make_pair(elements, res);
91 } else {
92 LOG_ECMA(FATAL) << "this branch is unreachable";
93 UNREACHABLE();
94 }
95 }
96 } // anonymous namespace
97
98 // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray)
FunctionPrototypeApply(EcmaRuntimeCallInfo * argv)99 JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv)
100 {
101 ASSERT(argv);
102 JSThread *thread = argv->GetThread();
103 BUILTINS_API_TRACE(thread, Function, PrototypeApply);
104 [[maybe_unused]] EcmaHandleScope handleScope(thread);
105
106 // 1. If IsCallable(func) is false, throw a TypeError exception.
107 if (!GetThis(argv)->IsCallable()) {
108 THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception());
109 }
110
111 JSHandle<JSTaggedValue> func = GetThis(argv);
112 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
113 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
114 // 2. If argArray is null or undefined, then
115 if (GetCallArg(argv, 1)->IsUndefined()) { // null will also get undefined
116 // a. Return Call(func, thisArg).
117 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, 0);
118 return JSFunction::Call(info);
119 }
120 // 3. Let argList be CreateListFromArrayLike(argArray).
121 JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1);
122 std::pair<TaggedArray*, size_t> argumentsList = BuildArgumentsListFast(thread, arrayObj);
123 if (!argumentsList.first) {
124 JSHandle<JSTaggedValue> num = JSObject::CreateListFromArrayLike(thread, arrayObj);
125 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
126 JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::Cast(num);
127 // 4. ReturnIfAbrupt(argList).
128 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
129 const uint32_t argsLength = argList->GetLength();
130 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
131 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132 info->SetCallArg(argsLength, argList);
133 return JSFunction::Call(info);
134 }
135 // 6. Return Call(func, thisArg, argList).
136 const uint32_t argsLength = static_cast<uint32_t>(argumentsList.second);
137 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
138 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
139 info->SetCallArg(argsLength, argumentsList.first);
140 return JSFunction::Call(info);
141 }
142
143 // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args)
FunctionPrototypeBind(EcmaRuntimeCallInfo * argv)144 JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv)
145 {
146 ASSERT(argv);
147 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind);
148 JSThread *thread = argv->GetThread();
149 [[maybe_unused]] EcmaHandleScope handleScope(thread);
150
151 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
152 // 1. Let Target be the this value.
153 JSHandle<JSTaggedValue> target = GetThis(argv);
154 // 2. If IsCallable(Target) is false, throw a TypeError exception.
155 if (!target->IsCallable()) {
156 THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception());
157 }
158
159 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
160 uint32_t argsLength = 0;
161 if (argv->GetArgsNumber() > 1) {
162 argsLength = argv->GetArgsNumber() - 1;
163 }
164
165 // 3. Let args be a new (possibly empty) List consisting of all of the argument
166 // values provided after thisArg in order.
167 JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(argsLength);
168 for (uint32_t index = 0; index < argsLength; ++index) {
169 argsArray->Set(thread, index, GetCallArg(argv, index + 1));
170 }
171 // 4. Let F be BoundFunctionCreate(Target, thisArg, args).
172 JSHandle<JSFunctionBase> targetFunction = JSHandle<JSFunctionBase>::Cast(target);
173 JSHandle<JSBoundFunction> boundFunction = factory->NewJSBoundFunction(targetFunction, thisArg, argsArray);
174 // 5. ReturnIfAbrupt(F)
175 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
176
177 // 6. Let targetHasLength be HasOwnProperty(Target, "length").
178 auto globalConst = thread->GlobalConstants();
179 JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
180 bool targetHasLength =
181 JSTaggedValue::HasOwnProperty(thread, JSHandle<JSTaggedValue>::Cast(targetFunction), lengthKey);
182 // 7. ReturnIfAbrupt(targetHasLength).
183 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
184
185 double lengthValue = 0.0;
186 // 8. If targetHasLength is true, then
187 if (targetHasLength) {
188 // a. Let targetLen be Get(Target, "length").
189 JSHandle<JSTaggedValue> targetLen = JSObject::GetProperty(thread, target, lengthKey).GetValue();
190 // b. ReturnIfAbrupt(targetLen).
191 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
192
193 // c. If Type(targetLen) is not Number, let L be 0.
194 // d. Else,
195 // i. Let targetLen be ToInteger(targetLen).
196 // ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
197 if (targetLen->IsNumber()) {
198 // argv include thisArg
199 lengthValue =
200 std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - static_cast<double>(argsLength));
201 }
202 }
203 // 9. Else let L be 0.
204
205 // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L,
206 // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
207 PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(lengthValue)), false, false, true);
208 [[maybe_unused]] bool status =
209 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc);
210 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
211 // 11. Assert: status is not an abrupt completion.
212 ASSERT_PRINT(status, "DefinePropertyOrThrow failed");
213
214 // 12. Let targetName be Get(Target, "name").
215 JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
216 JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue();
217 // 13. ReturnIfAbrupt(targetName).
218 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219
220 JSHandle<JSTaggedValue> boundName = thread->GlobalConstants()->GetHandledBoundString();
221 // 14. If Type(targetName) is not String, let targetName be the empty string.
222 // 15. Perform SetFunctionName(F, targetName, "bound").
223 if (!targetName->IsString()) {
224 JSHandle<JSTaggedValue> emptyString(factory->GetEmptyString());
225 status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), emptyString, boundName);
226 } else {
227 status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), targetName, boundName);
228 }
229 // Assert: status is not an abrupt completion.
230 ASSERT_PRINT(status, "SetFunctionName failed");
231
232 // 16. Return F.
233 return boundFunction.GetTaggedValue();
234 }
235
236 // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args)
FunctionPrototypeCall(EcmaRuntimeCallInfo * argv)237 JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv)
238 {
239 ASSERT(argv);
240 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall);
241 JSThread *thread = argv->GetThread();
242 [[maybe_unused]] EcmaHandleScope handleScope(thread);
243
244 // 1. If IsCallable(func) is false, throw a TypeError exception.
245 if (!GetThis(argv)->IsCallable()) {
246 THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception());
247 }
248
249 JSHandle<JSTaggedValue> func = GetThis(argv);
250 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
251 uint32_t argsLength = 0;
252 if (argv->GetArgsNumber() > 1) {
253 argsLength = argv->GetArgsNumber() - 1;
254 }
255 // 2. Let argList be an empty List.
256 // 3. If this method was called with more than one argument then in left to right order,
257 // starting with the second argument, append each argument as the last element of argList.
258 // 5. Return Call(func, thisArg, argList).
259 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
260 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength);
261 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
262 info->SetCallArg(argsLength, 0, argv, 1);
263 return JSFunction::Call(info);
264 }
265
266 // ecma 19.2.3.5 Function.prototype.toString ()
FunctionPrototypeToString(EcmaRuntimeCallInfo * argv)267 JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv)
268 {
269 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString);
270 JSThread *thread = argv->GetThread();
271 [[maybe_unused]] EcmaHandleScope handleScope(thread);
272 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
273 if (thisValue->IsJSObject() && thisValue->IsCallable()) {
274 JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(thisValue);
275 JSHandle<Method> method(thread, func->GetMethod());
276 if (method->IsNativeWithCallField()) {
277 JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString();
278 JSHandle<EcmaString> methodName(JSObject::GetProperty(thread, thisValue, nameKey).GetValue());
279 std::string nameStr = EcmaStringAccessor(methodName).ToStdString();
280 std::string startStr = "function ";
281 std::string endStr = "() { [native code] }";
282 startStr.append(nameStr).append(endStr);
283 return GetTaggedString(thread, startStr.c_str());
284 }
285 DebugInfoExtractor *debugExtractor =
286 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile());
287 const std::string &sourceCode = debugExtractor->GetSourceCode(method->GetMethodId());
288 if (!sourceCode.empty()) {
289 return GetTaggedString(thread, sourceCode.c_str());
290 } else {
291 return GetTaggedString(thread, "Cannot get source code of funtion");
292 }
293 }
294
295 THROW_TYPE_ERROR_AND_RETURN(thread,
296 "function.toString() target is incompatible object", JSTaggedValue::Exception());
297 }
298
299 // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V)
FunctionPrototypeHasInstance(EcmaRuntimeCallInfo * argv)300 JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv)
301 {
302 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance);
303 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
304 // 1. Let F be the this value.
305 JSHandle<JSTaggedValue> thisValue = GetThis(argv);
306 // 2. Return OrdinaryHasInstance(F, V).
307 JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
308 return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true)
309 : GetTaggedBoolean(false);
310 }
311 } // namespace panda::ecmascript::builtins
312