JSFunction* JSFunction::createBuiltinFunction(VM& vm, FunctionExecutable* executable, JSGlobalObject* globalObject) { JSFunction* function = create(vm, executable, globalObject); function->putDirect(vm, vm.propertyNames->name, jsString(&vm, executable->name().string()), DontDelete | ReadOnly | DontEnum); function->putDirect(vm, vm.propertyNames->length, jsNumber(executable->parameterCount()), DontDelete | ReadOnly | DontEnum); return function; }
static EncodedJSValue JSC_HOST_CALL constructPromise(ExecState* exec) { // NOTE: We ignore steps 1-4 as they only matter if you support subclassing, which we do not yet. // 1. Let promise be the this value. // 2. If Type(promise) is not Object, then throw a TypeError exception. // 3. If promise does not have a [[PromiseStatus]] internal slot, then throw a TypeError exception. // 4. If promise's [[PromiseStatus]] internal slot is not undefined, then throw a TypeError exception. JSValue resolver = exec->argument(0); // 5. IsCallable(resolver) is false, then throw a TypeError exception CallData callData; CallType callType = getCallData(resolver, callData); if (callType == CallTypeNone) return JSValue::encode(throwTypeError(exec, ASCIILiteral("Promise constructor takes a function argument"))); VM& vm = exec->vm(); JSGlobalObject* globalObject = exec->callee()->globalObject(); JSPromise* promise = JSPromise::create(vm, globalObject, jsCast<JSPromiseConstructor*>(exec->callee())); // NOTE: Steps 6-8 are handled by JSPromise::create(). // 6. Set promise's [[PromiseStatus]] internal slot to "unresolved". // 7. Set promise's [[ResolveReactions]] internal slot to a new empty List. // 8. Set promise's [[RejectReactions]] internal slot to a new empty List. // 9. Let 'resolve' be a new built-in function object as defined in Resolve Promise Functions. JSFunction* resolve = createResolvePromiseFunction(vm, globalObject); // 10. Set the [[Promise]] internal slot of 'resolve' to 'promise'. resolve->putDirect(vm, vm.propertyNames->promisePrivateName, promise); // 11. Let 'reject' be a new built-in function object as defined in Reject Promise Functions JSFunction* reject = createRejectPromiseFunction(vm, globalObject); // 12. Set the [[Promise]] internal slot of 'reject' to 'promise'. reject->putDirect(vm, vm.propertyNames->promisePrivateName, promise); // 13. Let 'result' be the result of calling the [[Call]] internal method of resolver with // undefined as thisArgument and a List containing resolve and reject as argumentsList. MarkedArgumentBuffer arguments; arguments.append(resolve); arguments.append(reject); call(exec, resolver, callType, callData, jsUndefined(), arguments); // 14. If result is an abrupt completion, call PromiseReject(promise, result.[[value]]). if (exec->hadException()) { JSValue exception = exec->exception()->value(); exec->clearException(); promise->reject(vm, exception); } // 15. Return promise. return JSValue::encode(promise); }
bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { JSFunction* thisObject = jsCast<JSFunction*>(cell); if (thisObject->isHostFunction()) return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (propertyName == exec->propertyNames().prototype) { JSGlobalData& globalData = exec->globalData(); PropertyOffset offset = thisObject->getDirectOffset(globalData, propertyName); if (!isValidOffset(offset)) { JSObject* prototype = constructEmptyObject(exec); prototype->putDirect(globalData, exec->propertyNames().constructor, thisObject, DontEnum); thisObject->putDirect(globalData, exec->propertyNames().prototype, prototype, DontDelete | DontEnum); offset = thisObject->getDirectOffset(globalData, exec->propertyNames().prototype); ASSERT(isValidOffset(offset)); } slot.setValue(thisObject, thisObject->getDirect(offset), offset); } if (propertyName == exec->propertyNames().arguments) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, argumentsGetter); return true; } if (propertyName == exec->propertyNames().length) { slot.setCacheableCustom(thisObject, lengthGetter); return true; } if (propertyName == exec->propertyNames().name) { slot.setCacheableCustom(thisObject, nameGetter); return true; } if (propertyName == exec->propertyNames().caller) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Accessor); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, callerGetter); return true; } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
bool JSFunction::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { JSFunction* thisObject = static_cast<JSFunction*>(cell); if (thisObject->isHostFunction()) return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (propertyName == exec->propertyNames().prototype) { WriteBarrierBase<Unknown>* location = thisObject->getDirectLocation(exec->globalData(), propertyName); if (!location) { JSObject* prototype = constructEmptyObject(exec, thisObject->globalObject()->emptyObjectStructure()); prototype->putDirect(exec->globalData(), exec->propertyNames().constructor, thisObject, DontEnum); PutPropertySlot slot; thisObject->putDirect(exec->globalData(), exec->propertyNames().prototype, prototype, DontDelete | DontEnum, false, slot); location = thisObject->getDirectLocation(exec->globalData(), exec->propertyNames().prototype); } slot.setValue(thisObject, location->get(), thisObject->offsetForLocation(location)); } if (propertyName == exec->propertyNames().arguments) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, argumentsGetter); return true; } if (propertyName == exec->propertyNames().length) { slot.setCacheableCustom(thisObject, lengthGetter); return true; } if (propertyName == exec->propertyNames().caller) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->initializeGetterSetterProperty(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec), DontDelete | DontEnum | Getter | Setter); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, callerGetter); return true; } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
// ECMA 15.3.2 The Function Constructor JSObject* constructFunction(ExecState* exec, const ArgList& args, const Identifier& functionName, const UString& sourceURL, int lineNumber) { UString p(""); UString body; int argsSize = args.size(); if (argsSize == 0) body = ""; else if (argsSize == 1) body = args[0]->toString(exec); else { p = args[0]->toString(exec); for (int k = 1; k < argsSize - 1; k++) p += "," + args[k]->toString(exec); body = args[argsSize - 1]->toString(exec); } // parse the source code int sourceId; int errLine; UString errMsg; RefPtr<SourceProvider> source = UStringSourceProvider::create(body); RefPtr<FunctionBodyNode> functionBody = exec->parser()->parse<FunctionBodyNode>(exec, sourceURL, lineNumber, source, &sourceId, &errLine, &errMsg); // No program node == syntax error - throw a syntax error if (!functionBody) // We can't return a Completion(Throw) here, so just set the exception // and return it return throwError(exec, SyntaxError, errMsg, errLine, sourceId, sourceURL); functionBody->setSource(SourceRange(source, 0, source->length())); ScopeChain scopeChain(exec->lexicalGlobalObject(), exec->globalThisValue()); JSFunction* function = new (exec) JSFunction(exec, functionName, functionBody.get(), scopeChain.node()); // parse parameter list. throw syntax error on illegal identifiers int len = p.size(); const UChar* c = p.data(); int i = 0, params = 0; UString param; while (i < len) { while (*c == ' ' && i < len) c++, i++; if (Lexer::isIdentStart(c[0])) { // else error param = UString(c, 1); c++, i++; while (i < len && (Lexer::isIdentPart(c[0]))) { param.append(*c); c++, i++; } while (i < len && *c == ' ') c++, i++; if (i == len) { functionBody->parameters().append(Identifier(exec, param)); params++; break; } else if (*c == ',') { functionBody->parameters().append(Identifier(exec, param)); params++; c++, i++; continue; } // else error } return throwError(exec, SyntaxError, "Syntax error in parameter list"); } JSObject* prototype = constructEmptyObject(exec); prototype->putDirect(exec->propertyNames().constructor, function, DontEnum); function->putDirect(exec->propertyNames().prototype, prototype, DontDelete); return function; }
bool JSFunction::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { JSFunction* thisObject = jsCast<JSFunction*>(object); if (thisObject->isHostOrBuiltinFunction()) return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (propertyName == exec->propertyNames().prototype) { VM& vm = exec->vm(); unsigned attributes; PropertyOffset offset = thisObject->getDirectOffset(vm, propertyName, attributes); if (!isValidOffset(offset)) { JSObject* prototype = constructEmptyObject(exec); prototype->putDirect(vm, exec->propertyNames().constructor, thisObject, DontEnum); thisObject->putDirect(vm, exec->propertyNames().prototype, prototype, DontDelete | DontEnum); offset = thisObject->getDirectOffset(vm, exec->propertyNames().prototype, attributes); ASSERT(isValidOffset(offset)); } slot.setValue(thisObject, attributes, thisObject->getDirect(offset), offset); } if (propertyName == exec->propertyNames().arguments) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, argumentsGetter); return true; } if (propertyName == exec->propertyNames().length) { slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, lengthGetter); return true; } if (propertyName == exec->propertyNames().name) { slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, nameGetter); return true; } if (propertyName == exec->propertyNames().caller) { if (thisObject->jsExecutable()->isStrictMode()) { bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); if (!result) { thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor); result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); ASSERT(result); } return result; } slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, callerGetter); return true; } return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot); }
static JSValue performPromiseAll(ExecState* exec, JSValue iterator, JSValue C, JSPromiseDeferred* deferred) { JSObject* thisObject = asObject(C); VM& vm = exec->vm(); // 6. Let 'values' be the result of calling ArrayCreate(0). JSArray* values = constructEmptyArray(exec, nullptr, thisObject->globalObject()); // 7. Let 'countdownHolder' be Record { [[Countdown]]: 0 }. NumberObject* countdownHolder = constructNumber(exec, thisObject->globalObject(), JSValue(0)); // 8. Let 'index' be 0. unsigned index = 0; // 9. Repeat. do { // i. Let 'next' be the result of calling IteratorStep(iterator). JSValue next = iteratorStep(exec, iterator); if (exec->hadException()) return jsUndefined(); // iii. If 'next' is false, if (next.isFalse()) { // a. If 'index' is 0, if (!index) { // a. Let 'resolveResult' be the result of calling the [[Call]] internal method // of deferred.[[Resolve]] with undefined as thisArgument and a List containing // values as argumentsList. performDeferredResolve(exec, deferred, values); // b. ReturnIfAbrupt(resolveResult). if (exec->hadException()) return jsUndefined(); } // b. Return deferred.[[Promise]]. return deferred->promise(); } // iv. Let 'nextValue' be the result of calling IteratorValue(next). // v. RejectIfAbrupt(nextValue, deferred). JSValue nextValue = iteratorValue(exec, next); if (exec->hadException()) return jsUndefined(); values->push(exec, jsUndefined()); // vi. Let 'nextPromise' be the result of calling Invoke(C, "resolve", (nextValue)). JSValue resolveFunction = C.get(exec, vm.propertyNames->resolve); if (exec->hadException()) return jsUndefined(); CallData resolveFunctionCallData; CallType resolveFunctionCallType = getCallData(resolveFunction, resolveFunctionCallData); if (resolveFunctionCallType == CallTypeNone) { throwTypeError(exec); return jsUndefined(); } MarkedArgumentBuffer resolveFunctionArguments; resolveFunctionArguments.append(nextValue); JSValue nextPromise = call(exec, resolveFunction, resolveFunctionCallType, resolveFunctionCallData, C, resolveFunctionArguments); // vii. RejectIfAbrupt(nextPromise, deferred). if (exec->hadException()) return jsUndefined(); // viii. Let 'countdownFunction' be a new built-in function object as defined in Promise.all Countdown Functions. JSFunction* countdownFunction = createPromiseAllCountdownFunction(vm, thisObject->globalObject()); // ix. Set the [[Index]] internal slot of 'countdownFunction' to 'index'. countdownFunction->putDirect(vm, vm.propertyNames->indexPrivateName, JSValue(index)); // x. Set the [[Values]] internal slot of 'countdownFunction' to 'values'. countdownFunction->putDirect(vm, vm.propertyNames->valuesPrivateName, values); // xi. Set the [[Deferred]] internal slot of 'countdownFunction' to 'deferred'. countdownFunction->putDirect(vm, vm.propertyNames->deferredPrivateName, deferred); // xii. Set the [[CountdownHolder]] internal slot of 'countdownFunction' to 'countdownHolder'. countdownFunction->putDirect(vm, vm.propertyNames->countdownHolderPrivateName, countdownHolder); // xiii. Let 'result' be the result of calling Invoke(nextPromise, "then", (countdownFunction, deferred.[[Reject]])). JSValue thenFunction = nextPromise.get(exec, vm.propertyNames->then); if (exec->hadException()) return jsUndefined(); CallData thenFunctionCallData; CallType thenFunctionCallType = getCallData(thenFunction, thenFunctionCallData); if (thenFunctionCallType == CallTypeNone) { throwTypeError(exec); return jsUndefined(); } MarkedArgumentBuffer thenFunctionArguments; thenFunctionArguments.append(countdownFunction); thenFunctionArguments.append(deferred->reject()); call(exec, thenFunction, thenFunctionCallType, thenFunctionCallData, nextPromise, thenFunctionArguments); // xiv. RejectIfAbrupt(result, deferred). if (exec->hadException()) return jsUndefined(); // xv. Set index to index + 1. index++; // xvi. Set countdownHolder.[[Countdown]] to countdownHolder.[[Countdown]] + 1. uint32_t newCountdownValue = countdownHolder->internalValue().asUInt32() + 1; countdownHolder->setInternalValue(vm, JSValue(newCountdownValue)); } while (true); ASSERT_NOT_REACHED(); return jsUndefined(); }