-
Notifications
You must be signed in to change notification settings - Fork 62
Fix multiple object comprehension bugs #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
05ed13c
280b679
2b76aeb
64edef1
ee94db1
7a281cb
856c193
3239399
1c51f11
55bcb26
fd038d8
b74a493
fd3e9f4
cb5593e
bd4e1b2
f5e40c2
4867920
63cad73
7df617e
e06e7a7
5cd4ed8
0b2fce2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -680,15 +680,15 @@ class Evaluator( | |
| visitExpr(k) match { | ||
| case Val.Str(_, k1) => k1 | ||
| case Val.Null(_) => null | ||
| case x => | ||
| Error.fail( | ||
| s"Field name must be string or null, not ${x.prettyName}", | ||
| pos | ||
| ) | ||
| case x => fieldNameTypeError(x, pos) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private def fieldNameTypeError(fieldName: Val, pos: Position): Nothing = { | ||
| Error.fail(s"Field name must be string or null, not ${fieldName.prettyName}", pos) | ||
| } | ||
|
|
||
| def visitMethod(rhs: Expr, params: Params, outerPos: Position)(implicit | ||
| scope: ValScope): Val.Func = | ||
| new Val.Func(outerPos, scope, params) { | ||
|
|
@@ -697,20 +697,16 @@ class Evaluator( | |
| override def evalDefault(expr: Expr, vs: ValScope, es: EvalScope) = visitExpr(expr)(vs) | ||
| } | ||
|
|
||
| def visitBindings( | ||
| bindings: Array[Bind], | ||
| scope: (Val.Obj, Val.Obj) => ValScope): Array[(Val.Obj, Val.Obj) => Lazy] = { | ||
| val arrF = new Array[(Val.Obj, Val.Obj) => Lazy](bindings.length) | ||
| def visitBindings(bindings: Array[Bind], scope: => ValScope): Array[Lazy] = { | ||
| val arrF = new Array[Lazy](bindings.length) | ||
| var i = 0 | ||
| while (i < bindings.length) { | ||
| val b = bindings(i) | ||
| arrF(i) = b.args match { | ||
| case null => | ||
| (self: Val.Obj, sup: Val.Obj) => | ||
| new LazyWithComputeFunc(() => visitExpr(b.rhs)(scope(self, sup))) | ||
| new LazyWithComputeFunc(() => visitExpr(b.rhs)(scope)) | ||
| case argSpec => | ||
| (self: Val.Obj, sup: Val.Obj) => | ||
| new LazyWithComputeFunc(() => visitMethod(b.rhs, argSpec, b.pos)(scope(self, sup))) | ||
| new LazyWithComputeFunc(() => visitMethod(b.rhs, argSpec, b.pos)(scope)) | ||
| } | ||
| i += 1 | ||
| } | ||
|
|
@@ -841,42 +837,37 @@ class Evaluator( | |
| def visitObjComp(e: ObjBody.ObjComp, sup: Val.Obj)(implicit scope: ValScope): Val.Obj = { | ||
| val binds = e.preLocals ++ e.postLocals | ||
| val compScope: ValScope = scope // .clearSuper | ||
|
|
||
| lazy val newSelf: Val.Obj = { | ||
| val builder = new java.util.LinkedHashMap[String, Val.Obj.Member] | ||
| for (s <- visitComp(e.first :: e.rest, Array(compScope))) { | ||
| lazy val newScope: ValScope = s.extend(newBindings, newSelf, null) | ||
|
|
||
| lazy val newBindings = visitBindings(binds, (self, sup) => newScope) | ||
|
|
||
| visitExpr(e.key)(s) match { | ||
| case Val.Str(_, k) => | ||
| val prev_length = builder.size() | ||
| builder.put( | ||
| k, | ||
| new Val.Obj.Member(e.plus, Visibility.Normal) { | ||
| def invoke(self: Val.Obj, sup: Val.Obj, fs: FileScope, ev: EvalScope): Val = | ||
| visitExpr(e.value)( | ||
| s.extend(newBindings, self, null) | ||
| ) | ||
| val builder = new java.util.LinkedHashMap[String, Val.Obj.Member] | ||
| for (s <- visitComp(e.first :: e.rest, Array(compScope))) { | ||
| visitExpr(e.key)(s) match { | ||
| case Val.Str(_, k) => | ||
| val prev_length = builder.size() | ||
| builder.put( | ||
| k, | ||
| new Val.Obj.Member(e.plus, Visibility.Normal) { | ||
| def invoke(self: Val.Obj, sup: Val.Obj, fs: FileScope, ev: EvalScope): Val = { | ||
| // There is a circular dependency between `newScope` and `newBindings` because | ||
| // bindings may refer to other bindings (e.g. chains of locals that build on | ||
| // each other): | ||
| lazy val newScope: ValScope = s.extend(newBindings, self, sup) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are two changes here:
|
||
| lazy val newBindings = visitBindings(binds, newScope) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is a little weird, it references each others. |
||
| visitExpr(e.value)(newScope) | ||
| } | ||
| ) | ||
| if (prev_length == builder.size() && settings.noDuplicateKeysInComprehension) { | ||
| Error.fail(s"Duplicate key ${k} in evaluated object comprehension.", e.pos); | ||
| } | ||
| case Val.Null(_) => // do nothing | ||
| case _ => | ||
| } | ||
| } | ||
| val valueCache = if (sup == null) { | ||
| Val.Obj.getEmptyValueCacheForObjWithoutSuper(builder.size()) | ||
| } else { | ||
| new java.util.HashMap[Any, Val]() | ||
| ) | ||
| if (prev_length == builder.size() && settings.noDuplicateKeysInComprehension) { | ||
| Error.fail(s"Duplicate key ${k} in evaluated object comprehension.", e.pos); | ||
| } | ||
| case Val.Null(_) => // do nothing | ||
| case x => fieldNameTypeError(x, e.pos) | ||
| } | ||
| new Val.Obj(e.pos, builder, false, null, sup, valueCache) | ||
| } | ||
|
|
||
| newSelf | ||
| val valueCache = if (sup == null) { | ||
| Val.Obj.getEmptyValueCacheForObjWithoutSuper(builder.size()) | ||
| } else { | ||
| new java.util.HashMap[Any, Val]() | ||
| } | ||
| new Val.Obj(e.pos, builder, false, null, sup, valueCache) | ||
| } | ||
|
|
||
| @tailrec | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,23 +18,11 @@ final class ValScope private (val bindings: Array[Lazy]) extends AnyVal { | |
|
|
||
| def length: Int = bindings.length | ||
|
|
||
| def extend( | ||
| newBindingsF: Array[(Val.Obj, Val.Obj) => Lazy] = null, | ||
| newSelf: Val.Obj, | ||
| newSuper: Val.Obj): ValScope = { | ||
| val by = if (newBindingsF == null) 2 else newBindingsF.length + 2 | ||
| val b = Arrays.copyOf(bindings, bindings.length + by) | ||
| def extend(newBindings: Array[Lazy], newSelf: Val.Obj, newSuper: Val.Obj): ValScope = { | ||
| val b = Arrays.copyOf(bindings, bindings.length + newBindings.length + 2) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bindings.length and newbindings.length can be extracted to a local variable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to leave this as-is for now, given that the other methods (including |
||
| b(bindings.length) = newSelf | ||
| b(bindings.length + 1) = newSuper | ||
| if (newBindingsF != null) { | ||
| var i = 0 | ||
| var j = bindings.length + 2 | ||
| while (i < newBindingsF.length) { | ||
| b(j) = newBindingsF(i).apply(newSelf, newSuper) | ||
| i += 1 | ||
| j += 1 | ||
| } | ||
| } | ||
| System.arraycopy(newBindings, 0, b, bindings.length + 2, newBindings.length) | ||
| new ValScope(b) | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.