Stage 1 Draft / October 19, 2021

Partial Application for ECMAScript

Introduction

This proposal introduces syntax and semantics to support partial application of call and new expressions.

See the proposal repository for background material and discussion.

1 ECMAScript Data Types and Values

1.1 The ArgumentPlaceholder Record Specification Type

A ArgumentPlaceholder Record is a Record value used to encapsulate information about an argument placeholder in a partial application.

ArgumentPlaceholder Records have the fields listed in Table 1.

Table 1: ArgumentPlaceholder Record Fields
Field Name Value Meaning
[[Kind]] positional, ordinal, or rest. A value indicating a positional placeholder (i.e., ?), an ordinal placeholder (i.e., ?0), or a "rest" placeholder (i.e., ...) in a partial application.
[[Ordinal]] A Number. For an ordinal placeholder, the ordinal position of the related parameter in the resulting bound function.

2 Ordinary and Exotic Objects Behaviours

2.1 Built-in Exotic Object Internal Methods and Slots

2.1.1 Bound Function Exotic Objects

A bound function exotic object is an exotic object that wraps another function object. A bound function exotic object is callable (it has a [[Call]] internal method and may have a [[Construct]] internal method). Calling a bound function exotic object generally results in a call of its wrapped function.

An object is a bound function exotic object if its [[Call]] and (if applicable) [[Construct]] internal methods use the following implementations, and its other essential internal methods use the definitions found in 9.1. These methods are installed in BoundFunctionCreate.

Bound function exotic objects do not have the internal slots of ECMAScript function objects listed in . Instead they have the internal slots listed in Table 2, in addition to [[Prototype]] and [[Extensible]].

Table 2: Internal Slots of Bound Function Exotic Objects
Internal Slot Type Description
[[BoundTargetFunction]] Callable Object The wrapped function object.
[[BoundThis]] Any The value that is always passed as the this value when calling the wrapped function.
[[BoundArguments]] List of Any A list of values whose elements are used as the first arguments to any call to the wrapped function.
[[Kind]] bound, partial-call, or partial-construct A value indicating whether the bound function is the result of partial application.

2.1.1.1 [[Call]] ( thisArgument, argumentsList )

The [[Call]] internal method of a bound function exotic object F takes arguments thisArgument (an ECMAScript language value) and argumentsList (a List of ECMAScript language values). It performs the following steps when called:

  1. Let target be F.[[BoundTargetFunction]].
  2. Let boundThis be F.[[BoundThis]].
  3. Let boundArgs be F.[[BoundArguments]].
  4. Let kind be F.[[Kind]].
  5. If kind is partial-construct, then
    1. Let args be ? ApplyPartialArguments(boundArgs, argumentsList).
    2. Assert: IsConstructor(target) is true.
    3. Return ? Construct(target, args).
  6. If kind is partial-call, then
    1. Let args be ? ApplyPartialArguments(boundArgs, argumentsList).
  7. Else, Letlet args be the list-concatenation of boundArgs and argumentsList.
  8. Return ? Call(target, boundThis, args).

2.1.1.2 [[Construct]] ( argumentsList, newTarget )

The [[Construct]] internal method of a bound function exotic object F takes arguments argumentsList (a List of ECMAScript language values) and newTarget (a constructor). It performs the following steps when called:

  1. Let target be F.[[BoundTargetFunction]].
  2. Assert: IsConstructor(target) is true.
  3. Let boundArgs be F.[[BoundArguments]].
  4. Let kind be F.[[Kind]].
  5. If kind is partial-construct or partial-call, then
    1. Let args be ? ApplyPartialArguments(boundArgs, argumentsList).
  6. Else, Letlet args be the list-concatenation of boundArgs and argumentsList.
  7. If SameValue(F, newTarget) is true, set newTarget to target.
  8. Return ? Construct(target, args, newTarget).

2.1.1.3 BoundFunctionCreate ( targetFunction, boundThis, boundArgs [ , kind ] )

The abstract operation BoundFunctionCreate takes arguments targetFunction (a function object), boundThis (an ECMAScript language value), and boundArgs (a List of ECMAScript language values) and optional argument kind (bound, partial-call, or partial-construct). It is used to specify the creation of new bound function exotic objects. It performs the following steps when called:

  1. If kind is not present, let kind be bound.
  2. Assert: If kind is bound, boundArgs contains no ArgumentPlaceholder Records.
  3. Let proto be ? targetFunction.[[GetPrototypeOf]]().
  4. Let internalSlotsList be the internal slots listed in Table 2, plus [[Prototype]] and [[Extensible]].
  5. Let obj be ! MakeBasicObject(internalSlotsList).
  6. Set obj.[[Prototype]] to proto.
  7. Set obj.[[Call]] as described in 2.1.1.1.
  8. If IsConstructor(targetFunction) is true, then
    1. Set obj.[[Construct]] as described in 2.1.1.2.
  9. Set obj.[[BoundTargetFunction]] to targetFunction.
  10. Set obj.[[BoundThis]] to boundThis.
  11. Set obj.[[BoundArguments]] to boundArgs.
  12. Set obj.[[Kind]] to kind.
  13. Return obj.

