Skip to content

Commit 701d834

Browse files
committed
Make require alias binding atomic
1 parent c849e57 commit 701d834

2 files changed

Lines changed: 50 additions & 5 deletions

File tree

vibes/modules.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,17 @@ func isValidModuleAlias(name string) bool {
517517
}
518518

519519
func bindRequireAlias(root *Env, alias string, module Value) error {
520+
if err := validateRequireAliasBinding(root, alias, module); err != nil {
521+
return err
522+
}
523+
if alias == "" {
524+
return nil
525+
}
526+
root.Define(alias, module)
527+
return nil
528+
}
529+
530+
func validateRequireAliasBinding(root *Env, alias string, module Value) error {
520531
if alias == "" {
521532
return nil
522533
}
@@ -526,7 +537,6 @@ func bindRequireAlias(root *Env, alias string, module Value) error {
526537
}
527538
return fmt.Errorf("require: alias %q already defined", alias)
528539
}
529-
root.Define(alias, module)
530540
return nil
531541
}
532542

@@ -610,12 +620,14 @@ func builtinRequire(exec *Execution, receiver Value, args []Value, kwargs map[st
610620
}
611621
}
612622

613-
bindModuleExportsWithoutOverwrite(exec.root, exports)
614-
615623
exportsVal := NewObject(exports)
616-
exec.modules[entry.key] = exportsVal
617-
if err := bindRequireAlias(exec.root, alias, exportsVal); err != nil {
624+
if err := validateRequireAliasBinding(exec.root, alias, exportsVal); err != nil {
618625
return NewNil(), err
619626
}
627+
bindModuleExportsWithoutOverwrite(exec.root, exports)
628+
exec.modules[entry.key] = exportsVal
629+
if alias != "" {
630+
exec.root.Define(alias, exportsVal)
631+
}
620632
return exportsVal, nil
621633
}

vibes/modules_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,39 @@ end`)
125125
}
126126
}
127127

128+
func TestRequireAliasConflictDoesNotLeakExportsWhenRescued(t *testing.T) {
129+
engine := MustNewEngine(Config{ModulePaths: []string{filepath.Join("testdata", "modules")}})
130+
131+
script, err := engine.Compile(`def helpers(value)
132+
value
133+
end
134+
135+
def run(value)
136+
begin
137+
require("helper", as: "helpers")
138+
rescue
139+
nil
140+
end
141+
142+
begin
143+
double(value)
144+
rescue
145+
"missing"
146+
end
147+
end`)
148+
if err != nil {
149+
t.Fatalf("compile failed: %v", err)
150+
}
151+
152+
result, err := script.Call(context.Background(), "run", []Value{NewInt(3)}, CallOptions{})
153+
if err != nil {
154+
t.Fatalf("call failed: %v", err)
155+
}
156+
if result.Kind() != KindString || result.String() != "missing" {
157+
t.Fatalf("expected leaked export lookup to fail, got %#v", result)
158+
}
159+
}
160+
128161
func TestRequirePreservesModuleLocalResolution(t *testing.T) {
129162
engine := MustNewEngine(Config{ModulePaths: []string{filepath.Join("testdata", "modules")}})
130163

0 commit comments

Comments
 (0)