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