2.1.1.4 ApplyPartialArguments ( boundArgs, argumentsList )

The abstract operation ApplyPartialArguments takes arguments boundArgs (a List of ECMAScript language values and ArgumentPlaceholder Records) and argumentsList (a List of ECMAScript language values). It performs the following steps when called:

  1. Let args be a new empty List.
  2. Let position be 0.
  3. Let maxPosition be 0.
  4. Let boundLen be the number of elements in boundArgs.
  5. Let argLen be the number of elements in argumentsList.
  6. Let i be 0;
  7. While i < boundLen, do
    1. Let arg be boundArgs[i].
    2. If arg is an ArgumentPlaceholder Record, then
      1. If arg.[[Kind]] is positional, then
        1. Set position to position + 1
      2. Else, if arg.[[Kind]] is ordinal, then
        1. Let ordinal be arg.[[Ordinal]].
        2. If ordinal + 1 > maxPosition, set maxPosition to ordinal + 1.
    3. Set i to i + 1.
  8. If position > maxPosition, set maxPosition to position.
  9. Let position be 0.
  10. Set i to 0.
  11. While i < boundLen, do
    1. Let arg be boundArgs[i].
    2. If arg is an ArgumentPlaceholder Record, then
      1. If arg.[[Kind]] is positional, then
        1. If position < argLen, set arg to argumentsList[position].
        2. Else, set arg to undefined.
        3. Set position to position + 1.
        4. Append arg to args.
      2. Else, if arg.[[Kind]] is ordinal, then
        1. Let ordinal be arg.[[Ordinal]].
        2. If ordinal < argLen, set arg to argumentsList[ordinal].
        3. Else, set arg to undefined.
        4. Append arg to args.
      3. Else,
        1. Assert: arg.[[Kind]] is rest.
        2. Let restIndex be maxPosition.
        3. While restIndex < argLen, do
          1. Set arg to argumentsList[restIndex].
          2. Append arg to args.
          3. Set restIndex to restIndex + 1.
    3. Else,
      1. Append arg to args.
    4. Set i to i + 1.
  12. Return args.

3 ECMAScript Language: Expressions

3.1 Operations on Objects

3.1.1 PartialCall ( F, V, argumentsList )

The abstract operation PartialCall takes arguments F (an ECMAScript language value), V (an ECMAScript language value), and argumentsList (a List of ECMAScript language values and ArgumentPlaceholder Records). It performs the following steps when called:

  1. If IsCallable(F) is false, throw a TypeError exception.
  2. Let bound be BoundFunctionCreate(F, V, argumentsList, partial-call).
  3. Let L be 0.
  4. Let maxOrdinal be 0.
  5. For each element of argumentsList, do
    1. If arg is an ArgumentPlaceholder Record, then
      1. If arg.[[Kind]] is positional, set L to L + 1.
      2. Else, if arg.[[Kind]] is ordinal, then
        1. Let ordinal be arg.[[Ordinal]]
        2. If ordinal + 1 > maxOrdinal, set maxOrdinal to ordinal + 1.
  6. If maxOrdinal > L, set L to maxOrdinal.
  7. Perform ! SetFunctionLength(bound, L).
  8. Let targetName be ? Get (F, "name").
  9. If Type(targetName) is not String, set targetName to the empty String.
  10. Perform SetFunctionName(bound, targetName, "bound").
  11. Return bound.

3.1.2 PartialConstruct ( F, argumentsList )

The abstract operation PartialConstruct takes arguments F (a constructor) and argumentsList (a List of ECMAScript language values and ArgumentPlaceholder Records). It performs the following steps when called:

  1. If IsConstructor(F) is false, throw a TypeError exception.
  2. Let bound be BoundFunctionCreate(F, null, argumentsList, partial-construct).
  3. Let L be 0.
  4. Let maxOrdinal be 0.
  5. For each element of argumentsList, do
    1. If arg is an ArgumentPlaceholder Record, then
      1. If arg.[[Kind]] is positional, set L to L + 1.
      2. Else, if arg.[[Kind]] is ordinal, then
        1. Let ordinal be arg.[[Ordinal]]
        2. If ordinal + 1 > maxOrdinal, set maxOrdinal to ordinal + 1.
  6. If maxOrdinal > L, set L to maxOrdinal.
  7. Perform ! SetFunctionLength(bound, L).
  8. Let targetName be ? Get (F, "name").
  9. If Type(targetName) is not String, set targetName to the empty String.
  10. Perform SetFunctionName(bound, targetName, "bound").
  11. Return bound.

