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