Chromium Code Reviews| Index: pkg/compiler/lib/src/native/js.dart |
| diff --git a/pkg/compiler/lib/src/native/js.dart b/pkg/compiler/lib/src/native/js.dart |
| index 1cbe8da5c8fb978d81a05f35d7e040febc8b2e19..0d1e30fea308bf35fc840a50e882929abee299e9 100644 |
| --- a/pkg/compiler/lib/src/native/js.dart |
| +++ b/pkg/compiler/lib/src/native/js.dart |
| @@ -81,3 +81,179 @@ class SideEffectsVisitor extends js.BaseVisitor { |
| node.visitChildren(this); |
| } |
| } |
| + |
| + |
| +/// ThrowBehaviorVisitor generates a NativeThrowBehavior describing the |
| +/// exception behavior of a JavaScript expression. |
| +/// |
| +/// The result is semi-conservative, giving reasonable results for many simple |
| +/// JS fragments. The non-conservative part is the assumption that binary |
| +/// operators are used on 'good' operands thatdo not force arbirary code to be |
|
floitsch
2015/04/10 13:34:16
that do
sra1
2015/04/10 15:57:48
Done.
|
| +/// executed via conversions (valueOf() and toString() methods). |
| +/// |
| +/// In many cases a JS fragment has more precise behavior. In these cases the |
| +/// behavior should be described as a property of the JS fragment. |
| +/// |
| +class ThrowBehaviorVisitor extends js.BaseVisitor<NativeThrowBehavior> { |
| + |
| + ThrowBehaviorVisitor(); |
| + |
| + NativeThrowBehavior analyze(js.Node node) { |
| + return visit(node); |
| + } |
| + |
| + // TODO(sra): Add [sequence] functionality to NativeThrowBehavior. |
| + static NativeThrowBehavior sequence(NativeThrowBehavior first, |
|
floitsch
2015/04/10 13:34:16
Add comment explaining what this is.
(probably my
sra1
2015/04/10 15:57:48
Done.
|
| + NativeThrowBehavior second) { |
| + if (first == NativeThrowBehavior.MUST) return first; |
| + if (second == NativeThrowBehavior.MUST) return second; |
| + if (second == NativeThrowBehavior.NEVER) return first; |
| + if (first == NativeThrowBehavior.NEVER) return second; |
| + // Both are one of MAY or MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS. |
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + // TODO(sra): Add [choice] functionality to NativeThrowBehavior. |
| + static NativeThrowBehavior choice(NativeThrowBehavior first, |
| + NativeThrowBehavior second) { |
| + if (first == second) return first; // Both paths have same behaviour. |
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + NativeThrowBehavior visit(js.Node node) { |
| + return node.accept(this); |
| + } |
| + |
| + NativeThrowBehavior visitNode(js.Node node) { |
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + NativeThrowBehavior visitLiteral(js.Literal node) { |
| + return NativeThrowBehavior.NEVER; |
| + } |
| + |
| + NativeThrowBehavior visitInterpolatedExpression(js.InterpolatedNode node) { |
| + return NativeThrowBehavior.NEVER; |
| + } |
| + |
| + NativeThrowBehavior visitInterpolatedSelector(js.InterpolatedNode node) { |
| + return NativeThrowBehavior.NEVER; |
| + } |
| + |
| + NativeThrowBehavior visitObjectInitializer(js.ObjectInitializer node) { |
| + NativeThrowBehavior result = NativeThrowBehavior.NEVER; |
| + for (js.Property property in node.properties) { |
| + result = sequence(result, visit(property)); |
| + } |
| + return result; |
| + } |
| + |
| + NativeThrowBehavior visitProperty(js.Property node) { |
| + return sequence(visit(node.name), visit(node.value)); |
| + } |
| + |
| + NativeThrowBehavior visitAssignment(js.Assignment node) { |
| + // TODO(sra): Can we make "#.p = #" be null(1)? |
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + NativeThrowBehavior visitCall(js.Call node) { |
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + NativeThrowBehavior visitNew(js.New node) { |
| + // TODO(sra): `new Array(x)` where `x` is is a small number. |
|
floitsch
2015/04/10 13:34:16
-is-
sra1
2015/04/10 15:57:48
Done.
|
| + return NativeThrowBehavior.MAY; |
| + } |
| + |
| + NativeThrowBehavior visitBinary(js.Binary node) { |
| + NativeThrowBehavior left = visit(node.left); |
| + NativeThrowBehavior right = visit(node.right); |
| + switch (node.op) { |
| + // We make the non-conservative assumption that these operations are not |
| + // used in ways that force calling arbitrary code via valueOf or |
| + // toString(). |
| + case "*": |
| + case "/": |
| + case "%": |
| + case "+": |
| + case "-": |
| + case "<<": |
| + case ">>": |
| + case ">>>": |
| + case "<": |
| + case ">": |
| + case "<=": |
| + case ">=": |
| + case "==": |
| + case "===": |
| + case "!=": |
| + case "!==": |
| + case "&": |
| + case "^": |
| + case "|": |
| + return sequence(left, right); |
| + |
| + case ',': |
| + return sequence(left, right); |
| + |
| + case "&&": |
| + case "||": |
| + return choice(left, sequence(left, right)); |
| + |
| + case "instanceof": |
| + case "in": |
| + default: |
| + return NativeThrowBehavior.MAY; |
| + } |
| + } |
| + |
| + NativeThrowBehavior visitThrow(js.Throw node) { |
| + return NativeThrowBehavior.MUST; |
| + } |
| + |
| + NativeThrowBehavior visitPrefix(js.Prefix node) { |
| + NativeThrowBehavior result = visit(node.argument); |
| + switch (node.op) { |
| + case '!': |
| + case '~': |
| + case 'void': |
| + case 'typeof': |
| + return result; |
| + default: |
| + return NativeThrowBehavior.MAY; |
| + } |
| + } |
| + |
| + NativeThrowBehavior visitVariableUse(js.VariableUse node) { |
| + // We could get a ReferenceError unless the variable is in scope. The AST |
| + // could distinguish in-scope and out-of scope references. For JS |
| + // fragments, the only use of VariableUse should be for gloabl references. |
|
floitsch
2015/04/10 13:34:16
global
sra1
2015/04/10 15:57:48
Done.
|
| + // Certain global names are almost certainly not reference errors, e.g |
| + // 'Array'. |
| + switch (node.name) { |
| + case 'Array': |
|
floitsch
2015/04/10 13:34:16
add
case 'Object':
sra1
2015/04/10 15:57:49
Done, but it is not really useful, since most uses
|
| + return NativeThrowBehavior.NEVER; |
| + default: |
| + return NativeThrowBehavior.MAY; |
| + } |
| + } |
| + |
| + NativeThrowBehavior visitAccess(js.PropertyAccess node) { |
| + // TODO(sra): We need a representation where the nsm guard behaviour is |
| + // maintained when combined with other throwing behaviour. |
| + js.Node receiver = node.receiver; |
| + NativeThrowBehavior first = visit(receiver); |
| + NativeThrowBehavior second = visit(node.selector); |
| + |
| + if (receiver is js.InterpolatedExpression && |
| + receiver.isPositional && |
| + receiver.nameOrPosition == 0) { |
| + first = NativeThrowBehavior.MAY_THROW_ONLY_ON_FIRST_ARGUMENT_ACCESS; |
| + } else { |
| + first = NativeThrowBehavior.MAY; |
| + } |
| + |
| + return sequence(first, second); |
| + } |
| +} |