3.2 Left-Hand-Side Expressions

Syntax

MemberExpression[Yield, Await] : PrimaryExpression[?Yield, ?Await] MemberExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] MemberExpression[?Yield, ?Await] . IdentifierName MemberExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] SuperProperty[?Yield, ?Await] MetaProperty new MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await, +Partial] MemberExpression[?Yield, ?Await] . PrivateIdentifier SuperProperty[Yield, Await] : super [ Expression[+In, ?Yield, ?Await] ] super . IdentifierName MetaProperty : NewTarget ImportMeta NewTarget : new . target ImportMeta : import . meta NewExpression[Yield, Await] : MemberExpression[?Yield, ?Await] new NewExpression[?Yield, ?Await] CallExpression[Yield, Await] : CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await] SuperCall[?Yield, ?Await] ImportCall[?Yield, ?Await] CallExpression[?Yield, ?Await] Arguments[?Yield, ?Await, +Partial] CallExpression[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] CallExpression[?Yield, ?Await] . IdentifierName CallExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] CallExpression[?Yield, ?Await] . PrivateIdentifier SuperCall[Yield, Await] : super Arguments[?Yield, ?Await] ImportCall[Yield, Await] : import ( AssignmentExpression[+In, ?Yield, ?Await] ) Arguments[Yield, Await, Partial] : ( ) ( ArgumentList[?Yield, ?Await, ~Partial] ) ( ArgumentList[?Yield, ?Await, ~Partial] , ) [+Partial] [no LineTerminator here] ~ ( ) [+Partial] [no LineTerminator here] ~ ( ArgumentList[?Yield, ?Await, +Partial] ) [+Partial] [no LineTerminator here] ~ ( ArgumentList[?Yield, ?Await, +Partial] , ) ArgumentList[Yield, Await, Partial] : AssignmentExpression[+In, ?Yield, ?Await] ... AssignmentExpression[+In, ?Yield, ?Await] ArgumentList[?Yield, ?Await, ?Partial] , AssignmentExpression[+In, ?Yield, ?Await] ArgumentList[?Yield, ?Await, ?Partial] , ... AssignmentExpression[+In, ?Yield, ?Await] [+Partial] ? DecimalIntegerLiteralopt [+Partial]... [+Partial] ArgumentList[?Yield, ?Await, ?Partial] , ? DecimalIntegerLiteralopt [+Partial] ArgumentList[?Yield, ?Await, ?Partial] , ... OptionalChain[Yield, Await] : ?. Arguments[?Yield, ?Await, +Partial] ?. [ Expression[+In, ?Yield, ?Await] ] ?. IdentifierName ?. TemplateLiteral[?Yield, ?Await, +Tagged] ?. PrivateIdentifier OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await, +Partial] OptionalChain[?Yield, ?Await] [ Expression[+In, ?Yield, ?Await] ] OptionalChain[?Yield, ?Await] . IdentifierName OptionalChain[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] OptionalChain[?Yield, ?Await] . PrivateIdentifier LeftHandSideExpression[Yield, Await] : NewExpression[?Yield, ?Await] CallExpression[?Yield, ?Await] OptionalExpression[?Yield, ?Await]

Supplemental Syntax

When processing an instance of the production
CallExpression : CoverCallExpressionAndAsyncArrowHead
the interpretation of CoverCallExpressionAndAsyncArrowHead is refined using the following grammar:

CallMemberExpression[Yield, Await, Partial] : MemberExpression[?Yield, ?Await] Arguments[?Yield, ?Await, ?Partial]

3.2.1 Static Semantics

3.2.1.1 Static Semantics: Early Errors

Arguments : ~ ( ArgumentList ) ~ ( ArgumentList , )
  • It is a Syntax Error if ContainsDuplicateRestPlaceholder for ArgumentList is true.

3.2.1.2 Static Semantics: ContainsDuplicateRestPlaceholder ( [ found ] )

ArgumentList : AssignmentExpression ... AssignmentExpression ? DecimalIntegerLiteralopt
  1. Return false.
