Skip to content

Commit

Permalink
REALMC-11535: add destructuring, rest, spread, default functions (#45)
Browse files Browse the repository at this point in the history
* Avoid calling iterator's return method again when it throws
* Fixed dynamic variable resolution when a parent lexical binding exists
* Support dereferencing in ExportTo(). Closes dop251#300
* Removed the mention of the es6 branch, updated the section about new features development.
* Destructuring assignments, rest and spread properties, default function parameters (dop251#303)
* Fixed argument variable reference resolution in stashless functions
* Fixed binding order for global function declarations when there are duplicate names. Fixes dop251#344.
* Make sure variables dynamically bound in parameter scope conflict with parameter bindings. See dop251#305.
* Fixed setting the stack pointer in enterFuncBody. Fixes dop251#309
* Support for assignment patterns in for-in and for-of loops. Support for spread elements in call arguments. Fixed various issues related to destructuring assignments. See dop251#305.
  • Loading branch information
Gabri3l authored Jan 31, 2022
1 parent 8366332 commit ef06792
Show file tree
Hide file tree
Showing 23 changed files with 3,256 additions and 572 deletions.
15 changes: 6 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,15 @@ and it includes an event loop.

### Can you implement (feature X from ES6 or higher)?

Some ES6 functionality has been implemented. So far this is mostly built-ins, not syntax enhancements.
See https://github.com/dop251/goja/milestone/1 for more details.

The ongoing work is done in the es6 branch which is merged into master when appropriate. Every commit
in this branch represents a relatively stable state (i.e. it compiles and passes all enabled tc39 tests),
however because the version of tc39 tests I use is quite old, it may be not as well tested as the ES5.1
functionality. Because ES6 is a superset of ES5.1 it should not break your existing code.

I will be adding features in their dependency order and as quickly as my time allows. Please do not ask
I will be adding features in their dependency order and as quickly as time permits. Please do not ask
for ETAs. Features that are open in the [milestone](https://github.com/dop251/goja/milestone/1) are either in progress
or will be worked on next.

The ongoing work is done in separate feature branches which are merged into master when appropriate.
Every commit in these branches represents a relatively stable state (i.e. it compiles and passes all enabled tc39 tests),
however because the version of tc39 tests I use is quite old, it may be not as well tested as the ES5.1 functionality. Because there are (usually) no major breaking changes between ECMAScript revisions
it should not break your existing code. You are encouraged to give it a try and report any bugs found. Please do not submit fixes though without discussing it first, as the code could be changed in the meantime.

### How do I contribute?

Before submitting a pull request please make sure that:
Expand Down
2 changes: 1 addition & 1 deletion array.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ func (a *arrayObject) _defineIdxProperty(idx uint32, desc PropertyDescriptor, th
a.propValueCount++
}
} else {
a.val.self.(*sparseArrayObject).add(uint32(idx), prop)
a.val.self.(*sparseArrayObject).add(idx, prop)
}
}
return ok
Expand Down
164 changes: 121 additions & 43 deletions ast/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ import (
"github.com/dop251/goja/unistring"
)

type PropertyKind string

const (
PropertyKindValue PropertyKind = "value"
PropertyKindGet PropertyKind = "get"
PropertyKindSet PropertyKind = "set"
PropertyKindMethod PropertyKind = "method"
)

// All nodes implement the Node interface.
type Node interface {
Idx0() file.Idx // The index of the first character belonging to the node
Expand All @@ -32,12 +41,34 @@ type (
_expressionNode()
}

BindingTarget interface {
Expression
_bindingTarget()
}

Binding struct {
Target BindingTarget
Initializer Expression
}

Pattern interface {
BindingTarget
_pattern()
}

ArrayLiteral struct {
LeftBracket file.Idx
RightBracket file.Idx
Value []Expression
}

ArrayPattern struct {
LeftBracket file.Idx
RightBracket file.Idx
Elements []Expression
Rest Expression
}

AssignExpression struct {
Operator token.Token
Left Expression
Expand Down Expand Up @@ -127,18 +158,40 @@ type (
Value []Property
}

ObjectPattern struct {
LeftBrace file.Idx
RightBrace file.Idx
Properties []Property
Rest Expression
}

ParameterList struct {
Opening file.Idx
List []*Identifier
List []*Binding
Rest Expression
Closing file.Idx
}

Property struct {
Property interface {
Expression
_property()
}

PropertyShort struct {
Name Identifier
Initializer Expression
}

PropertyKeyed struct {
Key Expression
Kind string
Kind PropertyKind
Value Expression
}

SpreadElement struct {
Expression
}

RegExpLiteral struct {
Idx file.Idx
Literal string
Expand Down Expand Up @@ -167,12 +220,6 @@ type (
Postfix bool
}

VariableExpression struct {
Name unistring.String
Idx file.Idx
Initializer Expression
}

MetaProperty struct {
Meta, Property *Identifier
Idx file.Idx
Expand Down Expand Up @@ -201,8 +248,13 @@ func (*SequenceExpression) _expressionNode() {}
func (*StringLiteral) _expressionNode() {}
func (*ThisExpression) _expressionNode() {}
func (*UnaryExpression) _expressionNode() {}
func (*VariableExpression) _expressionNode() {}
func (*MetaProperty) _expressionNode() {}
func (*ObjectPattern) _expressionNode() {}
func (*ArrayPattern) _expressionNode() {}
func (*Binding) _expressionNode() {}

func (*PropertyShort) _expressionNode() {}
func (*PropertyKeyed) _expressionNode() {}

// ========= //
// Statement //
Expand Down Expand Up @@ -323,13 +375,13 @@ type (

VariableStatement struct {
Var file.Idx
List []*VariableExpression
List []*Binding
}

LexicalDeclaration struct {
Idx file.Idx
Token token.Token
List []*VariableExpression
List []*Binding
}

WhileStatement struct {
Expand Down Expand Up @@ -382,7 +434,7 @@ func (*FunctionDeclaration) _statementNode() {}
type (
VariableDeclaration struct {
Var file.Idx
List []*VariableExpression
List []*Binding
}
)

Expand All @@ -397,7 +449,7 @@ type (

ForLoopInitializerVarDeclList struct {
Var file.Idx
List []*VariableExpression
List []*Binding
}

ForLoopInitializerLexicalDecl struct {
Expand All @@ -409,22 +461,13 @@ type (
}

ForIntoVar struct {
Binding *VariableExpression
}

ForBinding interface {
_forBinding()
}

BindingIdentifier struct {
Idx file.Idx
Name unistring.String
Binding *Binding
}

ForDeclaration struct {
Idx file.Idx
IsConst bool
Binding ForBinding
Target BindingTarget
}

ForIntoExpression struct {
Expand All @@ -440,7 +483,19 @@ func (*ForIntoVar) _forInto() {}
func (*ForDeclaration) _forInto() {}
func (*ForIntoExpression) _forInto() {}

func (*BindingIdentifier) _forBinding() {}
func (*ArrayPattern) _pattern() {}
func (*ArrayPattern) _bindingTarget() {}

func (*ObjectPattern) _pattern() {}
func (*ObjectPattern) _bindingTarget() {}

func (*BadExpression) _bindingTarget() {}

func (*PropertyShort) _property() {}
func (*PropertyKeyed) _property() {}
func (*SpreadElement) _property() {}

func (*Identifier) _bindingTarget() {}

// ==== //
// Node //
Expand All @@ -459,6 +514,8 @@ type Program struct {
// ==== //

func (self *ArrayLiteral) Idx0() file.Idx { return self.LeftBracket }
func (self *ArrayPattern) Idx0() file.Idx { return self.LeftBracket }
func (self *ObjectPattern) Idx0() file.Idx { return self.LeftBrace }
func (self *AssignExpression) Idx0() file.Idx { return self.Left.Idx0() }
func (self *BadExpression) Idx0() file.Idx { return self.From }
func (self *BinaryExpression) Idx0() file.Idx { return self.Left.Idx0() }
Expand All @@ -478,7 +535,6 @@ func (self *SequenceExpression) Idx0() file.Idx { return self.Sequence[0].Idx
func (self *StringLiteral) Idx0() file.Idx { return self.Idx }
func (self *ThisExpression) Idx0() file.Idx { return self.Idx }
func (self *UnaryExpression) Idx0() file.Idx { return self.Idx }
func (self *VariableExpression) Idx0() file.Idx { return self.Idx }
func (self *MetaProperty) Idx0() file.Idx { return self.Idx }

func (self *BadStatement) Idx0() file.Idx { return self.From }
Expand All @@ -505,14 +561,18 @@ func (self *WhileStatement) Idx0() file.Idx { return self.While }
func (self *WithStatement) Idx0() file.Idx { return self.With }
func (self *LexicalDeclaration) Idx0() file.Idx { return self.Idx }
func (self *FunctionDeclaration) Idx0() file.Idx { return self.Function.Idx0() }
func (self *Binding) Idx0() file.Idx { return self.Target.Idx0() }

func (self *ForLoopInitializerVarDeclList) Idx0() file.Idx { return self.List[0].Idx0() }
func (self *PropertyShort) Idx0() file.Idx { return self.Name.Idx }
func (self *PropertyKeyed) Idx0() file.Idx { return self.Key.Idx0() }

// ==== //
// Idx1 //
// ==== //

func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket }
func (self *ArrayLiteral) Idx1() file.Idx { return self.RightBracket + 1 }
func (self *ArrayPattern) Idx1() file.Idx { return self.RightBracket + 1 }
func (self *AssignExpression) Idx1() file.Idx { return self.Right.Idx1() }
func (self *BadExpression) Idx1() file.Idx { return self.To }
func (self *BinaryExpression) Idx1() file.Idx { return self.Right.Idx1() }
Expand All @@ -526,23 +586,18 @@ func (self *Identifier) Idx1() file.Idx { return file.Idx(int(self.Id
func (self *NewExpression) Idx1() file.Idx { return self.RightParenthesis + 1 }
func (self *NullLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + 4) } // "null"
func (self *NumberLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace }
func (self *ObjectLiteral) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *ObjectPattern) Idx1() file.Idx { return self.RightBrace + 1 }
func (self *RegExpLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[0].Idx1() }
func (self *SequenceExpression) Idx1() file.Idx { return self.Sequence[len(self.Sequence)-1].Idx1() }
func (self *StringLiteral) Idx1() file.Idx { return file.Idx(int(self.Idx) + len(self.Literal)) }
func (self *ThisExpression) Idx1() file.Idx { return self.Idx }
func (self *ThisExpression) Idx1() file.Idx { return self.Idx + 4 }
func (self *UnaryExpression) Idx1() file.Idx {
if self.Postfix {
return self.Operand.Idx1() + 2 // ++ --
}
return self.Operand.Idx1()
}
func (self *VariableExpression) Idx1() file.Idx {
if self.Initializer == nil {
return file.Idx(int(self.Idx) + len(self.Name) + 1)
}
return self.Initializer.Idx1()
}
func (self *MetaProperty) Idx1() file.Idx {
return self.Property.Idx1()
}
Expand All @@ -565,16 +620,39 @@ func (self *IfStatement) Idx1() file.Idx {
}
return self.Consequent.Idx1()
}
func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 }
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
func (self *ReturnStatement) Idx1() file.Idx { return self.Return }
func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
func (self *ThrowStatement) Idx1() file.Idx { return self.Throw }
func (self *TryStatement) Idx1() file.Idx { return self.Try }
func (self *LabelledStatement) Idx1() file.Idx { return self.Colon + 1 }
func (self *Program) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
func (self *ReturnStatement) Idx1() file.Idx { return self.Return + 6 }
func (self *SwitchStatement) Idx1() file.Idx { return self.Body[len(self.Body)-1].Idx1() }
func (self *ThrowStatement) Idx1() file.Idx { return self.Argument.Idx1() }
func (self *TryStatement) Idx1() file.Idx {
if self.Finally != nil {
return self.Finally.Idx1()
}
if self.Catch != nil {
return self.Catch.Idx1()
}
return self.Body.Idx1()
}
func (self *VariableStatement) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
func (self *WhileStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *WithStatement) Idx1() file.Idx { return self.Body.Idx1() }
func (self *LexicalDeclaration) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }
func (self *FunctionDeclaration) Idx1() file.Idx { return self.Function.Idx1() }
func (self *Binding) Idx1() file.Idx {
if self.Initializer != nil {
return self.Initializer.Idx1()
}
return self.Target.Idx1()
}

func (self *ForLoopInitializerVarDeclList) Idx1() file.Idx { return self.List[len(self.List)-1].Idx1() }

func (self *PropertyShort) Idx1() file.Idx {
if self.Initializer != nil {
return self.Initializer.Idx1()
}
return self.Name.Idx1()
}

func (self *PropertyKeyed) Idx1() file.Idx { return self.Value.Idx1() }
1 change: 1 addition & 0 deletions builtin_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ func (r *Runtime) arrayproto_splice(call FunctionCall) Value {
for k := int64(0); k < actualDeleteCount; k++ {
createDataPropertyOrThrow(a, intToValue(k), src.values[k+actualStart])
}
a.self.setOwnStr("length", intToValue(actualDeleteCount), true)
}
var values []Value
if itemCount < actualDeleteCount {
Expand Down
11 changes: 5 additions & 6 deletions builtin_function.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ repeat:
case *funcObject:
return newStringValue(f.src)
case *nativeFuncObject:
return newStringValue(fmt.Sprintf("function %s() { [native code] }", f.nameProp.get(call.This).toString()))
return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
case *boundFuncObject:
return newStringValue(fmt.Sprintf("function %s() { [native code] }", f.nameProp.get(call.This).toString()))
return newStringValue(fmt.Sprintf("function %s() { [native code] }", nilSafe(f.getStr("name", nil)).toString()))
case *lazyObject:
obj.self = f.create(obj)
goto repeat
Expand All @@ -48,9 +48,9 @@ repeat:
case *funcObject:
name = c.src
case *nativeFuncObject:
name = nilSafe(c.nameProp.get(call.This)).toString().String()
name = nilSafe(f.getStr("name", nil)).toString().String()
case *boundFuncObject:
name = nilSafe(c.nameProp.get(call.This)).toString().String()
name = nilSafe(f.getStr("name", nil)).toString().String()
case *lazyObject:
f.target.self = c.create(obj)
goto repeat2
Expand Down Expand Up @@ -189,8 +189,7 @@ func (r *Runtime) functionproto_bind(call FunctionCall) Value {
func (r *Runtime) initFunction() {
o := r.global.FunctionPrototype.self.(*nativeFuncObject)
o.prototype = r.global.ObjectPrototype
o.nameProp.value = stringEmpty

o._putProp("name", stringEmpty, false, false, true)
o._putProp("apply", r.newNativeFunc(r.functionproto_apply, nil, "apply", nil, 2), true, false, true)
o._putProp("bind", r.newNativeFunc(r.functionproto_bind, nil, "bind", nil, 1), true, false, true)
o._putProp("call", r.newNativeFunc(r.functionproto_call, nil, "call", nil, 1), true, false, true)
Expand Down
Loading

0 comments on commit ef06792

Please sign in to comment.