• 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 #include "ecmascript/ecma_vm.h"
18 #include "ecmascript/global_env.h"
19 #include "ecmascript/internal_call_params.h"
20 #include "ecmascript/js_arguments.h"
21 #include "ecmascript/js_stable_array.h"
22 #include "ecmascript/tagged_array-inl.h"
23 
24 namespace panda::ecmascript::builtins {
25 // ecma 19.2.1 Function (p1, p2, ... , pn, body)
FunctionConstructor(EcmaRuntimeCallInfo * argv)26 JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv)
27 {
28     // not support
29     JSThread *thread = argv->GetThread();
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     return JSTaggedValue::Undefined();
40 }
41 namespace {
BuildArgumentsListFast(JSThread * thread,const JSHandle<JSTaggedValue> & arrayObj)42 static bool BuildArgumentsListFast(JSThread *thread, const JSHandle<JSTaggedValue> &arrayObj)
43 {
44     if (!arrayObj->HasStableElements(thread)) {
45         return false;
46     }
47     InternalCallParams *arguments = thread->GetInternalCallParams();
48     if (arrayObj->IsStableJSArguments(thread)) {
49         JSHandle<JSArguments> argList = JSHandle<JSArguments>::Cast(arrayObj);
50         TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
51         auto env = thread->GetEcmaVM()->GetGlobalEnv();
52         if (argList->GetClass() != env->GetArgumentsClass().GetObject<JSHClass>()) {
53             return false;
54         }
55         auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX);
56         if (!result.IsInt()) {
57             return false;
58         }
59         uint32_t length = static_cast<uint32_t>(result.GetInt());
60         arguments->MakeArgListWithHole(elements, length);
61     } else if (arrayObj->IsStableJSArray(thread)) {
62         JSHandle<JSArray> argList = JSHandle<JSArray>::Cast(arrayObj);
63         TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject());
64         uint32_t length = argList->GetArrayLength();
65         arguments->MakeArgListWithHole(elements, length);
66     } else {
67         UNREACHABLE();
68     }
69     return true;
70 }
71 }  // anonymous namespace
72 
73 // ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray)
FunctionPrototypeApply(EcmaRuntimeCallInfo * argv)74 JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv)
75 {
76     ASSERT(argv);
77     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeApply);
78     JSThread *thread = argv->GetThread();
79     [[maybe_unused]] EcmaHandleScope handleScope(thread);
80 
81     // 1. If IsCallable(func) is false, throw a TypeError exception.
82     if (!GetThis(argv)->IsCallable()) {
83         THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception());
84     }
85 
86     JSHandle<JSTaggedValue> func = GetThis(argv);
87     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
88     // 2. If argArray is null or undefined, then
89     if (GetCallArg(argv, 1)->IsUndefined()) {  // null will also get undefined
90         // a. Return Call(func, thisArg).
91         return JSFunction::Call(thread, func, thisArg, 0, nullptr);
92     }
93     // 3. Let argList be CreateListFromArrayLike(argArray).
94     JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1);
95     InternalCallParams *arguments = thread->GetInternalCallParams();
96     if (!BuildArgumentsListFast(thread, arrayObj)) {
97         JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::Cast(
98             JSObject::CreateListFromArrayLike(thread, arrayObj));
99         // 4. ReturnIfAbrupt(argList).
100         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
101         arguments->MakeArgList(*argList);
102     }
103 
104     // 6. Return Call(func, thisArg, argList).
105     return JSFunction::Call(thread, func, thisArg, arguments->GetLength(), arguments->GetArgv());
106 }
107 
108 // ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args)
FunctionPrototypeBind(EcmaRuntimeCallInfo * argv)109 JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv)
110 {
111     ASSERT(argv);
112     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind);
113     JSThread *thread = argv->GetThread();
114     [[maybe_unused]] EcmaHandleScope handleScope(thread);
115 
116     ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
117     // 1. Let Target be the this value.
118     JSHandle<JSTaggedValue> target = GetThis(argv);
119     // 2. If IsCallable(Target) is false, throw a TypeError exception.
120     if (!target->IsCallable()) {
121         THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception());
122     }
123 
124     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
125     uint32_t argsLength = 0;
126     if (argv->GetArgsNumber() > 1) {
127         argsLength = argv->GetArgsNumber() - 1;
128     }
129 
130     // 3. Let args be a new (possibly empty) List consisting of all of the argument
131     //    values provided after thisArg in order.
132     JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(argsLength);
133     for (uint32_t index = 0; index < argsLength; ++index) {
134         argsArray->Set(thread, index, GetCallArg(argv, index + 1));
135     }
136     // 4. Let F be BoundFunctionCreate(Target, thisArg, args).
137     JSHandle<JSFunctionBase> targetFunction = JSHandle<JSFunctionBase>::Cast(target);
138     JSHandle<JSBoundFunction> boundFunction = factory->NewJSBoundFunction(targetFunction, thisArg, argsArray);
139     // 5. ReturnIfAbrupt(F)
140     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
141 
142     // 6. Let targetHasLength be HasOwnProperty(Target, "length").
143     auto globalConst = thread->GlobalConstants();
144     JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString();
145     bool targetHasLength =
146         JSTaggedValue::HasOwnProperty(thread, JSHandle<JSTaggedValue>::Cast(targetFunction), lengthKey);
147     // 7. ReturnIfAbrupt(targetHasLength).
148     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
149 
150     double lengthValue = 0.0;
151     // 8. If targetHasLength is true, then
152     if (targetHasLength) {
153         // a. Let targetLen be Get(Target, "length").
154         JSHandle<JSTaggedValue> targetLen = JSObject::GetProperty(thread, target, lengthKey).GetValue();
155         // b. ReturnIfAbrupt(targetLen).
156         RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
157 
158         // c. If Type(targetLen) is not Number, let L be 0.
159         // d. Else,
160         //    i. Let targetLen be ToInteger(targetLen).
161         //    ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args.
162         if (targetLen->IsNumber()) {
163             // argv include thisArg
164             lengthValue =
165                 std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - static_cast<double>(argsLength));
166         }
167     }
168     // 9. Else let L be 0.
169 
170     // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L,
171     //     [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
172     PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(lengthValue)), false, false, true);
173     [[maybe_unused]] bool status =
174         JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc);
175     // 11. Assert: status is not an abrupt completion.
176     ASSERT_PRINT(status, "DefinePropertyOrThrow failed");
177 
178     // 12. Let targetName be Get(Target, "name").
179     JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString();
180     JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue();
181     // 13. ReturnIfAbrupt(targetName).
182     RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
183 
184     JSHandle<JSTaggedValue> boundName(factory->NewFromCanBeCompressString("bound"));
185     // 14. If Type(targetName) is not String, let targetName be the empty string.
186     // 15. Perform SetFunctionName(F, targetName, "bound").
187     if (!targetName->IsString()) {
188         JSHandle<JSTaggedValue> emptyString(factory->GetEmptyString());
189         status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), emptyString, boundName);
190     } else {
191         status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), targetName, boundName);
192     }
193     // Assert: status is not an abrupt completion.
194     ASSERT_PRINT(status, "SetFunctionName failed");
195 
196     // 16. Return F.
197     return boundFunction.GetTaggedValue();
198 }
199 
200 // ecma 19.2.3.3 Function.prototype.call (thisArg , ...args)
FunctionPrototypeCall(EcmaRuntimeCallInfo * argv)201 JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv)
202 {
203     ASSERT(argv);
204     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall);
205     JSThread *thread = argv->GetThread();
206     [[maybe_unused]] EcmaHandleScope handleScope(thread);
207 
208     // 1. If IsCallable(func) is false, throw a TypeError exception.
209     if (!GetThis(argv)->IsCallable()) {
210         THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception());
211     }
212 
213     JSHandle<JSTaggedValue> func = GetThis(argv);
214     JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0);
215     uint32_t argsLength = 0;
216     if (argv->GetArgsNumber() > 1) {
217         argsLength = argv->GetArgsNumber() - 1;
218     }
219     // 2. Let argList be an empty List.
220     // 3. If this method was called with more than one argument then in left to right order,
221     //    starting with the second argument, append each argument as the last element of argList.
222     InternalCallParams *argsList = thread->GetInternalCallParams();
223     argsList->MakeArgv(argv, 1);
224 
225     // 5. Return Call(func, thisArg, argList).
226     return JSFunction::Call(thread, func, thisArg, argsLength, argsList->GetArgv());
227 }
228 
229 // ecma 19.2.3.5 Function.prototype.toString ()
FunctionPrototypeToString(EcmaRuntimeCallInfo * argv)230 JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv)
231 {
232     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString);
233     // not implement due to that runtime can not get JS Source Code now.
234     JSThread *thread = argv->GetThread();
235     [[maybe_unused]] EcmaHandleScope handleScope(thread);
236     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
237     if (!thisValue->IsCallable()) {
238         THROW_TYPE_ERROR_AND_RETURN(thread, "function.toString() target is not callable", JSTaggedValue::Exception());
239     }
240     return GetTaggedString(thread, "Not support function.toString() due to Runtime can not obtain Source Code yet.");
241 }
242 
243 // ecma 19.2.3.6 Function.prototype[@@hasInstance] (V)
FunctionPrototypeHasInstance(EcmaRuntimeCallInfo * argv)244 JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv)
245 {
246     BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance);
247     [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread());
248     // 1. Let F be the this value.
249     JSHandle<JSTaggedValue> thisValue = GetThis(argv);
250     // 2. Return OrdinaryHasInstance(F, V).
251     JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0);
252     return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true)
253                                                                               : GetTaggedBoolean(false);
254 }
255 }  // namespace panda::ecmascript::builtins
256