ArgumentList : ArgumentList , AssignmentExpression ArgumentList , ... AssignmentExpression ArgumentList , ? DecimalIntegerLiteralopt
  1. If found is not present, let found be false.
  2. Return the result of ContainsDuplicateRestPlaceholder for ArgumentList with argument found.
ArgumentList : ...
  1. If found is not present, return false.
  2. If found is true, return true.
  3. Return false.
ArgumentList : ArgumentList , ...
  1. If found is not present, let found be false.
  2. If found is true, return true.
  3. Return the result of ContainsDuplicateRestPlaceholder for ArgumentList with argument true.

3.2.1.3 Static Semantics: IsPartialArguments

Arguments : ( ) ( ArgumentList ) ( ArgumentList , )
  1. Return false.
Arguments : ~ ( ) ~ ( ArgumentList ) ~ ( ArgumentList , )
  1. Return true.

3.2.2 The new Operator

3.2.2.1 Runtime Semantics: Evaluation

3.2.2.1.1 EvaluateNew ( constructExpr, arguments )

The abstract operation EvaluateNew takes arguments constructExpr (a NewExpression Parse Node or a MemberExpression Parse Node) and arguments (empty or an Arguments Parse Node). It performs the following steps when called:

  1. Let ref be the result of evaluating constructExpr.
  2. Let constructor be ? GetValue(ref).
  3. If arguments is empty, let argList be a new empty List., then
    1. Let isPartial be false.
    2. Let argList be a new empty List.
  4. Else,
    1. Let isPartial be ! IsPartialArguments of arguments.
    2. Let argList be ? ArgumentListEvaluation of arguments with argument isPartial.
  5. If IsConstructor(constructor) is false, throw a TypeError exception.
  6. If isPartial is true, return ? PartialConstruct(constructor, argList).
  7. Return ? Construct(constructor, argList).

3.2.3 Function Calls

3.2.3.1 Runtime Semantics: Evaluation

CallExpression : CoverCallExpressionAndAsyncArrowHead
  1. Let expr be the CallMemberExpression that is covered by CoverCallExpressionAndAsyncArrowHead.
  2. Let memberExpr be the MemberExpression of expr.
  3. Let arguments be the Arguments of expr.
  4. Let isPartial be ! IsPartialArguments of Arguments.
  5. Let ref be the result of evaluating memberExpr.
  6. Let func be ? GetValue(ref).
  7. If ref is a Reference Record, IsPropertyReference(ref) is false, isPartial is false, and ref.[[ReferencedName]] is "eval", then
    1. If SameValue(func, %eval%) is true, then
      1. Let argList be ? ArgumentListEvaluation of arguments with argument false.
      2. If argList has no elements, return undefined.
      3. Let evalArg be the first element of argList.
      4. If the source code matching this CallExpression is strict mode code, let strictCaller be true. Otherwise let strictCaller be false.
      5. Let evalRealm be the current Realm Record.
      6. Return ? PerformEval(evalArg, evalRealm, strictCaller, true).
  8. Let thisCall be this CallExpression.
  9. Let tailCall be IsInTailPosition(thisCall).
  10. Return ? EvaluateCall(func, ref, arguments, tailCall).

3.2.3.2 EvaluateCall ( func, ref, arguments, tailPosition )

The abstract operation EvaluateCall takes arguments func (an ECMAScript language value), ref (an ECMAScript language value or a Reference Record), arguments (a Parse Node), and tailPosition (a Boolean). It performs the following steps when called:

  1. If ref is a Reference Record, then
    1. If IsPropertyReference(ref) is true, then
      1. Let thisValue be GetThisValue(ref).
    2. Else,
      1. Let refEnv be ref.[[Base]].
      2. Assert: refEnv is an Environment Record.
      3. Let thisValue be refEnv.WithBaseObject().
  2. Else,
    1. Let thisValue be undefined.
  3. Let isPartial be ! IsPartialArguments of arguments.
  4. Let argList be ? ArgumentListEvaluation of arguments with argument isPartial.
  5. If Type(func) is not Object, throw a TypeError exception.
  6. If IsCallable(func) is false, throw a TypeError exception.
  7. If tailPosition is true and isPartial is false, perform PrepareForTailCall().
  8. If isPartial is true, let result be PartialCall(func, thisValue, argList).
  9. Else, Letlet result be Call(func, thisValue, argList).
  10. Assert: If result is not an abrupt completion, then Type(result) is an ECMAScript language type.
  11. Return result.

3.2.4 Argument Lists

Note

The evaluation of an argument list produces a List of values.

3.2.4.1 Runtime Semantics: ArgumentListEvaluation ( isPartial )

