Esempio n. 1
0
// https://tc39.github.io/ecma262/#sec-reflect.construct
EncodedJSValue JSC_HOST_CALL reflectObjectConstruct(ExecState* exec)
{
    VM& vm = exec->vm();
    auto scope = DECLARE_THROW_SCOPE(vm);

    JSValue target = exec->argument(0);
    if (!target.isObject())
        return JSValue::encode(throwTypeError(exec, scope, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));

    ConstructData constructData;
    ConstructType constructType;
    if (!target.isConstructor(constructType, constructData))
        return JSValue::encode(throwTypeError(exec, scope, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));

    JSValue newTarget = target;
    if (exec->argumentCount() >= 3) {
        newTarget = exec->argument(2);
        if (!newTarget.isConstructor())
            return JSValue::encode(throwTypeError(exec, scope, ASCIILiteral("Reflect.construct requires the third argument be a constructor if present")));
    }

    MarkedArgumentBuffer arguments;
    JSObject* argumentsObject = jsDynamicCast<JSObject*>(exec->argument(1));
    if (!argumentsObject)
        return JSValue::encode(throwTypeError(exec, scope, ASCIILiteral("Reflect.construct requires the second argument be an object")));

    createListFromArrayLike(exec, argumentsObject, RuntimeTypeMaskAllTypes, ASCIILiteral("This error must not be raised"), [&] (JSValue value, RuntimeType) -> bool {
        arguments.append(value);
        return false;
    });
    RETURN_IF_EXCEPTION(scope, encodedJSValue());

    scope.release();
    return JSValue::encode(construct(exec, target, constructType, constructData, arguments, newTarget));
}
// https://tc39.github.io/ecma262/#sec-reflect.construct
EncodedJSValue JSC_HOST_CALL reflectObjectConstruct(ExecState* exec)
{
    JSValue target = exec->argument(0);
    if (!target.isObject())
        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));

    ConstructData constructData;
    ConstructType constructType;
    if (!target.isConstructor(constructType, constructData))
        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the first argument be a constructor")));

    JSValue newTarget = target;
    if (exec->argumentCount() >= 3) {
        newTarget = exec->argument(2);
        if (!newTarget.isConstructor())
            return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the third argument be a constructor if present")));
    }

    MarkedArgumentBuffer arguments;
    JSObject* argumentsObject = jsDynamicCast<JSObject*>(exec->argument(1));
    if (!argumentsObject)
        return JSValue::encode(throwTypeError(exec, ASCIILiteral("Reflect.construct requires the second argument be an object")));

    createListFromArrayLike(exec, argumentsObject, RuntimeTypeMaskAllTypes, ASCIILiteral("This error must not be raised"), [&] (JSValue value, RuntimeType) -> bool {
        arguments.append(value);
        return false;
    });
    if (exec->hadException())
        return JSValue::encode(jsUndefined());

    return JSValue::encode(construct(exec, target, constructType, constructData, arguments, newTarget));
}
// https://html.spec.whatwg.org/#dom-customelementsregistry-define
JSValue JSCustomElementsRegistry::define(ExecState& state)
{
    if (UNLIKELY(state.argumentCount() < 2))
        return state.vm().throwException(&state, createNotEnoughArgumentsError(&state));

    AtomicString localName(state.uncheckedArgument(0).toString(&state)->toAtomicString(&state));
    if (UNLIKELY(state.hadException()))
        return jsUndefined();

    JSValue constructorValue = state.uncheckedArgument(1);
    if (!constructorValue.isConstructor())
        return throwTypeError(&state, ASCIILiteral("The second argument must be a constructor"));
    JSObject* constructor = constructorValue.getObject();

    // FIXME: Throw a TypeError if constructor doesn't inherit from HTMLElement.
    // https://github.com/w3c/webcomponents/issues/541

    switch (Document::validateCustomElementName(localName)) {
    case CustomElementNameValidationStatus::Valid:
        break;
    case CustomElementNameValidationStatus::ConflictsWithBuiltinNames:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name cannot be same as one of the builtin elements"));
    case CustomElementNameValidationStatus::NoHyphen:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name must contain a hyphen"));
    case CustomElementNameValidationStatus::ContainsUpperCase:
        return throwSyntaxError(&state, ASCIILiteral("Custom element name cannot contain an upper case letter"));
    }

    // FIXME: Check re-entrancy here.
    // https://github.com/w3c/webcomponents/issues/545

    CustomElementsRegistry& registry = wrapped();
    if (registry.findInterface(localName)) {
        throwNotSupportedError(state, ASCIILiteral("Cannot define multiple custom elements with the same tag name"));
        return jsUndefined();
    }

    if (registry.containsConstructor(constructor)) {
        throwNotSupportedError(state, ASCIILiteral("Cannot define multiple custom elements with the same class"));
        return jsUndefined();
    }

    auto& vm = globalObject()->vm();
    JSValue prototypeValue = constructor->get(&state, vm.propertyNames->prototype);
    if (state.hadException())
        return jsUndefined();
    if (!prototypeValue.isObject())
        return throwTypeError(&state, ASCIILiteral("Custom element constructor's prototype must be an object"));
    JSObject& prototypeObject = *asObject(prototypeValue);

    QualifiedName name(nullAtom, localName, HTMLNames::xhtmlNamespaceURI);
    auto elementInterface = JSCustomElementInterface::create(name, constructor, globalObject());

    auto* connectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "connectedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (connectedCallback)
        elementInterface->setConnectedCallback(connectedCallback);

    auto* disconnectedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "disconnectedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (disconnectedCallback)
        elementInterface->setDisconnectedCallback(disconnectedCallback);

    // FIXME: Add the support for adoptedCallback.
    getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "adoptedCallback"));
    if (state.hadException())
        return jsUndefined();

    auto* attributeChangedCallback = getCustomElementCallback(state, prototypeObject, Identifier::fromString(&vm, "attributeChangedCallback"));
    if (state.hadException())
        return jsUndefined();
    if (attributeChangedCallback) {
        auto value = convertOptional<Vector<String>>(state, constructor->get(&state, Identifier::fromString(&state, "observedAttributes")));
        if (state.hadException())
            return jsUndefined();
        if (value)
            elementInterface->setAttributeChangedCallback(attributeChangedCallback, *value);
    }

    PrivateName uniquePrivateName;
    globalObject()->putDirect(vm, uniquePrivateName, constructor);

    registry.addElementDefinition(WTFMove(elementInterface));

    // FIXME: 17. Let map be registry's upgrade candidates map.
    // FIXME: 18. Upgrade a newly-defined element given map and definition.
    // FIXME: 19. Resolve whenDefined promise.

    return jsUndefined();
}