Arguments : ( )
  1. Assert: isPartial is false.
  2. Return a new empty List.
Arguments : ~ ( )
  1. Assert: isPartial is true.
  2. Return a new empty List.
ArgumentList : AssignmentExpression
  1. Let ref be the result of evaluating AssignmentExpression.
  2. Let arg be ? GetValue(ref).
  3. Return a List whose sole element is arg.
ArgumentList : ... AssignmentExpression
  1. Let list be a new empty List.
  2. Let spreadRef be the result of evaluating AssignmentExpression.
  3. Let spreadObj be ? GetValue(spreadRef).
  4. Let iteratorRecord be ? GetIterator(spreadObj).
  5. Repeat,
    1. Let next be ? IteratorStep(iteratorRecord).
    2. If next is false, return list.
    3. Let nextArg be ? IteratorValue(next).
    4. Append nextArg as the last element of list.
ArgumentList : ArgumentList , AssignmentExpression
  1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList with argument isPartial.
  2. Let ref be the result of evaluating AssignmentExpression.
  3. Let arg be ? GetValue(ref).
  4. Return the list-concatenation of precedingArgs and « arg ».
ArgumentList : ArgumentList , ... AssignmentExpression
  1. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList with argument isPartial.
  2. Let spreadRef be the result of evaluating AssignmentExpression.
  3. Let iteratorRecord be ? GetIterator(? GetValue(spreadRef)).
  4. Repeat,
    1. Let next be ? IteratorStep(iteratorRecord).
    2. If next is false, return precedingArgs.
    3. Let nextArg be ? IteratorValue(next).
    4. Append nextArg as the last element of precedingArgs.
ArgumentList : ?
  1. Assert: isPartial is true.
  2. Let arg be an ArgumentPlaceholder Record { [[Kind]]: positional, [[Ordinal]]: -1 }.
  3. Return a List whose sole element is arg.
ArgumentList : ? DecimalIntegerLiteral
  1. Assert: isPartial is true.
  2. Let arg be an ArgumentPlaceholder Record { [[Kind]]: ordinal, [[Ordinal]]: RoundMVResult(MV of DecimalIntegerLiteral) }.
  3. Return a List whose sole element is arg.
ArgumentList : ...
  1. Assert: isPartial is true.
  2. Let arg be an ArgumentPlaceholder Record { [[Kind]]: rest, [[Ordinal]]: -1 }.
  3. Return a List whose sole element is arg.
ArgumentList : ArgumentList , ?
  1. Assert: isPartial is true.
  2. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList with argument true.
  3. Let arg be an ArgumentPlaceholder Record { [[Kind]]: positional, [[Ordinal]]: -1 }.
  4. Return the list-concatenation of precedingArgs and « arg ».
ArgumentList : ArgumentList , ? DecimalIntegerLiteral
  1. Assert: isPartial is true.
  2. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList with argument true.
  3. Let arg be an ArgumentPlaceholder Record { [[Kind]]: ordinal, [[Ordinal]]: RoundMVResult(MV of DecimalIntegerLiteral) }.
  4. Return the list-concatenation of precedingArgs and « arg ».
ArgumentList : ArgumentList , ...
  1. Assert: isPartial is true.
  2. Let precedingArgs be ? ArgumentListEvaluation of ArgumentList with argument true.
  3. Let arg be an ArgumentPlaceholder Record { [[Kind]]: rest, [[Ordinal]]: -1 }.
  4. Return the list-concatenation of precedingArgs and « arg ».
TemplateLiteral : NoSubstitutionTemplate
  1. Assert: isPartial is false.
  2. Let templateLiteral be this TemplateLiteral.
  3. Let siteObj be GetTemplateObject(templateLiteral).
  4. Return a List whose sole element is siteObj.
TemplateLiteral : SubstitutionTemplate
  1. Assert: isPartial is false.
  2. Let templateLiteral be this TemplateLiteral.
  3. Let siteObj be GetTemplateObject(templateLiteral).
  4. Let remaining be ? ArgumentListEvaluation of SubstitutionTemplate with argument false.
  5. Return the list-concatenation of « siteObj » and remaining.
SubstitutionTemplate : TemplateHead Expression TemplateSpans
  1. Assert: isPartial is false.
  2. Let firstSubRef be the result of evaluating Expression.
  3. Let firstSub be ? GetValue(firstSubRef).
  4. Let restSub be ? SubstitutionEvaluation of TemplateSpans.
  5. Assert: restSub is a possibly empty List.
  6. Return the list-concatenation of « firstSub » and restSub.

A Copyright & Software License

Copyright Notice

© 2021 Ron Buckton, Ecma International

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.